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,564 @@
1
+ -- Ekwo OS (assets) — a depreciation is written at the decimals of the
2
+ -- company's currency, like every other amount.
3
+ --
4
+ -- The module rounded to the cent in nine places: the cost and the residual
5
+ -- value of an asset, the annuity of both methods, the declining cap, the
6
+ -- monthly share of a yearly annuity, the proceeds of a disposal, and the two
7
+ -- readings. Two decimals is what the euro has; a company keeping its books in
8
+ -- yen would have had a schedule that never summed to the cost, because the
9
+ -- last line takes the remainder and the remainder was in a decimal the
10
+ -- currency does not have.
11
+ --
12
+ -- Every one of them now asks `public.rounding_of(company)` once and passes the
13
+ -- answer to `public.round_amount`. The rule that made the schedule tie out is
14
+ -- unchanged — everything is rounded as it goes and the last line takes what is
15
+ -- left — it simply ties out in the currency's own unit now.
16
+
17
+
18
+ create or replace function assets.create_asset(
19
+ p_company_id uuid,
20
+ p_code text,
21
+ p_name text,
22
+ p_acquisition_date date,
23
+ p_cost numeric,
24
+ p_asset_account text,
25
+ p_depreciation_account text,
26
+ p_expense_account text,
27
+ p_category_code text default null,
28
+ p_duration_months integer default null,
29
+ p_method assets.depreciation_method default null,
30
+ p_coefficient numeric default null,
31
+ p_residual_value numeric default 0,
32
+ p_in_service_date date default null,
33
+ p_document_line_id uuid default null,
34
+ p_contact_id uuid default null,
35
+ p_description text default null
36
+ )
37
+ returns uuid
38
+ language plpgsql
39
+ as $$
40
+ declare
41
+ v_country char(2);
42
+ v_category assets.category_templates%rowtype;
43
+ v_method assets.depreciation_method;
44
+ v_duration integer;
45
+ v_coef numeric(7, 3);
46
+ v_id uuid;
47
+ v_asset uuid;
48
+ v_deprec uuid;
49
+ v_round public.money_rounding;
50
+ v_expense uuid;
51
+ begin
52
+ if not public.module_is_enabled(p_company_id, 'assets') then
53
+ raise exception 'module_not_enabled: assets is not enabled on this company'
54
+ using errcode = '55006';
55
+ end if;
56
+
57
+ select country into v_country from public.companies where id = p_company_id;
58
+ if v_country is null then
59
+ raise exception 'unknown_company: %', p_company_id;
60
+ end if;
61
+
62
+ if p_category_code is not null then
63
+ select * into v_category
64
+ from assets.category_templates
65
+ where country = v_country and code = p_category_code;
66
+ if not found then
67
+ raise exception 'unknown_asset_category: % is not a category of the % pack', p_category_code, v_country;
68
+ end if;
69
+ end if;
70
+
71
+ -- The category is a suggestion, so anything the caller gave wins over it.
72
+ -- Same rule as a product pre-filling a document line.
73
+ v_method := coalesce(p_method, v_category.method, 'straight_line');
74
+ v_duration := coalesce(p_duration_months, v_category.duration_months);
75
+ v_coef := coalesce(p_coefficient, v_category.coefficient);
76
+
77
+ if v_duration is null then
78
+ raise exception 'no_duration: name a duration in months, or a category of the % pack that carries one', v_country;
79
+ end if;
80
+
81
+ v_round := public.rounding_of(p_company_id);
82
+ v_asset := public.account_id_by_code(p_company_id, p_asset_account);
83
+ v_deprec := public.account_id_by_code(p_company_id, p_depreciation_account);
84
+ v_expense := public.account_id_by_code(p_company_id, p_expense_account);
85
+ if v_asset is null then
86
+ raise exception 'unknown_account: % is not an account of this company', p_asset_account;
87
+ end if;
88
+ if v_deprec is null then
89
+ raise exception 'unknown_account: % is not an account of this company', p_depreciation_account;
90
+ end if;
91
+ if v_expense is null then
92
+ raise exception 'unknown_account: % is not an account of this company', p_expense_account;
93
+ end if;
94
+
95
+ insert into assets.assets (
96
+ company_id, code, name, description, category_code, document_line_id, contact_id,
97
+ acquisition_date, in_service_date, cost, residual_value, method, duration_months,
98
+ coefficient, prorata, asset_account_id, depreciation_account_id, expense_account_id, state
99
+ )
100
+ values (
101
+ p_company_id, p_code, p_name, p_description, p_category_code, p_document_line_id, p_contact_id,
102
+ p_acquisition_date, p_in_service_date,
103
+ public.round_amount(p_cost, v_round),
104
+ public.round_amount(coalesce(p_residual_value, 0), v_round),
105
+ v_method, v_duration, v_coef, v_category.prorata, v_asset, v_deprec, v_expense, 'active'
106
+ )
107
+ returning id into v_id;
108
+
109
+ perform assets.generate_schedule(v_id);
110
+ return v_id;
111
+ end;
112
+ $$;
113
+
114
+ create or replace function assets.generate_schedule(p_asset_id uuid)
115
+ returns integer
116
+ language plpgsql
117
+ as $$
118
+ declare
119
+ v_asset assets.assets%rowtype;
120
+ v_rules assets.country_rules%rowtype;
121
+ v_prorata assets.prorata_rule;
122
+ v_start date;
123
+ v_year public.fiscal_years%rowtype;
124
+ v_base numeric;
125
+ v_rate numeric;
126
+ v_period_start date;
127
+ v_period_end date;
128
+ v_fraction numeric;
129
+ v_amount numeric;
130
+ v_linear numeric;
131
+ v_cap numeric;
132
+ v_accum numeric := 0;
133
+ -- A schedule is a ledger figure, so each annuity is written at the decimals
134
+ -- of the currency the company keeps its books in.
135
+ v_round public.money_rounding;
136
+ v_sequence integer := 0;
137
+ v_periods integer;
138
+ v_left integer;
139
+ v_guard integer := 0;
140
+ begin
141
+ select * into v_asset from assets.assets where id = p_asset_id;
142
+ if not found then
143
+ raise exception 'unknown_asset: %', p_asset_id;
144
+ end if;
145
+ if v_asset.state = 'disposed' then
146
+ raise exception 'asset_disposed: % has left the books', v_asset.code;
147
+ end if;
148
+ if v_asset.method = 'units_of_production' then
149
+ raise exception 'units_of_production_unsupported: a schedule by output needs the units of each period, which this module does not record. Name a duration and a straight line, or keep the schedule outside Ekwo.';
150
+ end if;
151
+
152
+ -- A schedule is generated once. Once a line has been booked, the schedule is
153
+ -- what the ledger says happened, and regenerating it would leave the two
154
+ -- disagreeing without anything looking wrong — which is the failure the
155
+ -- column `posted_at` exists to make visible.
156
+ if exists (select 1 from assets.depreciation_lines l
157
+ where l.asset_id = p_asset_id and l.posted_at is not null) then
158
+ raise exception 'schedule_already_posted: % has depreciation already booked; a schedule is not rewritten under the ledger', v_asset.code
159
+ using errcode = '55006';
160
+ end if;
161
+ delete from assets.depreciation_lines where asset_id = p_asset_id;
162
+
163
+ v_round := public.rounding_of(v_asset.company_id);
164
+ v_start := coalesce(v_asset.in_service_date, v_asset.acquisition_date);
165
+ v_base := v_asset.cost - v_asset.residual_value;
166
+
167
+ v_rules := assets.rules(v_asset.company_id);
168
+ v_prorata := coalesce(
169
+ v_asset.prorata,
170
+ case when v_asset.method = 'declining_balance' then v_rules.prorata_declining
171
+ else v_rules.prorata_straight_line end);
172
+ if v_prorata is null then
173
+ raise exception 'no_assets_country_rules: the pack of this company''s country says nothing about how a first period is prorated. Add an assets section to the pack, or set assets.prorata on this asset.'
174
+ using errcode = '55006';
175
+ end if;
176
+
177
+ select * into v_year
178
+ from public.fiscal_years f
179
+ where f.company_id = v_asset.company_id
180
+ and v_start between f.start_date and f.end_date
181
+ limit 1;
182
+ if not found then
183
+ raise exception 'no_fiscal_year: % entered service on %, which falls in no financial year of this company', v_asset.code, v_start;
184
+ end if;
185
+
186
+ v_period_start := v_year.start_date;
187
+ v_rate := 12::numeric / v_asset.duration_months;
188
+ -- How many annuities the duration is worth. A declining balance measures
189
+ -- what is left to run in periods and not in days, because that is what the
190
+ -- rule it comes from says: at the start of the second year of a five-year
191
+ -- asset, four annuities remain, whatever day of the first year it was
192
+ -- bought on.
193
+ v_periods := ceil(v_asset.duration_months / 12.0)::integer;
194
+ if v_rules.declining_cap_percent is not null then
195
+ v_cap := public.round_amount(v_asset.cost * v_rules.declining_cap_percent / 100, v_round);
196
+ end if;
197
+
198
+ while v_accum < v_base loop
199
+ v_guard := v_guard + 1;
200
+ if v_guard > 1200 then
201
+ raise exception 'schedule_runaway: % produced more than 1200 periods; check its duration and its coefficient', v_asset.code;
202
+ end if;
203
+
204
+ v_period_end := assets.period_end_for(v_asset.company_id, v_period_start);
205
+ v_fraction := assets.prorata_fraction(v_prorata, v_rules.day_count, v_start, v_period_start, v_period_end);
206
+
207
+ if v_asset.method = 'declining_balance' then
208
+ v_amount := public.round_amount(
209
+ (v_base - v_accum) * v_rate * v_asset.coefficient * v_fraction, v_round);
210
+ if v_cap is not null then
211
+ v_amount := least(v_amount, public.round_amount(v_cap * v_fraction, v_round));
212
+ end if;
213
+ -- The straight line over what is left to run. A declining balance is a
214
+ -- front-loaded schedule that never reaches zero on its own, and the
215
+ -- switch is what makes it end on the last period rather than never.
216
+ -- The last period is `v_left = 1`, where the straight line is the whole
217
+ -- remaining value.
218
+ v_left := greatest(1, v_periods - v_sequence);
219
+ if coalesce(v_rules.declining_switch_to_linear, true) then
220
+ v_linear := public.round_amount((v_base - v_accum) / v_left * v_fraction, v_round);
221
+ v_amount := greatest(v_amount, v_linear);
222
+ end if;
223
+ else
224
+ v_amount := public.round_amount(v_base * v_rate * v_fraction, v_round);
225
+ end if;
226
+
227
+ if v_amount <= 0 then
228
+ v_amount := v_base - v_accum;
229
+ end if;
230
+ -- The last period takes the remainder, whatever the rounding did on the
231
+ -- way. This is the line that makes the schedule sum to the cent.
232
+ if v_accum + v_amount > v_base then
233
+ v_amount := v_base - v_accum;
234
+ end if;
235
+
236
+ v_accum := v_accum + v_amount;
237
+ v_sequence := v_sequence + 1;
238
+
239
+ insert into assets.depreciation_lines
240
+ (asset_id, company_id, sequence, period_start, period_end, amount, accumulated, net_book_value)
241
+ values (p_asset_id, v_asset.company_id, v_sequence, v_period_start, v_period_end,
242
+ v_amount, v_accum, v_asset.cost - v_accum);
243
+
244
+ v_period_start := v_period_end + 1;
245
+ end loop;
246
+
247
+ if assets.period_is_monthly(v_asset.company_id) then
248
+ perform assets.split_into_months(p_asset_id);
249
+ end if;
250
+
251
+ update assets.assets
252
+ set state = case when state = 'draft' then 'active' else state end
253
+ where id = p_asset_id;
254
+
255
+ return (select count(*)::integer from assets.depreciation_lines where asset_id = p_asset_id);
256
+ end;
257
+ $$;
258
+
259
+ create or replace function assets.split_into_months(p_asset_id uuid)
260
+ returns integer
261
+ language plpgsql
262
+ as $$
263
+ declare
264
+ v_line assets.depreciation_lines%rowtype;
265
+ v_asset assets.assets%rowtype;
266
+ v_months integer;
267
+ v_start date;
268
+ v_end date;
269
+ v_share numeric;
270
+ v_done numeric;
271
+ v_accum numeric := 0;
272
+ v_round public.money_rounding;
273
+ v_sequence integer := 0;
274
+ v_index integer;
275
+ v_new jsonb := '[]'::jsonb;
276
+ begin
277
+ select * into v_asset from assets.assets where id = p_asset_id;
278
+ v_round := public.rounding_of(v_asset.company_id);
279
+
280
+ for v_line in
281
+ select * from assets.depreciation_lines where asset_id = p_asset_id order by sequence
282
+ loop
283
+ v_months := assets.months_inclusive(v_line.period_start, v_line.period_end);
284
+ v_done := 0;
285
+ for v_index in 1 .. v_months loop
286
+ v_start := (date_trunc('month', v_line.period_start) + make_interval(months => v_index - 1))::date;
287
+ v_end := (date_trunc('month', v_start) + interval '1 month' - interval '1 day')::date;
288
+ if v_index = 1 then v_start := v_line.period_start; end if;
289
+ if v_index = v_months then v_end := v_line.period_end; end if;
290
+
291
+ v_share := case when v_index = v_months
292
+ then v_line.amount - v_done
293
+ else public.round_amount(v_line.amount / v_months, v_round) end;
294
+ v_done := v_done + v_share;
295
+ if v_share = 0 then continue; end if;
296
+
297
+ v_accum := v_accum + v_share;
298
+ v_sequence := v_sequence + 1;
299
+ v_new := v_new || jsonb_build_object(
300
+ 'sequence', v_sequence, 'period_start', v_start, 'period_end', v_end,
301
+ 'amount', v_share, 'accumulated', v_accum,
302
+ 'net_book_value', v_asset.cost - v_accum);
303
+ end loop;
304
+ end loop;
305
+
306
+ delete from assets.depreciation_lines where asset_id = p_asset_id;
307
+ insert into assets.depreciation_lines
308
+ (asset_id, company_id, sequence, period_start, period_end, amount, accumulated, net_book_value)
309
+ select p_asset_id, v_asset.company_id,
310
+ (row ->> 'sequence')::integer, (row ->> 'period_start')::date, (row ->> 'period_end')::date,
311
+ (row ->> 'amount')::numeric, (row ->> 'accumulated')::numeric, (row ->> 'net_book_value')::numeric
312
+ from jsonb_array_elements(v_new) as row;
313
+
314
+ return v_sequence;
315
+ end;
316
+ $$;
317
+
318
+ create or replace function assets.dispose_asset(
319
+ p_asset_id uuid,
320
+ p_date date,
321
+ p_proceeds numeric default 0,
322
+ p_counterpart_account text default null,
323
+ p_contact_id uuid default null
324
+ )
325
+ returns uuid
326
+ language plpgsql
327
+ as $$
328
+ declare
329
+ v_asset assets.assets%rowtype;
330
+ v_rules assets.country_rules%rowtype;
331
+ v_defaults public.country_defaults%rowtype;
332
+ v_country char(2);
333
+ v_accum numeric;
334
+ v_nbv numeric;
335
+ v_result numeric;
336
+ v_round public.money_rounding;
337
+ v_proceeds numeric;
338
+ v_counter uuid;
339
+ v_account uuid;
340
+ v_lines jsonb := '[]'::jsonb;
341
+ v_entry uuid;
342
+ begin
343
+ select * into v_asset from assets.assets where id = p_asset_id for update;
344
+ if not found then
345
+ raise exception 'unknown_asset: %', p_asset_id;
346
+ end if;
347
+ if v_asset.state = 'disposed' then
348
+ raise exception 'asset_disposed: % has already left the books', v_asset.code;
349
+ end if;
350
+ if p_date < coalesce(v_asset.in_service_date, v_asset.acquisition_date) then
351
+ raise exception 'disposal_before_service: % cannot leave the books before it entered them', v_asset.code;
352
+ end if;
353
+
354
+ -- Everything the schedule planned for a period that has already ended has to
355
+ -- be booked before the asset goes: a disposal reads the accumulated
356
+ -- depreciation off the ledger, and a period left unbooked would make it read
357
+ -- a figure the accounts do not carry.
358
+ if exists (select 1 from assets.depreciation_lines l
359
+ where l.asset_id = p_asset_id and l.posted_at is null and l.period_end < p_date) then
360
+ raise exception 'depreciation_pending: run assets.run_depreciation(company, %) before disposing of %',
361
+ p_date, v_asset.code
362
+ using errcode = '55006';
363
+ end if;
364
+
365
+ select country into v_country from public.companies where id = v_asset.company_id;
366
+ v_round := public.rounding_of(v_asset.company_id);
367
+ v_proceeds := public.round_amount(coalesce(p_proceeds, 0), v_round);
368
+ v_rules := assets.rules(v_asset.company_id);
369
+ if v_rules.disposal_style is null then
370
+ raise exception 'no_disposal_style: the pack of % says nothing about how an asset leaves the books. Set assets.disposal, and the roles it needs, in the pack.', v_country
371
+ using errcode = '55006';
372
+ end if;
373
+ select * into v_defaults from public.country_defaults where country = v_country;
374
+
375
+ select coalesce(sum(l.amount), 0) into v_accum
376
+ from assets.depreciation_lines l
377
+ where l.asset_id = p_asset_id and l.posted_at is not null;
378
+ v_nbv := v_asset.cost - v_accum;
379
+ v_result := v_proceeds - v_nbv;
380
+
381
+ -- What the schedule still planned never happened. Deleting only the unposted
382
+ -- lines keeps everything the ledger knows about.
383
+ delete from assets.depreciation_lines
384
+ where asset_id = p_asset_id and posted_at is null;
385
+
386
+ -- 1. The asset leaves, and its accumulated depreciation with it.
387
+ if v_accum > 0 then
388
+ v_lines := v_lines || jsonb_build_object(
389
+ 'account_id', v_asset.depreciation_account_id,
390
+ 'label', v_asset.code || ' — accumulated depreciation', 'debit', v_accum);
391
+ end if;
392
+ v_lines := v_lines || jsonb_build_object(
393
+ 'account_id', v_asset.asset_account_id,
394
+ 'label', v_asset.code || ' — ' || v_asset.name, 'credit', v_asset.cost);
395
+
396
+ -- 2. What the buyer owes, if anything.
397
+ if v_proceeds > 0 then
398
+ v_counter := case
399
+ when p_counterpart_account is not null
400
+ then public.account_id_by_code(v_asset.company_id, p_counterpart_account)
401
+ else (select receivable_account_id from public.companies where id = v_asset.company_id)
402
+ end;
403
+ if v_counter is null then
404
+ raise exception 'no_counterpart_account: name the account the proceeds of % land on', v_asset.code;
405
+ end if;
406
+ v_lines := v_lines || jsonb_build_object(
407
+ 'account_id', v_counter, 'label', v_asset.code || ' — proceeds',
408
+ 'debit', v_proceeds, 'contact_id', p_contact_id);
409
+ end if;
410
+
411
+ -- 3. The result, the way the country presents it.
412
+ if v_rules.disposal_style = 'gross' then
413
+ -- The net book value is a charge and the proceeds an income, both in full.
414
+ -- The two lines above already cleared the asset and its depreciation, so
415
+ -- what is left to write is the charge; the proceeds line of the income
416
+ -- account replaces nothing, it is the other half of the same presentation.
417
+ v_account := public.account_id_by_code(v_asset.company_id, v_defaults.asset_disposal_value_code);
418
+ if v_account is null then
419
+ raise exception 'no_asset_disposal_value_account: % files a gross disposal and its pack names no account for the value of what was sold', v_country;
420
+ end if;
421
+ if v_nbv > 0 then
422
+ v_lines := v_lines || jsonb_build_object(
423
+ 'account_id', v_account, 'label', v_asset.code || ' — net book value sold',
424
+ 'debit', v_nbv);
425
+ end if;
426
+ if v_proceeds > 0 then
427
+ v_account := public.account_id_by_code(v_asset.company_id, v_defaults.asset_disposal_proceeds_code);
428
+ if v_account is null then
429
+ raise exception 'no_asset_disposal_proceeds_account: % files a gross disposal and its pack names no account for the proceeds', v_country;
430
+ end if;
431
+ v_lines := v_lines || jsonb_build_object(
432
+ 'account_id', v_account, 'label', v_asset.code || ' — proceeds of disposal',
433
+ 'credit', v_proceeds);
434
+ end if;
435
+ -- The two proceeds lines are the two halves of one movement and not a
436
+ -- double count: the buyer is debited above, the income account is credited
437
+ -- here. What the gross style adds to the net-result one is the charge, and
438
+ -- an income statement that prints both is what France asks for.
439
+ elsif v_result <> 0 then
440
+ v_account := public.account_id_by_code(
441
+ v_asset.company_id,
442
+ case when v_result > 0 then v_defaults.asset_disposal_gain_code
443
+ else coalesce(v_defaults.asset_disposal_loss_code, v_defaults.asset_disposal_gain_code) end);
444
+ if v_account is null then
445
+ raise exception 'no_asset_disposal_account: the pack of % names no account for a % on the disposal of a fixed asset',
446
+ v_country, case when v_result > 0 then 'gain' else 'loss' end;
447
+ end if;
448
+ v_lines := v_lines || jsonb_build_object(
449
+ 'account_id', v_account,
450
+ 'label', v_asset.code || ' — ' || case when v_result > 0 then 'gain' else 'loss' end || ' on disposal',
451
+ 'debit', case when v_result < 0 then -v_result else 0 end,
452
+ 'credit', case when v_result > 0 then v_result else 0 end);
453
+ end if;
454
+
455
+ v_entry := public.post_module_entry(
456
+ v_asset.company_id, 'assets', 'disposal:' || v_asset.code, p_date,
457
+ 'Disposal of ' || v_asset.code || ' — ' || v_asset.name, v_lines);
458
+
459
+ insert into assets.disposals
460
+ (asset_id, company_id, disposal_date, proceeds, counterpart_account_id, contact_id,
461
+ cost, accumulated, net_book_value, result, entry_id)
462
+ values (p_asset_id, v_asset.company_id, p_date, v_proceeds, v_counter, p_contact_id,
463
+ v_asset.cost, v_accum, v_nbv, v_result, v_entry);
464
+
465
+ update assets.assets set state = 'disposed' where id = p_asset_id;
466
+ return v_entry;
467
+ end;
468
+ $$;
469
+
470
+ create or replace function assets.register(p_company_id uuid, p_at date)
471
+ returns table (
472
+ code text,
473
+ name text,
474
+ category_code text,
475
+ acquisition_date date,
476
+ cost numeric,
477
+ accumulated numeric,
478
+ net_book_value numeric,
479
+ state text
480
+ )
481
+ language sql
482
+ stable
483
+ as $$
484
+ select a.code,
485
+ a.name,
486
+ a.category_code,
487
+ a.acquisition_date,
488
+ a.cost,
489
+ public.round_amount(coalesce(d.accumulated, 0), public.rounding_of(p_company_id)),
490
+ public.round_amount(a.cost - coalesce(d.accumulated, 0), public.rounding_of(p_company_id)),
491
+ a.state::text
492
+ from assets.assets a
493
+ left join lateral (
494
+ select coalesce(sum(l.amount), 0) as accumulated
495
+ from assets.depreciation_lines l
496
+ where l.asset_id = a.id and l.posted_at is not null and l.period_end <= p_at
497
+ ) d on true
498
+ where a.company_id = p_company_id
499
+ and a.acquisition_date <= p_at
500
+ and not exists (
501
+ select 1 from assets.disposals x
502
+ where x.asset_id = a.id and x.disposal_date <= p_at
503
+ )
504
+ order by a.code;
505
+ $$;
506
+
507
+ create or replace function assets.movements(p_company_id uuid, p_from date, p_to date)
508
+ returns table (
509
+ code text,
510
+ name text,
511
+ opening_cost numeric,
512
+ additions numeric,
513
+ disposals_cost numeric,
514
+ closing_cost numeric,
515
+ opening_accumulated numeric,
516
+ depreciation numeric,
517
+ disposals_accumulated numeric,
518
+ closing_accumulated numeric,
519
+ net_book_value numeric
520
+ )
521
+ language sql
522
+ stable
523
+ as $$
524
+ with movement as (
525
+ select a.id, a.code, a.name, a.cost,
526
+ a.acquisition_date < p_from as held_before,
527
+ a.acquisition_date between p_from and p_to as added,
528
+ x.disposal_date, x.accumulated as disposed_accumulated,
529
+ coalesce((select sum(l.amount) from assets.depreciation_lines l
530
+ where l.asset_id = a.id and l.posted_at is not null
531
+ and l.period_end < p_from), 0) as accum_before,
532
+ coalesce((select sum(l.amount) from assets.depreciation_lines l
533
+ where l.asset_id = a.id and l.posted_at is not null
534
+ and l.period_end between p_from and p_to), 0) as accum_period
535
+ from assets.assets a
536
+ left join assets.disposals x on x.asset_id = a.id
537
+ where a.company_id = p_company_id
538
+ and a.acquisition_date <= p_to
539
+ )
540
+ select m.code, m.name,
541
+ public.round_amount(case when m.held_before then m.cost else 0 end, r.rounding),
542
+ public.round_amount(case when m.added then m.cost else 0 end, r.rounding),
543
+ public.round_amount(case when m.disposal_date between p_from and p_to
544
+ then m.cost else 0 end, r.rounding),
545
+ public.round_amount(case when m.disposal_date is not null and m.disposal_date <= p_to
546
+ then 0 else m.cost end, r.rounding),
547
+ public.round_amount(m.accum_before, r.rounding),
548
+ public.round_amount(m.accum_period, r.rounding),
549
+ public.round_amount(case when m.disposal_date between p_from and p_to
550
+ then coalesce(m.disposed_accumulated, 0) else 0 end, r.rounding),
551
+ public.round_amount(case when m.disposal_date is not null and m.disposal_date <= p_to
552
+ then 0 else m.accum_before + m.accum_period end, r.rounding),
553
+ public.round_amount(case when m.disposal_date is not null and m.disposal_date <= p_to
554
+ then 0 else m.cost - m.accum_before - m.accum_period end, r.rounding)
555
+ from movement m
556
+ cross join (select public.rounding_of(p_company_id) as rounding) r
557
+ order by m.code;
558
+ $$;
559
+
560
+ comment on function assets.generate_schedule(uuid) is
561
+ 'Writes the depreciation schedule of an asset, period by period, rounded at the decimals of the company''s currency with the last line taking the remainder. Refuses to rewrite a schedule whose lines are already booked.';
562
+
563
+
564
+ revoke execute on all functions in schema assets from public;
@@ -0,0 +1,37 @@
1
+ -- The `assets` module answers which accounts of a company it holds.
2
+ --
3
+ -- `public.accounts_in_use()` builds the working chart of a company and has to
4
+ -- count the accounts a module points at — an asset booked on 240000 belongs
5
+ -- in the list whether or not a depreciation has been run yet. It does not name
6
+ -- this schema to find out: it asks, by the convention `disable_module()`
7
+ -- already reads, `<schema>.accounts_in_use(uuid)`. A module that references no
8
+ -- account writes no such function and `to_regprocedure` answers null.
9
+ --
10
+ -- Four columns of this schema carry an account: the three an asset is booked
11
+ -- on, and the counterpart a disposal is settled against.
12
+ --
13
+ -- Security invoker, so row level security answers: a caller who is not a
14
+ -- member of the company, or a company that has not enabled this module, gets
15
+ -- an empty set from the policies rather than a refusal from here.
16
+
17
+ create or replace function assets.accounts_in_use(p_company_id uuid)
18
+ returns setof uuid
19
+ language sql
20
+ stable
21
+ as $$
22
+ select unnest(array[a.asset_account_id, a.depreciation_account_id, a.expense_account_id])
23
+ from assets.assets a
24
+ where a.company_id = p_company_id
25
+ union
26
+ select d.counterpart_account_id
27
+ from assets.disposals d
28
+ where d.company_id = p_company_id and d.counterpart_account_id is not null;
29
+ $$;
30
+
31
+ comment on function assets.accounts_in_use(uuid) is
32
+ 'The accounts this module points at for one company: what its assets are booked, depreciated and charged on, and what a disposal was settled against. Read by public.accounts_in_use() through the module convention.';
33
+
34
+ revoke execute on function assets.accounts_in_use(uuid) from public, anon;
35
+ grant execute on function assets.accounts_in_use(uuid) to authenticated, service_role;
36
+
37
+ revoke execute on all functions in schema assets from public;
@@ -0,0 +1,63 @@
1
+ -- The `assets` module grants its own rights, by name.
2
+ --
3
+ -- The socle stopped borrowing the project's default privileges in
4
+ -- `20260914151207`; this is the same change for a module schema, where the
5
+ -- starting point was different and wrong in its own way.
6
+ --
7
+ -- `20260913081447` opened the schema with three wildcards:
8
+ --
9
+ -- grant select on all tables in schema assets to anon;
10
+ -- grant select, insert, update, delete on all tables in schema assets
11
+ -- to authenticated, service_role;
12
+ --
13
+ -- The first contradicts the doctrine of `20260911210131` — `anon` reaches the
14
+ -- policy helpers and nothing else — and it was the only place in the whole
15
+ -- schema where the anonymous role held a privilege on a table. Row level
16
+ -- security answered every one of those reads with an empty set, so nothing
17
+ -- leaked; the surface should be closed rather than merely empty, which is the
18
+ -- same sentence that migration wrote about functions.
19
+ --
20
+ -- The second is too wide by two tables. `category_templates` and
21
+ -- `country_rules` are what the module's pack section installs — the
22
+ -- depreciation categories of a country and the rules that pick a method —
23
+ -- and their only policy is a SELECT. A grant that offers three verbs no
24
+ -- policy will ever accept is a grant that describes the schema incorrectly,
25
+ -- and from here the inventory test reads it as the declaration it is.
26
+ --
27
+ -- Published migrations are never edited, so the wildcards stand in their file
28
+ -- and are corrected here.
29
+
30
+ -- `usage` stays as it is for all three roles. A schema nobody holds an object
31
+ -- privilege in reaches nothing, and PostgREST needs it for the roles that do.
32
+
33
+ revoke all on all tables in schema assets from anon, authenticated, service_role;
34
+ revoke all on all sequences in schema assets from anon, authenticated, service_role;
35
+
36
+ alter default privileges in schema assets revoke all on tables from anon, authenticated, service_role;
37
+ alter default privileges in schema assets revoke all on sequences from anon, authenticated, service_role;
38
+ alter default privileges in schema assets revoke execute on functions from public, anon, authenticated, service_role;
39
+
40
+ -- The register and what it produces: a policy `for all` governs each, so the
41
+ -- four verbs are the ones row level security is prepared to judge. Posting is
42
+ -- guarded by `assets.assert_may_post`, not by a withheld privilege, so DELETE
43
+ -- on a draft line stays possible and DELETE on a posted one stays refused.
44
+ grant select, insert, update, delete on table
45
+ assets.assets,
46
+ assets.depreciation_lines,
47
+ assets.disposals
48
+ to authenticated, service_role;
49
+
50
+ -- What the country pack installs. SELECT only, on both sides of the grant and
51
+ -- of the policy.
52
+ grant select on table
53
+ assets.category_templates,
54
+ assets.country_rules
55
+ to authenticated, service_role;
56
+
57
+ -- The fourteen callable functions keep the nominative grants their own
58
+ -- migration gave them. The trigger body does not: it is fired by the table,
59
+ -- never called, and PostgreSQL checks EXECUTE when a trigger is created
60
+ -- rather than when it fires.
61
+ revoke execute on function assets.assert_may_post() from public, anon, authenticated, service_role;
62
+
63
+ revoke execute on all functions in schema assets from public;
@@ -0,0 +1,20 @@
1
+ -- Ekwo module `assets` — the client preset reads this module too.
2
+ --
3
+ -- The socle's `client` preset holds what `viewer` holds, and it was filled
4
+ -- from `role_capabilities` when the socle's migration ran: on an installation
5
+ -- that already carried this module, `assets.read` was copied then. On a fresh
6
+ -- one the socle is applied before any module, so the row has to come from
7
+ -- here.
8
+ --
9
+ -- The label is read from the catalogue rather than written as a literal. A
10
+ -- module may be applied on a socle older than the preset, where
11
+ -- `'client'::member_role` does not parse; there this inserts nothing, and the
12
+ -- socle's migration copies the row the day it arrives.
13
+
14
+ insert into public.role_capabilities (role, capability)
15
+ select e.enumlabel::text::public.member_role, 'assets.read'
16
+ from pg_catalog.pg_enum e
17
+ join pg_catalog.pg_type t on t.oid = e.enumtypid
18
+ join pg_catalog.pg_namespace n on n.oid = t.typnamespace
19
+ where n.nspname = 'public' and t.typname = 'member_role' and e.enumlabel = 'client'
20
+ on conflict do nothing;