ekwo-os 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (361) hide show
  1. package/README.md +873 -0
  2. package/dist/args.d.ts +57 -0
  3. package/dist/args.d.ts.map +1 -0
  4. package/dist/args.js +134 -0
  5. package/dist/args.js.map +1 -0
  6. package/dist/assets/expected-objects.json +9432 -0
  7. package/dist/assets/migrations/20260911120000_core_companies.sql +260 -0
  8. package/dist/assets/migrations/20260911120100_accounts_journals.sql +211 -0
  9. package/dist/assets/migrations/20260911120200_contacts.sql +90 -0
  10. package/dist/assets/migrations/20260911120300_taxes.sql +146 -0
  11. package/dist/assets/migrations/20260911120400_entries.sql +338 -0
  12. package/dist/assets/migrations/20260911120500_documents.sql +279 -0
  13. package/dist/assets/migrations/20260911120600_payments_reconciliation.sql +251 -0
  14. package/dist/assets/migrations/20260911120700_bank.sql +178 -0
  15. package/dist/assets/migrations/20260911120800_analytics_attachments.sql +128 -0
  16. package/dist/assets/migrations/20260911120900_post_document.sql +279 -0
  17. package/dist/assets/migrations/20260911121000_reporting.sql +235 -0
  18. package/dist/assets/migrations/20260911121100_country_templates.sql +226 -0
  19. package/dist/assets/migrations/20260911121200_fec.sql +71 -0
  20. package/dist/assets/migrations/20260911130000_instance.sql +77 -0
  21. package/dist/assets/migrations/20260911130100_instance_members.sql +224 -0
  22. package/dist/assets/migrations/20260911140000_instance_admins.sql +112 -0
  23. package/dist/assets/migrations/20260911160000_tax_posting_templates_unique.sql +17 -0
  24. package/dist/assets/migrations/20260911170000_document_amount_paid.sql +82 -0
  25. package/dist/assets/migrations/20260911173000_post_payment.sql +122 -0
  26. package/dist/assets/migrations/20260911173100_sequence_counters_under_rls.sql +84 -0
  27. package/dist/assets/migrations/20260911183000_country_journal_defaults.sql +143 -0
  28. package/dist/assets/migrations/20260911193853_line_account_defaults.sql +274 -0
  29. package/dist/assets/migrations/20260911195054_products.sql +221 -0
  30. package/dist/assets/migrations/20260911210131_anon_surface.sql +85 -0
  31. package/dist/assets/migrations/20260912074712_country_packs.sql +318 -0
  32. package/dist/assets/migrations/20260912080311_report_code_and_region.sql +198 -0
  33. package/dist/assets/migrations/20260912081014_pack_certification_maintained.sql +31 -0
  34. package/dist/assets/migrations/20260912081015_pack_certification_backfill.sql +16 -0
  35. package/dist/assets/migrations/20260912090407_tax_report_boxes.sql +329 -0
  36. package/dist/assets/migrations/20260912091917_tax_on_base_value.sql +25 -0
  37. package/dist/assets/migrations/20260912091918_tax_engine_columns.sql +776 -0
  38. package/dist/assets/migrations/20260912094412_opening_and_closing.sql +759 -0
  39. package/dist/assets/migrations/20260912095825_charts_of_accounts.sql +326 -0
  40. package/dist/assets/migrations/20260912100412_financial_statements.sql +632 -0
  41. package/dist/assets/migrations/20260912104719_one_formula_evaluator.sql +209 -0
  42. package/dist/assets/migrations/20260912105720_entry_kind_appropriation.sql +26 -0
  43. package/dist/assets/migrations/20260912105721_appropriation_entry_kind.sql +444 -0
  44. package/dist/assets/migrations/20260912111751_document_rules.sql +274 -0
  45. package/dist/assets/migrations/20260912112132_cash_basis_vat_and_fx.sql +1189 -0
  46. package/dist/assets/migrations/20260913074512_modules.sql +493 -0
  47. package/dist/assets/migrations/20260913075903_asset_disposal_roles.sql +47 -0
  48. package/dist/assets/migrations/20260913083216_capabilities.sql +637 -0
  49. package/dist/assets/migrations/20260913083901_company_invitations.sql +262 -0
  50. package/dist/assets/migrations/20260913084402_user_preferences.sql +376 -0
  51. package/dist/assets/migrations/20260913084847_company_profile.sql +261 -0
  52. package/dist/assets/migrations/20260913085436_numbering_reads_the_pack.sql +235 -0
  53. package/dist/assets/migrations/20260913085932_api_keys.sql +303 -0
  54. package/dist/assets/migrations/20260913090216_fiscal_year_bounds.sql +159 -0
  55. package/dist/assets/migrations/20260913092527_entries_import.sql +219 -0
  56. package/dist/assets/migrations/20260913101536_null_safe_guards.sql +188 -0
  57. package/dist/assets/migrations/20260913102115_installer_is_named.sql +462 -0
  58. package/dist/assets/migrations/20260913102758_no_currency_default.sql +138 -0
  59. package/dist/assets/migrations/20260913103355_cash_basis_needs_a_box.sql +453 -0
  60. package/dist/assets/migrations/20260913104014_aged_balance_named_group.sql +79 -0
  61. package/dist/assets/migrations/20260913104232_foreign_key_indexes.sql +144 -0
  62. package/dist/assets/migrations/20260913105120_declared_no_reader.sql +41 -0
  63. package/dist/assets/migrations/20260913111407_pack_languages.sql +223 -0
  64. package/dist/assets/migrations/20260913112233_schema_comments_for_a_reader.sql +27 -0
  65. package/dist/assets/migrations/20260913114535_fec_opening_balances.sql +307 -0
  66. package/dist/assets/migrations/20260914103412_audit_log.sql +509 -0
  67. package/dist/assets/migrations/20260914111907_pack_upgrade.sql +428 -0
  68. package/dist/assets/migrations/20260914120500_rounding_reads_the_currency.sql +206 -0
  69. package/dist/assets/migrations/20260914121200_amounts_round_at_the_currency.sql +2403 -0
  70. package/dist/assets/migrations/20260914134325_schema_version_0_2_0.sql +36 -0
  71. package/dist/assets/migrations/20260914143915_accounts_in_use.sql +428 -0
  72. package/dist/assets/migrations/20260914144731_account_code_frozen.sql +99 -0
  73. package/dist/assets/migrations/20260914151207_schema_grants_its_own_rights.sql +284 -0
  74. package/dist/assets/migrations/20260914152840_pack_upgrade_records_its_own_line.sql +237 -0
  75. package/dist/assets/migrations/20260914163943_declaration_periodicity.sql +349 -0
  76. package/dist/assets/migrations/20260915094000_foreign_services_received.sql +37 -0
  77. package/dist/assets/migrations/20260915094500_mentions_read_a_foreign_service.sql +72 -0
  78. package/dist/assets/migrations/20260915153000_document_shares.sql +568 -0
  79. package/dist/assets/migrations/20260915160000_ec_sales_list.sql +181 -0
  80. package/dist/assets/migrations/20260915161842_pack_sources.sql +56 -0
  81. package/dist/assets/migrations/20260915170500_schema_version_0_3_0.sql +45 -0
  82. package/dist/assets/migrations/20260915174500_a_vat_category_is_a_code.sql +177 -0
  83. package/dist/assets/migrations/20260915180000_document_rule_references.sql +85 -0
  84. package/dist/assets/migrations/20260915181000_territories.sql +342 -0
  85. package/dist/assets/migrations/20260915181500_ec_sales_list_reads_the_territories.sql +222 -0
  86. package/dist/assets/migrations/20260915182000_intracom_triangular.sql +53 -0
  87. package/dist/assets/migrations/20260915182500_a_triangular_supply_says_reverse_charge.sql +99 -0
  88. package/dist/assets/migrations/20260915191200_a_document_knows_its_language.sql +633 -0
  89. package/dist/assets/migrations/20260915195000_a_price_that_holds_its_tax.sql +649 -0
  90. package/dist/assets/migrations/20260915200000_a_posting_names_its_boxes.sql +1004 -0
  91. package/dist/assets/migrations/20260916094500_a_company_files_more_than_one_declaration.sql +795 -0
  92. package/dist/assets/migrations/20260916103000_a_ledger_line_names_its_posting.sql +902 -0
  93. package/dist/assets/migrations/20260916123000_a_vocabulary_for_a_tax_that_is_not_a_vat.sql +144 -0
  94. package/dist/assets/migrations/20260916124000_a_box_can_be_a_rate_of_a_box.sql +525 -0
  95. package/dist/assets/migrations/20260916125000_a_tax_follows_the_territory.sql +1318 -0
  96. package/dist/assets/migrations/20260916126000_a_tax_point_and_its_exception.sql +1211 -0
  97. package/dist/assets/migrations/20260917090000_a_counterparty_that_learns.sql +561 -0
  98. package/dist/assets/migrations/20260917120000_what_the_money_pays.sql +545 -0
  99. package/dist/assets/migrations/20260917150000_a_filing_is_a_row.sql +488 -0
  100. package/dist/assets/migrations/20260917170000_when_it_is_due.sql +194 -0
  101. package/dist/assets/migrations/20260917180000_a_box_is_a_number_and_a_kind.sql +213 -0
  102. package/dist/assets/migrations/20260917190000_what_a_declaration_owes.sql +486 -0
  103. package/dist/assets/migrations/20260917200000_what_moved_after_it_went.sql +227 -0
  104. package/dist/assets/migrations/20260918090000_a_form_names_its_file.sql +27 -0
  105. package/dist/assets/migrations/20260918110000_a_deposit_and_what_came_back.sql +286 -0
  106. package/dist/assets/migrations/20260918113741_a_fourth_preset_is_named.sql +9 -0
  107. package/dist/assets/migrations/20260918113807_a_client_reads_and_hands_over.sql +96 -0
  108. package/dist/assets/migrations/20260918114322_two_definer_functions_check_their_caller.sql +75 -0
  109. package/dist/assets/migrations/20260918130000_every_company_somebody_keeps.sql +177 -0
  110. package/dist/assets/migrations/20260918140000_a_session_nobody_prepared.sql +40 -0
  111. package/dist/assets/migrations/20260918141107_a_line_keeps_the_tax_it_was_posted_with.sql +256 -0
  112. package/dist/assets/migrations/20260918141342_a_company_has_an_electronic_address.sql +66 -0
  113. package/dist/assets/migrations/20260918141605_an_invoice_reads_whole_from_the_views.sql +313 -0
  114. package/dist/assets/migrations/20260918141627_a_policy_asks_once.sql +132 -0
  115. package/dist/assets/migrations/20260918143352_a_suggestion_reads_the_words_once.sql +204 -0
  116. package/dist/assets/migrations/20260918143417_a_reference_of_the_caller_and_a_rehearsal.sql +115 -0
  117. package/dist/assets/migrations/20260918150712_a_company_leaves_with_its_books.sql +1119 -0
  118. package/dist/assets/migrations/20260918150931_a_statement_is_imported_once.sql +654 -0
  119. package/dist/assets/migrations/20260918161204_a_posted_document_does_not_move.sql +437 -0
  120. package/dist/assets/migrations/20260918161538_a_posted_entry_does_not_move.sql +252 -0
  121. package/dist/assets/migrations/20260918171946_an_entry_is_posted_by_post_entry.sql +289 -0
  122. package/dist/assets/migrations/20260918174312_schema_version_0_4_0.sql +53 -0
  123. package/dist/assets/modules/assets/module.json +13 -0
  124. package/dist/assets/modules/assets/supabase/migrations/20260913081447_assets.sql +1190 -0
  125. package/dist/assets/modules/assets/supabase/migrations/20260913104234_foreign_key_indexes.sql +47 -0
  126. package/dist/assets/modules/assets/supabase/migrations/20260913104500_assets_capabilities.sql +145 -0
  127. package/dist/assets/modules/assets/supabase/migrations/20260914122100_assets_round_at_the_currency.sql +564 -0
  128. package/dist/assets/modules/assets/supabase/migrations/20260914145012_assets_accounts_in_use.sql +37 -0
  129. package/dist/assets/modules/assets/supabase/migrations/20260914151530_assets_grants_its_own_rights.sql +63 -0
  130. package/dist/assets/modules/assets/supabase/migrations/20260918113912_assets_client_reads.sql +20 -0
  131. package/dist/assets/modules/assets/supabase/migrations/20260918150801_assets_leave_with_the_company.sql +41 -0
  132. package/dist/assets/modules/budgets/module.json +12 -0
  133. package/dist/assets/modules/budgets/supabase/migrations/20260913083012_budgets.sql +210 -0
  134. package/dist/assets/modules/budgets/supabase/migrations/20260913104233_foreign_key_indexes.sql +37 -0
  135. package/dist/assets/modules/budgets/supabase/migrations/20260913104501_budgets_capabilities.sql +72 -0
  136. package/dist/assets/modules/budgets/supabase/migrations/20260914122400_budgets_round_at_the_currency.sql +68 -0
  137. package/dist/assets/modules/budgets/supabase/migrations/20260914145048_budgets_accounts_in_use.sql +30 -0
  138. package/dist/assets/modules/budgets/supabase/migrations/20260914151812_budgets_grants_its_own_rights.sql +31 -0
  139. package/dist/assets/modules/budgets/supabase/migrations/20260918113954_budgets_client_reads.sql +20 -0
  140. package/dist/assets/modules/budgets/supabase/migrations/20260918150823_budgets_leave_with_the_company.sql +37 -0
  141. package/dist/assets/modules/schema/module.1.json +69 -0
  142. package/dist/assets/seed/00_currencies.sql +15 -0
  143. package/dist/assets/seed/00_territories.sql +280 -0
  144. package/dist/assets/seed/05_framework_generic.sql +110 -0
  145. package/dist/assets/seed/10_pack_be.sql +1284 -0
  146. package/dist/assets/seed/11_pack_fr.sql +1112 -0
  147. package/dist/assets/seed/12_pack_lu.sql +2484 -0
  148. package/dist/assets/seed/13_pack_ee.sql +733 -0
  149. package/dist/assets/seed/14_pack_gb.sql +796 -0
  150. package/dist/assets/seed/15_pack_us.sql +751 -0
  151. package/dist/assets/seed/90_demo_company.sql +368 -0
  152. package/dist/assets/seed/modules/assets/10_pack_be.sql +58 -0
  153. package/dist/assets/seed/modules/assets/11_pack_fr.sql +57 -0
  154. package/dist/assets/seed/modules/assets/14_pack_gb.sql +58 -0
  155. package/dist/assets/seed/modules/assets/15_pack_us.sql +60 -0
  156. package/dist/auth.d.ts +59 -0
  157. package/dist/auth.d.ts.map +1 -0
  158. package/dist/auth.js +134 -0
  159. package/dist/auth.js.map +1 -0
  160. package/dist/bin.d.ts +6 -0
  161. package/dist/bin.d.ts.map +1 -0
  162. package/dist/bin.js +8 -0
  163. package/dist/bin.js.map +1 -0
  164. package/dist/books.d.ts +60 -0
  165. package/dist/books.d.ts.map +1 -0
  166. package/dist/books.js +168 -0
  167. package/dist/books.js.map +1 -0
  168. package/dist/bootstrap.d.ts +236 -0
  169. package/dist/bootstrap.d.ts.map +1 -0
  170. package/dist/bootstrap.js +459 -0
  171. package/dist/bootstrap.js.map +1 -0
  172. package/dist/bundle.d.ts +27 -0
  173. package/dist/bundle.d.ts.map +1 -0
  174. package/dist/bundle.js +47 -0
  175. package/dist/bundle.js.map +1 -0
  176. package/dist/checklist.d.ts +35 -0
  177. package/dist/checklist.d.ts.map +1 -0
  178. package/dist/checklist.js +64 -0
  179. package/dist/checklist.js.map +1 -0
  180. package/dist/cli.d.ts +14 -0
  181. package/dist/cli.d.ts.map +1 -0
  182. package/dist/cli.js +318 -0
  183. package/dist/cli.js.map +1 -0
  184. package/dist/commands/company.d.ts +59 -0
  185. package/dist/commands/company.d.ts.map +1 -0
  186. package/dist/commands/company.js +287 -0
  187. package/dist/commands/company.js.map +1 -0
  188. package/dist/commands/contact.d.ts +9 -0
  189. package/dist/commands/contact.d.ts.map +1 -0
  190. package/dist/commands/contact.js +82 -0
  191. package/dist/commands/contact.js.map +1 -0
  192. package/dist/commands/demo.d.ts +19 -0
  193. package/dist/commands/demo.d.ts.map +1 -0
  194. package/dist/commands/demo.js +72 -0
  195. package/dist/commands/demo.js.map +1 -0
  196. package/dist/commands/doctor.d.ts +12 -0
  197. package/dist/commands/doctor.d.ts.map +1 -0
  198. package/dist/commands/doctor.js +68 -0
  199. package/dist/commands/doctor.js.map +1 -0
  200. package/dist/commands/document.d.ts +17 -0
  201. package/dist/commands/document.d.ts.map +1 -0
  202. package/dist/commands/document.js +217 -0
  203. package/dist/commands/document.js.map +1 -0
  204. package/dist/commands/init.d.ts +31 -0
  205. package/dist/commands/init.d.ts.map +1 -0
  206. package/dist/commands/init.js +535 -0
  207. package/dist/commands/init.js.map +1 -0
  208. package/dist/commands/login.d.ts +24 -0
  209. package/dist/commands/login.d.ts.map +1 -0
  210. package/dist/commands/login.js +128 -0
  211. package/dist/commands/login.js.map +1 -0
  212. package/dist/commands/migrate.d.ts +30 -0
  213. package/dist/commands/migrate.d.ts.map +1 -0
  214. package/dist/commands/migrate.js +115 -0
  215. package/dist/commands/migrate.js.map +1 -0
  216. package/dist/commands/module.d.ts +32 -0
  217. package/dist/commands/module.d.ts.map +1 -0
  218. package/dist/commands/module.js +285 -0
  219. package/dist/commands/module.js.map +1 -0
  220. package/dist/commands/pack.d.ts +22 -0
  221. package/dist/commands/pack.d.ts.map +1 -0
  222. package/dist/commands/pack.js +481 -0
  223. package/dist/commands/pack.js.map +1 -0
  224. package/dist/commands/payment.d.ts +15 -0
  225. package/dist/commands/payment.d.ts.map +1 -0
  226. package/dist/commands/payment.js +97 -0
  227. package/dist/commands/payment.js.map +1 -0
  228. package/dist/commands/register.d.ts +20 -0
  229. package/dist/commands/register.d.ts.map +1 -0
  230. package/dist/commands/register.js +134 -0
  231. package/dist/commands/register.js.map +1 -0
  232. package/dist/commands/status.d.ts +8 -0
  233. package/dist/commands/status.d.ts.map +1 -0
  234. package/dist/commands/status.js +134 -0
  235. package/dist/commands/status.js.map +1 -0
  236. package/dist/commands/whoami.d.ts +21 -0
  237. package/dist/commands/whoami.d.ts.map +1 -0
  238. package/dist/commands/whoami.js +133 -0
  239. package/dist/commands/whoami.js.map +1 -0
  240. package/dist/company.d.ts +16 -0
  241. package/dist/company.d.ts.map +1 -0
  242. package/dist/company.js +26 -0
  243. package/dist/company.js.map +1 -0
  244. package/dist/config.d.ts +24 -0
  245. package/dist/config.d.ts.map +1 -0
  246. package/dist/config.js +50 -0
  247. package/dist/config.js.map +1 -0
  248. package/dist/connection.d.ts +79 -0
  249. package/dist/connection.d.ts.map +1 -0
  250. package/dist/connection.js +116 -0
  251. package/dist/connection.js.map +1 -0
  252. package/dist/context.d.ts +47 -0
  253. package/dist/context.d.ts.map +1 -0
  254. package/dist/context.js +93 -0
  255. package/dist/context.js.map +1 -0
  256. package/dist/doctor.d.ts +45 -0
  257. package/dist/doctor.d.ts.map +1 -0
  258. package/dist/doctor.js +448 -0
  259. package/dist/doctor.js.map +1 -0
  260. package/dist/grants.d.ts +88 -0
  261. package/dist/grants.d.ts.map +1 -0
  262. package/dist/grants.js +174 -0
  263. package/dist/grants.js.map +1 -0
  264. package/dist/identity.d.ts +54 -0
  265. package/dist/identity.d.ts.map +1 -0
  266. package/dist/identity.js +132 -0
  267. package/dist/identity.js.map +1 -0
  268. package/dist/index.d.ts +44 -0
  269. package/dist/index.d.ts.map +1 -0
  270. package/dist/index.js +43 -0
  271. package/dist/index.js.map +1 -0
  272. package/dist/inventory.d.ts +155 -0
  273. package/dist/inventory.d.ts.map +1 -0
  274. package/dist/inventory.js +303 -0
  275. package/dist/inventory.js.map +1 -0
  276. package/dist/migrations.d.ts +76 -0
  277. package/dist/migrations.d.ts.map +1 -0
  278. package/dist/migrations.js +226 -0
  279. package/dist/migrations.js.map +1 -0
  280. package/dist/module/read.d.ts +91 -0
  281. package/dist/module/read.d.ts.map +1 -0
  282. package/dist/module/read.js +172 -0
  283. package/dist/module/read.js.map +1 -0
  284. package/dist/output.d.ts +110 -0
  285. package/dist/output.d.ts.map +1 -0
  286. package/dist/output.js +173 -0
  287. package/dist/output.js.map +1 -0
  288. package/dist/pack/certification.d.ts +27 -0
  289. package/dist/pack/certification.d.ts.map +1 -0
  290. package/dist/pack/certification.js +38 -0
  291. package/dist/pack/certification.js.map +1 -0
  292. package/dist/pack/compile.d.ts +75 -0
  293. package/dist/pack/compile.d.ts.map +1 -0
  294. package/dist/pack/compile.js +801 -0
  295. package/dist/pack/compile.js.map +1 -0
  296. package/dist/pack/filing.d.ts +48 -0
  297. package/dist/pack/filing.d.ts.map +1 -0
  298. package/dist/pack/filing.js +56 -0
  299. package/dist/pack/filing.js.map +1 -0
  300. package/dist/pack/read.d.ts +766 -0
  301. package/dist/pack/read.d.ts.map +1 -0
  302. package/dist/pack/read.js +2336 -0
  303. package/dist/pack/read.js.map +1 -0
  304. package/dist/pack/schema.d.ts +26 -0
  305. package/dist/pack/schema.d.ts.map +1 -0
  306. package/dist/pack/schema.js +181 -0
  307. package/dist/pack/schema.js.map +1 -0
  308. package/dist/pack/territories.d.ts +98 -0
  309. package/dist/pack/territories.d.ts.map +1 -0
  310. package/dist/pack/territories.js +0 -0
  311. package/dist/pack/territories.js.map +1 -0
  312. package/dist/pack/upgrade.d.ts +86 -0
  313. package/dist/pack/upgrade.d.ts.map +1 -0
  314. package/dist/pack/upgrade.js +76 -0
  315. package/dist/pack/upgrade.js.map +1 -0
  316. package/dist/pack/vat-codes.d.ts +273 -0
  317. package/dist/pack/vat-codes.d.ts.map +1 -0
  318. package/dist/pack/vat-codes.js +513 -0
  319. package/dist/pack/vat-codes.js.map +1 -0
  320. package/dist/profiles.d.ts +67 -0
  321. package/dist/profiles.d.ts.map +1 -0
  322. package/dist/profiles.js +187 -0
  323. package/dist/profiles.js.map +1 -0
  324. package/dist/prompt.d.ts +40 -0
  325. package/dist/prompt.d.ts.map +1 -0
  326. package/dist/prompt.js +120 -0
  327. package/dist/prompt.js.map +1 -0
  328. package/dist/registry.d.ts +84 -0
  329. package/dist/registry.d.ts.map +1 -0
  330. package/dist/registry.js +104 -0
  331. package/dist/registry.js.map +1 -0
  332. package/dist/rest.d.ts +81 -0
  333. package/dist/rest.d.ts.map +1 -0
  334. package/dist/rest.js +245 -0
  335. package/dist/rest.js.map +1 -0
  336. package/dist/schema.d.ts +15 -0
  337. package/dist/schema.d.ts.map +1 -0
  338. package/dist/schema.js +15 -0
  339. package/dist/schema.js.map +1 -0
  340. package/dist/seeds.d.ts +27 -0
  341. package/dist/seeds.d.ts.map +1 -0
  342. package/dist/seeds.js +47 -0
  343. package/dist/seeds.js.map +1 -0
  344. package/dist/session.d.ts +39 -0
  345. package/dist/session.d.ts.map +1 -0
  346. package/dist/session.js +92 -0
  347. package/dist/session.js.map +1 -0
  348. package/dist/sql.d.ts +48 -0
  349. package/dist/sql.d.ts.map +1 -0
  350. package/dist/sql.js +112 -0
  351. package/dist/sql.js.map +1 -0
  352. package/dist/status.d.ts +94 -0
  353. package/dist/status.d.ts.map +1 -0
  354. package/dist/status.js +120 -0
  355. package/dist/status.js.map +1 -0
  356. package/dist/ui.d.ts +55 -0
  357. package/dist/ui.d.ts.map +1 -0
  358. package/dist/ui.js +114 -0
  359. package/dist/ui.js.map +1 -0
  360. package/package.json +29 -0
  361. package/schema/output.1.json +613 -0
