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,488 @@
1
+ -- Ekwo OS — a declaration that was filed is a row, not a report.
2
+ --
3
+ -- `vat_return()` recomputes from the ledger every time it is asked. That is
4
+ -- right for preparing a return and wrong for having filed one: post an entry
5
+ -- into a period that was declared three months ago — a late supplier invoice,
6
+ -- a correction, an accountant's adjustment — and the same function now answers
7
+ -- different figures than the ones the administration holds. **Nothing in the
8
+ -- database says which were the ones sent.**
9
+ --
10
+ -- That is where a tax audit starts, and it is the first thing this file fixes:
11
+ -- filing freezes the figures, box by box, and the freeze is what the file sent
12
+ -- is built from and what a later disagreement is measured against.
13
+ --
14
+ -- Three shapes hold it:
15
+ --
16
+ -- tax_filings one row per declaration of a period, with its state
17
+ -- tax_filing_boxes the figures as filed, one row per box
18
+ -- filing_drift() what the ledger would say today against what was sent
19
+ --
20
+ -- The boxes are rows and not a JSON document on purpose. A box is already an
21
+ -- object of this schema — it has a name, a sequence, a legal reference — and a
22
+ -- filed return has to be queryable the way everything else is: summed,
23
+ -- compared period to period, joined to the form it belongs to. A JSON blob
24
+ -- would make the filed figures the only numbers in this database nobody can
25
+ -- ask a question about.
26
+
27
+ create type tax_filing_state as enum (
28
+ 'draft', 'ready', 'filed', 'accepted', 'rejected', 'paid', 'superseded'
29
+ );
30
+
31
+ comment on type tax_filing_state is
32
+ 'Where a declaration is. draft while it is being prepared, ready when a person has approved the figures, filed once it has gone, accepted or rejected by the administration, paid when the money followed, superseded when a corrective replaced it.';
33
+
34
+ create table tax_filings (
35
+ id uuid primary key default gen_random_uuid(),
36
+ company_id uuid not null references companies(id) on delete cascade,
37
+ -- The form, as the pack names it. No foreign key, for the reason
38
+ -- `company_filing_periods` has none: `tax_report_templates` is keyed on
39
+ -- (country, code) and a company may file the form of a country that is not
40
+ -- its own. A trigger refuses a code no pack carries.
41
+ report_code text not null,
42
+ period_start date not null,
43
+ period_end date not null,
44
+ state tax_filing_state not null default 'draft',
45
+ due_date date,
46
+ -- What the administration gave back: a deposit number, a receipt, whatever
47
+ -- the portal calls it. Free text because every administration calls it
48
+ -- something else, and a closed vocabulary here would be an opinion about
49
+ -- portals rather than about accounting.
50
+ reference text,
51
+ -- When the figures were last computed into this filing. Distinct from
52
+ -- having figures at all: a period where nothing happened produces a
53
+ -- declaration with no lines, and a nil return is a return — most
54
+ -- administrations require it and fine its absence.
55
+ prepared_at timestamptz,
56
+ filed_at timestamptz,
57
+ filed_by uuid,
58
+ -- The filing this one replaces. A corrective never overwrites: what was sent
59
+ -- was sent.
60
+ supersedes_id uuid references tax_filings(id) on delete set null,
61
+ notes text,
62
+ created_at timestamptz not null default now(),
63
+ updated_at timestamptz not null default now(),
64
+ constraint tax_filings_period check (period_start <= period_end),
65
+ constraint tax_filings_filed_has_a_date check (
66
+ (state in ('draft', 'ready')) = (filed_at is null)
67
+ or state = 'superseded'
68
+ )
69
+ );
70
+
71
+ comment on table tax_filings is
72
+ 'One row per declaration of a period: what was filed, when, under which reference, and in which state. The figures are in tax_filing_boxes and they are frozen — this table exists so that "what did we declare" is a question with an answer.';
73
+
74
+ comment on column tax_filings.report_code is
75
+ 'The form, as the pack names it. A company may file the form of a country that is not its own, which is why this is a code and not a foreign key.';
76
+ comment on column tax_filings.supersedes_id is
77
+ 'The filing this corrective replaces, which moves to superseded. Nothing is ever overwritten: a declaration that went out stays as it went out.';
78
+ comment on column tax_filings.prepared_at is
79
+ 'When the figures were last computed into this filing. A declaration with no lines is not an unprepared one: a period where nothing happened is filed nil, and that is an obligation rather than an omission.';
80
+ comment on column tax_filings.reference is
81
+ 'What the administration gave back — a deposit number, a receipt. Free text: every portal calls it something else.';
82
+
83
+ create unique index tax_filings_live_period_idx
84
+ on tax_filings (company_id, report_code, period_start, period_end)
85
+ where state <> 'superseded';
86
+ create index tax_filings_company_state_idx on tax_filings (company_id, state);
87
+ create index tax_filings_due_idx on tax_filings (company_id, due_date);
88
+ create index tax_filings_supersedes_idx on tax_filings (supersedes_id);
89
+
90
+ create trigger tax_filings_set_updated_at
91
+ before update on tax_filings
92
+ for each row execute function set_updated_at();
93
+
94
+ create table tax_filing_boxes (
95
+ filing_id uuid not null references tax_filings(id) on delete cascade,
96
+ box text not null,
97
+ amount numeric(16, 2) not null,
98
+ primary key (filing_id, box)
99
+ );
100
+
101
+ comment on table tax_filing_boxes is
102
+ 'The figures as they were filed, one row per box. Frozen at filing and never recomputed: this is what the administration holds.';
103
+
104
+ create index tax_filing_boxes_box_idx on tax_filing_boxes (box);
105
+
106
+ alter table tax_filings enable row level security;
107
+ alter table tax_filing_boxes enable row level security;
108
+
109
+ -- ---------------------------------------------------------------------------
110
+ -- Who may file
111
+ --
112
+ -- `entries.read` is not enough and `company.write` is too much. Filing is an
113
+ -- act with an outside consequence — an administration receives something in
114
+ -- the company's name — and it is exactly the act a company may want to keep
115
+ -- for one person while several people keep the books. It sits on the two
116
+ -- presets that already carry the other outward acts.
117
+ -- ---------------------------------------------------------------------------
118
+
119
+ insert into capabilities (code, area, description) values
120
+ ('filings.read', 'filings', 'Read the declarations this company has prepared and filed, and their figures.'),
121
+ ('filings.write', 'filings', 'Prepare a declaration, freeze its figures, record that it was filed, accepted, rejected or paid.')
122
+ on conflict (code) do nothing;
123
+
124
+ insert into role_capabilities (role, capability)
125
+ select r.role, c.code
126
+ from (values ('owner'::member_role), ('accountant'::member_role)) as r(role),
127
+ (values ('filings.read'), ('filings.write')) as c(code)
128
+ on conflict do nothing;
129
+
130
+ -- Reading is granted more widely than filing: whoever can read the ledger can
131
+ -- see what the company declared from it.
132
+ insert into role_capabilities (role, capability)
133
+ select r.role, 'filings.read'
134
+ from (select distinct role from role_capabilities where capability = 'entries.read') as r
135
+ on conflict do nothing;
136
+
137
+ create policy tax_filings_select on tax_filings
138
+ for select using (has_capability(company_id, 'filings.read'));
139
+ create policy tax_filings_write on tax_filings
140
+ for all using (has_capability(company_id, 'filings.write'))
141
+ with check (has_capability(company_id, 'filings.write'));
142
+
143
+ create policy tax_filing_boxes_select on tax_filing_boxes
144
+ for select using (exists (
145
+ select 1 from tax_filings f
146
+ where f.id = tax_filing_boxes.filing_id
147
+ and has_capability(f.company_id, 'filings.read')));
148
+ create policy tax_filing_boxes_write on tax_filing_boxes
149
+ for all using (exists (
150
+ select 1 from tax_filings f
151
+ where f.id = tax_filing_boxes.filing_id
152
+ and has_capability(f.company_id, 'filings.write')))
153
+ with check (exists (
154
+ select 1 from tax_filings f
155
+ where f.id = tax_filing_boxes.filing_id
156
+ and has_capability(f.company_id, 'filings.write')));
157
+
158
+ comment on policy tax_filings_select on tax_filings is
159
+ 'filings.read: what a company declared is not the same secret as what it books, and a bookkeeper reads it without being able to file.';
160
+ comment on policy tax_filings_write on tax_filings is
161
+ 'filings.write: preparing and filing is an act with an outside consequence, granted apart from keeping the books.';
162
+
163
+ grant select, insert, update, delete on table tax_filings to authenticated, service_role;
164
+ grant select, insert, update, delete on table tax_filing_boxes to authenticated, service_role;
165
+
166
+ create trigger tax_filings_audit
167
+ after insert or update or delete on tax_filings
168
+ for each row execute function audit_changes('{"company":"company_id","key":["report_code","period_start"]}');
169
+
170
+ -- A form nobody carries is a typo, and a typo caught at filing time is a
171
+ -- declaration filed under the wrong form. Same guard, and same reason, as the
172
+ -- one `company_filing_periods` carries on the same column.
173
+ create or replace function tax_filings_report_exists()
174
+ returns trigger
175
+ language plpgsql
176
+ as $$
177
+ begin
178
+ if not exists (select 1 from tax_report_templates t where t.code = new.report_code) then
179
+ raise exception 'unknown_report_code: no pack in this installation carries the form %',
180
+ new.report_code;
181
+ end if;
182
+ return new;
183
+ end;
184
+ $$;
185
+
186
+ revoke execute on function tax_filings_report_exists() from public, anon;
187
+
188
+ create trigger tax_filings_report_exists
189
+ before insert or update of report_code on tax_filings
190
+ for each row execute function tax_filings_report_exists();
191
+
192
+ -- A filed declaration's figures are the ones that were filed. Changing them
193
+ -- afterwards is the one thing this whole file exists to prevent, so it is
194
+ -- refused by the database rather than by a convention.
195
+ create or replace function tax_filing_boxes_are_frozen()
196
+ returns trigger
197
+ language plpgsql
198
+ as $$
199
+ declare
200
+ v_state tax_filing_state;
201
+ begin
202
+ select f.state into v_state
203
+ from tax_filings f
204
+ where f.id = coalesce(new.filing_id, old.filing_id);
205
+
206
+ if v_state not in ('draft', 'ready') then
207
+ raise exception 'filing_is_frozen: the figures of a % declaration are what was filed', v_state;
208
+ end if;
209
+ return coalesce(new, old);
210
+ end;
211
+ $$;
212
+
213
+ revoke execute on function tax_filing_boxes_are_frozen() from public, anon;
214
+
215
+ create trigger tax_filing_boxes_are_frozen
216
+ before insert or update or delete on tax_filing_boxes
217
+ for each row execute function tax_filing_boxes_are_frozen();
218
+
219
+ -- ---------------------------------------------------------------------------
220
+ -- prepare_filing
221
+ --
222
+ -- Reads the declaration the way anybody would, and keeps the answer. Called
223
+ -- again on a draft it refreshes: preparing is iterative, and the figures move
224
+ -- until somebody says they are ready.
225
+ -- ---------------------------------------------------------------------------
226
+
227
+ create or replace function prepare_filing(
228
+ p_company_id uuid,
229
+ p_from date,
230
+ p_to date,
231
+ p_report_code text default null
232
+ )
233
+ returns tax_filings
234
+ language plpgsql
235
+ volatile
236
+ security invoker
237
+ as $$
238
+ declare
239
+ v_code text;
240
+ v_filing tax_filings;
241
+ begin
242
+ v_code := coalesce(p_report_code, periodic_return_code(p_company_id));
243
+ if v_code is null then
244
+ raise exception 'no_report_code: the pack of this company names no periodic return, so say which form this is';
245
+ end if;
246
+
247
+ select * into v_filing
248
+ from tax_filings f
249
+ where f.company_id = p_company_id
250
+ and f.report_code = v_code
251
+ and f.period_start = p_from
252
+ and f.period_end = p_to
253
+ and f.state <> 'superseded';
254
+
255
+ if found and v_filing.state not in ('draft', 'ready') then
256
+ raise exception 'filing_already_% : % for % to % has gone; a change to it is a corrective',
257
+ v_filing.state, v_code, p_from, p_to;
258
+ end if;
259
+
260
+ if not found then
261
+ insert into tax_filings (company_id, report_code, period_start, period_end)
262
+ values (p_company_id, v_code, p_from, p_to)
263
+ returning * into v_filing;
264
+ else
265
+ update tax_filings set state = 'draft' where id = v_filing.id returning * into v_filing;
266
+ delete from tax_filing_boxes where filing_id = v_filing.id;
267
+ end if;
268
+
269
+ insert into tax_filing_boxes (filing_id, box, amount)
270
+ select v_filing.id, r.box, r.amount
271
+ from vat_return(p_company_id, p_from, p_to, v_code) r
272
+ where not r.hidden;
273
+
274
+ update tax_filings set prepared_at = now() where id = v_filing.id
275
+ returning * into v_filing;
276
+
277
+ return v_filing;
278
+ end;
279
+ $$;
280
+
281
+ comment on function prepare_filing(uuid, date, date, text) is
282
+ 'Computes the declaration and keeps the answer, box by box. Called again on a draft it refreshes; on a declaration that has gone it refuses and says the word for what is needed instead — a corrective.';
283
+
284
+ revoke execute on function prepare_filing(uuid, date, date, text) from public, anon;
285
+ grant execute on function prepare_filing(uuid, date, date, text) to authenticated, service_role;
286
+
287
+ -- ---------------------------------------------------------------------------
288
+ -- The transitions
289
+ --
290
+ -- Functions and not an `update`, for the reason posting is a function: what
291
+ -- changes is a state, and the row is one the member may already write. Each
292
+ -- refusal says which state the declaration is in, because "not allowed" on a
293
+ -- return somebody is trying to file is the least useful sentence a database
294
+ -- can produce.
295
+ -- ---------------------------------------------------------------------------
296
+
297
+ create or replace function file_filing(
298
+ p_filing_id uuid,
299
+ p_reference text default null,
300
+ p_filed_at timestamptz default null
301
+ )
302
+ returns tax_filings
303
+ language plpgsql
304
+ volatile
305
+ security invoker
306
+ as $$
307
+ declare
308
+ v_filing tax_filings;
309
+ begin
310
+ select * into v_filing from tax_filings f where f.id = p_filing_id;
311
+ if not found then
312
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
313
+ end if;
314
+ if v_filing.state not in ('draft', 'ready') then
315
+ raise exception 'filing_already_%: this declaration has already gone', v_filing.state;
316
+ end if;
317
+
318
+ if v_filing.prepared_at is null then
319
+ raise exception 'filing_not_prepared: compute the figures before filing them';
320
+ end if;
321
+
322
+ update tax_filings
323
+ set state = 'filed',
324
+ reference = coalesce(p_reference, reference),
325
+ filed_at = coalesce(p_filed_at, now()),
326
+ filed_by = auth.uid()
327
+ where id = p_filing_id
328
+ returning * into v_filing;
329
+ return v_filing;
330
+ end;
331
+ $$;
332
+
333
+ comment on function file_filing(uuid, text, timestamptz) is
334
+ 'Records that a declaration has gone, with the reference the administration gave back. It asks that the figures were computed, not that there are any: a period where nothing happened is filed nil. From here the figures are frozen by a trigger, and a change to them is a corrective.';
335
+
336
+ create or replace function record_filing_outcome(
337
+ p_filing_id uuid,
338
+ p_state tax_filing_state,
339
+ p_reference text default null
340
+ )
341
+ returns tax_filings
342
+ language plpgsql
343
+ volatile
344
+ security invoker
345
+ as $$
346
+ declare
347
+ v_filing tax_filings;
348
+ begin
349
+ select * into v_filing from tax_filings f where f.id = p_filing_id;
350
+ if not found then
351
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
352
+ end if;
353
+
354
+ if p_state not in ('accepted', 'rejected', 'paid') then
355
+ raise exception 'not_an_outcome: % is not something an administration answers', p_state;
356
+ end if;
357
+ if v_filing.state = 'draft' or v_filing.state = 'ready' then
358
+ raise exception 'filing_not_sent: a declaration that has not gone cannot come back %', p_state;
359
+ end if;
360
+ if p_state = 'paid' and v_filing.state <> 'accepted' then
361
+ raise exception 'filing_not_accepted: paying follows acceptance, and this one is %', v_filing.state;
362
+ end if;
363
+
364
+ update tax_filings
365
+ set state = p_state,
366
+ reference = coalesce(p_reference, reference)
367
+ where id = p_filing_id
368
+ returning * into v_filing;
369
+ return v_filing;
370
+ end;
371
+ $$;
372
+
373
+ comment on function record_filing_outcome(uuid, tax_filing_state, text) is
374
+ 'What came back: accepted, rejected, or paid. Paying follows acceptance, and a declaration that never went cannot come back at all.';
375
+
376
+ create or replace function supersede_filing(p_filing_id uuid)
377
+ returns tax_filings
378
+ language plpgsql
379
+ volatile
380
+ security invoker
381
+ as $$
382
+ declare
383
+ v_old tax_filings;
384
+ v_new tax_filings;
385
+ begin
386
+ select * into v_old from tax_filings f where f.id = p_filing_id;
387
+ if not found then
388
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
389
+ end if;
390
+ if v_old.state in ('draft', 'ready') then
391
+ raise exception 'filing_not_sent: a declaration that has not gone is corrected by preparing it again';
392
+ end if;
393
+ if v_old.state = 'superseded' then
394
+ raise exception 'filing_already_superseded: % was already replaced', p_filing_id;
395
+ end if;
396
+
397
+ update tax_filings set state = 'superseded' where id = p_filing_id;
398
+
399
+ insert into tax_filings (company_id, report_code, period_start, period_end,
400
+ due_date, supersedes_id)
401
+ values (v_old.company_id, v_old.report_code, v_old.period_start, v_old.period_end,
402
+ v_old.due_date, v_old.id)
403
+ returning * into v_new;
404
+
405
+ insert into tax_filing_boxes (filing_id, box, amount)
406
+ select v_new.id, r.box, r.amount
407
+ from vat_return(v_old.company_id, v_old.period_start, v_old.period_end, v_old.report_code) r
408
+ where not r.hidden;
409
+
410
+ update tax_filings set prepared_at = now() where id = v_new.id returning * into v_new;
411
+
412
+ return v_new;
413
+ end;
414
+ $$;
415
+
416
+ comment on function supersede_filing(uuid) is
417
+ 'Opens a corrective: the declaration that went becomes superseded and a new draft is prepared from today''s ledger, pointing at it. What was sent stays as it was sent.';
418
+
419
+ revoke execute on function file_filing(uuid, text, timestamptz) from public, anon;
420
+ grant execute on function file_filing(uuid, text, timestamptz) to authenticated, service_role;
421
+ revoke execute on function record_filing_outcome(uuid, tax_filing_state, text) from public, anon;
422
+ grant execute on function record_filing_outcome(uuid, tax_filing_state, text) to authenticated, service_role;
423
+ revoke execute on function supersede_filing(uuid) from public, anon;
424
+ grant execute on function supersede_filing(uuid) to authenticated, service_role;
425
+
426
+ -- ---------------------------------------------------------------------------
427
+ -- filing_drift
428
+ --
429
+ -- What the ledger would say today, against what was sent. It falls out of the
430
+ -- freeze for nothing, and it is the most useful health check this schema has:
431
+ -- a figure that moved after filing is either a correction somebody owes the
432
+ -- administration, or an entry that landed in the wrong period.
433
+ -- ---------------------------------------------------------------------------
434
+
435
+ create or replace function filing_drift(p_filing_id uuid)
436
+ returns table (
437
+ box text,
438
+ filed numeric,
439
+ ledger numeric,
440
+ difference numeric
441
+ )
442
+ language plpgsql
443
+ stable
444
+ security invoker
445
+ as $$
446
+ declare
447
+ v_filing tax_filings;
448
+ begin
449
+ select * into v_filing from tax_filings f where f.id = p_filing_id;
450
+ if not found then
451
+ raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
452
+ end if;
453
+
454
+ return query
455
+ with today as (
456
+ select r.box, r.amount
457
+ from vat_return(v_filing.company_id, v_filing.period_start, v_filing.period_end,
458
+ v_filing.report_code) r
459
+ where not r.hidden
460
+ ),
461
+ filed as (
462
+ select b.box, b.amount from tax_filing_boxes b where b.filing_id = p_filing_id
463
+ )
464
+ select coalesce(f.box, t.box),
465
+ coalesce(f.amount, 0),
466
+ coalesce(t.amount, 0),
467
+ coalesce(t.amount, 0) - coalesce(f.amount, 0)
468
+ from filed f
469
+ full outer join today t on t.box = f.box
470
+ where coalesce(t.amount, 0) <> coalesce(f.amount, 0)
471
+ order by 1;
472
+ end;
473
+ $$;
474
+
475
+ comment on function filing_drift(uuid) is
476
+ 'Box by box, what the ledger says now against what was filed, for the boxes where the two disagree. Empty is the answer everybody wants; anything else is either a corrective to file or an entry in the wrong period.';
477
+
478
+ revoke execute on function filing_drift(uuid) from public, anon;
479
+ grant execute on function filing_drift(uuid) to authenticated, service_role;
480
+
481
+ -- The file that was sent and the receipt that came back are attachments, like
482
+ -- every other piece: one place where files live, one set of policies, one
483
+ -- storage path convention.
484
+ alter table attachments drop constraint attachments_entity_type;
485
+ alter table attachments add constraint attachments_entity_type check (
486
+ entity_type in ('company', 'contact', 'document', 'entry', 'payment',
487
+ 'bank_statement', 'bank_transaction', 'fiscal_year', 'tax_filing')
488
+ );
@@ -0,0 +1,194 @@
1
+ -- Ekwo OS — when a declaration is due.
2
+ --
3
+ -- The schema knows how *often* a company files — `company_filing_periods`,
4
+ -- one cadence per form — and has never known **when**. A cadence without a
5
+ -- deadline answers "every quarter" to the question "what do I owe this week",
6
+ -- which is not an answer.
7
+ --
8
+ -- The date is not a fact about the ledger, so it is not in the core: it is a
9
+ -- rule of the country, and it goes where every other country rule goes — into
10
+ -- the pack, in a closed vocabulary, with the text that sets it.
11
+ --
12
+ -- day_of_month_after_period the twentieth of the month that follows
13
+ -- last_day_of_month_after_period the last day of that month
14
+ --
15
+ -- Either may add `plus_days`, which exists because the United Kingdom grants
16
+ -- seven of them to a return filed online while the regulation still says the
17
+ -- last day of the month.
18
+ --
19
+ -- **What the vocabulary deliberately cannot say** is a schedule that depends on
20
+ -- *who* is filing rather than on *what period*: France staggers its dates by
21
+ -- the taxpayer's identification number and legal form, so `packs/fr/` declares
22
+ -- no deadline at all and `filing_deadline()` answers null for it. A null that
23
+ -- means "this pack does not know" is worth more than a date that is wrong for
24
+ -- most filers, and it is written up in docs/international.md.
25
+
26
+ create type filing_deadline_rule as enum (
27
+ 'day_of_month_after_period', 'last_day_of_month_after_period'
28
+ );
29
+
30
+ comment on type filing_deadline_rule is
31
+ 'How a filing date is worked out from the end of a period. Two values, closed: a fixed day of the month that follows, or the last day of it. A third needs a migration, and the first country that needs one will say so in a gap rather than in a regular expression.';
32
+
33
+ alter table tax_report_templates
34
+ add column if not exists deadline_rule filing_deadline_rule,
35
+ add column if not exists deadline_day smallint,
36
+ add column if not exists deadline_plus_days smallint,
37
+ add column if not exists deadline_reference text,
38
+ add column if not exists deadline_source_key text;
39
+
40
+ comment on column tax_report_templates.deadline_rule is
41
+ 'How the filing date follows the end of the period. Null where the pack says nothing, which is the honest answer for a country whose schedule depends on the taxpayer.';
42
+ comment on column tax_report_templates.deadline_day is
43
+ 'Day of the month that follows the period. Filled exactly when the rule is day_of_month_after_period.';
44
+ comment on column tax_report_templates.deadline_plus_days is
45
+ 'Days added to the date the rule produces — the seven the United Kingdom grants for filing online, while the regulation still says the last day of the month.';
46
+ comment on column tax_report_templates.deadline_reference is
47
+ 'The text that sets the date, and the conditions on it: an extension that does not reach every filer is said here.';
48
+ comment on column tax_report_templates.deadline_source_key is
49
+ 'Key of the register entry that reference is in.';
50
+
51
+ alter table tax_report_templates
52
+ add constraint tax_report_templates_deadline_shape check (
53
+ case deadline_rule
54
+ when 'day_of_month_after_period' then deadline_day between 1 and 31
55
+ when 'last_day_of_month_after_period' then deadline_day is null
56
+ else deadline_day is null and deadline_plus_days is null
57
+ and deadline_reference is null and deadline_source_key is null
58
+ end
59
+ );
60
+
61
+ -- ---------------------------------------------------------------------------
62
+ -- filing_deadline
63
+ --
64
+ -- The end of a period in, the day it is due out. Null where the pack says
65
+ -- nothing — a caller that wants a date has to face a country that does not
66
+ -- publish one as a rule.
67
+ -- ---------------------------------------------------------------------------
68
+
69
+ create or replace function filing_deadline(
70
+ p_company_id uuid,
71
+ p_report_code text,
72
+ p_period_end date
73
+ )
74
+ returns date
75
+ language sql
76
+ stable
77
+ security invoker
78
+ as $$
79
+ select case t.deadline_rule
80
+ when 'day_of_month_after_period' then
81
+ -- The day the pack names, in the month that follows. A day the
82
+ -- month does not have lands on its last: `make_date` would raise,
83
+ -- and the thirty-first of a thirty-day month is a date the
84
+ -- administration reads as the end of it.
85
+ least(
86
+ (date_trunc('month', p_period_end + interval '1 month')
87
+ + make_interval(days => t.deadline_day - 1))::date,
88
+ (date_trunc('month', p_period_end + interval '2 month') - interval '1 day')::date
89
+ )
90
+ when 'last_day_of_month_after_period' then
91
+ (date_trunc('month', p_period_end + interval '2 month') - interval '1 day')::date
92
+ else null
93
+ end
94
+ + coalesce(t.deadline_plus_days, 0)
95
+ from tax_report_templates t
96
+ join companies c on c.id = p_company_id
97
+ where t.code = p_report_code
98
+ and t.country in (c.country, c.fiscal_country)
99
+ and t.deadline_rule is not null
100
+ limit 1;
101
+ $$;
102
+
103
+ comment on function filing_deadline(uuid, text, date) is
104
+ 'When a period closed on this date has to be declared, under the rule the pack carries. Null where the pack declares none — which is a country whose schedule depends on the filer, not a country without deadlines.';
105
+
106
+ revoke execute on function filing_deadline(uuid, text, date) from public, anon;
107
+ grant execute on function filing_deadline(uuid, text, date) to authenticated, service_role;
108
+
109
+ -- ---------------------------------------------------------------------------
110
+ -- upcoming_filings
111
+ --
112
+ -- What is due between two dates, for every declaration this company files,
113
+ -- with what has already been prepared or sent against it. The periods are
114
+ -- generated from the cadence the company recorded — the whole point of
115
+ -- `company_filing_periods` — so a company that files monthly and files a
116
+ -- recapitulative statement quarterly gets both, on their own rhythms.
117
+ -- ---------------------------------------------------------------------------
118
+
119
+ create or replace function upcoming_filings(
120
+ p_company_id uuid,
121
+ p_from date,
122
+ p_to date
123
+ )
124
+ returns table (
125
+ report_code text,
126
+ period_start date,
127
+ period_end date,
128
+ due_date date,
129
+ state tax_filing_state,
130
+ filing_id uuid
131
+ )
132
+ language sql
133
+ stable
134
+ security invoker
135
+ as $$
136
+ with forms as (
137
+ -- Every form this company has a cadence for, and the periodic return it
138
+ -- files even when nobody has written a cadence down for it.
139
+ select p.report_code, p.period::text as cadence
140
+ from company_filing_periods p
141
+ where p.company_id = p_company_id
142
+ union
143
+ select periodic_return_code(p_company_id),
144
+ coalesce(
145
+ (select p.period::text from company_filing_periods p
146
+ where p.company_id = p_company_id
147
+ and p.report_code = periodic_return_code(p_company_id)),
148
+ (select t.period_default::text from tax_report_templates t
149
+ join companies c on c.id = p_company_id
150
+ where t.code = periodic_return_code(p_company_id)
151
+ and t.country in (c.country, c.fiscal_country)))
152
+ where periodic_return_code(p_company_id) is not null
153
+ ),
154
+ periods as (
155
+ select f.report_code,
156
+ g.start::date as period_start,
157
+ (g.start + case f.cadence
158
+ when 'month' then interval '1 month'
159
+ when 'quarter' then interval '3 month'
160
+ else interval '1 year'
161
+ end - interval '1 day')::date as period_end
162
+ from forms f
163
+ cross join lateral generate_series(
164
+ date_trunc(case f.cadence when 'month' then 'month'
165
+ when 'quarter' then 'quarter'
166
+ else 'year' end, p_from::timestamp),
167
+ p_to::timestamp,
168
+ case f.cadence when 'month' then interval '1 month'
169
+ when 'quarter' then interval '3 month'
170
+ else interval '1 year' end
171
+ ) as g(start)
172
+ where f.cadence is not null
173
+ )
174
+ select p.report_code,
175
+ p.period_start,
176
+ p.period_end,
177
+ filing_deadline(p_company_id, p.report_code, p.period_end),
178
+ f.state,
179
+ f.id
180
+ from periods p
181
+ left join tax_filings f
182
+ on f.company_id = p_company_id
183
+ and f.report_code = p.report_code
184
+ and f.period_start = p.period_start
185
+ and f.period_end = p.period_end
186
+ and f.state <> 'superseded'
187
+ order by p.period_end, p.report_code;
188
+ $$;
189
+
190
+ comment on function upcoming_filings(uuid, date, date) is
191
+ 'What this company has to file between two dates: the periods its cadences produce, the day each is due where the pack says, and the declaration already prepared or sent against it. Read-only; sending the reminder is somebody else''s job, because a reminder needs a channel and somebody to operate it.';
192
+
193
+ revoke execute on function upcoming_filings(uuid, date, date) from public, anon;
194
+ grant execute on function upcoming_filings(uuid, date, date) to authenticated, service_role;