aliasing of account fields

This commit is contained in:
Priec
2026-08-12 20:17:57 +02:00
parent f8a333e540
commit 49323b58d6
7 changed files with 39 additions and 13 deletions

View File

@@ -353,6 +353,9 @@ message ColumnBehavior {
// Logical source column name, empty for ordinary user-defined columns. // Logical source column name, empty for ordinary user-defined columns.
string generated_from = 3; string generated_from = 3;
// True when clients may offer this column in an alias rename picker.
bool renameable = 4;
} }
// A script that targets a specific column in a table. // A script that targets a specific column in a table.

Binary file not shown.

View File

@@ -363,6 +363,9 @@ pub struct ColumnBehavior {
/// Logical source column name, empty for ordinary user-defined columns. /// Logical source column name, empty for ordinary user-defined columns.
#[prost(string, tag = "3")] #[prost(string, tag = "3")]
pub generated_from: ::prost::alloc::string::String, pub generated_from: ::prost::alloc::string::String,
/// True when clients may offer this column in an alias rename picker.
#[prost(bool, tag = "4")]
pub renameable: bool,
} }
/// A script that targets a specific column in a table. /// A script that targets a specific column in a table.
#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)]

2
server

Submodule server updated: 5b6b860372...da8681a1c2

View File

@@ -177,6 +177,7 @@ pub(crate) async fn load_page(
generated_from: behavior generated_from: behavior
.map(|behavior| behavior.generated_from.clone()) .map(|behavior| behavior.generated_from.clone())
.unwrap_or_default(), .unwrap_or_default(),
renameable: behavior.is_some_and(|behavior| behavior.renameable),
} }
}) })
.collect(), .collect(),

View File

@@ -74,13 +74,13 @@ impl TableDetailView {
self.table_kind == "system" self.table_kind == "system"
} }
/// Columns a rename may target. A generated companion (`phone_country`, /// Columns a rename may target. Provenance and renameability are separate:
/// the accounting fields) belongs to the column it was derived from, and /// accounting companions remain renameable while protected generated
/// the server refuses to rename one. /// columns do not.
pub(crate) fn renameable_columns(&self) -> Vec<&DetailColumn> { pub(crate) fn renameable_columns(&self) -> Vec<&DetailColumn> {
self.columns self.columns
.iter() .iter()
.filter(|column| !column.generated) .filter(|column| column.renameable)
.collect() .collect()
} }
} }
@@ -98,6 +98,7 @@ pub(crate) struct DetailColumn {
pub generated: bool, pub generated: bool,
pub read_only: bool, pub read_only: bool,
pub generated_from: String, pub generated_from: String,
pub renameable: bool,
} }
impl DetailColumn { impl DetailColumn {
@@ -116,12 +117,10 @@ impl DetailColumn {
if self.read_only { if self.read_only {
flags.push("read only".to_string()); flags.push("read only".to_string());
} }
if self.generated { if !self.generated_from.is_empty() {
flags.push(if self.generated_from.is_empty() { flags.push(format!("generated from {}", self.generated_from));
"generated".to_string() } else if self.generated {
} else { flags.push("generated".to_string());
format!("generated from {}", self.generated_from)
});
} }
flags flags
} }
@@ -361,7 +360,7 @@ mod tests {
} }
#[test] #[test]
fn a_generated_column_says_what_it_came_from_and_cannot_be_renamed() { fn provenance_and_renameability_are_independent() {
let detail = TableDetailView { let detail = TableDetailView {
id: 1, id: 1,
name: "contact".to_string(), name: "contact".to_string(),
@@ -379,6 +378,7 @@ mod tests {
generated: false, generated: false,
read_only: false, read_only: false,
generated_from: String::new(), generated_from: String::new(),
renameable: true,
}, },
DetailColumn { DetailColumn {
name: "work_phone_country".to_string(), name: "work_phone_country".to_string(),
@@ -390,16 +390,34 @@ mod tests {
generated: true, generated: true,
read_only: true, read_only: true,
generated_from: "work_phone".to_string(), generated_from: "work_phone".to_string(),
renameable: false,
},
DetailColumn {
name: "charge".to_string(),
field_type: "money".to_string(),
sql_type: "NUMERIC".to_string(),
currency: "EUR".to_string(),
quantity_ledger: false,
rounded: false,
generated: true,
read_only: false,
generated_from: "accounting".to_string(),
renameable: true,
}, },
], ],
}; };
let renameable = detail.renameable_columns(); let renameable = detail.renameable_columns();
assert_eq!(renameable.len(), 1); assert_eq!(renameable.len(), 2);
assert_eq!(renameable[0].name, "work_phone"); assert_eq!(renameable[0].name, "work_phone");
assert_eq!(renameable[1].name, "charge");
assert_eq!( assert_eq!(
detail.columns[1].flags(), detail.columns[1].flags(),
vec!["read only", "generated from work_phone"] vec!["read only", "generated from work_phone"]
); );
assert_eq!(
detail.columns[2].flags(),
vec!["EUR", "generated from accounting"]
);
} }
} }

View File

@@ -153,6 +153,7 @@ mod tests {
generated: false, generated: false,
read_only: false, read_only: false,
generated_from: String::new(), generated_from: String::new(),
renameable: true,
}], }],
}), }),
history: Vec::new(), history: Vec::new(),