@@ -0,0 +1,633 @@
1
+ -- Ekwo OS — a document records the language it was written in.
2
+ --
3
+ -- Until here it did not. The language of an invoice was re-derived on every
4
+ -- read, from the customer's preference, then the company's, then the one the
5
+ -- country pack declares — and re-derived is the whole problem. A customer who
6
+ -- switches to another language rewrites every invoice ever sent to them: the
7
+ -- legal mentions at the foot of a document somebody filed last year come back
8
+ -- in a language that document was never written in. On the one part of an
9
+ -- invoice a country actually legislates, that is not a display preference, it
10
+ -- is a different document.
11
+ --
12
+ -- `documents.language` is the fix, and it is the same fix the lines already
13
+ -- have. `document_lines.vat_category` and `vat_rate` are snapshots — the value
14
+ -- the tax had when the line was written — so that a rate change cannot rewrite
15
+ -- history. The language is that, for the sentences.
16
+ --
17
+ -- Four decisions.
18
+ --
19
+ -- **The column is written from the chain that already existed, not from a
20
+ -- literal.** The customer's language, else the company's, else the one the
21
+ -- pack declares. No country and no language is named anywhere in this file:
22
+ -- `companies.language` is not null — it is filled from the pack when the
23
+ -- company is created — so the chain always ends somewhere, and it ends in
24
+ -- data.
25
+ --
26
+ -- **It stops moving when the document is posted**, and not before. A draft is
27
+ -- not a document anybody has seen: it carries no number, no entry, and the
28
+ -- customer it is addressed to may still change. So while a document is a
29
+ -- draft its language is the chain's answer, kept in step when the chain moves
30
+ -- under it; the moment it is posted it is the document's own, and the guard
31
+ -- refuses to move it. That is the shape `accounts_guard_frozen` already has
32
+ -- for the code of an account, and the reason is the same: a policy judges
33
+ -- which company a row belongs to and has nothing to say about which column
34
+ -- changes.
35
+ --
36
+ -- **`preferred_languages()` gains the overload it was missing.** The published
37
+ -- way of choosing a language starts at `user_preferences` for `auth.uid()`,
38
+ -- which is null for the one reader a shared link exists for, so
39
+ -- `shared_document()` wrote the chain out a second time inside itself. The
40
+ -- chain is not about a *user*, it is about a *starting point*: a person's
41
+ -- preference, or a document's own language. `preferred_languages(language,
42
+ -- company)` takes that starting point explicitly, and the function that takes
43
+ -- a company alone is now one line on top of it — the signed-in reader's
44
+ -- preference is simply the starting point a session supplies.
45
+ --
46
+ -- **The reader of the sentences reads the document.**
47
+ -- `document_legal_mentions` published the pack's own text and the object of
48
+ -- translations beside it, and left every caller to pick a language; the only
49
+ -- caller in this repository picked it from the contact of the day. The view
50
+ -- now answers in the document's language, because it is a view *about a
51
+ -- document*, and keeps `text_i18n` for a renderer that wants a second one.
52
+ --
53
+ -- Nothing in a pack moves: this is a column, a function and two views. The
54
+ -- golden files are identical to the byte.
55
+
56
+ -- ---------------------------------------------------------------------------
57
+ -- 1. The chain, from a starting point that is not a session
58
+ --
59
+ -- `stable security definer`, like the function it now carries: it answers
60
+ -- about a company the caller is not necessarily a member of — which is exactly
61
+ -- the case of somebody holding a link to one invoice — and what comes out is
62
+ -- two-letter language codes and nothing else.
63
+ --
64
+ -- Neither argument carries a default. A one-argument call would then be
65
+ -- ambiguous between this function and the one below it, and an ambiguity that
66
+ -- resolves on the type of an untyped literal is a call that works until
67
+ -- somebody writes it differently.
68
+ -- ---------------------------------------------------------------------------
69
+
70
+ create or replace function preferred_languages(p_language text, p_company_id uuid)
71
+ returns text[]
72
+ language sql
73
+ stable
74
+ security definer
75
+ set search_path = public, pg_temp
76
+ as $$
77
+ select array_remove(array[
78
+ p_language,
79
+ (select c.language from companies c where c.id = p_company_id),
80
+ (select d.language_default
81
+ from companies c join country_defaults d on d.country = c.country
82
+ where c.id = p_company_id)
83
+ ], null);
84
+ $$;
85
+
86
+ comment on function preferred_languages(text, uuid) is
87
+ 'The languages to try, in order, from a starting point the caller names: that one, then the company''s, then the one the country pack declares. Feed it to label_for(). The starting point is a person''s preference for a reader who is signed in, and documents.language for a document being rendered.';
88
+
89
+ -- The published form, now one line on top of the one above. A signed-in
90
+ -- reader's preference is a starting point like any other; what was specific to
91
+ -- it was never the chain, only where the chain began.
92
+ create or replace function preferred_languages(p_company_id uuid default null)
93
+ returns text[]
94
+ language sql
95
+ stable
96
+ security definer
97
+ set search_path = public, pg_temp
98
+ as $$
99
+ select preferred_languages(
100
+ (select p.language from user_preferences p where p.user_id = auth.uid()),
101
+ p_company_id);
102
+ $$;
103
+
104
+ comment on function preferred_languages(uuid) is
105
+ 'The languages to try, in order: the signed-in user''s own, then the company''s, then the one the country pack declares. The particular case of preferred_languages(language, company) where the starting point comes from the session.';
106
+
107
+ -- ---------------------------------------------------------------------------
108
+ -- 2. The column
109
+ --
110
+ -- `char(2)`, which is what `contacts.language` and `companies.language` are: a
111
+ -- language here is ISO 639-1, two characters by definition, and the padding
112
+ -- that made `char(2)` wrong for a VAT category cannot happen to a value that
113
+ -- is always exactly two long.
114
+ --
115
+ -- Not null, once the backfill below has filled what exists. A document that
116
+ -- did not know its own language is the thing this migration exists to remove,
117
+ -- and a nullable column would leave every reader with a coalesce to write —
118
+ -- which is the chain, written a third time, in as many places as there are
119
+ -- renderers.
120
+ -- ---------------------------------------------------------------------------
121
+
122
+ alter table documents
123
+ add column if not exists language char(2);
124
+
125
+ comment on column documents.language is
126
+ 'The language this document is written in: its legal mentions, and whatever a renderer prints from the labels of the schema. Taken from the customer, else the company, else the country pack when the document is created, kept in step while it is a draft, and frozen the moment it is posted — a document already sent is not rewritten because its customer later changed preference.';
127
+
128
+ -- ---------------------------------------------------------------------------
129
+ -- 3. What the documents already here were written in
130
+ --
131
+ -- The same chain, applied once. It is a guess and it is worth saying so: for a
132
+ -- document already sent, nobody knows what language the PDF in the customer's
133
+ -- inbox was in — the only evidence the installation holds is the chain, which
134
+ -- is what produced that PDF at the time unless somebody has since changed a
135
+ -- preference. This is the best thing that can be said about a past document,
136
+ -- and from here on nothing is guessed.
137
+ -- ---------------------------------------------------------------------------
138
+
139
+ update documents d
140
+ set language = (preferred_languages(ct.language, d.company_id))[1]
141
+ from contacts ct
142
+ where ct.id = d.contact_id
143
+ and d.language is null;
144
+
145
+ alter table documents
146
+ alter column language set not null;
147
+
148
+ -- ---------------------------------------------------------------------------
149
+ -- 4. Writing it, and refusing to rewrite it
150
+ --
151
+ -- One function on both events, because it is one rule read twice: the language
152
+ -- of a document is the chain's answer until the document is posted, and the
153
+ -- document's own afterwards.
154
+ --
155
+ -- on insert the caller's value stands; where there is none, the chain
156
+ -- answers. `contact_id` is not null on this table, so there is
157
+ -- always a customer to start from.
158
+ -- on update a draft may be given another language by hand, and follows the
159
+ -- chain again when the customer under it changes. A document
160
+ -- that is no longer a draft keeps the language it was sent in,
161
+ -- and says so by name.
162
+ --
163
+ -- `cancelled` is not a draft either. A cancelled invoice was sent before it
164
+ -- was cancelled, and the credit note that undoes it quotes it.
165
+ -- ---------------------------------------------------------------------------
166
+
167
+ create or replace function documents_guard_language()
168
+ returns trigger
169
+ language plpgsql
170
+ as $$
171
+ declare
172
+ v_contact_language text;
173
+ begin
174
+ if tg_op = 'INSERT' then
175
+ if new.language is null then
176
+ select ct.language into v_contact_language
177
+ from contacts ct where ct.id = new.contact_id;
178
+ new.language := (preferred_languages(v_contact_language, new.company_id))[1];
179
+ end if;
180
+ return new;
181
+ end if;
182
+
183
+ if old.state <> 'draft' then
184
+ if new.language is distinct from old.language then
185
+ raise exception 'document_language_frozen: % was sent in %, so it stays in %. A document already issued is not rewritten because its customer changed preference; issue the new one in the new language.',
186
+ coalesce(new.number, old.number, new.id::text), old.language, old.language
187
+ using errcode = '55006';
188
+ end if;
189
+ return new;
190
+ end if;
191
+
192
+ -- A draft whose customer changes follows the new customer, unless the same
193
+ -- statement names a language itself.
194
+ if new.contact_id is distinct from old.contact_id
195
+ and new.language is not distinct from old.language then
196
+ select ct.language into v_contact_language
197
+ from contacts ct where ct.id = new.contact_id;
198
+ new.language := (preferred_languages(v_contact_language, new.company_id))[1];
199
+ end if;
200
+
201
+ return new;
202
+ end;
203
+ $$;
204
+
205
+ comment on function documents_guard_language() is
206
+ 'Fills documents.language from the customer, then the company, then the country pack when a document is created, keeps a draft in step with the customer it is addressed to, and refuses document_language_frozen on anything that is no longer a draft.';
207
+
208
+ create trigger documents_guard_language
209
+ before insert or update on documents
210
+ for each row execute function documents_guard_language();
211
+
212
+ -- A trigger body is invoked by its table, never called.
213
+ revoke execute on function documents_guard_language() from public, anon, authenticated, service_role;
214
+
215
+ -- ---------------------------------------------------------------------------
216
+ -- 5. A customer who changes language, and the drafts addressed to them
217
+ --
218
+ -- The other half of "a draft follows the chain": the chain moves when the
219
+ -- customer moves, and a draft that is not touched again would keep an answer
220
+ -- that is no longer the chain's. Bounded to drafts of that one customer, and
221
+ -- to the one column — `documents_contact_idx` is the index it reads.
222
+ --
223
+ -- `security definer`, and the reason is worth writing down: the caller is
224
+ -- somebody editing a contact, and editing a contact is `contacts.write`, which
225
+ -- is not `documents.write`. A member allowed to correct a customer's record
226
+ -- should not be refused because a draft of theirs sits behind a policy the
227
+ -- member does not satisfy, and should not thereby gain the right to write a
228
+ -- document either. What this function writes is derived data, in one column,
229
+ -- on rows nothing has issued yet.
230
+ -- ---------------------------------------------------------------------------
231
+
232
+ create or replace function contacts_language_reaches_drafts()
233
+ returns trigger
234
+ language plpgsql
235
+ security definer
236
+ set search_path = public, pg_temp
237
+ as $$
238
+ begin
239
+ update documents d
240
+ set language = (preferred_languages(new.language, d.company_id))[1]
241
+ where d.contact_id = new.id
242
+ and d.state = 'draft'
243
+ and d.language is distinct from (preferred_languages(new.language, d.company_id))[1];
244
+ return null;
245
+ end;
246
+ $$;
247
+
248
+ comment on function contacts_language_reaches_drafts() is
249
+ 'A customer who changes language changes the drafts addressed to them, and nothing else: a posted document keeps the language it was sent in.';
250
+
251
+ create trigger contacts_language_reaches_drafts
252
+ after update of language on contacts
253
+ for each row
254
+ when (new.language is distinct from old.language)
255
+ execute function contacts_language_reaches_drafts();
256
+
257
+ revoke execute on function contacts_language_reaches_drafts() from public, anon, authenticated, service_role;
258
+
259
+ -- ---------------------------------------------------------------------------
260
+ -- 6. The sentences, in the language of the document they are on
261
+ --
262
+ -- `document_legal_mentions` handed back `text` — the pack's own wording — and
263
+ -- `text_i18n` beside it, and left the choice of a language to whoever read it.
264
+ -- There was one reader, and it chose from the contact of the day.
265
+ --
266
+ -- The view is about a document, so it answers in the document's language, and
267
+ -- `language` is published beside the sentence so that a reader can see which
268
+ -- one it picked. `label_for` is fed the whole chain rather than the one
269
+ -- language, so a pack that has not translated a mention into the customer's
270
+ -- language falls back to the company's and then to its own wording instead of
271
+ -- printing nothing — which is what `label_for` was built to do.
272
+ --
273
+ -- `text_i18n` stays. A renderer printing a bilingual invoice — which several
274
+ -- countries' customers ask for — has every language of the pack in it and does
275
+ -- not have to go back to the templates for the second one.
276
+ --
277
+ -- Dropped and recreated rather than replaced: a column moves, and `create or
278
+ -- replace view` may only add at the end. The grants the drop takes away are
279
+ -- given back by name in section 8, which is the arrangement
280
+ -- `20260915174500` used for the same reason.
281
+ -- ---------------------------------------------------------------------------
282
+
283
+ drop view if exists document_legal_mentions;
284
+
285
+ create view document_legal_mentions
286
+ with (security_invoker = true) as
287
+ with document_taxes as (
288
+ select d.id as document_id,
289
+ d.company_id,
290
+ c.fiscal_country as country,
291
+ d.document_date,
292
+ d.doc_type,
293
+ d.language,
294
+ coalesce(
295
+ array_agg(distinct t.treatment::text) filter (where t.treatment is not null),
296
+ array[]::text[]
297
+ ) as treatments,
298
+ coalesce(bool_or(t.cash_basis), false) as on_cash_basis
299
+ from documents d
300
+ join companies c on c.id = d.company_id
301
+ left join document_lines l on l.document_id = d.id
302
+ left join taxes t on t.id = l.tax_id
303
+ group by d.id, d.company_id, c.fiscal_country, d.document_date, d.doc_type, d.language
304
+ )
305
+ select dt.document_id,
306
+ dt.company_id,
307
+ m.country,
308
+ m.code,
309
+ m.applies_when,
310
+ dt.language,
311
+ label_for(m.text, m.text_i18n,
312
+ preferred_languages(dt.language, dt.company_id)) as text,
313
+ m.text_i18n,
314
+ m.sequence,
315
+ m.legal_reference
316
+ from document_taxes dt
317
+ join legal_mention_templates m
318
+ on m.country = dt.country
319
+ and m.valid_from <= dt.document_date
320
+ and (m.valid_to is null or m.valid_to >= dt.document_date)
321
+ where case m.applies_when
322
+ when 'always' then true
323
+ when 'reverse_charge' then dt.treatments
324
+ && array['domestic_reverse_charge',
325
+ 'foreign_services_received',
326
+ 'intracom_triangular']
327
+ when 'intra_eu_goods' then dt.treatments && array['intracom_goods', 'intracom_acquisition_goods']
328
+ when 'intra_eu_services' then dt.treatments && array['intracom_services', 'intracom_acquisition_services']
329
+ when 'export' then dt.treatments && array['export']
330
+ when 'exempt' then dt.treatments && array['exempt']
331
+ when 'cash_basis' then dt.on_cash_basis
332
+ when 'late_payment' then dt.doc_type in ('sale_invoice', 'sale_credit_note')
333
+ else false
334
+ end;
335
+
336
+ comment on view document_legal_mentions is
337
+ 'The legal mentions that apply to a document, each in the language the document was written in: its country, the validity of the mention on its date, and the treatments of the taxes its lines carry. A reverse-charge mention comes out for a domestic reverse charge, for a service received from a supplier who is not established here, and for the middle supply of a triangular arrangement — three articles behind one sentence, which is that the customer owes the tax. A small_business mention never comes out, because no column records the regime.';
338
+
339
+ -- ---------------------------------------------------------------------------
340
+ -- 7. The header a renderer reads once
341
+ --
342
+ -- It already carried everything printed above the lines except the one thing
343
+ -- that decides how the rest of it is worded. Appended at the end, which is
344
+ -- what `create or replace view` allows and what keeps the grants and the
345
+ -- comment of the object it replaces.
346
+ -- ---------------------------------------------------------------------------
347
+
348
+ create or replace view document_header
349
+ with (security_invoker = true) as
350
+ select d.id as document_id,
351
+ d.company_id,
352
+ d.doc_type,
353
+ d.state,
354
+ d.payment_state,
355
+ d.number,
356
+ d.supplier_reference,
357
+ d.document_date,
358
+ d.accounting_date,
359
+ d.due_date,
360
+ d.delivery_date,
361
+ d.currency_code,
362
+ d.amount_untaxed,
363
+ d.amount_tax,
364
+ d.amount_total,
365
+ d.amount_paid,
366
+ d.amount_residual,
367
+ d.payment_terms,
368
+ d.payment_means_code,
369
+ d.payment_reference,
370
+ d.buyer_reference,
371
+ d.order_reference,
372
+ d.contract_reference,
373
+ d.project_reference,
374
+ d.note,
375
+ coalesce(d.payee_iban, seller_bank.iban) as payee_iban,
376
+ seller_bank.bic as payee_bic,
377
+
378
+ -- The seller: this company.
379
+ coalesce(c.trade_name, c.name) as seller_name,
380
+ c.legal_name as seller_legal_name,
381
+ c.legal_form as seller_legal_form,
382
+ c.vat_number as seller_vat_number,
383
+ c.registration_number as seller_registration_number,
384
+ c.address_line1 as seller_address_line1,
385
+ c.address_line2 as seller_address_line2,
386
+ c.postal_code as seller_postal_code,
387
+ c.city as seller_city,
388
+ c.country as seller_country,
389
+ c.region as seller_region,
390
+ c.email as seller_email,
391
+ c.phone as seller_phone,
392
+ c.website as seller_website,
393
+ c.logo_url as seller_logo_url,
394
+ c.share_capital as seller_share_capital,
395
+ c.share_capital_currency as seller_share_capital_currency,
396
+ c.activity_code as seller_activity_code,
397
+ c.activity_scheme as seller_activity_scheme,
398
+ c.document_template,
399
+
400
+ -- The buyer: the contact the document is written against.
401
+ ct.id as buyer_id,
402
+ ct.name as buyer_name,
403
+ ct.vat_number as buyer_vat_number,
404
+ ct.registration_number as buyer_registration_number,
405
+ ct.address_line1 as buyer_address_line1,
406
+ ct.address_line2 as buyer_address_line2,
407
+ ct.postal_code as buyer_postal_code,
408
+ ct.city as buyer_city,
409
+ ct.country as buyer_country,
410
+ ct.region as buyer_region,
411
+ ct.email as buyer_email,
412
+
413
+ -- What the country of the document requires. Its country is the
414
+ -- company's fiscal_country — the country whose VAT rules apply — and
415
+ -- not its address, which is the same distinction
416
+ -- document_legal_mentions makes.
417
+ c.fiscal_country as country,
418
+ cd.number_format,
419
+ cd.numbering_gapless,
420
+ cd.legal_payment_days,
421
+ cd.late_payment_reference,
422
+ cd.tax_point_rule,
423
+ cd.einvoice_profile,
424
+ cd.einvoice_mandatory_from,
425
+ cd.party_scheme,
426
+ cd.vat_scheme,
427
+
428
+ -- What the document is written in. Not the reader's preference and
429
+ -- not the customer's of today: the document's own, as of the day it
430
+ -- was issued.
431
+ d.language
432
+ from documents d
433
+ join companies c on c.id = d.company_id
434
+ join contacts ct on ct.id = d.contact_id
435
+ left join bank_accounts seller_bank on seller_bank.id = c.default_bank_account_id
436
+ left join country_defaults cd on cd.country = c.fiscal_country;
437
+
438
+ comment on view document_header is
439
+ 'One document with everything printed above its lines: the seller, the buyer, the amounts, where it is paid, what its country requires and the language it is written in. The lines are document_line_items and the sentences are document_legal_mentions.';
440
+
441
+ -- ---------------------------------------------------------------------------
442
+ -- 8. The document behind a link, in the language it was written in
443
+ --
444
+ -- `shared_document()` as `20260915153000` published it, with one paragraph
445
+ -- gone: the two-column chain it resolved on every read. The document carries
446
+ -- its language now, and the sentences come out of the view already in it, so
447
+ -- what is left is a read of a column and a read of a view — and the second
448
+ -- place a language was chosen in this schema is no longer there.
449
+ --
450
+ -- Everything else is that function unchanged, republished because a function
451
+ -- is replaced whole.
452
+ -- ---------------------------------------------------------------------------
453
+
454
+ create or replace function shared_document(p_token text)
455
+ returns jsonb
456
+ language plpgsql
457
+ security definer
458
+ set search_path = public, pg_temp
459
+ as $$
460
+ declare
461
+ v_share document_shares%rowtype;
462
+ v_doc documents%rowtype;
463
+ v_header document_header%rowtype;
464
+ v_last_payment date;
465
+ v_payload jsonb;
466
+ begin
467
+ select * into v_share
468
+ from document_shares
469
+ where token_hash = encode(sha256(convert_to(coalesce(p_token, ''), 'UTF8')), 'hex');
470
+
471
+ if v_share.id is null
472
+ or v_share.revoked_at is not null
473
+ or (v_share.expires_at is not null and v_share.expires_at <= now())
474
+ or v_share.subject_kind <> 'document' then
475
+ return null;
476
+ end if;
477
+
478
+ select * into v_doc from documents where id = v_share.document_id;
479
+ if v_doc.id is null or document_share_refusal(v_doc) is not null then
480
+ return null;
481
+ end if;
482
+
483
+ -- The link was used. Counted before the payload is built, so a reader who
484
+ -- gives up halfway is still a reader.
485
+ update document_shares
486
+ set view_count = view_count + 1,
487
+ last_viewed_at = now()
488
+ where id = v_share.id;
489
+
490
+ select * into v_header from document_header where document_id = v_doc.id;
491
+
492
+ -- When the last money against it arrived. The other side of every matching
493
+ -- on the document's own third-party lines, where that side is a payment.
494
+ select max(p.payment_date) into v_last_payment
495
+ from entry_lines dl
496
+ join reconciliations r
497
+ on r.debit_line_id = dl.id or r.credit_line_id = dl.id
498
+ join entry_lines ol
499
+ on ol.id = case when r.debit_line_id = dl.id then r.credit_line_id else r.debit_line_id end
500
+ join payments p on p.entry_id = ol.entry_id
501
+ where dl.entry_id = v_doc.entry_id;
502
+
503
+ v_payload := jsonb_build_object(
504
+ 'document', jsonb_build_object(
505
+ 'type', v_header.doc_type,
506
+ 'number', v_header.number,
507
+ 'document_date', v_header.document_date,
508
+ 'due_date', v_header.due_date,
509
+ 'delivery_date', v_header.delivery_date,
510
+ 'currency', v_header.currency_code,
511
+ -- The document's own, snapshotted when it was created and frozen when it
512
+ -- was posted. A customer who has since changed preference does not
513
+ -- change what was sent to them.
514
+ 'language', v_doc.language,
515
+ 'payment_terms', v_header.payment_terms,
516
+ 'payment_reference', v_header.payment_reference,
517
+ 'buyer_reference', v_header.buyer_reference,
518
+ 'order_reference', v_header.order_reference,
519
+ -- BT-22, the note the seller wrote on the invoice itself. An internal
520
+ -- remark about a customer is `contacts.notes` and is not in this object.
521
+ 'note', v_header.note
522
+ ),
523
+ 'seller', jsonb_build_object(
524
+ 'name', v_header.seller_name,
525
+ 'legal_name', v_header.seller_legal_name,
526
+ 'legal_form', v_header.seller_legal_form,
527
+ 'vat_number', v_header.seller_vat_number,
528
+ 'registration_number', v_header.seller_registration_number,
529
+ 'address_line1', v_header.seller_address_line1,
530
+ 'address_line2', v_header.seller_address_line2,
531
+ 'postal_code', v_header.seller_postal_code,
532
+ 'city', v_header.seller_city,
533
+ 'country', v_header.seller_country,
534
+ 'email', v_header.seller_email,
535
+ 'phone', v_header.seller_phone,
536
+ 'website', v_header.seller_website,
537
+ 'logo_url', v_header.seller_logo_url,
538
+ 'iban', v_header.payee_iban,
539
+ 'bic', v_header.payee_bic
540
+ ),
541
+ 'buyer', jsonb_build_object(
542
+ 'name', v_header.buyer_name,
543
+ 'vat_number', v_header.buyer_vat_number,
544
+ 'registration_number', v_header.buyer_registration_number,
545
+ 'address_line1', v_header.buyer_address_line1,
546
+ 'address_line2', v_header.buyer_address_line2,
547
+ 'postal_code', v_header.buyer_postal_code,
548
+ 'city', v_header.buyer_city,
549
+ 'country', v_header.buyer_country
550
+ ),
551
+ 'lines', coalesce((
552
+ select jsonb_agg(jsonb_build_object(
553
+ 'sequence', li.sequence,
554
+ 'type', li.line_type,
555
+ 'name', li.item_name,
556
+ 'description', li.item_description,
557
+ 'quantity', li.quantity::text,
558
+ 'unit_code', li.unit_code,
559
+ 'unit_price', li.unit_price::text,
560
+ 'discount_percent', li.discount_percent::text,
561
+ 'amount_untaxed', li.amount_untaxed::text,
562
+ 'tax_category', li.vat_category,
563
+ 'tax_rate', li.vat_rate::text,
564
+ 'tax_exemption_code', li.tax_exemption_code)
565
+ order by li.sequence)
566
+ from document_line_items li
567
+ where li.document_id = v_doc.id), '[]'::jsonb),
568
+ 'tax_summary', coalesce((
569
+ select jsonb_agg(jsonb_build_object(
570
+ 'name', ts.tax_name,
571
+ 'category', ts.vat_category,
572
+ 'rate', ts.tax_rate::text,
573
+ 'base_amount', ts.base_amount::text,
574
+ -- What the other party actually pays, which is what an invoice
575
+ -- prints: zero where the tax self-assesses, and what the four
576
+ -- totals below add up from.
577
+ 'tax_amount', ts.tax_charged::text)
578
+ order by ts.tax_rate, ts.tax_name)
579
+ from document_tax_summary ts
580
+ where ts.document_id = v_doc.id), '[]'::jsonb),
581
+ 'totals', jsonb_build_object(
582
+ 'amount_untaxed', v_header.amount_untaxed::text,
583
+ 'amount_tax', v_header.amount_tax::text,
584
+ 'amount_total', v_header.amount_total::text
585
+ ),
586
+ -- The one thing that legitimately moves after the document was sent, so a
587
+ -- link a customer keeps stays worth opening.
588
+ 'payment', jsonb_build_object(
589
+ 'state', v_header.payment_state,
590
+ 'amount_paid', v_header.amount_paid::text,
591
+ 'amount_residual', v_header.amount_residual::text,
592
+ 'last_payment_date', v_last_payment
593
+ ),
594
+ 'legal_mentions', coalesce((
595
+ select jsonb_agg(jsonb_build_object(
596
+ 'code', lm.code,
597
+ 'text', lm.text)
598
+ order by lm.sequence, lm.code)
599
+ from document_legal_mentions lm
600
+ where lm.document_id = v_doc.id), '[]'::jsonb)
601
+ );
602
+
603
+ return v_payload;
604
+ end;
605
+ $$;
606
+
607
+ comment on function shared_document(text) is
608
+ 'One document, read by whoever holds its link: the header, the lines, the tax breakdown, the totals, the legal mentions in the language the document was written in, and what is still owed today. Returns null — the same null, in the same shape — for a token that is unknown, withdrawn, expired, or onto a document that may no longer be shared.';
609
+
610
+ -- ---------------------------------------------------------------------------
611
+ -- 9. Grants
612
+ --
613
+ -- The rule of `supabase/migrations/README.md`: the schema closes behind itself
614
+ -- and then says, by name, who may reach what. The dropped view takes its
615
+ -- privileges with it and gets them back here, to exactly who held them before.
616
+ -- `anon` gains nothing: not the view, not the overload. What an anonymous
617
+ -- reader reaches is still one `security definer` function, which reads both as
618
+ -- its owner.
619
+ -- ---------------------------------------------------------------------------
620
+
621
+ revoke execute on all functions in schema public from public;
622
+
623
+ revoke all on table document_legal_mentions from public, anon;
624
+ grant select on table document_legal_mentions to authenticated, service_role;
625
+
626
+ revoke execute on function preferred_languages(text, uuid) from public, anon;
627
+ grant execute on function preferred_languages(text, uuid) to authenticated, service_role;
628
+
629
+ revoke execute on function preferred_languages(uuid) from public, anon;
630
+ grant execute on function preferred_languages(uuid) to authenticated, service_role;
631
+
632
+ revoke execute on function shared_document(text) from public;
633
+ grant execute on function shared_document(text) to anon, authenticated, service_role;