From 49323b58d6c20d62e8dbe4c79715ca53a0bea5a9 Mon Sep 17 00:00:00 2001 From: Priec Date: Wed, 12 Aug 2026 20:17:57 +0200 Subject: [PATCH] aliasing of account fields --- common/proto/table_definition.proto | 3 ++ common/src/proto/descriptor.bin | Bin 167503 -> 167665 bytes common/src/proto/komp_ac.table_definition.rs | 3 ++ server | 2 +- .../pages/admin/table_definition/loader.rs | 1 + web/src/pages/admin/table_definition/state.rs | 42 +++++++++++++----- web/src/pages/admin/table_definition/ui.rs | 1 + 7 files changed, 39 insertions(+), 13 deletions(-) diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index a0d9d328..c73de16a 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -353,6 +353,9 @@ message ColumnBehavior { // Logical source column name, empty for ordinary user-defined columns. 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. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 8a1045f97e9bf8c27dbde6a089ebbcd5271d9190..358dde94b81a6be45195e52aced2d8e476c71835 100644 GIT binary patch delta 2273 zcmY+GTWC~Q6ozN7b#i8APh!lO$xNJR=4fARi&IL17-CUW+TsO@+EPj_jWS|rj3rTu zFCCGRf(@iq97yAw`DqxciBfxe8I=P6d}p-3(e)q86NP{@!koov`Q7C;whRnkGTQ6kH@M`U}82`r?~ z64-kb_MLU_?omU^+ZHNM6!TEs14tibpm_>c`&nnP74=ZVG8TzeHG5eDEVR!WU>7Ou zI~!mZDN!ePp?xvSyib9@)sJVz&g^{(*^XmOx9v3Bv~zuJyApd_Z-p zJQT@?M0H+Lw-3@sg_8|O{VdRj6i+y4Hm2M6h-i(M)$N1yS>3)z6!x8U`yNsKYHwSp zr4p*!2kE1_eM=>joy9<#(Slmbd`y9VNvMYNVq^9(h1NQjwYmw*L~gB6u!MA{5)zin z6kOrd3JJ?)s&U7!R+svO$n6jmP)N5LNGBU!>7jato=`L~EvZ6S<~a^rQSU3UtNI*QBrvK* zh0uHVsh^bClj>vle(E4*rjK!pzQdP7W4C^_<5Mx2>St=uE!IIS>gToVdU8qZvu?AD$q6A% zozMjitJVSWZPPFtQdY~L8%9mT9H}k^@U_E?>$~8SSTUmBx-9ZdBjuz%p%IQmi=^(; zh(4ntiK0hU_A`*9Y=nvnbst9c+4@E3>oTgz>*Q67z*uhZbFp4Dj+LYOvW#)Krikk6 zGRAlxcX}k=n{l-~D0XieXCq!*ryrVePNYf!faARGDS6TZaStZgNG03U)*+Ep7q5sn zn-D0KjkCv!aI$Do0gDM<(;$EGTV-0tqu#ohJw6LzXeEz8MlfY@9AXoe;=rj;t(h83H-2r+Blw zrJj{LZ2YDsu8Nk}9j>gG{X&NHP`J}FqvF@ZK=TZnm9j>L{RDhxxT>Z+1m_uj1aB8{ zESgmd*Tli*+5h8uXlA)8QHJZWndM}?>?-1zn>p2eU7Xl7$EGY>t0!lUBdg0pz~?w+ z%dgn$Wf(t4xyBpfYq2fQMrE-BVVq8$t5%l+aLet^$JaUUB_tdQxm!2Iwcq~(0|iZs delta 2069 zcmY+FTWDNW6ozN5b>_@DdvZN9nVC+KCao_;?0_UlW8;N-LDKfADk2KS2f^1C!5c(K zu>?}A)po~_L`^TDLJ@*GzNF?MAh8h%R0D<*L8;BerfsS=6-xZ~nX_lc$A7K;{cG=a z*52pVnEG*4z56Bg^zz+bio1L16QpZ z@)~n%lq8^#o|R$Nx5fnY8gI#mXn%@1a8{T*qs0ss(y|P8g?;PHU{`o+M^qN7#GH;$ z0SakR0a{|e(PqQNK`KI>z*%M9(i3GJu+Wxuz^<}yogJ{NoXx4dXkR*TZ*c4{{l;PSz5*cbH@U>Wd%IuKqjh zZ3`V+%+WTOQ(-wMI6}Hs!f}HW4OYT&gPY4k)nayfm$`gyD4>w#%|M1(zbQh^WZdOc zT0M@aRAJLvAquNa+h9moZJGujp%6lL zKtFnl-YX19R6Y}y9}vHME=Vu~lF6z9%4Y-jsKk!x^;5L7@u+yM>IE2OP4z)t(DdTY zL5W&`AnXSvY5^pS1_dqH9U@2$>8~_BQW&zevtjKaiH;5lWJqxIeTw50xW^?nt~-Zm zZ{u-^XEAq9oJ{Q#`mJHw-*H09m(2t*DJKM1;(MGF8r8JEI!yB&T6nAJtO;U~mTlYB zKjlS`V|w9Z+UOjsI+>XpGfrm&4af!Vq~QHn8KKh@Op4c`p2cx;5)0h(!mE0Gln&(1 zi%P5SNVz6qHh4jtbpC77O@}G1+sEjOZbX!7REhEmZ0~|38>;~v{DR{CMaEe=~HT;E`+Ks=H5(;-&}DqH)mS#0`^5Fcu{8b*k`n_YX)AGb>=CVk!(jb z0B}aO->2S;K-`I0-8MmQchA;V#27fU;^!-19N?_9Z&g=ptK7hu)0ZY_xofVLbV1Gu z9$b537v!Aa$vqI2Oa#unzH*wL>z=O#69DJMukJMgaNg|ok}c^4&VqjR4E^xXLha}A zKrTqKsZ!DdxnKsmw_MVU&1G?Z)vukU-Ra9x*Q!QHd1ffegR`i=IZLBGi{jR)X64%i zd>5s@xi$pnMbr3$6&#C}^s#gFO3%{&aWgebQlG8C&D<H2}Agv=^!Yc$MVA vF7>pzSmySu>7UQjGlex_6~2iG!evd8ZnJP$EAIQ8&i?)%vUesH diff --git a/common/src/proto/komp_ac.table_definition.rs b/common/src/proto/komp_ac.table_definition.rs index d870c9c1..ffb8f478 100644 --- a/common/src/proto/komp_ac.table_definition.rs +++ b/common/src/proto/komp_ac.table_definition.rs @@ -363,6 +363,9 @@ pub struct ColumnBehavior { /// Logical source column name, empty for ordinary user-defined columns. #[prost(string, tag = "3")] 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. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] diff --git a/server b/server index 5b6b8603..da8681a1 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 5b6b860372459553eced168717d4dd4ecc639a26 +Subproject commit da8681a1c2df5c254ee9f6f1dc2c1f944e171185 diff --git a/web/src/pages/admin/table_definition/loader.rs b/web/src/pages/admin/table_definition/loader.rs index 542cd8bc..61cacc26 100644 --- a/web/src/pages/admin/table_definition/loader.rs +++ b/web/src/pages/admin/table_definition/loader.rs @@ -177,6 +177,7 @@ pub(crate) async fn load_page( generated_from: behavior .map(|behavior| behavior.generated_from.clone()) .unwrap_or_default(), + renameable: behavior.is_some_and(|behavior| behavior.renameable), } }) .collect(), diff --git a/web/src/pages/admin/table_definition/state.rs b/web/src/pages/admin/table_definition/state.rs index 1145bce2..a61ee357 100644 --- a/web/src/pages/admin/table_definition/state.rs +++ b/web/src/pages/admin/table_definition/state.rs @@ -74,13 +74,13 @@ impl TableDetailView { self.table_kind == "system" } - /// Columns a rename may target. A generated companion (`phone_country`, - /// the accounting fields) belongs to the column it was derived from, and - /// the server refuses to rename one. + /// Columns a rename may target. Provenance and renameability are separate: + /// accounting companions remain renameable while protected generated + /// columns do not. pub(crate) fn renameable_columns(&self) -> Vec<&DetailColumn> { self.columns .iter() - .filter(|column| !column.generated) + .filter(|column| column.renameable) .collect() } } @@ -98,6 +98,7 @@ pub(crate) struct DetailColumn { pub generated: bool, pub read_only: bool, pub generated_from: String, + pub renameable: bool, } impl DetailColumn { @@ -116,12 +117,10 @@ impl DetailColumn { if self.read_only { flags.push("read only".to_string()); } - if self.generated { - flags.push(if self.generated_from.is_empty() { - "generated".to_string() - } else { - format!("generated from {}", self.generated_from) - }); + if !self.generated_from.is_empty() { + flags.push(format!("generated from {}", self.generated_from)); + } else if self.generated { + flags.push("generated".to_string()); } flags } @@ -361,7 +360,7 @@ mod tests { } #[test] - fn a_generated_column_says_what_it_came_from_and_cannot_be_renamed() { + fn provenance_and_renameability_are_independent() { let detail = TableDetailView { id: 1, name: "contact".to_string(), @@ -379,6 +378,7 @@ mod tests { generated: false, read_only: false, generated_from: String::new(), + renameable: true, }, DetailColumn { name: "work_phone_country".to_string(), @@ -390,16 +390,34 @@ mod tests { generated: true, read_only: true, 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(); - assert_eq!(renameable.len(), 1); + assert_eq!(renameable.len(), 2); assert_eq!(renameable[0].name, "work_phone"); + assert_eq!(renameable[1].name, "charge"); assert_eq!( detail.columns[1].flags(), vec!["read only", "generated from work_phone"] ); + assert_eq!( + detail.columns[2].flags(), + vec!["EUR", "generated from accounting"] + ); } } diff --git a/web/src/pages/admin/table_definition/ui.rs b/web/src/pages/admin/table_definition/ui.rs index 7e15b0be..ba7d6bfe 100644 --- a/web/src/pages/admin/table_definition/ui.rs +++ b/web/src/pages/admin/table_definition/ui.rs @@ -153,6 +153,7 @@ mod tests { generated: false, read_only: false, generated_from: String::new(), + renameable: true, }], }), history: Vec::new(),