From 7c022daab5ee18b007d44d3e1851ca3221832483 Mon Sep 17 00:00:00 2001 From: Priec Date: Tue, 21 Jul 2026 22:55:25 +0200 Subject: [PATCH] map invoice with multioption select --- client | 2 +- common/build.rs | 12 ++ common/proto/table_definition.proto | 43 ++++++ common/src/proto/descriptor.bin | Bin 120423 -> 123218 bytes common/src/proto/komp_ac.table_definition.rs | 146 +++++++++++++++++++ server | 2 +- 6 files changed, 203 insertions(+), 2 deletions(-) diff --git a/client b/client index 1a472c7..087f2af 160000 --- a/client +++ b/client @@ -1 +1 @@ -Subproject commit 1a472c764b689208693748f2371ea501d933b616 +Subproject commit 087f2afaa3bb0fd961327ccbfa9037446e4cdc7c diff --git a/common/build.rs b/common/build.rs index 33b35f6..0074096 100644 --- a/common/build.rs +++ b/common/build.rs @@ -169,6 +169,18 @@ fn main() -> Result<(), Box> { ".komp_ac.table_definition.PostTableDefinitionRequest", "#[derive(serde::Serialize, serde::Deserialize)]", ) + .type_attribute( + ".komp_ac.table_definition.CreateInvoiceTemplateTableRequest", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + ".komp_ac.table_definition.GeneratedInvoiceTemplateTable", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) + .type_attribute( + ".komp_ac.table_definition.CreateInvoiceTemplateTableResponse", + "#[derive(serde::Serialize, serde::Deserialize)]", + ) .type_attribute( ".komp_ac.table_definition.AddTableColumnsRequest", "#[derive(serde::Serialize, serde::Deserialize)]", diff --git a/common/proto/table_definition.proto b/common/proto/table_definition.proto index 0136ef0..a674534 100644 --- a/common/proto/table_definition.proto +++ b/common/proto/table_definition.proto @@ -14,6 +14,11 @@ service TableDefinition { // Also inserts metadata and default validation rules. Entirely transactional. rpc PostTableDefinition(PostTableDefinitionRequest) returns (TableDefinitionResponse); + // Creates a regular user-table bundle from the restricted komp_ac_fields + // contract embedded in a Typst invoice template. This is invoked explicitly + // by the user; templates do not provision tables merely by existing. + rpc CreateInvoiceTemplateTable(CreateInvoiceTemplateTableRequest) returns (CreateInvoiceTemplateTableResponse); + // Appends new user-defined columns to an existing table. // Existing columns, links, and table logic are never changed by this call. rpc AddTableColumns(AddTableColumnsRequest) returns (TableDefinitionResponse); @@ -86,6 +91,44 @@ message PostTableDefinitionRequest { string row_display_column = 7; } +// Defines the input for explicitly creating a table backed by one invoice +// template. typst_source must contain exactly one declaration of the form: +// +// #let komp_ac_fields = ( +// "invoice.number": (source: "local", field_type: "TEXT", write: "user"), +// "items[].product": (source: "reference", table: "stock", required: true), +// "items[].quantity": (source: "local", field_type: "DECIMAL(12,3)", write: "user"), +// "items[].total": (source: "local", field_type: "MONEY", write: "script"), +// ) +// +// Root fields create columns and references on table_name. Every [] collection +// creates a child table with a required FK to its immediate parent; fields in +// that collection create columns and references on the child table. Multiple +// template paths in one scope may reuse the same reference. +message CreateInvoiceTemplateTableRequest { + string profile_name = 1; + string table_name = 2; + string typst_source = 3; + string base_currency = 4; + string row_display_column = 5; +} + +// One physical dynamic table created for an invoice template scope. The root +// has an empty collection_path and parent_table_name. Each [] scope names its +// collection and the generated table that owns those repeated rows. +message GeneratedInvoiceTemplateTable { + string table_name = 1; + string collection_path = 2; + string parent_table_name = 3; + string sql = 4; +} + +// Reports the complete table bundle created from one invoice template. +message CreateInvoiceTemplateTableResponse { + bool success = 1; + repeated GeneratedInvoiceTemplateTable tables = 2; +} + // Defines append-only column additions for an existing table. message AddTableColumnsRequest { // Existing profile/schema name. diff --git a/common/src/proto/descriptor.bin b/common/src/proto/descriptor.bin index 172bac1bf20729c1b47371a235ed660569290d34..2a2ab42c7b1802732a8a480bf711775c047d508e 100644 GIT binary patch delta 8283 zcmai3dvH`$n(uS^_U-Q5gd{g12?>x(18YJs2#F;RA0R={k)RNn861mhIv3MRzv%7& zVQZ(+wXoKuM7d({u`4(tJ2mSTyP?Jx?uw{8y2|2AW*K=cyE+>kS7!i6kewO!_nk*K zHRC##f8=-1_xsNGI^X$DGxB%AAHNpd^+w=}Czw^|?LJyJ)bz%?Y{GG7O zt73I5@2-mX=W=d3KG0xz-}$#zZ{mUaXmk&=#xHWyZVrljziOkws@=>fUSUg_!` zgH0QH|7Z;_{+wBc>Eq1y*YI_XN0}qH%sVkB=ic3)jOCpEyqmjHcWd+Dja@2G*;>~~)Z`9Bav?OCaBxAyDf4z79{RgL0 zbn4A9N{Z9>x@j7LQ-4jjwL@}%n!NqTyy<>$n#@`y)Rq-0oxz-CZG6bdjOQES*?TuphWG-8i5j>extQq zP)yrD#ryt=+uKc0rkFUcA|NIxQ=(lOfih)j&RQiXLEGQrwVb@GJqSum$*>?OEhWQ( zptQiS?+8lB_P2RMCs($IKxr!(76PTMWLOB4HW=1zal>aD(|xnOxu?1-r-!Po_Yo4q z$9=ZH!`pf4cv}Y#k!kb^3OmA$8bL+J_zBiNDUY>T-rxMkKTe&+O(j8@o)xUr2=cS+ z8mkLiGTXS;cc-`MbXVoIp{R9+R503dzW46wb8Yk0miPn}^TRbl1rSusudBBnmGW3y z=za2^J7+EAqPU=fA;t?MBIkriVvIBx2<(M0_#0v~gk!Py@_#Lvy|_f?ATN$Ism-Y5 z7LS{x5y*@GEp9tf|TPLLDsJG?b z;SWzuU0(8(DsOpMR5;iMG%??1o2ZAD3(s%}NNPN@$-e8t#C>nW)o zeysGC{rB$aD@*cJax25yHxu$JCp24oO(cWc##-Mx*7&&T{rTzoV)@iNak_JGcF{Cc z^NGc&G2mEd(yneHR;eGfJD1--PO2Y$hMG_eeaGF1PK1^~#9lX!t_Ia;7@0Mqv8>7D zQgbZJaf;5Qq+9sCUgliqv|tVYMOEKwPxqs#<)U+(7J1vvaiYmgJeG`3b434J1yfvn z>{!7OB4uF1B_d(AWGV!&* zIrRQS&h4G!6mtFUALv`xA4?Y!g@HeAUf03}H!qogYs-w8(>hwo&f?b^TF4aO;vaQ- z$*Z>?w zMTiQ_V-5iih!XfF*H$B@Ig{ z-XP8@=M1z|iAF6dr5YU8{C3fwLWh$)^r&=cI}TpD#3Q@XrR_MjOP96-$Z&boIj63a zDbc7aWmMb18cCvU$lcJCvfL>uGY#;vB_4IBtZE-zbf+xZ$Hxrq!*3hweSgLpPcUaG z&UUtMAfLcFbb1Fc@Fn7^7G#C=IymP@pl%(d&f$oR&T9$(-%WMp>L$Jg@Mbe zK}?*Y2L52UL~+LAeN>w=zcg?n z0mHIZun6A9rg3UJl*0gTRy-<)ffA2&4ydcykHiAurlY+232fr>S^@xVQ;A0IYzmK3 zDybYc6)USSu5iP!1K;)C!&u`@&N4Tf$>G#e)Z&;i(VuZ-Xr|ZGXeqlim0or=l=7>> zM`iTg&{ScVLE$TMdA9?=up&l>BF!3rkSqpA3(J6DS+Uv%GO&i}VKd{MR)vschJcoa zCc#LHhC;Y?x`wFM&1_}|h5(KVH}7M_@P!Jw(WtdwOgs!a&gNuUDwUuFV znIm)oZDZa`AI(s@{LrO2K@5+y_FUn;E6dy`X z(UiBHq1mWakhe2*n^6`I_-yl0=6j4Wl!8BiWE+nnB$VF(jh@CJL*E0B5EcPNWCt>{f-w>qPqI*hMGr8; zq-}RG?~Y%b5E%xBJG-h99!XD7iI4sc!z&EzXlH!;~)z)W5F?uBXKtv& zv&VRwg_^83C=B^gY40$8+)JLBTn~iBcbHXa-C~)vNOm6f_MYjQCjcxx%#1MBgK`z( zhZ%BH`hZ;z)2_1>9|G?&qqebi@X|YMf_L-Ts=1_z&NseWB_dh>9*b0#KS0xa4DX>H zr3vXA_8vLgeJk|CLRy(2m0SbrN#Hh7IDFi<1wVzug45F+a zz1$$m>roaNU*e|iJwu-nj z+dReihu#gJM-g{&{}i*T(RWEXz4eUK-p0=t!Z-jZKV4QXo_+XGik5i!;RB+zK=GmP z1MkA;*WE&Vx&l5ZM@u{d@u9+2#)+38KGcjYe}J_gFtjpJC1lQsSN!!A3qsrnWh1&H z0^%Hvu!t?n4iFoUuo~HH0ikAuVH8@5Dh%7^C(QSm_wlc9@s3;)Dc zr&po#0&AwOVs8h6^om>ywgA>*8rt!fMPXVr4*BEn|VzwjYhOBT)T(M>yZ$+fI%fpzTCq@ zat^^Nxc6{*TS6G2io^ZdARuT3l+FM`_I|DgBS2`qpA1?os91b}d)bQ%x++BdJ-}6x0HNmr z9+6Q16r$CWh3$aQ^8ml1MI>poZ9d3-k8lGs2vk7n7ob>GegT5|5cwsNG@6q1Fz1d^Oi2RN+mhyo zIqHb=09kM!=4c{vTcGV+jxboHBmwGeDM{P8niR1rgfUArTWZ)I#|$93kbR^~4M?b|Gz>pdrsDS|z!DXmVu!Z)>e1W;7g%#?&e~Bn8NWRN z!ep8tDsA&I?t7AZ!@rqVtc2iWJk%x(s-(<4uEP%mrGQc?AY>ocWd#VOkLxt*xK1`nU_nNcl&ZjeMmLr!QFnkk7F2i7aP<(tDyYwJ@erUeB5r&1 zNdb~jQ>pIuaQyH9D~cox?jC(qs>s`CIqy(5ni6UgsqUWT6WaAEsL%2lGnK_b+t{mX zwu%R_Euh|(hS|NkWU&hBUOrkp$hc8$n?K;b{oZdbEvu=9;18&0;vWT!YAT86cEs2n z9rzO(pF=*x?tl<{jt6Bo1BAxssHfi{cv#%G<70L^c+_V0m3dSQ`^r2jhJD2A5j@nw z&|r$O+d8YoMKGjY00eP}hwIh;l=>mw*n|NJC1ab0cA)5#i}X-Ku}+#A`n$= zQ&qpDs{jaGK+Pr1FR3cPKIF?wx(IASFLPe2Y@&V%sHxO1U*=)?41%l>#vCavM%i9> zfOGl2wME?mYA)670Up+xscsMO`UdgaL5*#`%6)GP{;8kcRIGvEs~p1~X|16U9@LEs z2nGR4rGSt^ zJcjHU9=ytAo53V&hRaN{W>{wht6&aOR@z}qoo)V%7 zHs}OdPY3>n%40eTX;m5K@i(pE%44ZInQ&> zd(L@3X5ZP9J-Q?N)GKDqpZd)!M`Y(4FIAa$veDn(+tB~t_peRoS1$>&VMbE@(c}+r zmGS*+K5Q_BbrN`f%1D?FqZj|R_A=4G{DDDLkkQWu)Oe{wY0%{EU}Ma|))@W%*3Rm=f3ML#{bvMmj#$sd$>rQtLUzT6@8nmR97XjAjTbaQm< z;B9RllxDv^aq^%vhhrRq(tO!Vtn$5-(*^VY)54Uf3 z(`7Hr4G%6QveA|-il%-&rb8zHNG6LCO%Se&l9WMcE+XYCED_V+b8~h=x)vNlI*tc4$e`ArLZ4k`950zl07=u`+9-Q%jRh!9!+g(kYNK>C_EY2HLX7KXr96&c=hdth@|u*|MYu#wu_LB?O|$kFlZ>!%u!DHs zJ9Jnuo%W@}%>Ah-I&tdCLE1iD%>B8+!C{?*wDxaBH2w5}D>Z*0`L9Uw2jROSUu|ay z#KKq9)dw$SER!wzQ2*R-Uo^$Ep?ls#0H{tiYQ!bf6zm4aG{w7I19=&kx+6FzN zHc4)T9R!a;dOcC~`Rm&G6Tu!Q2*j~RvNcu^h+vP@hrul@N_UgDC0ccUb_ajL+v2JS zRFmZ6+5q8gk=iSpC%~!2TbJK2K|NQ9itCN#JsYcNxV^miP_CA} zzxY74x)MHmyP`uEr{Ac2A=O=#YVm=Q-zC*ylAOKI_|Pz<@_}r338$5v4NYhF#^1{L zu8c_`wp%#ybn1af?Uq95Ac1#F!%zp2*)7}$bJzra=)Wwfzi-?xH#TrpCX-8p(zJo059F^amr?6mxjKDLFl&Juj#Ey(MO(l>w0-FwPH%?^}`|=B$XmB_moA zD#pDPs_rGAqv|N_txz?ey-HmWhh-b>b8E}leFTix21II~aDVAqfJp6=u-QSR_UY0l zv9x^XzbmPKNNHVKCKmH}zN<%x2FdH%_KS&+aS*D&_?!gdyWbsaKvehZu@+M?z9-yV znnB*4Y`}PJKx@s{IV;rnq+vk7C8}Xr+xxENAi@H(iL)-|eaSgxE_Gni^1PxC#KdC@ zf(mq0JqtdNT-kv*Up{fxx~n(jdj1RA^<(Txfe0Ox@t4>>6+-`z zr2Z|^G`eNZ^52x+_N2Mk7T7Db0Pm3G#@b{Hn(SeB4}ypRjB5bI_ps#RNdXe$uvFJ1 zRE(dybrx)Tz<8ZNYfW}AFkMvdr1S;t+7U4=HhB9csGe};uhIg4CFV+72M}ce#%%_Y?^jZ|#96KL zgv*1W+78S}eI@N3JQ9po9r~vv^}Uof=VV5)8rM^j(;rx#SFH)2=Et?w0g)guP7sLi zY2m%7sz6jv3lGMait$@FmeqE)fbo0)tu+}-Ak=SlEbWOTZUi}F~>WuL0kFx-M=g!S)!>eDRQ%a-sonH1amB}lD8{^@^+mbmrV=1_2 z-C0v@Uk*T53!S{P!rMQV(#bn3oHa3(x}9@-rP}D^0UcE*@0{>c3~G@&>gb(wo26P; zdtOX?BJ9V|rKpp4UWT_hE7bEc>53$5HuNsIeO7IG*{6P8y|@0DwB;5I3Sv<3j6SsZSsSXRP>NpZJLJiv@%|8=s#>y8%hVN%sK-~t_2 zl^-?nNDxQY@~BCU&ss(6j2V#Rq{k=FQT6y-XY%pSB7ChgFU&Y&W_bE~V}>MLJwAbs zt3$lrlX|Q)zea8-*5SC(@C;PdIxXQQlf2nLhyr6#AikSS za*Y5H-DHwmq)r!FGRe6Lf(mq0En&$dzbmmqEg2@%matAs*kh8b00bB4xT@S^lB)nK z+#Zvh=XHuUSEyP-prdLDH&>`y!p%yZLK*5q{|WxJ@la? zKY>sM#)A#S_eqodECizZq)FaS^_uNg_J-{X2r4j62xzUz`w0kjtL}+HCmDsIzs;on zTv}KsQwN85ZZkYqXV92X6MBZmH2tLX_GD=+?WvQo9ehQd&#^iX&NHq-K-8bn2K`Zg zIft5AZ)s?~tc0m~^;VdgSFh_2R+zoopElBJ2>oYGs=u_aUM4g&;P|ZJIdD6Y4VqS8 zsSwJ%cK$}P&s7PCXP>JQ5XnBRlK$Rk&|G$wHiR;HEPq4X>2d+8%_KDfLfrY6FGE?n F_kR{3b1?t_ diff --git a/common/src/proto/komp_ac.table_definition.rs b/common/src/proto/komp_ac.table_definition.rs index 71dc7a9..ca30eae 100644 --- a/common/src/proto/komp_ac.table_definition.rs +++ b/common/src/proto/komp_ac.table_definition.rs @@ -50,6 +50,58 @@ pub struct PostTableDefinitionRequest { #[prost(string, tag = "7")] pub row_display_column: ::prost::alloc::string::String, } +/// Defines the input for explicitly creating a table backed by one invoice +/// template. typst_source must contain exactly one declaration of the form: +/// +/// \#let komp_ac_fields = ( +/// "invoice.number": (source: "local", field_type: "TEXT", write: "user"), +/// "items\[\].product": (source: "reference", table: "stock", required: true), +/// "items\[\].quantity": (source: "local", field_type: "DECIMAL(12,3)", write: "user"), +/// "items\[\].total": (source: "local", field_type: "MONEY", write: "script"), +/// ) +/// +/// Root fields create columns and references on table_name. Every \[\] collection +/// creates a child table with a required FK to its immediate parent; fields in +/// that collection create columns and references on the child table. Multiple +/// template paths in one scope may reuse the same reference. +#[derive(serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct CreateInvoiceTemplateTableRequest { + #[prost(string, tag = "1")] + pub profile_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub table_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub typst_source: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub base_currency: ::prost::alloc::string::String, + #[prost(string, tag = "5")] + pub row_display_column: ::prost::alloc::string::String, +} +/// One physical dynamic table created for an invoice template scope. The root +/// has an empty collection_path and parent_table_name. Each \[\] scope names its +/// collection and the generated table that owns those repeated rows. +#[derive(serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, Eq, Hash, ::prost::Message)] +pub struct GeneratedInvoiceTemplateTable { + #[prost(string, tag = "1")] + pub table_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub collection_path: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub parent_table_name: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub sql: ::prost::alloc::string::String, +} +/// Reports the complete table bundle created from one invoice template. +#[derive(serde::Serialize, serde::Deserialize)] +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateInvoiceTemplateTableResponse { + #[prost(bool, tag = "1")] + pub success: bool, + #[prost(message, repeated, tag = "2")] + pub tables: ::prost::alloc::vec::Vec, +} /// Defines append-only column additions for an existing table. #[derive(serde::Serialize, serde::Deserialize)] #[derive(Clone, PartialEq, ::prost::Message)] @@ -481,6 +533,38 @@ pub mod table_definition_client { ); self.inner.unary(req, path, codec).await } + /// Creates a regular user-table bundle from the restricted komp_ac_fields + /// contract embedded in a Typst invoice template. This is invoked explicitly + /// by the user; templates do not provision tables merely by existing. + pub async fn create_invoice_template_table( + &mut self, + request: impl tonic::IntoRequest, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + > { + self.inner + .ready() + .await + .map_err(|e| { + tonic::Status::unknown( + format!("Service was not ready: {}", e.into()), + ) + })?; + let codec = tonic_prost::ProstCodec::default(); + let path = http::uri::PathAndQuery::from_static( + "/komp_ac.table_definition.TableDefinition/CreateInvoiceTemplateTable", + ); + let mut req = request.into_request(); + req.extensions_mut() + .insert( + GrpcMethod::new( + "komp_ac.table_definition.TableDefinition", + "CreateInvoiceTemplateTable", + ), + ); + self.inner.unary(req, path, codec).await + } /// Appends new user-defined columns to an existing table. /// Existing columns, links, and table logic are never changed by this call. pub async fn add_table_columns( @@ -719,6 +803,16 @@ pub mod table_definition_server { tonic::Response, tonic::Status, >; + /// Creates a regular user-table bundle from the restricted komp_ac_fields + /// contract embedded in a Typst invoice template. This is invoked explicitly + /// by the user; templates do not provision tables merely by existing. + async fn create_invoice_template_table( + &self, + request: tonic::Request, + ) -> std::result::Result< + tonic::Response, + tonic::Status, + >; /// Appends new user-defined columns to an existing table. /// Existing columns, links, and table logic are never changed by this call. async fn add_table_columns( @@ -908,6 +1002,58 @@ pub mod table_definition_server { }; Box::pin(fut) } + "/komp_ac.table_definition.TableDefinition/CreateInvoiceTemplateTable" => { + #[allow(non_camel_case_types)] + struct CreateInvoiceTemplateTableSvc(pub Arc); + impl< + T: TableDefinition, + > tonic::server::UnaryService< + super::CreateInvoiceTemplateTableRequest, + > for CreateInvoiceTemplateTableSvc { + type Response = super::CreateInvoiceTemplateTableResponse; + type Future = BoxFuture< + tonic::Response, + tonic::Status, + >; + fn call( + &mut self, + request: tonic::Request< + super::CreateInvoiceTemplateTableRequest, + >, + ) -> Self::Future { + let inner = Arc::clone(&self.0); + let fut = async move { + ::create_invoice_template_table( + &inner, + request, + ) + .await + }; + Box::pin(fut) + } + } + let accept_compression_encodings = self.accept_compression_encodings; + let send_compression_encodings = self.send_compression_encodings; + let max_decoding_message_size = self.max_decoding_message_size; + let max_encoding_message_size = self.max_encoding_message_size; + let inner = self.inner.clone(); + let fut = async move { + let method = CreateInvoiceTemplateTableSvc(inner); + let codec = tonic_prost::ProstCodec::default(); + let mut grpc = tonic::server::Grpc::new(codec) + .apply_compression_config( + accept_compression_encodings, + send_compression_encodings, + ) + .apply_max_message_size_config( + max_decoding_message_size, + max_encoding_message_size, + ); + let res = grpc.unary(method, req).await; + Ok(res) + }; + Box::pin(fut) + } "/komp_ac.table_definition.TableDefinition/AddTableColumns" => { #[allow(non_camel_case_types)] struct AddTableColumnsSvc(pub Arc); diff --git a/server b/server index d32d24d..e52b2ab 160000 --- a/server +++ b/server @@ -1 +1 @@ -Subproject commit d32d24d447f1999b0a7fb4da75745ebba5aae63f +Subproject commit e52b2ab6e45571abfd515af1e3a891cc5128c42c