From a7b8d2021a2368463a12a6acda42ded54457230a Mon Sep 17 00:00:00 2001 From: Priec Date: Sat, 8 Aug 2026 20:30:57 +0200 Subject: [PATCH] accounts are strings in the client side --- client | 2 +- common/proto/accounting.proto | 37 +++++----- common/proto/table_structure.proto | 4 +- common/proto/tables_data.proto | 6 +- common/src/proto/descriptor.bin | Bin 150800 -> 150337 bytes common/src/proto/komp_ac.accounting.rs | 77 ++++++++------------ common/src/proto/komp_ac.table_structure.rs | 4 +- common/src/proto/komp_ac.tables_data.rs | 8 +- common/src/system_column.rs | 10 ++- server | 2 +- 10 files changed, 69 insertions(+), 81 deletions(-) diff --git a/client b/client index da1557a2..827e6a40 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit da1557a2d4a08be07f7c3af93529aa240a060744 +Subproject commit 827e6a408a9f3df5f306461d3ade22f08e957c62 diff --git a/common/proto/accounting.proto b/common/proto/accounting.proto index 534324ba..ae298c70 100644 --- a/common/proto/accounting.proto +++ b/common/proto/accounting.proto @@ -76,7 +76,8 @@ service Accounting { message EnsureAccountRequest { string profile_name = 1; - repeated string segments = 2; + // Slash-delimited root-to-leaf account path, for example "123/12/1". + string account = 2; // Currency accepted by source postings to the leaf account. Empty uses the // profile accounting currency. Missing ancestors are created in the profile // accounting currency. @@ -87,9 +88,8 @@ message Account { int64 id = 1; optional int64 parent_account_id = 2; string segment = 3; - // Root-to-leaf path of this account. Same shape as the request that created - // it; the server never joins segments into a delimited code. - repeated string segments = 4; + // Slash-delimited root-to-leaf account path. + string account = 4; string denomination_currency = 5; } @@ -161,9 +161,8 @@ message PostJournalRequest { message JournalLineInput { JournalSide side = 1; - // Parsed account path segments. The client owns presentation parsing; the - // server accepts no delimiter-encoded account string. - repeated string account_segments = 2; + // Slash-delimited root-to-leaf account path. + string account = 2; string amount = 3; string description = 4; } @@ -281,8 +280,8 @@ message JournalLine { int64 id = 1; int32 line_number = 2; JournalSide side = 3; - // Root-to-leaf path of the posted account, as sent on JournalLineInput. - repeated string account_segments = 4; + // Slash-delimited root-to-leaf path of the posted account. + string account = 4; string amount = 5; string description = 6; bool deleted = 7; @@ -379,8 +378,8 @@ message ListPeriodBalancesRequest { message PeriodBalance { int64 period_id = 1; - // Root-to-leaf path of the account this frozen balance belongs to. - repeated string account_segments = 2; + // Slash-delimited root-to-leaf path of this frozen balance's account. + string account = 2; string currency = 3; // Signed nets use debit-positive convention. string opening_balance = 4; @@ -393,7 +392,7 @@ message PeriodDenominationBalance { int64 period_id = 1; // Exact denominated account. Unlike book balances, this quantity is not // rolled up into parent accounts with a different denomination. - repeated string account_segments = 2; + string account = 2; string denomination_currency = 3; // Signed nets use debit-positive convention and remain in the denomination // currency without conversion during carry-forward. @@ -424,13 +423,13 @@ message MapOpeningBalanceAccountRequest { // Open period whose profile receives the opening balance. Its configured // previous_period_id identifies the source profile and period. int64 target_period_id = 1; - repeated string source_account_segments = 2; - repeated string target_account_segments = 3; + string source_account = 2; + string target_account = 3; } message UnmapOpeningBalanceAccountRequest { int64 target_period_id = 1; - repeated string target_account_segments = 2; + string target_account = 2; } message UnmapOpeningBalanceAccountResponse { @@ -453,9 +452,9 @@ message OpeningBalanceAccountMapping { int64 target_period_id = 1; int64 source_period_id = 2; string source_profile_name = 3; - repeated string source_account_segments = 4; + string source_account = 4; string target_profile_name = 5; - repeated string target_account_segments = 6; + string target_account = 6; string currency = 7; // Signed balances use the debit-positive convention. string opening_balance = 8; @@ -478,7 +477,7 @@ message OpeningBalanceAccountMapping { message UnmappedSourceBalance { int64 source_period_id = 1; string source_profile_name = 2; - repeated string source_account_segments = 3; + string source_account = 3; string currency = 4; // Signed portion not covered by a mapping, using the debit-positive // convention. This is never zero and may be less than the account's @@ -490,7 +489,7 @@ message UnmappedSourceBalance { message UnmappedSourceDenominationBalance { int64 source_period_id = 1; string source_profile_name = 2; - repeated string source_account_segments = 3; + string source_account = 3; string denomination_currency = 4; // Exact foreign-currency quantity that no mapping carries forward. string unmapped_quantity = 5; diff --git a/common/proto/table_structure.proto b/common/proto/table_structure.proto index 70ea77b7..df653545 100644 --- a/common/proto/table_structure.proto +++ b/common/proto/table_structure.proto @@ -46,14 +46,14 @@ message GetTableStructureResponse { message TableStructureResponse { // Columns of the physical table, including system columns (id, deleted, // created_at, row_revision), user-defined columns, and any foreign-key columns such as - // named by whoever declared each link, plus the dedicated "account_id" on + // named by whoever declared each link, plus the dedicated "account" on // ACCOUNTING-enabled tables. May be empty if the physical table is missing. repeated TableColumn columns = 1; } // One physical column entry as reported by information_schema. message TableColumn { - // Column name exactly as defined in PostgreSQL. + // Public column name. Physical account_id columns are exposed as account. string name = 1; // Normalized data type string derived from information_schema: diff --git a/common/proto/tables_data.proto b/common/proto/tables_data.proto index 2d95c3fe..968851bb 100644 --- a/common/proto/tables_data.proto +++ b/common/proto/tables_data.proto @@ -62,7 +62,7 @@ service TablesData { // - Validates profile and table definition // - Returns all columns as strings (COALESCE(col::TEXT, '') AS col) // including: id, deleted, row_revision, all user-defined columns, and FK columns - // named by whoever declared each link, plus "account_id" on + // named by whoever declared each link, plus "account" on // ACCOUNTING-enabled tables // - Fails with NOT_FOUND if record does not exist or is soft-deleted // - If the physical table is missing but the definition exists, returns INTERNAL @@ -102,7 +102,7 @@ message PostTableDataRequest { // - System/FK columns: // • "deleted" (BOOLEAN), optional; default FALSE if not provided // • one BIGINT per link, named by whoever declared it - // • "account_id" (BIGINT) on ACCOUNTING-enabled tables + // • "account" (slash-delimited account string) on ACCOUNTING-enabled tables // // Type expectations by SQL type: // - TEXT: string value; empty string is treated as NULL @@ -251,7 +251,7 @@ message GetTableDataResponse { // - id, deleted // - all user-defined columns from the table definition // - one column per link, named by whoever declared it - // - account_id for ACCOUNTING-enabled tables + // - account for ACCOUNTING-enabled tables // // All values are returned as TEXT via col::TEXT and COALESCEed to empty string // (NULL becomes ""). The row is returned only if deleted = FALSE. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index a0411d5e92f2df41dcb0c8886ad1f45fe23bcc28..e106dc3f681ee1457fa98187af909bc6a492fedd 100644 GIT binary patch delta 12913 zcma)@dz4khm51-CI(_@z+uhK8pn>j&=F&jZM8RM(GsgJHBs#{KJakAD;>u(QqOul( zi$_+HnXIGWBTM3g+$d4;0gB2)L>og0Veo-GG=QKyMiC+cMot-yw?2}gUpILQ&E4nF)?yBrI=YP`h)9yOIZBHvkb1OI3PWhzKr@eI! zsk^vfK8z~YHxBuv)u&xm-l^(SIo5b*yvCH8D%bRCs@&Gt`d7@|L|QtB_FQeM?ubf# zzh3!LOH(Cj{_KSVOI;>Sm@;wnnD2c$26g^l|Ir5~!EExViINf!u($tVP{>RdrUODvas)@zu-K~=~Gj$e5u&GGO;{9T3dOooc^sM0^a#p zzq9K~+vi55>ZB@{`>^xa(Al-ow<@!~cy4-ODUNd{O6)gHWAi!X;p0Y4x?;$sW5$iW za_r~Mf0vXcqVrvi>1jGBa zl}aTOM@f89?z<8HZQ}^5iwa#!HMogPF0mJzu1*5b|HYv8HINoVZX5=zP&-Kr5}xtk!j8l6Y=px`#$nl{!|fjAL$8?5fRZ=SE4l zpoJccOjBrNo`Nc6qF_-yGgGiZ^GR3 zSawDQKmNRQv_z*4;T7~#2eDlejRK=+nmPJj?E?m_~{=Ud^Xgx?VJ_@|_E6Q&0yfTjIH_OW6|7Ntd!Eib;Nb zl>6^6^&*z*qk`mS92aHk8+2(Q1_b)01+m=_g{1}Yy1^%#NK;xjYS@Y{EkHja(6xq) z0?}^Nj4Dc2ybu|m01(}RDwR0A5EXqKXhr&h#KDgk+T=<@N=Yh84HQ8po zTO;F(5`-4?dk{prHL9))(gM=0QGG)QaxsawWq~Rp1$D%dLffKZ>Jz+ZifFe*J){6j zq)JJ=JsWZfEvVWO+U-%XSLn8cbbHhyBQ|j<$-ffic7;S0%U7a;+yLUZB#F8s;*^$| zLaby8my`+dj;O{5f@ZWkqUJVjrI#HMC$)D)F3-+ zcm@4Rf!OYjs(WiIUUx@*%O!VQ)JUJNYhprZK|dSNwHAEpfoNaXysVMg?1@bO;8-RD zs#F~Bk@!o*u|>Kk;tD1)tPx80MrL4Wm0bv`R(9dus8H5ctoKI!2Lvr^w=Xg+p;2}r z=wn#pu6O&QqAv{~(tUb1)F$~iqul?57=ELcUfzrf1D)Gi3F3YY1cY1Ay9Ke`uNxG^ z?S2V_)Z|*3>f6-h&kcE7=fJvvbrCx( zsUuhHT8Y?um3bEpPv6TF5~ugH(qSStzS5bvF3G$iG`X8YdUwI{ zMpN(=x{IXubYq6O3IR#6hkdb!S(NFf#@8(n?Q}EXWVNQ3>E@Krx&d~Pr2Lr)H4b8c zppPl&T0@Nkk^ant8rMa#{U)Qu9z+ZK$$@BZ(#e5nZ!)3ScTMuQn%pnKMEi5&zCz-- ztIVdNc?lr}RVt1vrr`6ER-_f5mnN=H^0%8b_du9UJ*KxC4oIm)^)j28hL3JJB_P~_ z-YtmjOy;JoxSeVE?)Fj|e`&%wT<<0Ws#GTPOX3{vPLmoNG6D})HzG6)IstJ|a@7AV#c>B>v^}>f= zw0a}e>n;iUdRk;3HJ7A@%`x1z`~;+g&GB_eQi&3FzX_Kwh!KM-l@fNp z;ROL{D#mEHDx&2nZ<{;t#v#P$EFI4@-&g-Lf&}neb)R;I=vFg9sv>XTp~ih;*I_User5 z^EKNbx&>8Aj;Cbk?DOR=5lT?l9+$ZEj2m6xH4kYH`2wKL{fdR)`SLkS}!)CCN#)t{g`e}h~7ZI zIYFe4neb@_(%ECC-fzwZIjtYhA}yyi=m(n8iuQ5bhs2Upf7~?6LxAKYsotqO3_=U) zBqh~5b%W80v{N@&gQ?|3NG}NxQc$IG6JK)Tl0Y-sC3;C{Fm*f$>E?vcf~u99?Gt*A z(u(v6y(A<_{?{h=OxR!vmcKTI4mX)ZHrO&jc$prgAa0k*id{q?64?jK zD;JC&F=TnBkbSj0Q^>wrE{gM0g=z5j5)cO~ zO#7hH7o7uWW#v1Uo;h@-ZqLLW94k#v|D6hojo~}>7I`~MWK%s|dG^v7>C@RLWiLIg zdkGUHPy4-O;%-U)S(956!qpASXZ70AO~SQWdxi)X=<5-P?P?RAop3N!ih3sbttPjlvg5L^q+8{pBpa%yINYWVAsm9JY@?Ha|>}(zso( z1rSoumjKYUR{Lueh;+ML3;v#7^F8JE^K#{Z%TG&R&UzDvFKc zH&6#E_m@{p(HB)(ov|ybChnEwcbeSqLp*z7xzmLA*j_A567SO0@8#wX`qc+9-leM# z;&zv=ep8a)ZE|~p+a@e`YpOO$RA1MqLJSu4g9WjDJw!E)Vcfp1tKXdD_nF)ug42SLhVt0Z5paD5M-Y_-(RRTo%hUxA1GDs(H$X=FXza`107C$ zQ;Vd;+nH7lRZz9!`EAWaY|*}*Nuh&!qO`c&097ivaZu|Gt~d3b;~(_L=&jR6!P4N zVE4D>0}JIVjiXRn0LdFg<2??2|<7BW{nYTL`zHcMD>BT-}1WJuYsCJ1LF-E1yW`{y(2c z>4|Tov6IS|O`P!KG2_eMA2n{&_|anqP12u>!&g%um(aNNu?Af);j0N`2>+`$q&||* zA7!JLdJ{HeyNRX@0~eZcXyn+uYB?!nb01y$z>at1SEtR?_lxt|cIDVZZVq z#y8k-)PuOa!4kUL%+yGg5^7w;vBJt|_b+i10=mZnUkkUej%OXMvD{ zDiyEOEx+c=MTu6V(=G2T(|w=d&DOLw|3n|@#*djisXV1JeZr4Z8q|v{;mx+_ANgC| z65edX16Zpp;Vsz!WC=l)$`am^4M3Lg78!snVXF-GRvU^KFpFI{A?c zWOA@SHb6SP&DQub3!=TvHu;|qKzg~&4)D+7eI@j_TXVJ>AcQASJwNFH1`yjjbpwG6@J`)8 zWf|aIHk=s{Qc$I-4CyW#o-S!ddzTI8Pg$~VmTn*jE$A~HL^{ic&wdc;EZy>Dp}RAw z+=xJxN~+y0Qg^~&?5xAWpT4{K~dygjV3X_Jx6v*8UJVkDqn6414VH*65;JR9Dy+hj%NXHr=a z&`VQV(ayIgw6X~Ez1GD2ll&t#w>V6qKbDVZe)X3*EYj72a0~jdg4iz7$$)rWq?73{ zT|VkYBzp-&3;Q8~t~rbdMEj_YXh4#G+~$^q5e>ldab4&EGNMi!-q#?!g8u#vV%urM z`x=PXPMf+1HWLp_^1rgVUkA4XG5wWJbfCChsuP893;KzI*e=zHg1B88js(h5{x>%F zr1x4S3H%#d@)q;*AR!tcoO z3NiVHGl5oC=KtV=bY-Z0$YkkP`l4#$_9Xwb&20;FY{%u(HoSnh%V?g_IYP`4^m7EU zeMaX9GRJ3hG23Nqt1@XMM-;4DSaV z;bEy=XticFM6V!HNhuiZYRziUS%)3bE(g<^tXnylKRt~1Mx)IyuU|Q#H0d9zLFoCL-gK3=&4}sVsT_=}-FLNHY%N60S8n6=AW*akg(A+(@&D^Fn?vJuNu*aquX3lPx){Z$@B zy3zJb{SW;x+KsmC?+fkn7WRS-zqLVh3u?FW7WRT3ShOO2!G_=3+T|&1Qd}I#m0yEivc;2vwjS+EcBudH zt({*K;w{#k8X6mrf-05w@-4QggK9v!#hxb$C@1HvKbD+h?fSG970IDA#pdyv$Iow{le zQqa!>bgiLsfJk@h7ET9ARotcd4WR{9E1q}h7N!;LF5SX|gnq9{2_XelDn;k_x`krUt%4!n0ok z4>3T{PX=_YA@Cs5{TlcViTeQ;cd5}JT3EH>`G5wVRov%f-053zok2k zR%gt�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 Tw#~o$%Vvf1IP)v!(fEG>{?TJK diff --git a/common/src/proto/komp_ac.accounting.rs b/common/src/proto/komp_ac.accounting.rs index 15e13f9d..f0278312 100644 --- a/common/src/proto/komp_ac.accounting.rs +++ b/common/src/proto/komp_ac.accounting.rs @@ -3,8 +3,9 @@ pub struct EnsureAccountRequest { #[prost(string, tag = "1")] pub profile_name: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "2")] - pub segments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Slash-delimited root-to-leaf account path, for example "123/12/1". + #[prost(string, tag = "2")] + pub account: ::prost::alloc::string::String, /// Currency accepted by source postings to the leaf account. Empty uses the /// profile accounting currency. Missing ancestors are created in the profile /// accounting currency. @@ -19,10 +20,9 @@ pub struct Account { pub parent_account_id: ::core::option::Option, #[prost(string, tag = "3")] pub segment: ::prost::alloc::string::String, - /// Root-to-leaf path of this account. Same shape as the request that created - /// it; the server never joins segments into a delimited code. - #[prost(string, repeated, tag = "4")] - pub segments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Slash-delimited root-to-leaf account path. + #[prost(string, tag = "4")] + pub account: ::prost::alloc::string::String, #[prost(string, tag = "5")] pub denomination_currency: ::prost::alloc::string::String, } @@ -68,10 +68,9 @@ pub struct PostJournalRequest { pub struct JournalLineInput { #[prost(enumeration = "JournalSide", tag = "1")] pub side: i32, - /// Parsed account path segments. The client owns presentation parsing; the - /// server accepts no delimiter-encoded account string. - #[prost(string, repeated, tag = "2")] - pub account_segments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Slash-delimited root-to-leaf account path. + #[prost(string, tag = "2")] + pub account: ::prost::alloc::string::String, #[prost(string, tag = "3")] pub amount: ::prost::alloc::string::String, #[prost(string, tag = "4")] @@ -238,9 +237,9 @@ pub struct JournalLine { pub line_number: i32, #[prost(enumeration = "JournalSide", tag = "3")] pub side: i32, - /// Root-to-leaf path of the posted account, as sent on JournalLineInput. - #[prost(string, repeated, tag = "4")] - pub account_segments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Slash-delimited root-to-leaf path of the posted account. + #[prost(string, tag = "4")] + pub account: ::prost::alloc::string::String, #[prost(string, tag = "5")] pub amount: ::prost::alloc::string::String, #[prost(string, tag = "6")] @@ -386,9 +385,9 @@ pub struct ListPeriodBalancesRequest { pub struct PeriodBalance { #[prost(int64, tag = "1")] pub period_id: i64, - /// Root-to-leaf path of the account this frozen balance belongs to. - #[prost(string, repeated, tag = "2")] - pub account_segments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + /// Slash-delimited root-to-leaf path of this frozen balance's account. + #[prost(string, tag = "2")] + pub account: ::prost::alloc::string::String, #[prost(string, tag = "3")] pub currency: ::prost::alloc::string::String, /// Signed nets use debit-positive convention. @@ -407,8 +406,8 @@ pub struct PeriodDenominationBalance { pub period_id: i64, /// Exact denominated account. Unlike book balances, this quantity is not /// rolled up into parent accounts with a different denomination. - #[prost(string, repeated, tag = "2")] - pub account_segments: ::prost::alloc::vec::Vec<::prost::alloc::string::String>, + #[prost(string, tag = "2")] + pub account: ::prost::alloc::string::String, #[prost(string, tag = "3")] pub denomination_currency: ::prost::alloc::string::String, /// Signed nets use debit-positive convention and remain in the denomination @@ -453,23 +452,17 @@ pub struct MapOpeningBalanceAccountRequest { /// previous_period_id identifies the source profile and period. #[prost(int64, tag = "1")] pub target_period_id: i64, - #[prost(string, repeated, tag = "2")] - pub source_account_segments: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, - #[prost(string, repeated, tag = "3")] - pub target_account_segments: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, + #[prost(string, tag = "2")] + pub source_account: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub target_account: ::prost::alloc::string::String, } #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct UnmapOpeningBalanceAccountRequest { #[prost(int64, tag = "1")] pub target_period_id: i64, - #[prost(string, repeated, tag = "2")] - pub target_account_segments: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, + #[prost(string, tag = "2")] + pub target_account: ::prost::alloc::string::String, } #[derive(Clone, Copy, PartialEq, Eq, Hash, ::prost::Message)] pub struct UnmapOpeningBalanceAccountResponse { @@ -489,16 +482,12 @@ pub struct OpeningBalanceAccountMapping { pub source_period_id: i64, #[prost(string, tag = "3")] pub source_profile_name: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "4")] - pub source_account_segments: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, + #[prost(string, tag = "4")] + pub source_account: ::prost::alloc::string::String, #[prost(string, tag = "5")] pub target_profile_name: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "6")] - pub target_account_segments: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, + #[prost(string, tag = "6")] + pub target_account: ::prost::alloc::string::String, #[prost(string, tag = "7")] pub currency: ::prost::alloc::string::String, /// Signed balances use the debit-positive convention. @@ -536,10 +525,8 @@ pub struct UnmappedSourceBalance { pub source_period_id: i64, #[prost(string, tag = "2")] pub source_profile_name: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "3")] - pub source_account_segments: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, + #[prost(string, tag = "3")] + pub source_account: ::prost::alloc::string::String, #[prost(string, tag = "4")] pub currency: ::prost::alloc::string::String, /// Signed portion not covered by a mapping, using the debit-positive @@ -556,10 +543,8 @@ pub struct UnmappedSourceDenominationBalance { pub source_period_id: i64, #[prost(string, tag = "2")] pub source_profile_name: ::prost::alloc::string::String, - #[prost(string, repeated, tag = "3")] - pub source_account_segments: ::prost::alloc::vec::Vec< - ::prost::alloc::string::String, - >, + #[prost(string, tag = "3")] + pub source_account: ::prost::alloc::string::String, #[prost(string, tag = "4")] pub denomination_currency: ::prost::alloc::string::String, /// Exact foreign-currency quantity that no mapping carries forward. diff --git a/common/src/proto/komp_ac.table_structure.rs b/common/src/proto/komp_ac.table_structure.rs index 1d17cade..6f49055c 100644 --- a/common/src/proto/komp_ac.table_structure.rs +++ b/common/src/proto/komp_ac.table_structure.rs @@ -26,7 +26,7 @@ pub struct GetTableStructureResponse { pub struct TableStructureResponse { /// Columns of the physical table, including system columns (id, deleted, /// created_at, row_revision), user-defined columns, and any foreign-key columns such as - /// named by whoever declared each link, plus the dedicated "account_id" on + /// named by whoever declared each link, plus the dedicated "account" on /// ACCOUNTING-enabled tables. May be empty if the physical table is missing. #[prost(message, repeated, tag = "1")] pub columns: ::prost::alloc::vec::Vec, @@ -34,7 +34,7 @@ pub struct TableStructureResponse { /// One physical column entry as reported by information_schema. #[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] pub struct TableColumn { - /// Column name exactly as defined in PostgreSQL. + /// Public column name. Physical account_id columns are exposed as account. #[prost(string, tag = "1")] pub name: ::prost::alloc::string::String, /// Normalized data type string derived from information_schema: diff --git a/common/src/proto/komp_ac.tables_data.rs b/common/src/proto/komp_ac.tables_data.rs index ef531db7..deebf2d7 100644 --- a/common/src/proto/komp_ac.tables_data.rs +++ b/common/src/proto/komp_ac.tables_data.rs @@ -18,7 +18,7 @@ pub struct PostTableDataRequest { /// * System/FK columns: /// • "deleted" (BOOLEAN), optional; default FALSE if not provided /// • one BIGINT per link, named by whoever declared it - /// • "account_id" (BIGINT) on ACCOUNTING-enabled tables + /// • "account" (slash-delimited account string) on ACCOUNTING-enabled tables /// /// Type expectations by SQL type: /// @@ -191,7 +191,7 @@ pub struct GetTableDataResponse { /// * id, deleted /// * all user-defined columns from the table definition /// * one column per link, named by whoever declared it - /// * account_id for ACCOUNTING-enabled tables + /// * account for ACCOUNTING-enabled tables /// /// All values are returned as TEXT via col::TEXT and COALESCEed to empty string /// (NULL becomes ""). The row is returned only if deleted = FALSE. @@ -482,7 +482,7 @@ pub mod tables_data_client { /// * Validates profile and table definition /// * Returns all columns as strings (COALESCE(col::TEXT, '') AS col) /// including: id, deleted, row_revision, all user-defined columns, and FK columns - /// named by whoever declared each link, plus "account_id" on + /// named by whoever declared each link, plus "account" on /// ACCOUNTING-enabled tables /// * Fails with NOT_FOUND if record does not exist or is soft-deleted /// * If the physical table is missing but the definition exists, returns INTERNAL @@ -675,7 +675,7 @@ pub mod tables_data_server { /// * Validates profile and table definition /// * Returns all columns as strings (COALESCE(col::TEXT, '') AS col) /// including: id, deleted, row_revision, all user-defined columns, and FK columns - /// named by whoever declared each link, plus "account_id" on + /// named by whoever declared each link, plus "account" on /// ACCOUNTING-enabled tables /// * Fails with NOT_FOUND if record does not exist or is soft-deleted /// * If the physical table is missing but the definition exists, returns INTERNAL diff --git a/common/src/system_column.rs b/common/src/system_column.rs index 0a09c3cb..23171634 100644 --- a/common/src/system_column.rs +++ b/common/src/system_column.rs @@ -43,6 +43,7 @@ pub const TRAILING_SYSTEM_COLUMNS: [SystemColumn; 1] = [SystemColumn { /// Its declaration names that profile's schema, so it is built where the /// profile is known rather than spelled out here. pub const ACCOUNT_REFERENCE_COLUMN: &str = "account_id"; +pub const ACCOUNT_API_COLUMN: &str = "account"; /// Every system column name, whether or not the column is on a given table. /// @@ -59,12 +60,13 @@ pub fn system_column_names() -> impl Iterator { /// Whether `name` is a system column: a name that is safe to show a client /// as-is, and that a user may not claim for a column alias or a table. pub fn is_system_column(name: &str) -> bool { - system_column_names().any(|system_name| system_name == name) + name == ACCOUNT_API_COLUMN || system_column_names().any(|system_name| system_name == name) } /// The system column names in a message, as `'id', 'deleted', ...`. pub fn system_column_name_list() -> String { system_column_names() + .chain(std::iter::once(ACCOUNT_API_COLUMN)) .map(|name| format!("'{name}'")) .collect::>() .join(", ") @@ -73,7 +75,8 @@ pub fn system_column_name_list() -> String { #[cfg(test)] mod tests { use super::{ - ACCOUNT_REFERENCE_COLUMN, is_system_column, system_column_name_list, system_column_names, + ACCOUNT_API_COLUMN, ACCOUNT_REFERENCE_COLUMN, is_system_column, system_column_name_list, + system_column_names, }; #[test] @@ -86,6 +89,7 @@ mod tests { #[test] fn a_conditional_column_is_reserved_on_every_table() { assert!(is_system_column(ACCOUNT_REFERENCE_COLUMN)); + assert!(is_system_column(ACCOUNT_API_COLUMN)); } #[test] @@ -98,7 +102,7 @@ mod tests { fn the_name_list_reads_as_a_sentence_fragment() { assert_eq!( system_column_name_list(), - "'id', 'deleted', 'row_revision', 'created_at', 'account_id'" + "'id', 'deleted', 'row_revision', 'created_at', 'account_id', 'account'" ); } } diff --git a/server b/server index 1fe9ffa7..d29f1305 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit 1fe9ffa75942f81d511506d4855f66e945071d69 +Subproject commit d29f130523766cf7beb44e478950aca558fa2b23