From 5fe630fff729faeb3c2de42d6daff83d75bdf95d Mon Sep 17 00:00:00 2001 From: Priec Date: Thu, 13 Aug 2026 14:24:34 +0200 Subject: [PATCH] aliasing2 --- common/proto/table_definition.proto | 16 ++++--- common/src/proto/descriptor.bin | Bin 180398 -> 180697 bytes common/src/proto/komp_ac.table_definition.rs | 16 ++++--- server | 2 +- web/src/pages/add_table/draft.rs | 42 ++++++++++++------- web/src/pages/add_table/state.rs | 14 +++---- 6 files changed, 53 insertions(+), 37 deletions(-) diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index d67bedfc..c682a75e 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -108,14 +108,18 @@ message PostTableDefinitionRequest { // column, though, so it is free to be anything: this is where that choice is // made, instead of a RenameColumnAlias call afterwards. // -// The ACCOUNTING_TRANSFER connectors are the exception. They are refused here -// exactly as RenameColumnAlias refuses them. +// Only ACCOUNTING's columns may be renamed. Renaming a generated column +// requires its relationship to whatever generated it to be recorded, so the +// rest of the system can find it without knowing its name; ACCOUNTING has that +// in table_accounting_definitions, and nothing else does. The ACCOUNTING_ +// TRANSFER connectors and the PHONE and IBAN companions are refused, the +// latter because a row write finds them by rebuilding their names from their +// parent column's name. message GeneratedColumnAlias { // The name the backend would otherwise give the column: one of ACCOUNTING's - // "name", "tax_point_date", "debit", "credit" or "account", or a companion - // such as "work_phone_extension". Must name a column the request really - // generates -- an alias for anything else is rejected rather than ignored, - // so a typo cannot pass silently. + // "name", "tax_point_date", "debit", "credit" or "account". Must name a + // column the request really generates -- an alias for anything else is + // rejected rather than ignored, so a typo cannot pass silently. string generated_name = 1; // What the column should be called instead. Same rules as any column name. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 37ac7e138096a3e2fb778b0e592f4289e4c7f696..5d420cabab87030535c547577c0b9d559d02260f 100644 GIT binary patch delta 7574 zcmYjWdvsORoxZ={KKJC_6GD=c5J-4ku*x%0Omyj}9R;L_6%`O`JF_%EESZ7C<}nPm z_QDc}&JsufVH1pEQ6zwd#zKP?VFVoH;VagKE}5!SEQ-}48XwKl`F{KCotylXZ}0tm z`}aEgb=#L_zkPr9l3n!YyZG2Ao$uX6V+UvJKTwbr#wqrYr~mbbJfidRU(u>CcSBad z9)(_G?AKKCU{>cFt7s3UZ?0LeICb5mNjKj%ebx=re>AE-HMe#_YAUYDpNnNs;R20Y^a)N2L~ASi^l4@s`^xQLp{KP%7*INn)>UQ@~{rFTRbZcrG=8IU>g8Hh|yxOYzveYb0p9##JL2A~F>!#0~ za{UZAT~kvvx1qMK-f+dvTc+MT{d$9Ln0VcEj97S2WsQU&Raqx7yt}bpTr~b6I1LEK zoT|B%jcH6*nX0Q@lv-3*-B4wm6wBfBoW zuci{+vzo?HphvBtVS36M8b9`4P6YjexF|vR-sC`s6i~_F;C3?$y6)4hYv_*g_w|k! zL+(rVx8n)p@5>K^jUMWeLw_a3)^v_oOU0aCNr^ES<#`Niy;Z~|K@MgFiW``45M{iI zk^>x60tXdaDmt5p2iZ;n70^*(3t+Oq^@7?&MdFL+rI|VOnlsZfxPXq!;5KKbWpJA_ z(*ms~9&3e_XJFtUlvY!AzJpL&P2q(O8t&-+V}i4Ups3K-6l>K}*3-vnO^FMQbxLBg z!8ry5Nx;ZBKuB(IjsZe)gL6!Y-H&Uu)wzPcl$AnECFH5zA0PX~p?RzcF! zK|_WHi;Mz-w_88(FfC5+&M0`WVK-$*r4l#0&2CCfunMSa^j2JkY;Zgl`p;5qFHw*k zsJO)FSyZJYDjt(K{?UGgg3t+QzIK6d|Bv>w1Dt@~=WJ;MO6UMhEepip=cJZ@h_v~k zzn@}X>GoE-CT~9_1_l4E;EXTx9q#-`D?LbMhg}xX3aP`C%rWKz!u-ROmmi#$YVsw( zXF7L1N{xQ$nG8whKjTt|K_H!x)Lm;hu;#3uy@e*0o+Zw;!lHDR@}kerH=od1F}c=I zAn=)faSQ#T^fOoIzRAsJlvQLEBsZT?aY?Y)P>`GRdhV0-%hK~1!({6}Ps!+8^ktC& zEF2KrVkk&`x6a;5GcN7U%n}Eso3fmpaZtJ`EC{9`mRX_yEyb2Fm0Y18J3vKx%T^kf z`z_(%sx>W)Y zltsx_B;KjHkuUP&xygo%@9f`6xRdHEA^OdG(GLtmw-W^Hy+nQ7=;rT*1i2 zIAaQhelzpq`sj8Vo8QbBHq8jie(bH$@g4NzE7q`=w0yIX*}1(EV$(WC=6+=*P=_u0^Bwf-$t~<we>5+x-qJZNO50ZJ`=;YGo0s|4JtcRfWvncUh}N!F59&MD|6%4*Weg`+YJ!sM~z zf;+4ds&=tH> zKzOi+vwvWFFOXUG@P*^tXw0&Q%Pz)8%wBfr@8#Hjo!>#zbN6y06x$1jWgI@=+XO9%;-5f|G!elYnsYkSqvtas@Pl2Sz8aB49`z;?xMUe&vMz zOB_3{pM8cVrC;JiiJ2frY&*g{!bkwYHo(X>KqwyJqnxV=;571G? zn4=l0*mBfSZ_SZ|-ZAGF5c~p+`~rm3F;^jgkUGX;7+q>ule zE=j*C%d*6+zqs`p+oKy01OcO?8z2N$TLXaE!CpdPJNq@VGW==c7_WYVFL3qnq0|ZIH$P++FzUAr;5Rz|+HB%A4 zTru?>t#{M3Yu<5rK_e92;q1tHKpDjO(KiMNg?D(=Xp@(`(0`X>f7QeOfV{lR_Fj=E zCcVe@I06Jgz{pEL2)+jcO}qdh_#WrTWzO^R#H9C~H$b2QM&1CLUi(=Bg8IITZ8}f# za*8=+g(WY5Zdy@3C8Z;u5ypahDq7xTTA;tMz2X6Z3h1Vly!?gj6%V~;u)X5tNmYNq zJTz^Eah4bCxU$=P!1gMH!Qg(t_9~PoCY}b@QsWVHRM~G%XQ;Bmsj8%gII?C-#PY;esd4q zkp4R-3XE-`*me&2H4^~AHo(X>Kq#JLd#(nA;5o**S}H0O+dg;22m~r%R26`x*A^on zsGqxHOoviY=b1;_X(iQwkzeS9U+39gkN_cdp6vw*5K`wE7o^AvpfB7~7_waE0iBY9 z(id_hk=jHrl)m7ibW|6>hK#i^k^=Wjw(kO=+-wCpDJj1%-KvUSNPWrnT>ubDU-Iba zO(c}^>t-&u)5c*1pqn+G2Dh7smAP(kyLrN;)(Q;%%GF=UKBNF$Mx^|{a^;6!P`{G$ ziyX)by{|LL%mNiKTDC*Z0`+Uo!X>HL1S1RFuO*weqdW>izlUSL(dEz4?A#t%D(^s! z3#2^0(;J?npOt;*l+XyJ?>H-3$N(Yqovh-w845a=Dm`K^&AxQ0va=VMdRVG@IZ_Wx z6*#jD2j#F#;mK=?o<5Q<(Qogi5v6E|ye=>muuS0{OVS7k@-l^otQm$3v|NvRo@SLT z&&(&OUaqpDOH_f$^m2tu)Fe1t82S&Y*lNA)d77AhP$i;QheC07h2m(J07Ccxqk;y6 z;0l$91OYMY3K5jZ7fOk)RCfCT0u?YaAJFtBov`@6QepcMYPwLqg;mO~u0Y@dI<6?M zQpsp_MK8FklsP=gv_MUY3r$>rKm~ME2|<&>v09>rUNabH;K*WBceCQ+D{ZfoH?ZSM zc{i)1QRs#o_H@I;`P}eJ4^XUN+KCSG>1q3dj>R%H*XMG}NZls!2CK@u?f z1OXxWh_WXqKuA8K?8&J}Onp?@lM@iAfRQzTrq`aF06~3JzMpGhP1eBB->hQW^hGbw zKNoIRIGs*LMh1#8Paq>Dm+M>haglC%fyR}gA<{%QG@o!e0fgogl9Offcr{S$Y13z3 zp#Lgs%W%YwHkS;H0jEupQ3g8(h5l12_Ka>lK&81)DI5uY0--@-M!SCN0R6PI-Ngcp z5Nda^0EAGx#6m6)gJkXwedj@39y;9IXp~lEV*m(Bhr|Gv2W;v7ZWTMIj~=8Ob9bxw zK$NN%H+T0={ewev)9_CFeM%#0rc=J6qwo|Mx_fo=Av!*MuVQnRl12>Lt8kU-js^i9 z(7$|To_GQ2{z}qf`&leBR5fdX$=PwZ6<&?skm7~h%a!S@mc|;K{tT(6ik6y+j%4y|Ak1(bg#M5fv@Lr-UHK*0c3-Ce~ z5%C>-q`cuJ)q2IN__gEo%k(c5Xo#KE*A|Z58MG5@jzvr;-_~Wive|Rv&XyCa|gN zyw~XE%e(t#Cjn-+!X>~>nE@Dh@A9nkGjIr)8?pxh%VxT}|rK}$S;NXhinEd72L73f>LXzG<{>MJD$ zw8X>PQbt`$Xo-g%x4u^jHMC53by4FL%RE2mGghi-nU_7JZxiUtJluSrS2nImY)zf% zH)uaiY{EsoHyTM~s>#b4l4*cilQ*<1(*Ug|Z$i0wyeyW0tkp~2q^V_VJ$qI|BUILU Z_SgpqjkR7;^b7z9g|(gCZ_=KR{~w)v3n2gi delta 7218 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 Vec { let mut names = Vec::new(); for (index, column) in self.columns.added.iter().enumerate() { - if column.data_type == ACCOUNTING_TRANSFER_FIELD_TYPE { + if column.data_type != ACCOUNTING_FIELD_TYPE { continue; } names.extend( @@ -244,9 +245,7 @@ impl TableDraft { .iter() .map(|generated| generated.name.clone()), ); - if column.data_type == ACCOUNTING_FIELD_TYPE { - names.push(ACCOUNT_API_COLUMN.to_string()); - } + names.push(ACCOUNT_API_COLUMN.to_string()); } names } @@ -556,13 +555,24 @@ mod tests { ); } - /// The transfer connectors are resolved by name by the posting engine, and - /// the backend refuses to rename them, so they are never offered. + /// Only ACCOUNTING's columns are offered. The transfer connectors are + /// resolved by name by the posting engine, and a phone or IBAN companion is + /// found on write by rebuilding its name from its parent's -- the backend + /// refuses both, so neither is offered here. #[test] - fn accounting_transfer_connectors_are_not_aliasable() { - let draft = draft_with_column("accounting_transfer", "accounting_transfer"); + fn only_accounting_columns_are_aliasable() { + for (name, data_type) in [ + ("accounting_transfer", "accounting_transfer"), + ("work_phone", "phone"), + ("bank_account", "iban"), + ] { + let draft = draft_with_column(name, data_type); - assert!(draft.aliasable_generated_columns().is_empty()); + assert!( + draft.aliasable_generated_columns().is_empty(), + "`{data_type}` should offer no aliases" + ); + } } #[test] diff --git a/web/src/pages/add_table/state.rs b/web/src/pages/add_table/state.rs index 90f852ff..37caad79 100644 --- a/web/src/pages/add_table/state.rs +++ b/web/src/pages/add_table/state.rs @@ -13,10 +13,7 @@ use crate::schema::{ColumnCatalog, ColumnDraft, columns_from_rows}; -use super::draft::{ - ACCOUNT_API_COLUMN, ACCOUNTING_FIELD_TYPE, ACCOUNTING_TRANSFER_FIELD_TYPE, GeneratedAlias, - TableDraft, -}; +use super::draft::{ACCOUNT_API_COLUMN, ACCOUNTING_FIELD_TYPE, GeneratedAlias, TableDraft}; /// The `profile_name` option meaning "create a new profile too". pub(crate) const NEW_PROFILE: &str = "__new__"; @@ -227,10 +224,11 @@ impl AddTablePageState { }); // A generated column is named by the backend, but the name is a - // display name: it can be aliased, and the alias is applied as a - // rename the moment the table exists. The connectors of an - // ACCOUNTING_TRANSFER are the exception the backend protects. - let aliasable = column.data_type != ACCOUNTING_TRANSFER_FIELD_TYPE; + // display name, and ACCOUNTING's columns can be given one of the + // user's own in the request that creates them. Only ACCOUNTING's: + // see `TableDraft::aliasable_generated_columns` for why the other + // generated columns cannot be renamed at all. + let aliasable = column.data_type == ACCOUNTING_FIELD_TYPE; for generated in columns.generated_columns_of(index) { let mut tags = vec![format!("generated by {}", column.data_type)];