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,227 @@
1
+ -- Ekwo OS — what moved after it went.
2
+ --
3
+ -- A declaration leaves, and the period it covers stays open. That is not a
4
+ -- defect: a supplier invoice arrives late, an accountant adjusts an entry, a
5
+ -- correction lands. Every one of those is legitimate, and refusing them would
6
+ -- be refusing bookkeeping. **What is not legitimate is that nobody is told.**
7
+ --
8
+ -- D1 froze the figures and gave `filing_drift()` — box by box, what the ledger
9
+ -- says now against what was sent. This file adds the two things that were
10
+ -- missing around it:
11
+ --
12
+ -- filings_touched_since() which declarations the ledger disturbed after
13
+ -- they went, how many entries, and how many of
14
+ -- their figures moved
15
+ -- lock_filed_period() a company that wants the period shut says so
16
+ -- once, after the books for it are clear
17
+ --
18
+ -- and it makes the corrective settle **the difference**, which is the part D4
19
+ -- left named and undone.
20
+ --
21
+ -- On the lock: the database does not propose anything, it answers. The
22
+ -- proposal belongs to whatever is at the keyboard — the CLI, an agent, a
23
+ -- person — and what the schema owes it is a way of saying yes that cannot be
24
+ -- got wrong. So the lock is its own function, it moves forward only, and it
25
+ -- refuses while the period still has books to clear. A company that never
26
+ -- calls it keeps an open period and a reading that says what fell into it.
27
+ --
28
+ -- What is deliberately not here: **what a country does with a correction.**
29
+ -- Some want a full replacement return, some an adjustment carried on the next
30
+ -- period, most with a threshold below which nothing is filed at all. That is
31
+ -- pack data, nobody has read the texts yet, and a mechanism invented now would
32
+ -- be one country's habit given to five others. `supersede_filing()` does the
33
+ -- one thing that is true everywhere: what was sent stays sent, and the new
34
+ -- declaration points at it.
35
+
36
+ -- ---------------------------------------------------------------------------
37
+ -- filing_tax_movements, net of what an earlier settlement already carried
38
+ --
39
+ -- A corrective covers the same period as the declaration it replaces, and that
40
+ -- period's tax accounts were already cleared once. Left as it was, this would
41
+ -- hand `settle_filing()` the whole period again and book the debt twice.
42
+ --
43
+ -- So the movements of the window are netted against the settlement entries of
44
+ -- the declarations that came before it on the same period — restricted to the
45
+ -- accounts the window itself moved, which is what leaves out their
46
+ -- counterpart line on the debt account. What comes back is exactly what has
47
+ -- not been settled yet: nothing at all when nothing moved, and the difference
48
+ -- when something did.
49
+ -- ---------------------------------------------------------------------------
50
+
51
+ create or replace function filing_tax_movements(p_filing_id uuid)
52
+ returns table (account_id uuid, balance numeric)
53
+ language sql
54
+ stable
55
+ security invoker
56
+ as $$
57
+ with f as (
58
+ select * from tax_filings where id = p_filing_id
59
+ ),
60
+ moved as (
61
+ select l.account_id, sum(l.balance) as balance
62
+ from f
63
+ join entry_lines l on l.company_id = f.company_id
64
+ join entries e on e.id = l.entry_id
65
+ where e.state = 'posted'
66
+ and l.tax_line
67
+ and coalesce(l.tax_point_date, e.entry_date) between f.period_start and f.period_end
68
+ group by l.account_id
69
+ ),
70
+ already as (
71
+ select l.account_id, sum(l.balance) as balance
72
+ from f
73
+ join tax_filings p
74
+ on p.company_id = f.company_id
75
+ and p.report_code = f.report_code
76
+ and p.period_start = f.period_start
77
+ and p.period_end = f.period_end
78
+ and p.id <> f.id
79
+ and p.settlement_entry_id is not null
80
+ join entry_lines l on l.entry_id = p.settlement_entry_id
81
+ join moved m on m.account_id = l.account_id
82
+ group by l.account_id
83
+ )
84
+ select m.account_id,
85
+ round_amount(m.balance + coalesce(a.balance, 0), rounding_of(f.company_id))
86
+ from f, moved m
87
+ left join already a on a.account_id = m.account_id
88
+ where round_amount(m.balance + coalesce(a.balance, 0), rounding_of(f.company_id)) <> 0;
89
+ $$;
90
+
91
+ comment on function filing_tax_movements(uuid) is
92
+ 'The tax accounts a declared period moved and by how much, on the same window and the same tax-point rule the return read, net of what an earlier settlement of the same period already carried. What settle_filing() clears — the whole period the first time, the difference on a corrective — and what anybody can read before it does.';
93
+
94
+ revoke execute on function filing_tax_movements(uuid) from public, anon;
95
+ grant execute on function filing_tax_movements(uuid) to authenticated, service_role;
96
+
97
+ -- ---------------------------------------------------------------------------
98
+ -- filings_touched_since
99
+ --
100
+ -- The reading the whole freeze was for. A declaration is listed when the
101
+ -- ledger moved inside its period **after** it was filed — not when the figures
102
+ -- happen to differ, which is `filing_drift()`'s question, because an entry
103
+ -- that nets to nothing in every box is still an entry somebody posted into a
104
+ -- period that had gone.
105
+ --
106
+ -- What counts as moving it is a line that names a box, which is the same test
107
+ -- the tax lock uses to decide what it protects. Two readings of "an entry
108
+ -- that concerns the declaration" would eventually disagree, and a payroll
109
+ -- entry landing in a declared quarter is not news. It also leaves out the
110
+ -- settlement of the declaration itself, which is posted at the end of the
111
+ -- period and after the filing, and is the opposite of a disturbance.
112
+ --
113
+ -- Read-only, and it names nothing to do: whether a change is a corrective to
114
+ -- file, an entry in the wrong period, or a legitimate movement that changes no
115
+ -- figure, is a judgement, and the database's job is to stop it being invisible.
116
+ -- ---------------------------------------------------------------------------
117
+
118
+ create or replace function filings_touched_since(
119
+ p_company_id uuid,
120
+ p_from date default null,
121
+ p_to date default null
122
+ )
123
+ returns table (
124
+ filing_id uuid,
125
+ report_code text,
126
+ period_start date,
127
+ period_end date,
128
+ state tax_filing_state,
129
+ filed_at timestamptz,
130
+ entries integer,
131
+ last_entry_at timestamptz,
132
+ boxes_moved integer
133
+ )
134
+ language sql
135
+ stable
136
+ security invoker
137
+ as $$
138
+ select f.id, f.report_code, f.period_start, f.period_end, f.state, f.filed_at,
139
+ t.entries::integer, t.last_entry_at,
140
+ (select count(*) from filing_drift(f.id))::integer
141
+ from tax_filings f
142
+ cross join lateral (
143
+ select count(distinct e.id) as entries, max(e.posted_at) as last_entry_at
144
+ from entry_lines l
145
+ join entries e on e.id = l.entry_id
146
+ where l.company_id = f.company_id
147
+ and e.state = 'posted'
148
+ and e.posted_at > f.filed_at
149
+ and l.declaration_box is not null
150
+ and coalesce(l.tax_point_date, e.entry_date) between f.period_start and f.period_end
151
+ ) t
152
+ where f.company_id = p_company_id
153
+ and f.filed_at is not null
154
+ and f.state <> 'superseded'
155
+ and (p_from is null or f.period_end >= p_from)
156
+ and (p_to is null or f.period_start <= p_to)
157
+ and t.entries > 0
158
+ order by f.period_start, f.report_code;
159
+ $$;
160
+
161
+ comment on function filings_touched_since(uuid, date, date) is
162
+ 'Declarations that have gone and whose period the ledger moved afterwards: how many entries carrying a declaration box landed in it, when the last one did, and how many of the filed figures now disagree. An entry that changes no figure is still listed — it was posted into a period that had been declared, and that is the fact being reported.';
163
+
164
+ revoke execute on function filings_touched_since(uuid, date, date) from public, anon;
165
+ grant execute on function filings_touched_since(uuid, date, date) to authenticated, service_role;
166
+
167
+ -- ---------------------------------------------------------------------------
168
+ -- lock_filed_period
169
+ --
170
+ -- The other half of "signalled, not refused": a company that does want the
171
+ -- period shut says so, once, and the schema makes saying it safe.
172
+ --
173
+ -- It is a function of its own and not a flag on `file_filing()`, and the test
174
+ -- is what settled that. The tax lock is checked by `post_entry()` for every
175
+ -- entry it posts, whatever the entry names — so locking at the moment of
176
+ -- filing locks the declaration's own settlement out of its period, and the
177
+ -- books can never be cleared. The order that works is the order the work
178
+ -- actually has: file, hear back, settle, then shut.
179
+ --
180
+ -- So this refuses to shut a period whose settlement is still possible and not
181
+ -- done, and says which. It moves the lock forward only: a company filing an
182
+ -- old period late did not ask to reopen everything it had already closed.
183
+ -- ---------------------------------------------------------------------------
184
+
185
+ create or replace function lock_filed_period(p_filing_id uuid)
186
+ returns date
187
+ language plpgsql
188
+ volatile
189
+ security invoker
190
+ as $$
191
+ declare
192
+ v_filing tax_filings;
193
+ v_lock date;
194
+ begin
195
+ select * into v_filing from tax_filings f where f.id = p_filing_id;
196
+ if not found then
197
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
198
+ end if;
199
+ if v_filing.state in ('draft', 'ready') then
200
+ raise exception 'filing_not_sent: a period is shut behind a declaration that has gone, and this one is %',
201
+ v_filing.state;
202
+ end if;
203
+ if v_filing.state = 'superseded' then
204
+ raise exception 'filing_superseded: % was replaced, and it is the declaration that replaced it that shuts the period',
205
+ p_filing_id;
206
+ end if;
207
+
208
+ if v_filing.settlement_entry_id is null
209
+ and exists (select 1 from filing_tax_movements(p_filing_id)) then
210
+ raise exception 'settle_first: this period still has tax accounts to clear, and the lock would refuse the entry that clears them';
211
+ end if;
212
+
213
+ update companies
214
+ set tax_lock_date = v_filing.period_end
215
+ where id = v_filing.company_id
216
+ and (tax_lock_date is null or tax_lock_date < v_filing.period_end);
217
+
218
+ select c.tax_lock_date into v_lock from companies c where c.id = v_filing.company_id;
219
+ return v_lock;
220
+ end;
221
+ $$;
222
+
223
+ comment on function lock_filed_period(uuid) is
224
+ 'Carries the tax lock to the end of the period a declaration covers, so nothing more falls into it. Forward only, refused on a declaration that has not gone, and refused while the period still has tax accounts to clear — because the lock would then refuse the very entry that clears them. Returns the lock date the company ends up with. Nobody is obliged to call it: a period left open is a period filings_touched_since() reports on.';
225
+
226
+ revoke execute on function lock_filed_period(uuid) from public, anon;
227
+ grant execute on function lock_filed_period(uuid) to authenticated, service_role;
@@ -0,0 +1,27 @@
1
+ -- Ekwo OS — a form names the file it is deposited as.
2
+ --
3
+ -- Seven bricks write legal files today and not one of them writes a periodic
4
+ -- VAT return: `intra-consignment`, `des`, `vd` and `ecdf` carry recapitulative
5
+ -- statements, `xbrl-cbso` the annual accounts, `fec` and `factur-x` are
6
+ -- neither. The declaration a company actually files every month — the one D1
7
+ -- freezes and D4 settles — existed here as figures and never as a file.
8
+ --
9
+ -- `@ekwo-ai/vat-consignment` is the first, for the XML Intervat takes. This
10
+ -- column is what says a form has one, and it is a **name of a format** and not
11
+ -- of a country: the day two countries deposit the same shape of file, they
12
+ -- name the same brick, and the day one country changes form it names another.
13
+ --
14
+ -- Null is the ordinary answer, for now and on purpose. A form nobody can write
15
+ -- is still a form a company files by hand on a portal, and a column that had
16
+ -- to be filled would be filled with something invented.
17
+ --
18
+ -- What is not here: **the guard that refuses a pack naming a format nobody can
19
+ -- write.** It belongs with the one owed to `bank_statement_formats` and
20
+ -- `payment_formats`, which are declared with no reader for the same reason,
21
+ -- and it is one guard over the three of them rather than three.
22
+
23
+ alter table tax_report_templates
24
+ add column if not exists file_format text;
25
+
26
+ comment on column tax_report_templates.file_format is
27
+ 'The file this form is deposited as, by the name of the brick that writes it — vat-consignment for the Belgian XML Intervat takes. Named after the format and never after the country. Null where no brick writes the form, which is most of them: filing by hand on a portal is how it is done until one exists.';
@@ -0,0 +1,286 @@
1
+ -- Ekwo OS — a deposit, and what came back.
2
+ --
3
+ -- Filing was one act with one date: `filed_at`, `reference`, and a state that
4
+ -- went `filed` then `accepted` or `rejected`. That is the happy path and it is
5
+ -- the only one the schema could describe. A declaration is refused for a
6
+ -- malformed file, for a certificate that expired mid-session, for a figure the
7
+ -- administration disputes — and then it is **sent again**, which the schema had
8
+ -- no way of saying: `file_filing()` refused everything that was not a draft, so
9
+ -- a rejected declaration was a dead end whose only exit was a corrective. A
10
+ -- corrective replaces a declaration the administration **accepted**. One it
11
+ -- never received has nothing to correct.
12
+ --
13
+ -- So a deposit becomes an object of its own:
14
+ --
15
+ -- tax_filing_deposits one row per send, with what came back
16
+ -- file_filing() records a send, and accepts a rejected declaration
17
+ -- reopen_filing() a rejected declaration goes back to draft to be redone
18
+ --
19
+ -- **Why this is in the open core.** Sending the file is the operated side —
20
+ -- credentials, a certificate, a portal session, somebody answerable when a
21
+ -- return is late — and it stays in `ee/`, on the same line as the bank. What
22
+ -- comes back does not: the deposit number, the acknowledgement, the words the
23
+ -- administration used, and the file that was sent are the company's proof that
24
+ -- it filed. A company that stops paying for the transmission keeps every one of
25
+ -- them, in its own database, under the same policies as the rest of its books.
26
+ -- That is the whole argument for installing this.
27
+ --
28
+ -- The channel is two words and not a list of providers. `portal` is a person
29
+ -- uploading the file themselves, which is complete and free and is how most
30
+ -- installations will do it; `service` is a transmission held by somebody, whose
31
+ -- name is recorded as text. A closed list of providers here would be the
32
+ -- coupling this project refuses — one interface, one provider at a time, and
33
+ -- the core never knows which.
34
+
35
+ create type filing_channel as enum ('portal', 'service');
36
+
37
+ comment on type filing_channel is
38
+ 'How a declaration was sent: portal — a person uploaded it themselves — or service, a transmission somebody operates. Two words, because the core describes the act and never the provider.';
39
+
40
+ create table tax_filing_deposits (
41
+ id uuid primary key default gen_random_uuid(),
42
+ filing_id uuid not null references tax_filings(id) on delete cascade,
43
+ -- 1, 2, 3: a rejected declaration is sent again, and each send is a row.
44
+ sequence integer not null,
45
+ channel filing_channel not null default 'portal',
46
+ -- The name of the service, where one was used. Free text on purpose: the
47
+ -- core records what was used and never holds a list of what may be.
48
+ service text,
49
+ sent_at timestamptz not null default now(),
50
+ sent_by uuid,
51
+ -- What the administration gave back for **this** send.
52
+ reference text,
53
+ outcome tax_filing_state,
54
+ outcome_at timestamptz,
55
+ -- Its own words. A refusal summarised is a refusal half-read, and the
56
+ -- sentence is what a person needs to know what to change.
57
+ message text,
58
+ -- The two files of a deposit, where they were kept: what was sent, and the
59
+ -- receipt. Both are rows of `attachments` on this declaration; naming them
60
+ -- here is what tells them apart.
61
+ sent_file_id uuid references attachments(id) on delete set null,
62
+ acknowledgement_id uuid references attachments(id) on delete set null,
63
+ created_at timestamptz not null default now(),
64
+ unique (filing_id, sequence),
65
+ constraint tax_filing_deposits_outcome check (
66
+ outcome is null or outcome in ('accepted', 'rejected', 'paid')
67
+ ),
68
+ constraint tax_filing_deposits_outcome_has_a_date check (
69
+ (outcome is null) = (outcome_at is null)
70
+ )
71
+ );
72
+
73
+ comment on table tax_filing_deposits is
74
+ 'One row per send of a declaration, with what came back: the deposit number, the outcome, the administration''s own words, and the two files — what was sent and the receipt. A rejected declaration is sent again, so a declaration has as many deposits as it took.';
75
+ comment on column tax_filing_deposits.service is
76
+ 'The name of the transmission service, where one was used. Free text: the core records what was used and holds no list of what may be, which is what keeps it uncoupled from any provider.';
77
+ comment on column tax_filing_deposits.message is
78
+ 'What the administration answered, in its own words. Not summarised: a refusal is read to know what to change.';
79
+ comment on column tax_filing_deposits.sent_file_id is
80
+ 'The file that was sent, kept as an attachment of this declaration. Keeping it is what makes reopening a rejected declaration lossless — the core makes it possible and cannot impose it.';
81
+
82
+ create index tax_filing_deposits_filing_idx on tax_filing_deposits (filing_id, sequence desc);
83
+ -- The two files, indexed like every other foreign key of this schema: a
84
+ -- deleted attachment has to find the deposits that point at it without a scan.
85
+ create index tax_filing_deposits_sent_file_idx on tax_filing_deposits (sent_file_id);
86
+ create index tax_filing_deposits_acknowledgement_idx on tax_filing_deposits (acknowledgement_id);
87
+
88
+ alter table tax_filing_deposits enable row level security;
89
+
90
+ create policy tax_filing_deposits_select on tax_filing_deposits
91
+ for select using (exists (
92
+ select 1 from tax_filings f
93
+ where f.id = tax_filing_deposits.filing_id
94
+ and has_capability(f.company_id, 'filings.read')));
95
+ create policy tax_filing_deposits_write on tax_filing_deposits
96
+ for all using (exists (
97
+ select 1 from tax_filings f
98
+ where f.id = tax_filing_deposits.filing_id
99
+ and has_capability(f.company_id, 'filings.write')))
100
+ with check (exists (
101
+ select 1 from tax_filings f
102
+ where f.id = tax_filing_deposits.filing_id
103
+ and has_capability(f.company_id, 'filings.write')));
104
+
105
+ comment on policy tax_filing_deposits_select on tax_filing_deposits is
106
+ 'filings.read: what was sent and what came back is proof of filing, and it is read by whoever reads the declarations.';
107
+
108
+ grant select, insert, update, delete on table tax_filing_deposits to authenticated, service_role;
109
+
110
+ -- ---------------------------------------------------------------------------
111
+ -- file_filing, which now records the send
112
+ --
113
+ -- Replaces the three-argument function rather than sitting beside it: two
114
+ -- overloads with defaults would make the call everybody writes ambiguous.
115
+ -- ---------------------------------------------------------------------------
116
+
117
+ drop function if exists file_filing(uuid, text, timestamptz);
118
+
119
+ create or replace function file_filing(
120
+ p_filing_id uuid,
121
+ p_reference text default null,
122
+ p_filed_at timestamptz default null,
123
+ p_channel filing_channel default 'portal',
124
+ p_service text default null
125
+ )
126
+ returns tax_filings
127
+ language plpgsql
128
+ volatile
129
+ security invoker
130
+ as $$
131
+ declare
132
+ v_filing tax_filings;
133
+ v_at timestamptz;
134
+ v_next integer;
135
+ begin
136
+ select * into v_filing from tax_filings f where f.id = p_filing_id;
137
+ if not found then
138
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
139
+ end if;
140
+
141
+ -- Rejected is a state a declaration is sent again from. It was never
142
+ -- received, so there is nothing to correct and no corrective to open.
143
+ if v_filing.state not in ('draft', 'ready', 'rejected') then
144
+ raise exception 'filing_already_%: this declaration has already gone', v_filing.state;
145
+ end if;
146
+
147
+ if v_filing.prepared_at is null then
148
+ raise exception 'filing_not_prepared: compute the figures before filing them';
149
+ end if;
150
+
151
+ v_at := coalesce(p_filed_at, now());
152
+
153
+ select coalesce(max(d.sequence), 0) + 1 into v_next
154
+ from tax_filing_deposits d where d.filing_id = p_filing_id;
155
+
156
+ insert into tax_filing_deposits (filing_id, sequence, channel, service, sent_at, sent_by, reference)
157
+ values (p_filing_id, v_next, p_channel, p_service, v_at, auth.uid(), p_reference);
158
+
159
+ update tax_filings
160
+ set state = 'filed',
161
+ reference = coalesce(p_reference, reference),
162
+ filed_at = v_at,
163
+ filed_by = auth.uid()
164
+ where id = p_filing_id
165
+ returning * into v_filing;
166
+
167
+ return v_filing;
168
+ end;
169
+ $$;
170
+
171
+ comment on function file_filing(uuid, text, timestamptz, filing_channel, text) is
172
+ 'Records that a declaration has gone, with the reference the administration gave back, and keeps the send as a row of tax_filing_deposits. It asks that the figures were computed, not that there are any: a period where nothing happened is filed nil. A rejected declaration may be sent again — it was never received — and the second send is a second deposit, not a corrective.';
173
+
174
+ revoke execute on function file_filing(uuid, text, timestamptz, filing_channel, text) from public, anon;
175
+ grant execute on function file_filing(uuid, text, timestamptz, filing_channel, text) to authenticated, service_role;
176
+
177
+ -- ---------------------------------------------------------------------------
178
+ -- record_filing_outcome, which writes on the deposit it answers
179
+ -- ---------------------------------------------------------------------------
180
+
181
+ drop function if exists record_filing_outcome(uuid, tax_filing_state, text);
182
+
183
+ create or replace function record_filing_outcome(
184
+ p_filing_id uuid,
185
+ p_state tax_filing_state,
186
+ p_reference text default null,
187
+ p_message text default null
188
+ )
189
+ returns tax_filings
190
+ language plpgsql
191
+ volatile
192
+ security invoker
193
+ as $$
194
+ declare
195
+ v_filing tax_filings;
196
+ begin
197
+ select * into v_filing from tax_filings f where f.id = p_filing_id;
198
+ if not found then
199
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
200
+ end if;
201
+
202
+ if p_state not in ('accepted', 'rejected', 'paid') then
203
+ raise exception 'not_an_outcome: % is not something an administration answers', p_state;
204
+ end if;
205
+ if v_filing.state = 'draft' or v_filing.state = 'ready' then
206
+ raise exception 'filing_not_sent: a declaration that has not gone cannot come back %', p_state;
207
+ end if;
208
+ if p_state = 'paid' and v_filing.state <> 'accepted' then
209
+ raise exception 'filing_not_accepted: paying follows acceptance, and this one is %', v_filing.state;
210
+ end if;
211
+
212
+ -- On the last send, because that is the one being answered.
213
+ update tax_filing_deposits d
214
+ set outcome = p_state,
215
+ outcome_at = now(),
216
+ reference = coalesce(p_reference, d.reference),
217
+ message = coalesce(p_message, d.message)
218
+ where d.filing_id = p_filing_id
219
+ and d.sequence = (select max(x.sequence) from tax_filing_deposits x where x.filing_id = p_filing_id);
220
+
221
+ update tax_filings
222
+ set state = p_state,
223
+ reference = coalesce(p_reference, reference)
224
+ where id = p_filing_id
225
+ returning * into v_filing;
226
+ return v_filing;
227
+ end;
228
+ $$;
229
+
230
+ comment on function record_filing_outcome(uuid, tax_filing_state, text, text) is
231
+ 'What came back: accepted, rejected, or paid, written on the declaration and on the send it answers — with the administration''s own words where it gave any. Paying follows acceptance, and a declaration that never went cannot come back at all.';
232
+
233
+ revoke execute on function record_filing_outcome(uuid, tax_filing_state, text, text) from public, anon;
234
+ grant execute on function record_filing_outcome(uuid, tax_filing_state, text, text) to authenticated, service_role;
235
+
236
+ -- ---------------------------------------------------------------------------
237
+ -- reopen_filing
238
+ --
239
+ -- A rejection on the substance is not answered by sending the same file again:
240
+ -- the figures have to be worked out afresh, and `prepare_filing()` refuses
241
+ -- anything that has left draft. So this is the door back, and it opens for one
242
+ -- state only.
243
+ --
244
+ -- It is safe because of the table above: what was sent is a deposit, with its
245
+ -- reference, its answer and — where somebody kept it — the file itself. The
246
+ -- core cannot force an installation to archive that file; it can make keeping
247
+ -- it the thing that makes this lossless, and say so.
248
+ -- ---------------------------------------------------------------------------
249
+
250
+ create or replace function reopen_filing(p_filing_id uuid)
251
+ returns tax_filings
252
+ language plpgsql
253
+ volatile
254
+ security invoker
255
+ as $$
256
+ declare
257
+ v_filing tax_filings;
258
+ begin
259
+ select * into v_filing from tax_filings f where f.id = p_filing_id;
260
+ if not found then
261
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
262
+ end if;
263
+ if v_filing.state <> 'rejected' then
264
+ raise exception 'filing_not_rejected: a % declaration is not redone, it is superseded by a corrective',
265
+ v_filing.state;
266
+ end if;
267
+
268
+ -- The declaration goes back to being prepared. `filed_at` empties with it —
269
+ -- the schema's own rule that a draft carries no filing date — and nothing is
270
+ -- lost, because the send that was refused is a row of tax_filing_deposits.
271
+ update tax_filings
272
+ set state = 'draft',
273
+ filed_at = null,
274
+ filed_by = null,
275
+ reference = null
276
+ where id = p_filing_id
277
+ returning * into v_filing;
278
+ return v_filing;
279
+ end;
280
+ $$;
281
+
282
+ comment on function reopen_filing(uuid) is
283
+ 'Takes a rejected declaration back to draft so its figures can be worked out again. Only a rejected one: a declaration the administration accepted is replaced by a corrective, never rewritten. What was sent stays in tax_filing_deposits.';
284
+
285
+ revoke execute on function reopen_filing(uuid) from public, anon;
286
+ grant execute on function reopen_filing(uuid) to authenticated, service_role;
@@ -0,0 +1,9 @@
1
+ -- Ekwo OS — a fourth preset is named, and nothing else happens here.
2
+ --
3
+ -- `member_role` gains `client`: the person whose company this is, inside an
4
+ -- installation that belongs to whoever keeps their books. What the preset
5
+ -- holds is the next migration — a new enum value cannot be used in the
6
+ -- transaction that adds it, which is the reason `instance_admin` was added in
7
+ -- a file of its own on the first day, and the reason this one is.
8
+
9
+ alter type member_role add value if not exists 'client';
@@ -0,0 +1,96 @@
1
+ -- Ekwo OS — a client reads, and hands over a piece. It writes nothing.
2
+ --
3
+ -- Several companies inside one installation is the normal case: a firm and
4
+ -- the companies it keeps the books of. The person who runs one of those
5
+ -- companies had no preset to sit on. `viewer` reads and cannot hand anything
6
+ -- over; `accountant` hands over and can also draft an invoice, which is the
7
+ -- one thing the firm is there to do.
8
+ --
9
+ -- **What was missing was not a preset.** Filing a piece is an insert into
10
+ -- `attachments`, and the only policy that admits one tests `documents.write`
11
+ -- — the capability that also creates and changes draft documents and their
12
+ -- lines. There was no way to say "may give us the receipt" without saying
13
+ -- "may write the purchase invoice". So this migration adds one capability,
14
+ -- `documents.deposit`, and the narrowest policy that can carry it:
15
+ --
16
+ -- * INSERT only. There is no update and no delete behind it: a piece that
17
+ -- was handed over is the firm's to file, to re-point at the document it
18
+ -- becomes, or to discard, and all three are `documents.write`.
19
+ -- * On the company itself — `entity_type = 'company'`, `entity_id` the
20
+ -- company. `attachments` is polymorphic, and a capability that attached
21
+ -- to any row would let its holder pin a file of their own making on a
22
+ -- filed declaration, beside the receipt the administration sent. The
23
+ -- company row is the one thing a depositor is certain to be able to name,
24
+ -- and a file sitting on it is what an in-tray is.
25
+ -- * Signed. `uploaded_by` must be the caller, and the column now defaults
26
+ -- to it, so the firm reads who handed over what without trusting the
27
+ -- application to have said so.
28
+ --
29
+ -- **Reading back what one handed over.** `insert … returning` is checked
30
+ -- against the SELECT policies, and the one on `attachments` tests
31
+ -- `documents.read`. A client holds it; a member or a key granted
32
+ -- `documents.deposit` alone — a scanner in a hallway — does not, and would
33
+ -- have its insert refused for a row it is allowed to write. A second SELECT
34
+ -- policy lets a depositor read their own deposits. It reads no table, so it
35
+ -- cannot recurse.
36
+ --
37
+ -- **The preset.** `client` holds what `viewer` holds, read from
38
+ -- `role_capabilities` rather than listed again, plus `documents.deposit`. The
39
+ -- ledger is included on purpose: the books are the client's, the firm is the
40
+ -- guest, and a preset that hid a company's own entries from the person
41
+ -- answerable for them would be describing a different relationship. A firm
42
+ -- that wants less revokes it, per member — that is what
43
+ -- `capabilities_revoked` is for.
44
+ --
45
+ -- There is no approval in this preset because there is no act of approval in
46
+ -- the schema that is not also a write: `tax_filings.state = 'ready'` is
47
+ -- reached through `filings.write`. `docs/decisions.md` says so under this
48
+ -- date.
49
+
50
+ insert into capabilities (code, area, description) values
51
+ ('documents.deposit', 'documents',
52
+ 'Hand a file over to the company: an attachment on the company itself, signed by whoever dropped it. Insert only — filing it, moving it and deleting it are documents.write.')
53
+ on conflict (code) do nothing;
54
+
55
+ -- Whoever may write a document may already file an attachment anywhere, so
56
+ -- the two working presets hold the narrower act as well: an interface asks
57
+ -- one question — may this person drop a file here — and gets one answer.
58
+ insert into role_capabilities (role, capability) values
59
+ ('owner'::member_role, 'documents.deposit'),
60
+ ('accountant'::member_role, 'documents.deposit'),
61
+ ('client'::member_role, 'documents.deposit')
62
+ on conflict do nothing;
63
+
64
+ insert into role_capabilities (role, capability)
65
+ select 'client'::member_role, rc.capability
66
+ from role_capabilities rc
67
+ where rc.role = 'viewer'
68
+ on conflict do nothing;
69
+
70
+ alter table attachments alter column uploaded_by set default auth.uid();
71
+
72
+ comment on column attachments.uploaded_by is
73
+ 'Who filed it. Defaults to the caller; a deposit is refused unless it says the caller.';
74
+
75
+ create policy attachments_deposit on attachments
76
+ for insert with check (
77
+ has_capability(company_id, 'documents.deposit')
78
+ and entity_type = 'company'
79
+ and entity_id = company_id
80
+ and uploaded_by is not distinct from auth.uid()
81
+ );
82
+
83
+ comment on policy attachments_deposit on attachments is
84
+ 'A piece handed over: on the company itself, signed by the caller, insert only. Everything else on this table is documents.write.';
85
+
86
+ create policy attachments_select_own_deposit on attachments
87
+ for select using (
88
+ uploaded_by = auth.uid()
89
+ and has_capability(company_id, 'documents.deposit')
90
+ );
91
+
92
+ comment on policy attachments_select_own_deposit on attachments is
93
+ 'A depositor reads back what they handed over, so that `insert … returning` answers for somebody who holds documents.deposit and not documents.read.';
94
+
95
+ comment on table company_members is
96
+ 'Who may read or write a company. A role is a preset resolved through `role_capabilities`: `owner` administers, `accountant` books, `viewer` reads, `client` reads and hands pieces over.';