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,342 @@
1
+ -- Ekwo OS — whether a territory is inside the common system of VAT, as data.
2
+ --
3
+ -- `ec_sales_list()` was written with a hole in it, and the hole is written up
4
+ -- in `docs/international.md`: the core cannot say whether a country is a
5
+ -- Member State, and it cannot say that a Greek customer identifies under `EL`
6
+ -- and not under `GR`. Both questions are asked of every supply the
7
+ -- recapitulative statement carries, and neither has an answer anywhere in this
8
+ -- repository. So a supply to a customer in a third country is listed as an
9
+ -- intra-Community supply, and a Greek customer recorded without a prefix is
10
+ -- listed under a code every one of the four administrations refuses.
11
+ --
12
+ -- Neither can be fixed where the question is asked.
13
+ --
14
+ -- * **A list of Member States in a function is a country literal**, which is
15
+ -- the second invariant of `CONTRIBUTING.md` and the thing the whole pack
16
+ -- format exists to avoid: a `case` naming twenty-seven codes is twenty-seven
17
+ -- rules of law compiled into the core, and the day Croatia acceded it would
18
+ -- have been a release.
19
+ -- * **A list of Member States in a pack would be wrong.** Membership is the
20
+ -- Union's law, not any one country's. Every pack would carry the same
21
+ -- twenty-seven rows, four copies that can disagree, and a country that has
22
+ -- no pack here would have no answer at all.
23
+ --
24
+ -- So it is framework data, beside `currencies`: a small reference table the
25
+ -- release fills, that no company owns, that no pack writes, and that every
26
+ -- reader of it reads the same way.
27
+ --
28
+ -- **The table is here and the rows are in `supabase/seed/00_territories.sql`,
29
+ -- beside the currencies.** That is the division the repository already draws
30
+ -- and it draws it for a reason: a migration is the shape of the database and a
31
+ -- seed is the reference data in it, and re-applying a seed is how a correction
32
+ -- reaches an installation that already exists. Membership of the Union changes
33
+ -- — an accession, a withdrawal, a territory moved from one article of the
34
+ -- Directive to another — and every one of those changes is a row somebody has
35
+ -- to be able to send to a running installation without a schema change. The
36
+ -- seed upserts on the code, so re-applying it corrects a row instead of
37
+ -- skipping it, which is what the country packs already do and what the
38
+ -- currencies, whose list nobody corrects, do not need to.
39
+ --
40
+ -- It also keeps the guard that reads these files honest. `tests/tax_report.test.ts`
41
+ -- refuses a country code in any migration, and the guard is right: a migration
42
+ -- is the core. The seeds are where a country is allowed to be named, because
43
+ -- naming countries is what a seed is for.
44
+ --
45
+ -- One consequence had to be handled rather than hoped away. A database with
46
+ -- the table and without the rows would answer that nothing is in the Union,
47
+ -- and would go on writing recapitulative statements in which every customer is
48
+ -- a violation. `ec_sales_list()` therefore raises `no_territories` when the
49
+ -- table is empty, by name, in the migration that teaches it to read this one —
50
+ -- the same answer `rounding_of()` gives for a currency it does not know, and
51
+ -- the rule `CONTRIBUTING.md` states as *raise, do not warn*.
52
+ --
53
+ -- ---------------------------------------------------------------------------
54
+ -- Why this is not the country literal the invariant forbids
55
+ -- ---------------------------------------------------------------------------
56
+ --
57
+ -- The invariant is about a *rule* only one country can justify: `if country =
58
+ -- 'BE' then`, a default of `'EUR'`, an account code in a function. Nothing
59
+ -- here is one. Every row of this table says the same kind of thing about its
60
+ -- territory, no row is privileged, no function below branches on a code, and
61
+ -- adding a Member State is an insert in a seed. `00_currencies.sql` holds
62
+ -- `'EUR'` for the same reason and breaks nothing: it is data about the world,
63
+ -- read by name, never compiled into a decision.
64
+ --
65
+ -- ---------------------------------------------------------------------------
66
+ -- What is in the table, and what is not
67
+ -- ---------------------------------------------------------------------------
68
+ --
69
+ -- One rule decides membership, so that the next person adding a row knows
70
+ -- whether it belongs: **the table holds the territories Directive 2006/112/EC
71
+ -- names, the States that have been bound by it, and Northern Ireland.** That
72
+ -- is the twenty-seven Member States; the United Kingdom, which was one; the
73
+ -- territories articles 6 and 7 take out of the common system or put into it;
74
+ -- and Northern Ireland, which article 6 never heard of and which the
75
+ -- Withdrawal Agreement put inside the system for goods alone.
76
+ --
77
+ -- It is therefore **not a country list**. Switzerland is not here, and the
78
+ -- answer for Switzerland is the answer for every territory the table does not
79
+ -- carry: the common system of VAT does not apply there. A row is only needed
80
+ -- where the plain reading of the customer's ISO country would be wrong.
81
+ --
82
+ -- Three exclusions are knowingly absent, and each for a stated reason:
83
+ --
84
+ -- * **The Sovereign Base Areas of Akrotiri and Dhekelia**, which article
85
+ -- 7(1) treats as part of Cyprus. No register gives them a code and no VAT
86
+ -- number carries one, so a row would have a key nobody could look up with.
87
+ -- * **The overseas countries and territories** of annex II to the TFEU —
88
+ -- Saint-Barthélemy, Greenland, Aruba and the rest. They are outside the
89
+ -- common system under article 355(2) of the Treaty and not under article 6,
90
+ -- so the rule above puts them out of this table; the general answer covers
91
+ -- them.
92
+ -- * **Rates, thresholds and registration.** This table says where the system
93
+ -- applies. It says nothing about what it charges, which is a pack's
94
+ -- business — and for the One-Stop Shop is the open question
95
+ -- `docs/international.md` ends on.
96
+ --
97
+ -- ---------------------------------------------------------------------------
98
+ -- Sources
99
+ -- ---------------------------------------------------------------------------
100
+ --
101
+ -- * **Council Directive 2006/112/EC of 28 November 2006 on the common system
102
+ -- of value added tax**, articles 5, 6 and 7 — what the territory of the
103
+ -- Union is, which territories are taken out of it, and which non-Union
104
+ -- territories are treated as part of it. Article 6 as amended by Directive
105
+ -- (EU) 2019/475, which moved Campione d'Italia and the Italian waters of
106
+ -- Lake Lugano from paragraph 2 to paragraph 1 on 1 January 2020 — into the
107
+ -- customs territory, and outside the VAT one either way.
108
+ -- https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A02006L0112-20240101
109
+ -- * **Agreement on the withdrawal of the United Kingdom**, articles 126 and
110
+ -- 127 (the transition period) and the Protocol on Ireland/Northern Ireland,
111
+ -- article 8 and annex 3 (VAT on goods), as amended by the Windsor Framework.
112
+ -- https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX%3A12019W/TXT%2802%29
113
+ -- * **The VIES list of VAT identification numbers by Member State**, which is
114
+ -- where `EL` for Greece and `XI` for Northern Ireland are published as the
115
+ -- prefixes their numbers carry.
116
+ -- https://ec.europa.eu/taxation_customs/vies/#/vat-validation
117
+ -- * **The accession treaties**, for the day each State became bound. Cited on
118
+ -- the row itself, because a date nobody can trace is a date nobody can
119
+ -- check.
120
+ --
121
+ -- Nothing here quotes a word of those texts. A quotation ages without anybody
122
+ -- noticing, which is the rule the packs already follow.
123
+
124
+ -- ---------------------------------------------------------------------------
125
+ -- Two vocabularies
126
+ -- ---------------------------------------------------------------------------
127
+
128
+ -- How much of the common system of VAT reaches a territory. Three values,
129
+ -- because the Withdrawal Agreement invented the middle one and nothing else
130
+ -- in European law has needed it: Northern Ireland is inside the system for
131
+ -- supplies of goods and outside it for supplies of services.
132
+ create type eu_vat_scope as enum ('full', 'goods', 'none');
133
+
134
+ comment on type eu_vat_scope is
135
+ 'How much of the common system of VAT applies to a territory: the whole of it, supplies of goods only (Northern Ireland, under the Protocol on Ireland/Northern Ireland), or none of it.';
136
+
137
+ -- Where a territory's code comes from. A key is only useful if a reader knows
138
+ -- which register to look it up in, and these rows are not all in one: a
139
+ -- Member State has an ISO 3166-1 alpha-2 code, an excluded region usually has
140
+ -- an ISO 3166-2 one, Northern Ireland has a code the Union's own systems use
141
+ -- and ISO does not, and five territories of article 6 are named by the
142
+ -- Directive and coded by nobody.
143
+ create type territory_code_source as enum ('iso_3166_1', 'iso_3166_2', 'eu', 'named');
144
+
145
+ comment on type territory_code_source is
146
+ 'Which register a territory''s code comes from: ISO 3166-1 alpha-2, ISO 3166-2, the Union''s own VAT and customs systems (XI), or none at all — in which case the code is a name of this table and says so.';
147
+
148
+ -- ---------------------------------------------------------------------------
149
+ -- The table
150
+ -- ---------------------------------------------------------------------------
151
+
152
+ create table territories (
153
+ code text primary key,
154
+ code_source territory_code_source not null,
155
+ -- English, and the name the source uses. The labels a user reads are a
156
+ -- different question and are not answered here: this is a reference table,
157
+ -- not a catalogue, and nothing in the schema prints from it.
158
+ name text not null,
159
+ -- The Member State this territory hangs off: the one it is part of, the one
160
+ -- it is excluded from, or the one it is treated as part of. Null on a State
161
+ -- itself.
162
+ parent_code text references territories(code),
163
+ -- How much of the common system applied here, *during the window below*.
164
+ eu_vat_scope eu_vat_scope not null,
165
+ -- The window. `eu_vat_from` is the first day the common system reached the
166
+ -- territory and `eu_vat_to` the last; both null where it never did.
167
+ --
168
+ -- They are named for VAT and not for membership on purpose, and the United
169
+ -- Kingdom is why. It ceased to be a Member State on 31 January 2020 and
170
+ -- went on being inside the common system of VAT until 31 December 2020,
171
+ -- because articles 126 and 127 of the Withdrawal Agreement kept Union law
172
+ -- applying through the transition. A column called `eu_member_to` holding
173
+ -- 2020-12-31 would be false, and one holding 2020-01-31 would make
174
+ -- `ec_sales_list()` refuse a recapitulative statement that was lawfully
175
+ -- filed for every month of 2020. The reader of this table is VAT code, so
176
+ -- the column records the VAT date and the row's `legal_reference` records
177
+ -- the other one.
178
+ eu_vat_from date,
179
+ eu_vat_to date,
180
+ -- The prefix this territory's VAT identification numbers carry, when it is
181
+ -- not the code itself. Greece identifies under `EL`, Monaco under `FR`, the
182
+ -- Isle of Man under `GB`. Null everywhere else, so that the column holds a
183
+ -- difference and never a copy: `vat_prefix_of()` falls back to the code.
184
+ vat_prefix char(2),
185
+ -- The text that puts this territory where it is. Required, for the reason
186
+ -- `legal_reference` is required on every tax and every box of a pack: a rule
187
+ -- nobody can trace to a source is a rule nobody can review.
188
+ legal_reference text not null,
189
+ -- ISO 3166-1 is two letters; ISO 3166-2 and the names of article 6 are a
190
+ -- country and a subdivision.
191
+ constraint territories_code_format
192
+ check (code ~ '^[A-Z]{2}(-[A-Z0-9]{1,12})?$'),
193
+ constraint territories_prefix_format
194
+ check (vat_prefix is null or vat_prefix ~ '^[A-Z]{2}$'),
195
+ constraint territories_not_its_own_parent
196
+ check (parent_code is null or parent_code <> code),
197
+ -- A window that is closed before it opens, and a window with an end and no
198
+ -- beginning, are both a row somebody mistyped.
199
+ constraint territories_window_ordered
200
+ check (eu_vat_to is null or (eu_vat_from is not null and eu_vat_from <= eu_vat_to)),
201
+ -- The scope and the window say one thing between them: a territory the
202
+ -- system never reached has no window, and a territory with a window says how
203
+ -- much of the system it got.
204
+ constraint territories_scope_matches_window
205
+ check ((eu_vat_scope = 'none') = (eu_vat_from is null))
206
+ );
207
+
208
+ comment on table territories is
209
+ 'The territories of the common system of value added tax: the Member States with the day each became bound, the United Kingdom with the day it stopped being, Northern Ireland, and the territories articles 6 and 7 of Directive 2006/112/EC take out of the system or put into it. Framework data filled by the release, like currencies — no company owns a row and no country pack writes one. A territory this table does not carry is outside the common system, which is the answer for every third country.';
210
+
211
+ comment on column territories.code is
212
+ 'ISO 3166-1 alpha-2 where the territory has one, ISO 3166-2 where only a subdivision code exists, XI for Northern Ireland, and a name of this table for the five territories of article 6 that no register codes. code_source says which.';
213
+ comment on column territories.parent_code is
214
+ 'The Member State this territory hangs off: the one it is part of, excluded from, or treated as part of.';
215
+ comment on column territories.eu_vat_scope is
216
+ 'How much of the common system applied during the window below: all of it, supplies of goods only, or none.';
217
+ comment on column territories.eu_vat_from is
218
+ 'First day the common system of VAT reached this territory. Null where it never did.';
219
+ comment on column territories.eu_vat_to is
220
+ 'Last day it did. Null while it still does. A VAT date, not a membership one: the United Kingdom left the Union on 31 January 2020 and left the common system on 31 December 2020.';
221
+ comment on column territories.vat_prefix is
222
+ 'The prefix this territory''s VAT identification numbers carry, when it differs from the code: EL for Greece, FR for Monaco, GB for the Isle of Man. Null when the two are the same.';
223
+ comment on column territories.legal_reference is
224
+ 'The text that puts this territory where it is — an accession treaty, an article of Directive 2006/112/EC, the Withdrawal Agreement.';
225
+
226
+ create index territories_parent_code_idx on territories (parent_code);
227
+ create index territories_vat_prefix_idx on territories (vat_prefix);
228
+
229
+ -- ---------------------------------------------------------------------------
230
+ -- Reading it
231
+ --
232
+ -- Three functions, and the first is the only one that touches the table.
233
+ --
234
+ -- `territory_of()` resolves what a caller actually holds, which is two letters
235
+ -- off a VAT number or out of `contacts.country` — so it matches a code *or* a
236
+ -- prefix. A code wins when both match, which is what makes `FR` the French row
237
+ -- and not the Monegasque one that borrows its prefix.
238
+ --
239
+ -- They are `stable` and not `immutable`: they read a table. A migration that
240
+ -- corrects a row has to change what they answer, which is the whole reason the
241
+ -- rows are not in a function.
242
+ -- ---------------------------------------------------------------------------
243
+
244
+ create or replace function territory_of(p_code text)
245
+ returns territories
246
+ language sql
247
+ stable
248
+ as $$
249
+ select t.*
250
+ from territories t
251
+ where t.code = upper(btrim(p_code))
252
+ or t.vat_prefix = upper(btrim(p_code))
253
+ order by (t.code = upper(btrim(p_code))) desc
254
+ limit 1;
255
+ $$;
256
+
257
+ comment on function territory_of(text) is
258
+ 'The territory a code or a VAT prefix names, or null when this table carries none. A code wins over a prefix, so FR is France and not the Monaco row that identifies under it.';
259
+
260
+ create or replace function eu_vat_scope_of(p_code text, p_on date default current_date)
261
+ returns eu_vat_scope
262
+ language sql
263
+ stable
264
+ as $$
265
+ select coalesce(
266
+ (select case
267
+ when t.eu_vat_from is null then 'none'::eu_vat_scope
268
+ when p_on < t.eu_vat_from then 'none'::eu_vat_scope
269
+ when t.eu_vat_to is not null and p_on > t.eu_vat_to then 'none'::eu_vat_scope
270
+ else t.eu_vat_scope
271
+ end
272
+ from territory_of(p_code) t
273
+ where t.code is not null),
274
+ 'none'::eu_vat_scope);
275
+ $$;
276
+
277
+ comment on function eu_vat_scope_of(text, date) is
278
+ 'How much of the common system of VAT applied to a territory on a day: all of it, supplies of goods only, or none. A territory this table does not carry answers none, which is the right answer for every third country.';
279
+
280
+ create or replace function is_eu_member(p_code text, p_on date default current_date)
281
+ returns boolean
282
+ language sql
283
+ stable
284
+ as $$
285
+ select eu_vat_scope_of(p_code, p_on) = 'full';
286
+ $$;
287
+
288
+ comment on function is_eu_member(text, date) is
289
+ 'True when the whole of the common system of VAT applied to the territory on that day: every Member State, and Monaco, whose transactions article 7(1) treats as French. False for Northern Ireland, which is inside the system for goods alone, and false for a State from the day it left.';
290
+
291
+ create or replace function vat_prefix_of(p_code text)
292
+ returns char(2)
293
+ language sql
294
+ stable
295
+ as $$
296
+ select case when resolved.prefix ~ '^[A-Z]{2}$' then resolved.prefix::char(2) end
297
+ from (
298
+ select coalesce(
299
+ (select coalesce(t.vat_prefix::text, t.code)
300
+ from territory_of(p_code) t
301
+ where t.code is not null),
302
+ upper(btrim(p_code))
303
+ ) as prefix
304
+ ) resolved;
305
+ $$;
306
+
307
+ comment on function vat_prefix_of(text) is
308
+ 'The two letters a territory''s VAT identification numbers carry: EL for Greece, FR for Monaco, GB for the Isle of Man, and the code itself everywhere else — including for a territory this table does not carry, whose own two letters come back unchanged. Null when what it resolves to is not two letters, which is a territory that identifies under nobody.';
309
+
310
+ -- ---------------------------------------------------------------------------
311
+ -- Who may read it
312
+ --
313
+ -- Reference data, like the currencies: a select policy for anyone signed in,
314
+ -- and no policy that writes. The rows are the law and the writer is a
315
+ -- migration running on the owner's connection.
316
+ --
317
+ -- `anon` gets nothing, and the question was asked rather than assumed: the one
318
+ -- reader of this installation who has no session is somebody following a
319
+ -- document share link, and nothing that renders a shared document reads this
320
+ -- table — the legal mentions of an invoice come from the pack of its company.
321
+ -- The day a renderer needs to name a territory, the honest answer is a column
322
+ -- on the document, snapshotted like `vat_category` already is, and not a
323
+ -- reference table opened to the world.
324
+ -- ---------------------------------------------------------------------------
325
+
326
+ alter table territories enable row level security;
327
+
328
+ create policy territories_select on territories
329
+ for select using (auth.uid() is not null);
330
+
331
+ revoke all on table territories from public, anon;
332
+ grant select on table territories to authenticated, service_role;
333
+
334
+ revoke execute on function territory_of(text) from public, anon;
335
+ revoke execute on function eu_vat_scope_of(text, date) from public, anon;
336
+ revoke execute on function is_eu_member(text, date) from public, anon;
337
+ revoke execute on function vat_prefix_of(text) from public, anon;
338
+
339
+ grant execute on function territory_of(text) to authenticated, service_role;
340
+ grant execute on function eu_vat_scope_of(text, date) to authenticated, service_role;
341
+ grant execute on function is_eu_member(text, date) to authenticated, service_role;
342
+ grant execute on function vat_prefix_of(text) to authenticated, service_role;
@@ -0,0 +1,222 @@
1
+ -- Ekwo OS — the recapitulative statement stops guessing at the Union.
2
+ --
3
+ -- `20260915160000` wrote `ec_sales_list()` with two holes it could not fill,
4
+ -- and `docs/international.md` recorded both. `territories` fills them, and this
5
+ -- migration is the function reading it. Nothing else about the statement
6
+ -- changes: the same columns in the same order, the same aggregation, the same
7
+ -- reconciliation with the return. What changes is what goes in two of the
8
+ -- columns and what comes back in a third.
9
+ --
10
+ -- **A customer's VAT country is the prefix their numbers carry, not their ISO
11
+ -- code.** Greece identifies under `EL` and Northern Ireland under `XI`, and
12
+ -- neither is an ISO 3166-1 code. The old function took the two letters off the
13
+ -- number when there were two, and the contact's ISO country when there were
14
+ -- not — so a Greek customer recorded as `GR` with an unprefixed number was
15
+ -- listed under `GR`, which every one of the four administrations refuses. Both
16
+ -- paths now go through `vat_prefix_of()`, so a number typed `GR...` is
17
+ -- corrected as readily as a number typed with no prefix at all, and a
18
+ -- territory the table does not carry keeps its own two letters.
19
+ --
20
+ -- **The company's own country is compared through the same function.** It was
21
+ -- compared raw, which is a defect nobody could see from Belgium or France: a
22
+ -- Greek company's `fiscal_country` is `GR` and its own customers' numbers say
23
+ -- `EL`, so `vat_country_is_the_company_country` would never have fired there
24
+ -- and a domestic supply would have been listed as an intra-Community one. Two
25
+ -- prefixes are now compared with two prefixes.
26
+ --
27
+ -- **A supply to a territory the common system does not cover comes back as an
28
+ -- issue.** This is the check the function could not make at all — it could
29
+ -- only say that the customer was not in the company's own country — and it is
30
+ -- the one that matters most, because a tax treated `intracom_goods` on a
31
+ -- customer in a third country is a pack or a book-keeping mistake that
32
+ -- produces a file an administration rejects. Two reasons, because there are
33
+ -- two different things to fix:
34
+ --
35
+ -- * `vat_country_outside_the_union` — the common system did not reach that
36
+ -- territory on the day of the supply. A third country; a Member State
37
+ -- before it acceded; the United Kingdom from 1 January 2021.
38
+ -- * `vat_country_outside_the_union_for_this_supply` — the territory is
39
+ -- inside the system for part of what it covers and not for this. Northern
40
+ -- Ireland is the only such territory and services are the only such
41
+ -- supply: the Protocol keeps the Union's rules on goods and not its rules
42
+ -- on services, so a supply of goods to an `XI` customer belongs on the
43
+ -- statement and a supply of services to the same customer does not.
44
+ --
45
+ -- **The date of the supply decides, not today.** `eu_vat_scope_of()` takes a
46
+ -- date and the function passes the entry date of the line, so a statement for
47
+ -- a period in 2020 still reports supplies to the United Kingdom and a
48
+ -- statement for 2021 does not. A statement reprinted years later has to give
49
+ -- the same answer it gave when it was filed, which is the same rule the packs
50
+ -- follow with `valid_from` on every tax.
51
+ --
52
+ -- **The row shape does not move, and the format bricks do not change.** They
53
+ -- receive a prefix already resolved and an `issue` they put in `violations[]`
54
+ -- whatever it says, which is the contract they were written to. Nothing under
55
+ -- `packages/formats/` imports anything from the core, and nothing there had to
56
+ -- learn what a Member State is.
57
+ --
58
+ -- **A database without the rows says so.** `territories` is seeded, like the
59
+ -- currencies, and an installation that applied the migration and not the seed
60
+ -- would have the table, no rows, and a statement in which every single
61
+ -- customer came back as outside the Union. So the function refuses first, by
62
+ -- name — `no_territories` — which is the answer `rounding_of()` gives for a
63
+ -- currency it does not know and the rule `CONTRIBUTING.md` states as *raise,
64
+ -- do not warn*.
65
+ --
66
+ -- One thing this still does not read: which supplies a *triangular*
67
+ -- arrangement makes. The nature comes off the treatment as it always did, so
68
+ -- `intracom_triangular` — added two migrations further on — arrives here as
69
+ -- `triangular` with nothing in this file changing.
70
+
71
+ create or replace function ec_sales_list(
72
+ p_company_id uuid,
73
+ p_from date,
74
+ p_to date
75
+ )
76
+ returns table (
77
+ vat_country char(2),
78
+ vat_number text,
79
+ nature text,
80
+ amount numeric,
81
+ currency_code char(3),
82
+ documents integer,
83
+ contact_ids uuid[],
84
+ contact_names text[],
85
+ issue text
86
+ )
87
+ language plpgsql
88
+ stable
89
+ as $$
90
+ declare
91
+ v_country char(2);
92
+ v_prefix char(2);
93
+ v_currency char(3);
94
+ v_round money_rounding;
95
+ begin
96
+ select c.fiscal_country, c.currency_code into v_country, v_currency
97
+ from companies c where c.id = p_company_id;
98
+ if not found then
99
+ raise exception 'unknown_company: %', p_company_id;
100
+ end if;
101
+ -- The rows of `territories` are a seed, and a database that has the table
102
+ -- and not the rows would answer that no customer anywhere is in the Union —
103
+ -- a statement full of violations and a total of nothing, with no sign that
104
+ -- anything was missing. Raise, do not warn.
105
+ if not exists (select 1 from territories) then
106
+ raise exception 'no_territories: apply supabase/seed/00_territories.sql';
107
+ end if;
108
+
109
+ v_prefix := vat_prefix_of(v_country);
110
+ v_round := rounding_of(p_company_id);
111
+
112
+ return query
113
+ -- 1. The supplies themselves: the base lines of every tax treated as an
114
+ -- intra-Community supply, on posted entries of the period.
115
+ --
116
+ -- `credit - debit` is the amount with the sign the statement wants: a
117
+ -- sale credits the revenue account and a credit note debits it, so a
118
+ -- credit note is deducted from the customer's line without this having
119
+ -- to know what a credit note is. It is also why no total here can be
120
+ -- read off one box of the return: Belgium reports its credit notes in a
121
+ -- box of their own, France nets them into the box they came from.
122
+ --
123
+ -- The entry date travels with the line because the territory question is
124
+ -- asked as at the day of the supply and not as at today.
125
+ with supply as (
126
+ select d.contact_id,
127
+ ct.name as contact_name,
128
+ regexp_replace(t.treatment::text, '^intracom_', '') as nature,
129
+ -- The number as somebody typed it, reduced to what an
130
+ -- administration compares: upper case, letters and digits.
131
+ upper(regexp_replace(coalesce(ct.vat_number, ''), '[^A-Za-z0-9]', '', 'g')) as vat_raw,
132
+ ct.country as contact_country,
133
+ e.entry_date,
134
+ e.document_id,
135
+ l.credit - l.debit as amount
136
+ from entry_lines l
137
+ join entries e on e.id = l.entry_id
138
+ join taxes t on t.id = l.tax_id
139
+ left join documents d on d.id = e.document_id
140
+ left join contacts ct on ct.id = d.contact_id
141
+ where l.company_id = p_company_id
142
+ and e.state = 'posted'
143
+ and e.entry_date between p_from and p_to
144
+ and not l.tax_line
145
+ and t.treatment::text like 'intracom!_%' escape '!'
146
+ and t.treatment::text not like 'intracom!_acquisition!_%' escape '!'
147
+ ),
148
+ -- 2. The identifier the statement is keyed on. A VAT number carries its own
149
+ -- country when it was recorded with one; where it was not, the country of
150
+ -- the contact answers. Either way the two letters go through
151
+ -- `vat_prefix_of()`, which is the only place in this repository that
152
+ -- knows a Greek number says `EL`.
153
+ keyed as (
154
+ select s.*,
155
+ vat_prefix_of(
156
+ case when s.vat_raw ~ '^[A-Z]{2}' then substring(s.vat_raw from 1 for 2)
157
+ when s.vat_raw = '' then null
158
+ else s.contact_country
159
+ end
160
+ ) as vat_country,
161
+ case when s.vat_raw = '' then null
162
+ when s.vat_raw ~ '^[A-Z]{2}' then substring(s.vat_raw from 3)
163
+ else s.vat_raw
164
+ end as vat_number
165
+ from supply s
166
+ ),
167
+ -- 3. How much of the common system reached that territory on the day of the
168
+ -- supply. Asked of the table and of nothing else: no code in this
169
+ -- function names a State, and the day one accedes or leaves is a row.
170
+ scoped as (
171
+ select k.*, eu_vat_scope_of(k.vat_country, k.entry_date) as scope
172
+ from keyed k
173
+ ),
174
+ -- 4. Why a line cannot be declared, or null when it can. Ordered from the
175
+ -- most missing to the most contradictory, so a row carries the first
176
+ -- thing somebody has to fix.
177
+ judged as (
178
+ select s.*,
179
+ case
180
+ when s.contact_id is null then 'no_customer'
181
+ when s.vat_number is null or s.vat_number = '' then 'no_vat_number'
182
+ when s.vat_country is null then 'no_vat_country'
183
+ when s.vat_country = v_prefix then 'vat_country_is_the_company_country'
184
+ when s.scope = 'none' then 'vat_country_outside_the_union'
185
+ -- A scope narrower than the whole system covers goods and not
186
+ -- services, which is the one distinction the Protocol on
187
+ -- Ireland/Northern Ireland draws. `services` is the only nature
188
+ -- this function derives that is not a supply of goods: the
189
+ -- others — `goods`, and `triangular` when the vocabulary gains
190
+ -- it — are.
191
+ when s.scope <> 'full' and s.nature = 'services'
192
+ then 'vat_country_outside_the_union_for_this_supply'
193
+ end as issue
194
+ from scoped s
195
+ )
196
+ select j.vat_country::char(2),
197
+ j.vat_number,
198
+ j.nature,
199
+ round_amount(sum(j.amount), v_round),
200
+ v_currency,
201
+ count(distinct j.document_id)::integer,
202
+ array_agg(distinct j.contact_id) filter (where j.contact_id is not null),
203
+ array_agg(distinct j.contact_name) filter (where j.contact_name is not null),
204
+ j.issue
205
+ from judged j
206
+ -- A declarable line is keyed on the VAT number, so two contacts sharing one
207
+ -- — a site and the entity that is invoiced — become the single line the
208
+ -- form wants. A line that cannot be declared is keyed on the contact
209
+ -- instead, because the number is what is missing or wrong and the contact
210
+ -- is what somebody has to open.
211
+ group by j.vat_country, j.vat_number, j.nature, j.issue,
212
+ case when j.issue is not null then j.contact_id end
213
+ having round_amount(sum(j.amount), v_round) <> 0
214
+ order by (j.issue is not null), j.vat_country, j.vat_number, j.nature;
215
+ end;
216
+ $$;
217
+
218
+ comment on function ec_sales_list(uuid, date, date) is
219
+ 'The recapitulative statement of intra-Community supplies for a period: one line per customer VAT identification number and per nature — goods, services, and whatever the treatment vocabulary gains next — summed from the posted ledger in the company''s currency, credit notes deducted. The country of a line is the prefix the customer''s numbers carry, read from `territories`, so a Greek customer is listed under EL. A supply that cannot be declared comes back with the reason in `issue` rather than being left out, including a supply to a territory the common system of VAT did not cover on the day it was made. No country rule lives in this function, and it refuses no period: how often a statement is filed is not what companies.vat_period records.';
220
+
221
+ revoke execute on function ec_sales_list(uuid, date, date) from public, anon;
222
+ grant execute on function ec_sales_list(uuid, date, date) to authenticated, service_role;
@@ -0,0 +1,53 @@
1
+ -- Ekwo OS — the supply in the middle of a triangular arrangement.
2
+ --
3
+ -- Three businesses in three Member States: A sells to B, B sells to C, and the
4
+ -- goods go straight from A to C. Without a rule, B would have to register for
5
+ -- VAT in C's Member State, because B makes an intra-Community acquisition
6
+ -- there and then a domestic supply. Article 141 of Directive 2006/112/EC
7
+ -- relieves B of that, and article 197 makes C liable for the tax on B's
8
+ -- supply. It is the simplification every Member State applies, and it is old:
9
+ -- it came in with the internal market in 1993.
10
+ --
11
+ -- The `tax_treatment` vocabulary had no word for B's supply. It had
12
+ -- `intracom_goods` and `intracom_services` and nothing between them, so a
13
+ -- pack describing B's side had to call it an ordinary intra-Community supply
14
+ -- of goods — which is what `docs/international.md` recorded as a gap when
15
+ -- `ec_sales_list()` was written, because all four recapitulative statements
16
+ -- print a triangular supply as a category of its own: code `T` on the Belgian
17
+ -- listing, its own table in the Luxembourg `LIC`, the *kolmnurktehing* column
18
+ -- of the Estonian form VD, and the *opérations triangulaires* line of the
19
+ -- French DES.
20
+ --
21
+ -- So the value is added here, and nothing else in the core has to move for it.
22
+ --
23
+ -- * `ec_sales_list()` derives the nature by taking `intracom_` off the
24
+ -- treatment, so a tax treated this way comes out as `triangular` with not
25
+ -- one line of that function changing. It was written that way on purpose
26
+ -- and this is the migration that proves it.
27
+ -- * All four format bricks already hold a column and a code for
28
+ -- `triangular`, and they were published before the value existed. Nothing
29
+ -- under `packages/formats/` changes either.
30
+ -- * Nothing in the ledger branches on a treatment. What a tax *does* is said
31
+ -- by its postings, and a pack that declares a triangular tax declares its
32
+ -- postings the way it declares any other.
33
+ --
34
+ -- **A is unaffected.** A makes an ordinary exempt intra-Community supply of
35
+ -- goods to B and declares it as one. This value is B's side and only B's, and
36
+ -- the acquisition C makes is C's own `intracom_acquisition_goods` — there is
37
+ -- no fourth value to add, and no pack in this repository has a triangular tax
38
+ -- to declare, because writing one for a country nobody asked would be
39
+ -- inventing a rule rather than transcribing one.
40
+ --
41
+ -- The value is placed beside `intracom_services` rather than at the end, so
42
+ -- that the four intra-Community supplies read together in the enum, in the
43
+ -- database, in `TAX_TREATMENTS` and in the pack schema — which is the one
44
+ -- ordering `tests/vat_codes.test.ts` compares across the three.
45
+ --
46
+ -- A value added to an enum cannot be used in the transaction that adds it.
47
+ -- The runner applies each file as one transaction, so the value is added alone
48
+ -- here and read by the file that comes after.
49
+
50
+ alter type tax_treatment add value 'intracom_triangular' after 'intracom_services';
51
+
52
+ -- No object is created, so there is nothing to grant. The type itself is
53
+ -- reachable by everyone who can already read the column that uses it.