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.
- package/README.md +873 -0
- package/dist/args.d.ts +57 -0
- package/dist/args.d.ts.map +1 -0
- package/dist/args.js +134 -0
- package/dist/args.js.map +1 -0
- package/dist/assets/expected-objects.json +9432 -0
- package/dist/assets/migrations/20260911120000_core_companies.sql +260 -0
- package/dist/assets/migrations/20260911120100_accounts_journals.sql +211 -0
- package/dist/assets/migrations/20260911120200_contacts.sql +90 -0
- package/dist/assets/migrations/20260911120300_taxes.sql +146 -0
- package/dist/assets/migrations/20260911120400_entries.sql +338 -0
- package/dist/assets/migrations/20260911120500_documents.sql +279 -0
- package/dist/assets/migrations/20260911120600_payments_reconciliation.sql +251 -0
- package/dist/assets/migrations/20260911120700_bank.sql +178 -0
- package/dist/assets/migrations/20260911120800_analytics_attachments.sql +128 -0
- package/dist/assets/migrations/20260911120900_post_document.sql +279 -0
- package/dist/assets/migrations/20260911121000_reporting.sql +235 -0
- package/dist/assets/migrations/20260911121100_country_templates.sql +226 -0
- package/dist/assets/migrations/20260911121200_fec.sql +71 -0
- package/dist/assets/migrations/20260911130000_instance.sql +77 -0
- package/dist/assets/migrations/20260911130100_instance_members.sql +224 -0
- package/dist/assets/migrations/20260911140000_instance_admins.sql +112 -0
- package/dist/assets/migrations/20260911160000_tax_posting_templates_unique.sql +17 -0
- package/dist/assets/migrations/20260911170000_document_amount_paid.sql +82 -0
- package/dist/assets/migrations/20260911173000_post_payment.sql +122 -0
- package/dist/assets/migrations/20260911173100_sequence_counters_under_rls.sql +84 -0
- package/dist/assets/migrations/20260911183000_country_journal_defaults.sql +143 -0
- package/dist/assets/migrations/20260911193853_line_account_defaults.sql +274 -0
- package/dist/assets/migrations/20260911195054_products.sql +221 -0
- package/dist/assets/migrations/20260911210131_anon_surface.sql +85 -0
- package/dist/assets/migrations/20260912074712_country_packs.sql +318 -0
- package/dist/assets/migrations/20260912080311_report_code_and_region.sql +198 -0
- package/dist/assets/migrations/20260912081014_pack_certification_maintained.sql +31 -0
- package/dist/assets/migrations/20260912081015_pack_certification_backfill.sql +16 -0
- package/dist/assets/migrations/20260912090407_tax_report_boxes.sql +329 -0
- package/dist/assets/migrations/20260912091917_tax_on_base_value.sql +25 -0
- package/dist/assets/migrations/20260912091918_tax_engine_columns.sql +776 -0
- package/dist/assets/migrations/20260912094412_opening_and_closing.sql +759 -0
- package/dist/assets/migrations/20260912095825_charts_of_accounts.sql +326 -0
- package/dist/assets/migrations/20260912100412_financial_statements.sql +632 -0
- package/dist/assets/migrations/20260912104719_one_formula_evaluator.sql +209 -0
- package/dist/assets/migrations/20260912105720_entry_kind_appropriation.sql +26 -0
- package/dist/assets/migrations/20260912105721_appropriation_entry_kind.sql +444 -0
- package/dist/assets/migrations/20260912111751_document_rules.sql +274 -0
- package/dist/assets/migrations/20260912112132_cash_basis_vat_and_fx.sql +1189 -0
- package/dist/assets/migrations/20260913074512_modules.sql +493 -0
- package/dist/assets/migrations/20260913075903_asset_disposal_roles.sql +47 -0
- package/dist/assets/migrations/20260913083216_capabilities.sql +637 -0
- package/dist/assets/migrations/20260913083901_company_invitations.sql +262 -0
- package/dist/assets/migrations/20260913084402_user_preferences.sql +376 -0
- package/dist/assets/migrations/20260913084847_company_profile.sql +261 -0
- package/dist/assets/migrations/20260913085436_numbering_reads_the_pack.sql +235 -0
- package/dist/assets/migrations/20260913085932_api_keys.sql +303 -0
- package/dist/assets/migrations/20260913090216_fiscal_year_bounds.sql +159 -0
- package/dist/assets/migrations/20260913092527_entries_import.sql +219 -0
- package/dist/assets/migrations/20260913101536_null_safe_guards.sql +188 -0
- package/dist/assets/migrations/20260913102115_installer_is_named.sql +462 -0
- package/dist/assets/migrations/20260913102758_no_currency_default.sql +138 -0
- package/dist/assets/migrations/20260913103355_cash_basis_needs_a_box.sql +453 -0
- package/dist/assets/migrations/20260913104014_aged_balance_named_group.sql +79 -0
- package/dist/assets/migrations/20260913104232_foreign_key_indexes.sql +144 -0
- package/dist/assets/migrations/20260913105120_declared_no_reader.sql +41 -0
- package/dist/assets/migrations/20260913111407_pack_languages.sql +223 -0
- package/dist/assets/migrations/20260913112233_schema_comments_for_a_reader.sql +27 -0
- package/dist/assets/migrations/20260913114535_fec_opening_balances.sql +307 -0
- package/dist/assets/migrations/20260914103412_audit_log.sql +509 -0
- package/dist/assets/migrations/20260914111907_pack_upgrade.sql +428 -0
- package/dist/assets/migrations/20260914120500_rounding_reads_the_currency.sql +206 -0
- package/dist/assets/migrations/20260914121200_amounts_round_at_the_currency.sql +2403 -0
- package/dist/assets/migrations/20260914134325_schema_version_0_2_0.sql +36 -0
- package/dist/assets/migrations/20260914143915_accounts_in_use.sql +428 -0
- package/dist/assets/migrations/20260914144731_account_code_frozen.sql +99 -0
- package/dist/assets/migrations/20260914151207_schema_grants_its_own_rights.sql +284 -0
- package/dist/assets/migrations/20260914152840_pack_upgrade_records_its_own_line.sql +237 -0
- package/dist/assets/migrations/20260914163943_declaration_periodicity.sql +349 -0
- package/dist/assets/migrations/20260915094000_foreign_services_received.sql +37 -0
- package/dist/assets/migrations/20260915094500_mentions_read_a_foreign_service.sql +72 -0
- package/dist/assets/migrations/20260915153000_document_shares.sql +568 -0
- package/dist/assets/migrations/20260915160000_ec_sales_list.sql +181 -0
- package/dist/assets/migrations/20260915161842_pack_sources.sql +56 -0
- package/dist/assets/migrations/20260915170500_schema_version_0_3_0.sql +45 -0
- package/dist/assets/migrations/20260915174500_a_vat_category_is_a_code.sql +177 -0
- package/dist/assets/migrations/20260915180000_document_rule_references.sql +85 -0
- package/dist/assets/migrations/20260915181000_territories.sql +342 -0
- package/dist/assets/migrations/20260915181500_ec_sales_list_reads_the_territories.sql +222 -0
- package/dist/assets/migrations/20260915182000_intracom_triangular.sql +53 -0
- package/dist/assets/migrations/20260915182500_a_triangular_supply_says_reverse_charge.sql +99 -0
- package/dist/assets/migrations/20260915191200_a_document_knows_its_language.sql +633 -0
- package/dist/assets/migrations/20260915195000_a_price_that_holds_its_tax.sql +649 -0
- package/dist/assets/migrations/20260915200000_a_posting_names_its_boxes.sql +1004 -0
- package/dist/assets/migrations/20260916094500_a_company_files_more_than_one_declaration.sql +795 -0
- package/dist/assets/migrations/20260916103000_a_ledger_line_names_its_posting.sql +902 -0
- package/dist/assets/migrations/20260916123000_a_vocabulary_for_a_tax_that_is_not_a_vat.sql +144 -0
- package/dist/assets/migrations/20260916124000_a_box_can_be_a_rate_of_a_box.sql +525 -0
- package/dist/assets/migrations/20260916125000_a_tax_follows_the_territory.sql +1318 -0
- package/dist/assets/migrations/20260916126000_a_tax_point_and_its_exception.sql +1211 -0
- package/dist/assets/migrations/20260917090000_a_counterparty_that_learns.sql +561 -0
- package/dist/assets/migrations/20260917120000_what_the_money_pays.sql +545 -0
- package/dist/assets/migrations/20260917150000_a_filing_is_a_row.sql +488 -0
- package/dist/assets/migrations/20260917170000_when_it_is_due.sql +194 -0
- package/dist/assets/migrations/20260917180000_a_box_is_a_number_and_a_kind.sql +213 -0
- package/dist/assets/migrations/20260917190000_what_a_declaration_owes.sql +486 -0
- package/dist/assets/migrations/20260917200000_what_moved_after_it_went.sql +227 -0
- package/dist/assets/migrations/20260918090000_a_form_names_its_file.sql +27 -0
- package/dist/assets/migrations/20260918110000_a_deposit_and_what_came_back.sql +286 -0
- package/dist/assets/migrations/20260918113741_a_fourth_preset_is_named.sql +9 -0
- package/dist/assets/migrations/20260918113807_a_client_reads_and_hands_over.sql +96 -0
- package/dist/assets/migrations/20260918114322_two_definer_functions_check_their_caller.sql +75 -0
- package/dist/assets/migrations/20260918130000_every_company_somebody_keeps.sql +177 -0
- package/dist/assets/migrations/20260918140000_a_session_nobody_prepared.sql +40 -0
- package/dist/assets/migrations/20260918141107_a_line_keeps_the_tax_it_was_posted_with.sql +256 -0
- package/dist/assets/migrations/20260918141342_a_company_has_an_electronic_address.sql +66 -0
- package/dist/assets/migrations/20260918141605_an_invoice_reads_whole_from_the_views.sql +313 -0
- package/dist/assets/migrations/20260918141627_a_policy_asks_once.sql +132 -0
- package/dist/assets/migrations/20260918143352_a_suggestion_reads_the_words_once.sql +204 -0
- package/dist/assets/migrations/20260918143417_a_reference_of_the_caller_and_a_rehearsal.sql +115 -0
- package/dist/assets/migrations/20260918150712_a_company_leaves_with_its_books.sql +1119 -0
- package/dist/assets/migrations/20260918150931_a_statement_is_imported_once.sql +654 -0
- package/dist/assets/migrations/20260918161204_a_posted_document_does_not_move.sql +437 -0
- package/dist/assets/migrations/20260918161538_a_posted_entry_does_not_move.sql +252 -0
- package/dist/assets/migrations/20260918171946_an_entry_is_posted_by_post_entry.sql +289 -0
- package/dist/assets/migrations/20260918174312_schema_version_0_4_0.sql +53 -0
- package/dist/assets/modules/assets/module.json +13 -0
- package/dist/assets/modules/assets/supabase/migrations/20260913081447_assets.sql +1190 -0
- package/dist/assets/modules/assets/supabase/migrations/20260913104234_foreign_key_indexes.sql +47 -0
- package/dist/assets/modules/assets/supabase/migrations/20260913104500_assets_capabilities.sql +145 -0
- package/dist/assets/modules/assets/supabase/migrations/20260914122100_assets_round_at_the_currency.sql +564 -0
- package/dist/assets/modules/assets/supabase/migrations/20260914145012_assets_accounts_in_use.sql +37 -0
- package/dist/assets/modules/assets/supabase/migrations/20260914151530_assets_grants_its_own_rights.sql +63 -0
- package/dist/assets/modules/assets/supabase/migrations/20260918113912_assets_client_reads.sql +20 -0
- package/dist/assets/modules/assets/supabase/migrations/20260918150801_assets_leave_with_the_company.sql +41 -0
- package/dist/assets/modules/budgets/module.json +12 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260913083012_budgets.sql +210 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260913104233_foreign_key_indexes.sql +37 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260913104501_budgets_capabilities.sql +72 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260914122400_budgets_round_at_the_currency.sql +68 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260914145048_budgets_accounts_in_use.sql +30 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260914151812_budgets_grants_its_own_rights.sql +31 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260918113954_budgets_client_reads.sql +20 -0
- package/dist/assets/modules/budgets/supabase/migrations/20260918150823_budgets_leave_with_the_company.sql +37 -0
- package/dist/assets/modules/schema/module.1.json +69 -0
- package/dist/assets/seed/00_currencies.sql +15 -0
- package/dist/assets/seed/00_territories.sql +280 -0
- package/dist/assets/seed/05_framework_generic.sql +110 -0
- package/dist/assets/seed/10_pack_be.sql +1284 -0
- package/dist/assets/seed/11_pack_fr.sql +1112 -0
- package/dist/assets/seed/12_pack_lu.sql +2484 -0
- package/dist/assets/seed/13_pack_ee.sql +733 -0
- package/dist/assets/seed/14_pack_gb.sql +796 -0
- package/dist/assets/seed/15_pack_us.sql +751 -0
- package/dist/assets/seed/90_demo_company.sql +368 -0
- package/dist/assets/seed/modules/assets/10_pack_be.sql +58 -0
- package/dist/assets/seed/modules/assets/11_pack_fr.sql +57 -0
- package/dist/assets/seed/modules/assets/14_pack_gb.sql +58 -0
- package/dist/assets/seed/modules/assets/15_pack_us.sql +60 -0
- package/dist/auth.d.ts +59 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +134 -0
- package/dist/auth.js.map +1 -0
- package/dist/bin.d.ts +6 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +8 -0
- package/dist/bin.js.map +1 -0
- package/dist/books.d.ts +60 -0
- package/dist/books.d.ts.map +1 -0
- package/dist/books.js +168 -0
- package/dist/books.js.map +1 -0
- package/dist/bootstrap.d.ts +236 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +459 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/bundle.d.ts +27 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/bundle.js +47 -0
- package/dist/bundle.js.map +1 -0
- package/dist/checklist.d.ts +35 -0
- package/dist/checklist.d.ts.map +1 -0
- package/dist/checklist.js +64 -0
- package/dist/checklist.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +318 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/company.d.ts +59 -0
- package/dist/commands/company.d.ts.map +1 -0
- package/dist/commands/company.js +287 -0
- package/dist/commands/company.js.map +1 -0
- package/dist/commands/contact.d.ts +9 -0
- package/dist/commands/contact.d.ts.map +1 -0
- package/dist/commands/contact.js +82 -0
- package/dist/commands/contact.js.map +1 -0
- package/dist/commands/demo.d.ts +19 -0
- package/dist/commands/demo.d.ts.map +1 -0
- package/dist/commands/demo.js +72 -0
- package/dist/commands/demo.js.map +1 -0
- package/dist/commands/doctor.d.ts +12 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +68 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/document.d.ts +17 -0
- package/dist/commands/document.d.ts.map +1 -0
- package/dist/commands/document.js +217 -0
- package/dist/commands/document.js.map +1 -0
- package/dist/commands/init.d.ts +31 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +535 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/login.d.ts +24 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +128 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/migrate.d.ts +30 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +115 -0
- package/dist/commands/migrate.js.map +1 -0
- package/dist/commands/module.d.ts +32 -0
- package/dist/commands/module.d.ts.map +1 -0
- package/dist/commands/module.js +285 -0
- package/dist/commands/module.js.map +1 -0
- package/dist/commands/pack.d.ts +22 -0
- package/dist/commands/pack.d.ts.map +1 -0
- package/dist/commands/pack.js +481 -0
- package/dist/commands/pack.js.map +1 -0
- package/dist/commands/payment.d.ts +15 -0
- package/dist/commands/payment.d.ts.map +1 -0
- package/dist/commands/payment.js +97 -0
- package/dist/commands/payment.js.map +1 -0
- package/dist/commands/register.d.ts +20 -0
- package/dist/commands/register.d.ts.map +1 -0
- package/dist/commands/register.js +134 -0
- package/dist/commands/register.js.map +1 -0
- package/dist/commands/status.d.ts +8 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +134 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/whoami.d.ts +21 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +133 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/company.d.ts +16 -0
- package/dist/company.d.ts.map +1 -0
- package/dist/company.js +26 -0
- package/dist/company.js.map +1 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +50 -0
- package/dist/config.js.map +1 -0
- package/dist/connection.d.ts +79 -0
- package/dist/connection.d.ts.map +1 -0
- package/dist/connection.js +116 -0
- package/dist/connection.js.map +1 -0
- package/dist/context.d.ts +47 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +93 -0
- package/dist/context.js.map +1 -0
- package/dist/doctor.d.ts +45 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +448 -0
- package/dist/doctor.js.map +1 -0
- package/dist/grants.d.ts +88 -0
- package/dist/grants.d.ts.map +1 -0
- package/dist/grants.js +174 -0
- package/dist/grants.js.map +1 -0
- package/dist/identity.d.ts +54 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +132 -0
- package/dist/identity.js.map +1 -0
- package/dist/index.d.ts +44 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +43 -0
- package/dist/index.js.map +1 -0
- package/dist/inventory.d.ts +155 -0
- package/dist/inventory.d.ts.map +1 -0
- package/dist/inventory.js +303 -0
- package/dist/inventory.js.map +1 -0
- package/dist/migrations.d.ts +76 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +226 -0
- package/dist/migrations.js.map +1 -0
- package/dist/module/read.d.ts +91 -0
- package/dist/module/read.d.ts.map +1 -0
- package/dist/module/read.js +172 -0
- package/dist/module/read.js.map +1 -0
- package/dist/output.d.ts +110 -0
- package/dist/output.d.ts.map +1 -0
- package/dist/output.js +173 -0
- package/dist/output.js.map +1 -0
- package/dist/pack/certification.d.ts +27 -0
- package/dist/pack/certification.d.ts.map +1 -0
- package/dist/pack/certification.js +38 -0
- package/dist/pack/certification.js.map +1 -0
- package/dist/pack/compile.d.ts +75 -0
- package/dist/pack/compile.d.ts.map +1 -0
- package/dist/pack/compile.js +801 -0
- package/dist/pack/compile.js.map +1 -0
- package/dist/pack/filing.d.ts +48 -0
- package/dist/pack/filing.d.ts.map +1 -0
- package/dist/pack/filing.js +56 -0
- package/dist/pack/filing.js.map +1 -0
- package/dist/pack/read.d.ts +766 -0
- package/dist/pack/read.d.ts.map +1 -0
- package/dist/pack/read.js +2336 -0
- package/dist/pack/read.js.map +1 -0
- package/dist/pack/schema.d.ts +26 -0
- package/dist/pack/schema.d.ts.map +1 -0
- package/dist/pack/schema.js +181 -0
- package/dist/pack/schema.js.map +1 -0
- package/dist/pack/territories.d.ts +98 -0
- package/dist/pack/territories.d.ts.map +1 -0
- package/dist/pack/territories.js +0 -0
- package/dist/pack/territories.js.map +1 -0
- package/dist/pack/upgrade.d.ts +86 -0
- package/dist/pack/upgrade.d.ts.map +1 -0
- package/dist/pack/upgrade.js +76 -0
- package/dist/pack/upgrade.js.map +1 -0
- package/dist/pack/vat-codes.d.ts +273 -0
- package/dist/pack/vat-codes.d.ts.map +1 -0
- package/dist/pack/vat-codes.js +513 -0
- package/dist/pack/vat-codes.js.map +1 -0
- package/dist/profiles.d.ts +67 -0
- package/dist/profiles.d.ts.map +1 -0
- package/dist/profiles.js +187 -0
- package/dist/profiles.js.map +1 -0
- package/dist/prompt.d.ts +40 -0
- package/dist/prompt.d.ts.map +1 -0
- package/dist/prompt.js +120 -0
- package/dist/prompt.js.map +1 -0
- package/dist/registry.d.ts +84 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +104 -0
- package/dist/registry.js.map +1 -0
- package/dist/rest.d.ts +81 -0
- package/dist/rest.d.ts.map +1 -0
- package/dist/rest.js +245 -0
- package/dist/rest.js.map +1 -0
- package/dist/schema.d.ts +15 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +15 -0
- package/dist/schema.js.map +1 -0
- package/dist/seeds.d.ts +27 -0
- package/dist/seeds.d.ts.map +1 -0
- package/dist/seeds.js +47 -0
- package/dist/seeds.js.map +1 -0
- package/dist/session.d.ts +39 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +92 -0
- package/dist/session.js.map +1 -0
- package/dist/sql.d.ts +48 -0
- package/dist/sql.d.ts.map +1 -0
- package/dist/sql.js +112 -0
- package/dist/sql.js.map +1 -0
- package/dist/status.d.ts +94 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +120 -0
- package/dist/status.js.map +1 -0
- package/dist/ui.d.ts +55 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +114 -0
- package/dist/ui.js.map +1 -0
- package/package.json +29 -0
- package/schema/output.1.json +613 -0
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The compiler: a pack in, one seed file out.
|
|
3
|
+
*
|
|
4
|
+
* The SQL is a build artefact, like `docs/schema.md`. The source is the pack,
|
|
5
|
+
* the output is committed so that `supabase db push` and `psql -f` install a
|
|
6
|
+
* country without this CLI ever running, and `ekwo pack check` refuses an
|
|
7
|
+
* output that is no longer what its pack says. The order of every row is
|
|
8
|
+
* fixed here, so the same pack always compiles to the same bytes.
|
|
9
|
+
*
|
|
10
|
+
* Every insert **upserts on the template tables and on nothing else**. The
|
|
11
|
+
* seeds used to say `on conflict do nothing`, which meant an instance
|
|
12
|
+
* installed last month never received a single pack change — not even for a
|
|
13
|
+
* company created afterwards, since a company copies the templates at
|
|
14
|
+
* install. A template is reference data and a seed is allowed to re-state it;
|
|
15
|
+
* a row that belongs to a company is never touched here, and moving a company
|
|
16
|
+
* from one pack version to the next is `ekwo pack upgrade`, still to come, which
|
|
17
|
+
* shows the diff first.
|
|
18
|
+
*
|
|
19
|
+
* What an upsert cannot do is remove: a template dropped from a pack stays in
|
|
20
|
+
* the database. That is the rule anyway — nothing is ever deleted from a
|
|
21
|
+
* pack, an account is deprecated and a tax gets a `valid_to`.
|
|
22
|
+
*/
|
|
23
|
+
import { describeCertification } from './certification.js';
|
|
24
|
+
import { sourcesOf } from './read.js';
|
|
25
|
+
/**
|
|
26
|
+
* Which number each pack's compiled seed carries, by slug.
|
|
27
|
+
*
|
|
28
|
+
* **The number is the pack's own, and a number that has shipped never moves.**
|
|
29
|
+
* The seeds are applied in file-name order and are listed by name in
|
|
30
|
+
* `supabase/config.toml`, so a release that renamed one would rename a file an
|
|
31
|
+
* installation already holds — harmless, because the seeds upsert, and
|
|
32
|
+
* inexplicable to whoever reads the list a year later.
|
|
33
|
+
*
|
|
34
|
+
* It used to be the pack's rank in the alphabetical list of slugs, which is
|
|
35
|
+
* stable exactly until a country is added in the middle of it: inserting `ee`
|
|
36
|
+
* between `be` and `fr` moved France and Luxembourg one place each. So a pack
|
|
37
|
+
* **declares** its number, in `seed_sequence`, and nothing here sorts anything.
|
|
38
|
+
*/
|
|
39
|
+
export function seedFileNames(allSlugs, sequences) {
|
|
40
|
+
const names = new Map();
|
|
41
|
+
const taken = new Map();
|
|
42
|
+
for (const slug of allSlugs) {
|
|
43
|
+
const own = sequences.get(slug);
|
|
44
|
+
if (own === undefined) {
|
|
45
|
+
throw new Error(`seed_sequence_missing: packs/${slug} declares no seed_sequence`);
|
|
46
|
+
}
|
|
47
|
+
const other = taken.get(own);
|
|
48
|
+
if (other !== undefined) {
|
|
49
|
+
throw new Error(`seed_sequence_conflict: packs/${other} and packs/${slug} both declare ${own}`);
|
|
50
|
+
}
|
|
51
|
+
taken.set(own, slug);
|
|
52
|
+
names.set(slug, `${own}_pack_${slug}.sql`);
|
|
53
|
+
}
|
|
54
|
+
return names;
|
|
55
|
+
}
|
|
56
|
+
/** `packs/be` → `10_pack_be.sql`, `packs/ee` → `13_pack_ee.sql`. */
|
|
57
|
+
export function seedFileName(slug, allSlugs, sequences) {
|
|
58
|
+
const file = seedFileNames(allSlugs, sequences).get(slug);
|
|
59
|
+
if (file === undefined)
|
|
60
|
+
throw new Error(`unknown_pack: ${slug}`);
|
|
61
|
+
return file;
|
|
62
|
+
}
|
|
63
|
+
export function compilePack(pack) {
|
|
64
|
+
const { manifest } = pack;
|
|
65
|
+
const country = manifest.country;
|
|
66
|
+
const out = [];
|
|
67
|
+
out.push(...header(pack));
|
|
68
|
+
out.push(...manifestRow(pack, country));
|
|
69
|
+
out.push(...charts(pack, country));
|
|
70
|
+
out.push(...accounts(pack, country));
|
|
71
|
+
out.push(...journals(pack, country));
|
|
72
|
+
out.push(...taxes(pack, country));
|
|
73
|
+
out.push(...postings(pack, country));
|
|
74
|
+
out.push(...taxReport(pack, country));
|
|
75
|
+
out.push(...statements(pack.statements, pack.labels.statement_lines, country));
|
|
76
|
+
out.push(...defaults(pack, country));
|
|
77
|
+
out.push(...documentRules(pack, country));
|
|
78
|
+
return `${out.join('\n')}\n`;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* The country data of one module, for one pack:
|
|
82
|
+
* `supabase/seed/modules/assets/10_pack_be.sql`.
|
|
83
|
+
*
|
|
84
|
+
* It is a seed of its own, under the module's own folder, and deliberately not
|
|
85
|
+
* part of the pack seed. The pack seed is applied by every installation;
|
|
86
|
+
* `assets.category_templates` does not exist on one that does not carry the
|
|
87
|
+
* module, and a seed that half fails is a seed nobody can re-run. So the
|
|
88
|
+
* module migration runner applies these, and only for the modules it installed.
|
|
89
|
+
*
|
|
90
|
+
* The numbering is the pack's own — `10_pack_be`, `13_pack_ee` — so the file
|
|
91
|
+
* of a country is recognisable wherever it sits.
|
|
92
|
+
*/
|
|
93
|
+
export function moduleSeedFileName(slug, allSlugs, sequences) {
|
|
94
|
+
return seedFileName(slug, allSlugs, sequences);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* `packs/<cc>/assets.json` → the two reference tables of the `assets` module.
|
|
98
|
+
*
|
|
99
|
+
* Returns `undefined` when the pack says nothing about fixed assets, which is
|
|
100
|
+
* not a gap to fill: `assets.generate_schedule` refuses by name where it needs
|
|
101
|
+
* a prorata convention nobody has declared, rather than taking another
|
|
102
|
+
* country's.
|
|
103
|
+
*/
|
|
104
|
+
export function compileAssetsSeed(pack) {
|
|
105
|
+
const assets = pack.assets;
|
|
106
|
+
if (assets === null)
|
|
107
|
+
return undefined;
|
|
108
|
+
const country = pack.manifest.country;
|
|
109
|
+
const out = [
|
|
110
|
+
`-- Ekwo OS — ${pack.manifest.name}: how this country depreciates and derecognises a fixed asset.`,
|
|
111
|
+
'--',
|
|
112
|
+
`-- Generated from packs/${pack.slug}/assets.json at version ${pack.manifest.version}, do not edit.`,
|
|
113
|
+
`-- Change the pack and run \`ekwo pack build ${pack.slug}\`; \`ekwo pack check --all\``,
|
|
114
|
+
'-- refuses a seed that is not the exact output of its pack, and the CI runs it.',
|
|
115
|
+
'--',
|
|
116
|
+
'-- Applied by the module migration runner — `ekwo migrate`, or `ekwo module',
|
|
117
|
+
'-- migrate` — and never by the socle seed step: these tables exist only on an',
|
|
118
|
+
'-- installation that carries the `assets` module.',
|
|
119
|
+
'--',
|
|
120
|
+
'-- The accounts a disposal lands on are not here. They are roles of the chart,',
|
|
121
|
+
'-- in `country_defaults`, written by the pack seed beside every other role.',
|
|
122
|
+
'',
|
|
123
|
+
'insert into assets.country_rules',
|
|
124
|
+
' (country, prorata_straight_line, prorata_declining, day_count,',
|
|
125
|
+
' declining_cap_percent, declining_switch_to_linear, disposal_style, legal_reference)',
|
|
126
|
+
'values',
|
|
127
|
+
` (${text(country)}, ${text(assets.prorata_straight_line)}, ${text(assets.prorata_declining)}, ` +
|
|
128
|
+
`${text(assets.day_count)}, ${orNull(assets.declining_cap_percent, number)}, ` +
|
|
129
|
+
`${bool(assets.declining_switch_to_linear)}, ${text(assets.disposal_style)}, ` +
|
|
130
|
+
`${text(assets.legal_reference)})`,
|
|
131
|
+
'on conflict (country) do update set',
|
|
132
|
+
' prorata_straight_line = excluded.prorata_straight_line,',
|
|
133
|
+
' prorata_declining = excluded.prorata_declining,',
|
|
134
|
+
' day_count = excluded.day_count,',
|
|
135
|
+
' declining_cap_percent = excluded.declining_cap_percent,',
|
|
136
|
+
' declining_switch_to_linear = excluded.declining_switch_to_linear,',
|
|
137
|
+
' disposal_style = excluded.disposal_style,',
|
|
138
|
+
' legal_reference = excluded.legal_reference;',
|
|
139
|
+
'',
|
|
140
|
+
];
|
|
141
|
+
if (assets.categories.length === 0)
|
|
142
|
+
return `${out.join('\n')}\n`;
|
|
143
|
+
const values = [...assets.categories]
|
|
144
|
+
.sort((a, b) => a.sequence - b.sequence || (a.code < b.code ? -1 : 1))
|
|
145
|
+
.map((c) => ` (${text(country)}, ${text(c.code)}, ${text(c.name)}, ${json(c.name_i18n)}, ` +
|
|
146
|
+
`${text(c.method)}, ${number(c.duration_months)}, ${orNull(c.coefficient, number)}, ` +
|
|
147
|
+
`${text(c.prorata)}, ${text(c.account_type)}, ${number(c.sequence)}, ` +
|
|
148
|
+
`${text(c.legal_reference)})`);
|
|
149
|
+
out.push('insert into assets.category_templates', ' (country, code, name, name_i18n, method, duration_months, coefficient,', ' prorata, account_type, sequence, legal_reference)', 'select v.country::char(2), v.code, v.name, v.name_i18n::jsonb,', ' v.method::assets.depreciation_method, v.duration_months::integer,', ' v.coefficient::numeric, v.prorata::assets.prorata_rule,', ' v.account_type::account_type, v.sequence::integer, v.legal_reference', ' from (values', values.join(',\n'), ' ) as v (country, code, name, name_i18n, method, duration_months, coefficient,', ' prorata, account_type, sequence, legal_reference)', 'on conflict (country, code) do update set', ' name = excluded.name,', ' name_i18n = excluded.name_i18n,', ' method = excluded.method,', ' duration_months = excluded.duration_months,', ' coefficient = excluded.coefficient,', ' prorata = excluded.prorata,', ' account_type = excluded.account_type,', ' sequence = excluded.sequence,', ' legal_reference = excluded.legal_reference;', '');
|
|
150
|
+
return `${out.join('\n')}\n`;
|
|
151
|
+
}
|
|
152
|
+
/** Every module seed a pack compiles to, by module code. */
|
|
153
|
+
export function compileModuleSeeds(pack) {
|
|
154
|
+
const seeds = new Map();
|
|
155
|
+
const assets = compileAssetsSeed(pack);
|
|
156
|
+
if (assets !== undefined)
|
|
157
|
+
seeds.set('assets', assets);
|
|
158
|
+
return seeds;
|
|
159
|
+
}
|
|
160
|
+
/** `packs/generic` → `05_framework_generic.sql`. It sorts before every pack. */
|
|
161
|
+
export function frameworkSeedFileName(slug) {
|
|
162
|
+
return `05_framework_${slug}.sql`;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* The framework pack: statements with no country, and nothing else. They are
|
|
166
|
+
* seeded before the country packs because a chart may name one, and because a
|
|
167
|
+
* company whose country has no pack at all still gets a balance sheet.
|
|
168
|
+
*/
|
|
169
|
+
export function compileFrameworkPack(pack) {
|
|
170
|
+
const { manifest } = pack;
|
|
171
|
+
const out = [
|
|
172
|
+
`-- Ekwo OS — ${manifest.name}: financial statements by account type, for any chart of any country.`,
|
|
173
|
+
'--',
|
|
174
|
+
`-- Generated from packs/${pack.slug} at version ${manifest.version}, do not edit.`,
|
|
175
|
+
`-- Change the pack and run \`ekwo pack build ${pack.slug}\`; \`ekwo pack check --all\``,
|
|
176
|
+
'-- refuses a seed that is not the exact output of its pack, and the CI runs it.',
|
|
177
|
+
'--',
|
|
178
|
+
];
|
|
179
|
+
if (manifest.certification !== undefined) {
|
|
180
|
+
out.push(`-- ${capitalise(describeCertification(manifest.certification))}.`);
|
|
181
|
+
out.push('-- Written from:');
|
|
182
|
+
for (const source of manifest.certification.sources ?? [])
|
|
183
|
+
out.push(...citation(source));
|
|
184
|
+
out.push('--');
|
|
185
|
+
}
|
|
186
|
+
out.push('-- Reference data with no country and no chart: `financial_statement()` reads it', '-- directly, and nothing here is copied into a company.', '');
|
|
187
|
+
out.push(...statements(pack.statements, {}, null));
|
|
188
|
+
return `${out.join('\n')}\n`;
|
|
189
|
+
}
|
|
190
|
+
function header(pack) {
|
|
191
|
+
const { manifest } = pack;
|
|
192
|
+
const lines = [
|
|
193
|
+
`-- Ekwo OS — ${manifest.name}: chart of accounts, journals, taxes and defaults.`,
|
|
194
|
+
'--',
|
|
195
|
+
`-- Generated from packs/${pack.slug} at version ${manifest.version}, do not edit.`,
|
|
196
|
+
`-- Change the pack and run \`ekwo pack build ${pack.slug}\`; \`ekwo pack check --all\``,
|
|
197
|
+
'-- refuses a seed that is not the exact output of its pack, and the CI runs it.',
|
|
198
|
+
'--',
|
|
199
|
+
];
|
|
200
|
+
const certification = manifest.certification;
|
|
201
|
+
if (certification !== undefined) {
|
|
202
|
+
lines.push(`-- ${capitalise(describeCertification(certification))}.`);
|
|
203
|
+
lines.push('-- Written from:');
|
|
204
|
+
for (const source of certification.sources ?? [])
|
|
205
|
+
lines.push(...citation(source));
|
|
206
|
+
lines.push('--');
|
|
207
|
+
}
|
|
208
|
+
if (pack.deferred.length > 0) {
|
|
209
|
+
lines.push('-- In the pack, not compiled by this release:');
|
|
210
|
+
for (const section of pack.deferred)
|
|
211
|
+
lines.push(`-- ${section}`);
|
|
212
|
+
lines.push('--');
|
|
213
|
+
}
|
|
214
|
+
lines.push('-- Reference data: `install_country_template()` copies it into a company,', '-- nothing here belongs to a company.', '');
|
|
215
|
+
return lines;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* The charts this country offers. They come before the accounts: a template
|
|
219
|
+
* account points at its chart, so the chart has to exist first.
|
|
220
|
+
*/
|
|
221
|
+
function charts(pack, country) {
|
|
222
|
+
const values = pack.charts.map((c) => ` (${text(country)}, ${text(c.code)}, ${text(c.name)}, ${json(c.name_i18n)}, ` +
|
|
223
|
+
`${c.is_default ? 'true' : 'false'}, ${text(c.audience)}, ${array([...c.statements].sort())}, ` +
|
|
224
|
+
`${text(c.certification?.status ?? null)}, ${text(c.legal_reference)}, ${text(c.source)})`);
|
|
225
|
+
return [
|
|
226
|
+
'insert into chart_templates',
|
|
227
|
+
' (country, code, name, name_i18n, is_default, audience, statements,',
|
|
228
|
+
' certification_status, legal_reference, source_key)',
|
|
229
|
+
'values',
|
|
230
|
+
values.join(',\n'),
|
|
231
|
+
'on conflict (country, code) do update set',
|
|
232
|
+
' name = excluded.name,',
|
|
233
|
+
' name_i18n = excluded.name_i18n,',
|
|
234
|
+
' is_default = excluded.is_default,',
|
|
235
|
+
' audience = excluded.audience,',
|
|
236
|
+
' statements = excluded.statements,',
|
|
237
|
+
' certification_status = excluded.certification_status,',
|
|
238
|
+
' legal_reference = excluded.legal_reference,',
|
|
239
|
+
' source_key = excluded.source_key;',
|
|
240
|
+
'',
|
|
241
|
+
];
|
|
242
|
+
}
|
|
243
|
+
function accounts(pack, country) {
|
|
244
|
+
const values = [];
|
|
245
|
+
for (const chart of pack.charts) {
|
|
246
|
+
for (const a of [...chart.accounts].sort(byCode)) {
|
|
247
|
+
values.push(` (${text(country)}, ${text(chart.code)}, ${text(a.code)}, ${text(a.name)}, ` +
|
|
248
|
+
`${json(pack.labels.accounts[a.code])}, ${text(a.type)}, ` +
|
|
249
|
+
`${a.reconcilable ? 'true' : 'false'}, ${text(a.parent)}, ${a.sequence})`);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return [
|
|
253
|
+
'insert into account_templates',
|
|
254
|
+
' (country, chart_code, code, name, name_i18n, account_type, reconcilable,',
|
|
255
|
+
' parent_code, sequence)',
|
|
256
|
+
'values',
|
|
257
|
+
values.join(',\n'),
|
|
258
|
+
'on conflict (country, chart_code, code) do update set',
|
|
259
|
+
' name = excluded.name,',
|
|
260
|
+
' name_i18n = excluded.name_i18n,',
|
|
261
|
+
' account_type = excluded.account_type,',
|
|
262
|
+
' reconcilable = excluded.reconcilable,',
|
|
263
|
+
' parent_code = excluded.parent_code,',
|
|
264
|
+
' sequence = excluded.sequence;',
|
|
265
|
+
'',
|
|
266
|
+
];
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The statements and their lines and rules. The order is the order the scheme
|
|
270
|
+
* declares, because that is the order `financial_statement()` evaluates the
|
|
271
|
+
* totals in.
|
|
272
|
+
*/
|
|
273
|
+
function statements(list, labels, country) {
|
|
274
|
+
if (list.length === 0)
|
|
275
|
+
return [];
|
|
276
|
+
const out = [];
|
|
277
|
+
out.push('insert into statement_templates', ' (code, country, chart_code, name, kind, framework, valid_from, valid_to, legal_reference,', ' source_key)', 'values', [...list]
|
|
278
|
+
.sort(byCode)
|
|
279
|
+
.map((s) => ` (${text(s.code)}, ${text(country)}, ${text(s.chart_code)}, ${text(s.name)}, ` +
|
|
280
|
+
`${text(s.kind)}, ${text(s.framework)}, ${date(s.valid_from)}, ${date(s.valid_to)}, ` +
|
|
281
|
+
`${text(s.legal_reference)}, ${text(s.source)})`)
|
|
282
|
+
.join(',\n'), 'on conflict (code) do update set', ' country = excluded.country,', ' chart_code = excluded.chart_code,', ' name = excluded.name,', ' kind = excluded.kind,', ' framework = excluded.framework,', ' valid_from = excluded.valid_from,', ' valid_to = excluded.valid_to,', ' legal_reference = excluded.legal_reference,', ' source_key = excluded.source_key;', '');
|
|
283
|
+
const lines = [];
|
|
284
|
+
const rules = [];
|
|
285
|
+
for (const statement of [...list].sort(byCode)) {
|
|
286
|
+
for (const line of [...statement.lines].sort((a, b) => a.sequence - b.sequence || (a.code < b.code ? -1 : 1))) {
|
|
287
|
+
lines.push(` (${text(statement.code)}, ${text(line.code)}, ${text(line.parent)}, ${text(line.name)}, ` +
|
|
288
|
+
`${json(labels[`${statement.code}:${line.code}`])}, ${line.sequence}, ${line.sign}, ` +
|
|
289
|
+
`${line.is_total ? 'true' : 'false'}, ${array(line.plus)}, ${array(line.minus)}, ` +
|
|
290
|
+
`${text(line.xbrl)}, ${text(line.legal_reference)}, ${text(line.source)})`);
|
|
291
|
+
for (const rule of [...line.rules].sort((a, b) => a.sequence - b.sequence)) {
|
|
292
|
+
rules.push(` (${text(statement.code)}, ${text(line.code)}, ${rule.sequence}, ${text(rule.kind)}, ` +
|
|
293
|
+
`${text(rule.code_from)}, ${text(rule.code_to)}, ${text(rule.account_type)}, ${text(rule.side)})`);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
out.push('insert into statement_line_templates', ' (statement_code, code, parent_code, name, name_i18n, sequence, sign, is_total,', ' plus_lines, minus_lines, xbrl_element, legal_reference, source_key)', 'values', lines.join(',\n'), 'on conflict (statement_code, code) do update set', ' parent_code = excluded.parent_code,', ' name = excluded.name,', ' name_i18n = excluded.name_i18n,', ' sequence = excluded.sequence,', ' sign = excluded.sign,', ' is_total = excluded.is_total,', ' plus_lines = excluded.plus_lines,', ' minus_lines = excluded.minus_lines,', ' xbrl_element = excluded.xbrl_element,', ' legal_reference = excluded.legal_reference,', ' source_key = excluded.source_key;', '');
|
|
298
|
+
if (rules.length > 0) {
|
|
299
|
+
out.push('insert into statement_line_rules', ' (statement_code, line_code, sequence, rule_kind, code_from, code_to,', ' account_type, balance_side)', 'select v.statement_code, v.line_code, v.sequence, v.rule_kind, v.code_from,', ' v.code_to, v.account_type::account_type, v.balance_side', ' from (values', rules.map((row) => ` ${row}`).join(',\n'), ' ) as v (statement_code, line_code, sequence, rule_kind, code_from, code_to,', ' account_type, balance_side)', 'on conflict (statement_code, line_code, sequence) do update set', ' rule_kind = excluded.rule_kind,', ' code_from = excluded.code_from,', ' code_to = excluded.code_to,', ' account_type = excluded.account_type,', ' balance_side = excluded.balance_side;', '');
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
function journals(pack, country) {
|
|
304
|
+
const rows = [...pack.manifest.journals].sort((a, b) => a.code.localeCompare(b.code));
|
|
305
|
+
const values = rows.map((j, index) => ` (${text(country)}, ${text(j.code)}, ${text(j.name)}, ${json(pack.labels.journals[j.code])}, ` +
|
|
306
|
+
`${text(j.type)}, ${j.sequence ?? (index + 1) * 10})`);
|
|
307
|
+
return [
|
|
308
|
+
'insert into journal_templates (country, code, name, name_i18n, journal_type, sequence) values',
|
|
309
|
+
values.join(',\n'),
|
|
310
|
+
'on conflict (country, code) do update set',
|
|
311
|
+
' name = excluded.name,',
|
|
312
|
+
' name_i18n = excluded.name_i18n,',
|
|
313
|
+
' journal_type = excluded.journal_type,',
|
|
314
|
+
' sequence = excluded.sequence;',
|
|
315
|
+
'',
|
|
316
|
+
];
|
|
317
|
+
}
|
|
318
|
+
function taxes(pack, country) {
|
|
319
|
+
const rows = [...pack.taxes].sort(byCode);
|
|
320
|
+
const values = rows.map((t) => ` (${text(country)}, ${text(t.code)}, ${text(t.name)}, ${json(pack.labels.taxes[t.code])}, ` +
|
|
321
|
+
`${text(t.description)}, ` +
|
|
322
|
+
`${text(t.amount_type)}, ${number(t.rate)}, ${text(t.scope)}, ${text(t.treatment)}, ` +
|
|
323
|
+
`${date(t.valid_from)}, ${date(t.valid_to)}, ${text(t.legal_reference)}, ` +
|
|
324
|
+
`${text(t.vat_category)}, ${text(t.exemption_code)}, ${t.sequence}, ` +
|
|
325
|
+
`${text(t.kind)}, ${bool(t.recoverable)}, ${conditions(t.conditions)}, ` +
|
|
326
|
+
`${text(t.jurisdiction)}, ` +
|
|
327
|
+
`${bool(t.price_include)}, ${bool(t.cash_basis)}, ${text(t.cash_basis_transition_account)}, ` +
|
|
328
|
+
`${text(t.source)}, ${text(t.applies_seller_territory)}, ${text(t.applies_buyer_territory)}, ` +
|
|
329
|
+
`${text(t.applies_supply_territory)})`);
|
|
330
|
+
return [
|
|
331
|
+
'insert into tax_templates',
|
|
332
|
+
' (country, code, name, name_i18n, description, amount_type, amount, applies_to, treatment,',
|
|
333
|
+
' valid_from, valid_to, legal_reference, vat_category, exemption_code, sequence,',
|
|
334
|
+
' tax_kind, recoverable, conditions, jurisdiction, price_include, cash_basis,',
|
|
335
|
+
' cash_basis_transition_account_code, source_key,',
|
|
336
|
+
' applies_seller_territory, applies_buyer_territory, applies_supply_territory)',
|
|
337
|
+
'values',
|
|
338
|
+
values.join(',\n'),
|
|
339
|
+
'on conflict (country, code) do update set',
|
|
340
|
+
' name = excluded.name,',
|
|
341
|
+
' name_i18n = excluded.name_i18n,',
|
|
342
|
+
' description = excluded.description,',
|
|
343
|
+
' amount_type = excluded.amount_type,',
|
|
344
|
+
' amount = excluded.amount,',
|
|
345
|
+
' applies_to = excluded.applies_to,',
|
|
346
|
+
' treatment = excluded.treatment,',
|
|
347
|
+
' valid_from = excluded.valid_from,',
|
|
348
|
+
' valid_to = excluded.valid_to,',
|
|
349
|
+
' legal_reference = excluded.legal_reference,',
|
|
350
|
+
' vat_category = excluded.vat_category,',
|
|
351
|
+
' exemption_code = excluded.exemption_code,',
|
|
352
|
+
' sequence = excluded.sequence,',
|
|
353
|
+
' tax_kind = excluded.tax_kind,',
|
|
354
|
+
' recoverable = excluded.recoverable,',
|
|
355
|
+
' conditions = excluded.conditions,',
|
|
356
|
+
' jurisdiction = excluded.jurisdiction,',
|
|
357
|
+
' price_include = excluded.price_include,',
|
|
358
|
+
' cash_basis = excluded.cash_basis,',
|
|
359
|
+
' cash_basis_transition_account_code = excluded.cash_basis_transition_account_code,',
|
|
360
|
+
' source_key = excluded.source_key,',
|
|
361
|
+
' applies_seller_territory = excluded.applies_seller_territory,',
|
|
362
|
+
' applies_buyer_territory = excluded.applies_buyer_territory,',
|
|
363
|
+
' applies_supply_territory = excluded.applies_supply_territory;',
|
|
364
|
+
'',
|
|
365
|
+
];
|
|
366
|
+
}
|
|
367
|
+
function postings(pack, country) {
|
|
368
|
+
const values = [];
|
|
369
|
+
for (const tax of [...pack.taxes].sort(byCode)) {
|
|
370
|
+
for (const kind of ['invoice', 'credit_note']) {
|
|
371
|
+
const rows = [...tax.postings[kind]].sort((a, b) => a.sequence - b.sequence || a.type.localeCompare(b.type));
|
|
372
|
+
for (const posting of rows) {
|
|
373
|
+
values.push(` (${text(tax.code)}, ${text(kind)}, ${text(posting.type)}, ` +
|
|
374
|
+
`${number(posting.factor)}, ${text(posting.account)}, ${text(posting.box)}, ` +
|
|
375
|
+
`${posting.boxes.length === 0 ? 'null' : array(posting.boxes)}, ` +
|
|
376
|
+
`${number(posting.box_factor)}, ${text(posting.report)}, ${posting.sequence})`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (values.length === 0)
|
|
381
|
+
return [];
|
|
382
|
+
return [
|
|
383
|
+
'insert into tax_posting_templates',
|
|
384
|
+
' (tax_template_id, document_kind, posting_type, factor_percent, account_code,',
|
|
385
|
+
' declaration_box, declaration_boxes, box_factor_percent, report_code, sequence)',
|
|
386
|
+
'select t.id,',
|
|
387
|
+
' v.document_kind::tax_document_kind,',
|
|
388
|
+
' v.posting_type::tax_posting_type,',
|
|
389
|
+
' v.factor_percent::numeric,',
|
|
390
|
+
' v.account_code::text,',
|
|
391
|
+
' v.declaration_box::text,',
|
|
392
|
+
' v.declaration_boxes::text[],',
|
|
393
|
+
' v.box_factor_percent::numeric,',
|
|
394
|
+
' v.report_code::text,',
|
|
395
|
+
' v.sequence::integer',
|
|
396
|
+
' from (values',
|
|
397
|
+
values.join(',\n'),
|
|
398
|
+
' ) as v (tax_code, document_kind, posting_type, factor_percent, account_code,',
|
|
399
|
+
' declaration_box, declaration_boxes, box_factor_percent, report_code, sequence)',
|
|
400
|
+
` join tax_templates t on t.country = ${text(country)} and t.code = v.tax_code`,
|
|
401
|
+
'on conflict (tax_template_id, document_kind, posting_type, sequence) do update set',
|
|
402
|
+
' factor_percent = excluded.factor_percent,',
|
|
403
|
+
' account_code = excluded.account_code,',
|
|
404
|
+
' declaration_box = excluded.declaration_box,',
|
|
405
|
+
' declaration_boxes = excluded.declaration_boxes,',
|
|
406
|
+
' box_factor_percent = excluded.box_factor_percent,',
|
|
407
|
+
' report_code = excluded.report_code;',
|
|
408
|
+
'',
|
|
409
|
+
];
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* The declaration form and its boxes. Not copied into a company: a chart of
|
|
413
|
+
* accounts is customisable and a form is not. The rows are written in the
|
|
414
|
+
* order the pack declares them, which is the order `vat_return()` answers in;
|
|
415
|
+
* `print_sequence` carries the order the administration prints, and the order
|
|
416
|
+
* the totals are worked out in is their dependencies and neither of the two.
|
|
417
|
+
*/
|
|
418
|
+
function taxReport(pack, country) {
|
|
419
|
+
const report = pack.report;
|
|
420
|
+
if (report === null)
|
|
421
|
+
return [];
|
|
422
|
+
const out = [
|
|
423
|
+
'insert into tax_report_templates',
|
|
424
|
+
' (country, code, name, periods, period_default, valid_from, valid_to, legal_reference,',
|
|
425
|
+
' is_periodic_return, deadline_rule, deadline_day, deadline_plus_days,',
|
|
426
|
+
' deadline_reference, deadline_source_key, file_format)',
|
|
427
|
+
'values',
|
|
428
|
+
` (${text(country)}, ${text(report.code)}, ${text(report.name)}, ${periods(report.periods)}, ` +
|
|
429
|
+
`${enumeration(report.period_default, 'declaration_period')}, ` +
|
|
430
|
+
`${date(report.valid_from)}, ${date(report.valid_to)}, ${text(report.legal_reference)}, true,` +
|
|
431
|
+
`${enumeration(report.deadline?.rule ?? null, 'filing_deadline_rule')}, ` +
|
|
432
|
+
`${number(report.deadline?.day ?? null)}, ${number(report.deadline?.plus_days ?? null)}, ` +
|
|
433
|
+
`${text(report.deadline?.legal_reference ?? null)}, ${text(report.deadline?.source ?? null)}, ` +
|
|
434
|
+
`${text(report.file_format)})`,
|
|
435
|
+
'on conflict (country, code) do update set',
|
|
436
|
+
' name = excluded.name,',
|
|
437
|
+
' periods = excluded.periods,',
|
|
438
|
+
' period_default = excluded.period_default,',
|
|
439
|
+
' valid_from = excluded.valid_from,',
|
|
440
|
+
' valid_to = excluded.valid_to,',
|
|
441
|
+
' legal_reference = excluded.legal_reference,',
|
|
442
|
+
' is_periodic_return = excluded.is_periodic_return,',
|
|
443
|
+
' deadline_rule = excluded.deadline_rule,',
|
|
444
|
+
' deadline_day = excluded.deadline_day,',
|
|
445
|
+
' deadline_plus_days = excluded.deadline_plus_days,',
|
|
446
|
+
' deadline_reference = excluded.deadline_reference,',
|
|
447
|
+
' deadline_source_key = excluded.deadline_source_key,',
|
|
448
|
+
' file_format = excluded.file_format;',
|
|
449
|
+
'',
|
|
450
|
+
];
|
|
451
|
+
const boxes = [...report.boxes].sort((a, b) => a.sequence - b.sequence || (a.box < b.box ? -1 : 1));
|
|
452
|
+
const values = boxes.map((b) => ` (${text(country)}, ${text(report.code)}, ${text(b.box)}, ${text(b.kind)}, ${text(b.name)}, ` +
|
|
453
|
+
`${json(pack.labels.tax_report_boxes[`${b.box}|${b.kind}`])}, ${b.sequence}, ` +
|
|
454
|
+
`${b.print_sequence === null ? 'null' : number(b.print_sequence)}, ` +
|
|
455
|
+
`${array(b.plus)}, ${array(b.minus)}, ` +
|
|
456
|
+
`${b.rate === null ? 'null' : number(b.rate)}, ${text(b.rate_of)}, ` +
|
|
457
|
+
`${b.floor_zero ? 'true' : 'false'}, ` +
|
|
458
|
+
`${b.hidden ? 'true' : 'false'}, ${text(b.xml_element)}, ${text(b.legal_reference)}, ` +
|
|
459
|
+
`${text(b.source)})`);
|
|
460
|
+
out.push('insert into tax_report_box_templates', ' (country, report_code, box, kind, name, name_i18n, sequence, print_sequence,', ' plus_boxes, minus_boxes, rate, rate_of_box, floor_zero, hidden, xml_element,', ' legal_reference, source_key)', 'values', values.join(',\n'), 'on conflict (country, report_code, box, kind) do update set', ' name = excluded.name,', ' name_i18n = excluded.name_i18n,', ' sequence = excluded.sequence,', ' print_sequence = excluded.print_sequence,', ' plus_boxes = excluded.plus_boxes,', ' minus_boxes = excluded.minus_boxes,', ' rate = excluded.rate,', ' rate_of_box = excluded.rate_of_box,', ' floor_zero = excluded.floor_zero,', ' hidden = excluded.hidden,', ' xml_element = excluded.xml_element,', ' legal_reference = excluded.legal_reference,', ' source_key = excluded.source_key;', '');
|
|
461
|
+
return out;
|
|
462
|
+
}
|
|
463
|
+
function defaults(pack, country) {
|
|
464
|
+
const { roles, journal_roles: journalRoles = {} } = pack.manifest.defaults;
|
|
465
|
+
const row = [
|
|
466
|
+
text(country),
|
|
467
|
+
text(pack.manifest.name),
|
|
468
|
+
json(pack.labels.pack_name),
|
|
469
|
+
// The pack's own language first: it is the one every label is written in,
|
|
470
|
+
// and an installer that offered the translations before it would put the
|
|
471
|
+
// country's own wording last on its own chart of accounts.
|
|
472
|
+
array([
|
|
473
|
+
...(pack.manifest.defaults.language === undefined ? [] : [pack.manifest.defaults.language]),
|
|
474
|
+
...(pack.manifest.languages ?? []),
|
|
475
|
+
]),
|
|
476
|
+
text(pack.manifest.defaults.currency),
|
|
477
|
+
text(roles['receivable'] ?? null),
|
|
478
|
+
text(roles['payable'] ?? null),
|
|
479
|
+
text(roles['suspense'] ?? null),
|
|
480
|
+
text(roles['rounding'] ?? null),
|
|
481
|
+
text(roles['retained_earnings'] ?? null),
|
|
482
|
+
text(roles['sales'] ?? null),
|
|
483
|
+
text(roles['purchase'] ?? null),
|
|
484
|
+
text(roles['bank'] ?? null),
|
|
485
|
+
text(roles['cash'] ?? null),
|
|
486
|
+
text(journalRoles['sales'] ?? 'SAL'),
|
|
487
|
+
text(journalRoles['purchase'] ?? 'PUR'),
|
|
488
|
+
text(journalRoles['miscellaneous'] ?? 'MISC'),
|
|
489
|
+
text(pack.manifest.defaults.language ?? null),
|
|
490
|
+
// No fallback: a pack that says nothing about closing writes null, and
|
|
491
|
+
// close_fiscal_year refuses by name. A default here would be one
|
|
492
|
+
// country's mechanism given to every country that has not spoken.
|
|
493
|
+
text(pack.manifest.defaults['closing_style'] ?? null),
|
|
494
|
+
text(roles['current_year_result_profit'] ?? null),
|
|
495
|
+
text(roles['current_year_result_loss'] ?? null),
|
|
496
|
+
text(roles['retained_earnings_loss'] ?? null),
|
|
497
|
+
text(journalRoles['opening'] ?? null),
|
|
498
|
+
// The same rule for rounding, which has a column default where closing has
|
|
499
|
+
// none: the pack's value, or `default` so the column decides. Writing one
|
|
500
|
+
// here would make the CLI a second place where a country model lives.
|
|
501
|
+
defaulted(pack.manifest.defaults['rounding_method'], text),
|
|
502
|
+
defaulted(pack.manifest.defaults['cash_rounding_unit'], number),
|
|
503
|
+
// Where a realised exchange difference lands. No fallback, for the reason
|
|
504
|
+
// above: an account number is a fact about a chart.
|
|
505
|
+
text(roles['fx_gain'] ?? null),
|
|
506
|
+
text(roles['fx_loss'] ?? null),
|
|
507
|
+
// Where the disposal of a fixed asset lands. Four, because two countries
|
|
508
|
+
// present a disposal differently and neither is a variant of the other:
|
|
509
|
+
// one gain-or-loss line, or the value sold and the proceeds in full. Which
|
|
510
|
+
// of the two a country follows is in `assets.json`, with the rest of that
|
|
511
|
+
// module's country model; these are the accounts, and an account that
|
|
512
|
+
// plays a part is a role, so it is named where every other role is.
|
|
513
|
+
text(roles['asset_disposal_gain'] ?? null),
|
|
514
|
+
text(roles['asset_disposal_loss'] ?? null),
|
|
515
|
+
text(roles['asset_disposal_proceeds'] ?? null),
|
|
516
|
+
text(roles['asset_disposal_value'] ?? null),
|
|
517
|
+
// Where a declared period lands once its tax accounts are cleared: what is
|
|
518
|
+
// owed to the administration, and — where the chart keeps the two apart —
|
|
519
|
+
// what it owes back. No fallback for the same reason as every other
|
|
520
|
+
// account code, and the second one falls back to the first inside the
|
|
521
|
+
// database, for a chart that keeps one control account for both signs.
|
|
522
|
+
text(roles['tax_payable'] ?? null),
|
|
523
|
+
text(roles['tax_receivable'] ?? null),
|
|
524
|
+
// The wording the computed opening lines of an export carry, in the
|
|
525
|
+
// language the administration of this country reads. No fallback here
|
|
526
|
+
// either, but for the opposite reason to an account code: the schema keeps
|
|
527
|
+
// a neutral one, because a format that fixes no wording has no wrong
|
|
528
|
+
// answer to give, and a missing label is not a reason to refuse a file.
|
|
529
|
+
text(pack.manifest.defaults['opening_entry_label'] ?? null),
|
|
530
|
+
// How often a company of this country files its periodic return, when the
|
|
531
|
+
// law gives one answer for everybody. Deprecated by
|
|
532
|
+
// `tax_report_templates.period_default` and written from it, so the two can
|
|
533
|
+
// never disagree: the proposal is stated once, on the form, and this column
|
|
534
|
+
// carries a copy for the readers that already have it. Null where the law
|
|
535
|
+
// makes the cadence depend on a fact about the company — `ekwo init` asks
|
|
536
|
+
// rather than this file choosing.
|
|
537
|
+
enumeration(pack.report?.period_default ?? null, 'declaration_period'),
|
|
538
|
+
];
|
|
539
|
+
return [
|
|
540
|
+
'insert into country_defaults',
|
|
541
|
+
' (country, name, name_i18n, languages, currency_code, receivable_code, payable_code, suspense_code,',
|
|
542
|
+
' rounding_code, retained_earnings_code, sales_account_code, purchase_account_code,',
|
|
543
|
+
' bank_account_code, cash_account_code, sales_journal_code, purchase_journal_code,',
|
|
544
|
+
' misc_journal_code, language_default, closing_style, current_year_result_profit_code,',
|
|
545
|
+
' current_year_result_loss_code, retained_earnings_loss_code, opening_journal_code,',
|
|
546
|
+
' rounding_method, cash_rounding_unit, fx_gain_code, fx_loss_code,',
|
|
547
|
+
' asset_disposal_gain_code, asset_disposal_loss_code,',
|
|
548
|
+
' asset_disposal_proceeds_code, asset_disposal_value_code,',
|
|
549
|
+
' tax_payable_code, tax_receivable_code, opening_entry_label,',
|
|
550
|
+
' vat_period_default)',
|
|
551
|
+
'values',
|
|
552
|
+
` (${row.join(', ')})`,
|
|
553
|
+
'on conflict (country) do update set',
|
|
554
|
+
' name = excluded.name,',
|
|
555
|
+
' name_i18n = excluded.name_i18n,',
|
|
556
|
+
' languages = excluded.languages,',
|
|
557
|
+
' currency_code = excluded.currency_code,',
|
|
558
|
+
' receivable_code = excluded.receivable_code,',
|
|
559
|
+
' payable_code = excluded.payable_code,',
|
|
560
|
+
' suspense_code = excluded.suspense_code,',
|
|
561
|
+
' rounding_code = excluded.rounding_code,',
|
|
562
|
+
' retained_earnings_code = excluded.retained_earnings_code,',
|
|
563
|
+
' sales_account_code = excluded.sales_account_code,',
|
|
564
|
+
' purchase_account_code = excluded.purchase_account_code,',
|
|
565
|
+
' bank_account_code = excluded.bank_account_code,',
|
|
566
|
+
' cash_account_code = excluded.cash_account_code,',
|
|
567
|
+
' sales_journal_code = excluded.sales_journal_code,',
|
|
568
|
+
' purchase_journal_code = excluded.purchase_journal_code,',
|
|
569
|
+
' misc_journal_code = excluded.misc_journal_code,',
|
|
570
|
+
' language_default = excluded.language_default,',
|
|
571
|
+
' closing_style = excluded.closing_style,',
|
|
572
|
+
' current_year_result_profit_code = excluded.current_year_result_profit_code,',
|
|
573
|
+
' current_year_result_loss_code = excluded.current_year_result_loss_code,',
|
|
574
|
+
' retained_earnings_loss_code = excluded.retained_earnings_loss_code,',
|
|
575
|
+
' opening_journal_code = excluded.opening_journal_code,',
|
|
576
|
+
' rounding_method = excluded.rounding_method,',
|
|
577
|
+
' cash_rounding_unit = excluded.cash_rounding_unit,',
|
|
578
|
+
' fx_gain_code = excluded.fx_gain_code,',
|
|
579
|
+
' fx_loss_code = excluded.fx_loss_code,',
|
|
580
|
+
' asset_disposal_gain_code = excluded.asset_disposal_gain_code,',
|
|
581
|
+
' asset_disposal_loss_code = excluded.asset_disposal_loss_code,',
|
|
582
|
+
' asset_disposal_proceeds_code = excluded.asset_disposal_proceeds_code,',
|
|
583
|
+
' asset_disposal_value_code = excluded.asset_disposal_value_code,',
|
|
584
|
+
' tax_payable_code = excluded.tax_payable_code,',
|
|
585
|
+
' tax_receivable_code = excluded.tax_receivable_code,',
|
|
586
|
+
' opening_entry_label = excluded.opening_entry_label,',
|
|
587
|
+
' vat_period_default = excluded.vat_period_default;',
|
|
588
|
+
];
|
|
589
|
+
}
|
|
590
|
+
/**
|
|
591
|
+
* What a country requires on a document, how the document is exchanged, and
|
|
592
|
+
* the formats its banks speak: twenty columns of `country_defaults` and the
|
|
593
|
+
* sentences of `legal_mention_templates`.
|
|
594
|
+
*
|
|
595
|
+
* Eight of the twenty are citations — the article behind the numbering, the
|
|
596
|
+
* payment term, the tax point and the e-invoicing profile, and the register
|
|
597
|
+
* key each of them is read at. They ride in the same update as the rule they
|
|
598
|
+
* belong to, because a rule and the text that imposes it going into the
|
|
599
|
+
* database by two different routes is how one of them gets left behind.
|
|
600
|
+
*
|
|
601
|
+
* An `update` rather than a second `insert`. The row exists — `defaults()`
|
|
602
|
+
* writes it immediately above — and an insert would have to restate the name,
|
|
603
|
+
* the currency and the two account roles that its not-null columns need, which
|
|
604
|
+
* is the same values twice in one generated file. The update touches only the
|
|
605
|
+
* columns this section owns, so it stays independent of the row above, and a
|
|
606
|
+
* pack that declares nothing writes null where null already was.
|
|
607
|
+
*
|
|
608
|
+
* None of these columns has a default, so `null` is written literally rather
|
|
609
|
+
* than the `default` keyword the rounding columns use: there is nothing for
|
|
610
|
+
* the schema to decide.
|
|
611
|
+
*/
|
|
612
|
+
function documentRules(pack, country) {
|
|
613
|
+
const rules = pack.documents;
|
|
614
|
+
const out = [
|
|
615
|
+
'',
|
|
616
|
+
'update country_defaults set',
|
|
617
|
+
` numbering_gapless = ${orNull(rules.numbering_gapless, bool)},`,
|
|
618
|
+
` number_format = ${text(rules.number_format)},`,
|
|
619
|
+
` legal_payment_days = ${orNull(rules.legal_payment_days, number)},`,
|
|
620
|
+
` late_payment_reference = ${text(rules.late_payment_reference)},`,
|
|
621
|
+
` numbering_legal_reference = ${text(rules.numbering_reference.legal_reference)},`,
|
|
622
|
+
` numbering_source_key = ${text(rules.numbering_reference.source)},`,
|
|
623
|
+
` payment_terms_legal_reference = ${text(rules.payment_terms_reference.legal_reference)},`,
|
|
624
|
+
` payment_terms_source_key = ${text(rules.payment_terms_reference.source)},`,
|
|
625
|
+
` tax_point_rule = ${text(rules.tax_point_rule)},`,
|
|
626
|
+
` tax_point_legal_reference = ${text(rules.tax_point_reference.legal_reference)},`,
|
|
627
|
+
` tax_point_source_key = ${text(rules.tax_point_reference.source)},`,
|
|
628
|
+
` einvoice_profile = ${text(rules.einvoice_profile)},`,
|
|
629
|
+
` einvoice_mandatory_from = ${date(rules.einvoice_mandatory_from)},`,
|
|
630
|
+
` einvoice_legal_reference = ${text(rules.einvoice_reference.legal_reference)},`,
|
|
631
|
+
` einvoice_source_key = ${text(rules.einvoice_reference.source)},`,
|
|
632
|
+
` party_scheme = ${text(rules.party_scheme)},`,
|
|
633
|
+
` vat_scheme = ${text(rules.vat_scheme)},`,
|
|
634
|
+
` bank_statement_formats = ${listOrNull(rules.bank_statement_formats)},`,
|
|
635
|
+
` payment_formats = ${listOrNull(rules.payment_formats)},`,
|
|
636
|
+
` fiscal_year_default = ${text(rules.fiscal_year_default)}`,
|
|
637
|
+
` where country = ${text(country)};`,
|
|
638
|
+
];
|
|
639
|
+
if (rules.mentions.length === 0)
|
|
640
|
+
return out;
|
|
641
|
+
const mentions = [...rules.mentions].sort((a, b) => a.sequence - b.sequence || (a.code < b.code ? -1 : 1));
|
|
642
|
+
const values = mentions.map((m) => ` (${text(country)}, ${text(m.code)}, ${text(m.applies_when)}, ${text(m.text)}, ` +
|
|
643
|
+
`${json(m.text_i18n)}, ${m.sequence}, ${date(m.valid_from)}, ${date(m.valid_to)}, ` +
|
|
644
|
+
`${text(m.legal_reference)})`);
|
|
645
|
+
out.push('', 'insert into legal_mention_templates', ' (country, code, applies_when, text, text_i18n, sequence, valid_from, valid_to, legal_reference)', 'values', values.join(',\n'), 'on conflict (country, code) do update set', ' applies_when = excluded.applies_when,', ' text = excluded.text,', ' text_i18n = excluded.text_i18n,', ' sequence = excluded.sequence,', ' valid_from = excluded.valid_from,', ' valid_to = excluded.valid_to,', ' legal_reference = excluded.legal_reference;');
|
|
646
|
+
return out;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* `country_packs`: which pack this installation holds, and how much anyone
|
|
650
|
+
* has read it. `ekwo init` prints the certification status, `ekwo status`
|
|
651
|
+
* compares this version to what each company copied.
|
|
652
|
+
*/
|
|
653
|
+
function manifestRow(pack, country) {
|
|
654
|
+
const { manifest } = pack;
|
|
655
|
+
const certification = manifest.certification;
|
|
656
|
+
const row = [
|
|
657
|
+
text(country),
|
|
658
|
+
text(manifest.name),
|
|
659
|
+
text(manifest.version),
|
|
660
|
+
date(manifest.released_at ?? null),
|
|
661
|
+
text(manifest.schema_min),
|
|
662
|
+
text(certification?.status ?? 'community'),
|
|
663
|
+
text(certification?.by ?? null),
|
|
664
|
+
date(certification?.on ?? null),
|
|
665
|
+
text(pack.checksum),
|
|
666
|
+
// The register, as the pack declares it and in that order. A bare title of
|
|
667
|
+
// the deprecated form carries no link and reaches no column: what an
|
|
668
|
+
// application shows under "where do these rules come from" is a list of
|
|
669
|
+
// things a reader can open, or it is nothing.
|
|
670
|
+
register(sourcesOf(certification)),
|
|
671
|
+
];
|
|
672
|
+
return [
|
|
673
|
+
'insert into country_packs',
|
|
674
|
+
' (country, name, version, released_at, schema_min, certification_status,',
|
|
675
|
+
' certified_by, certified_at, checksum, sources)',
|
|
676
|
+
'values',
|
|
677
|
+
` (${row.join(', ')})`,
|
|
678
|
+
'on conflict (country) do update set',
|
|
679
|
+
' name = excluded.name,',
|
|
680
|
+
' version = excluded.version,',
|
|
681
|
+
' released_at = excluded.released_at,',
|
|
682
|
+
' schema_min = excluded.schema_min,',
|
|
683
|
+
' certification_status = excluded.certification_status,',
|
|
684
|
+
' certified_by = excluded.certified_by,',
|
|
685
|
+
' certified_at = excluded.certified_at,',
|
|
686
|
+
' checksum = excluded.checksum,',
|
|
687
|
+
' sources = excluded.sources;',
|
|
688
|
+
'',
|
|
689
|
+
];
|
|
690
|
+
}
|
|
691
|
+
/**
|
|
692
|
+
* One text of the register, in the header of the seed it produced.
|
|
693
|
+
*
|
|
694
|
+
* Two lines rather than one, because the link is the half a reader of the
|
|
695
|
+
* generated SQL cannot reconstruct: an operator who opens a seed to find out
|
|
696
|
+
* what their installation believes about their country should be able to go
|
|
697
|
+
* and read the law it came from without leaving the file.
|
|
698
|
+
*/
|
|
699
|
+
function citation(source) {
|
|
700
|
+
if (typeof source === 'string')
|
|
701
|
+
return [`-- ${source}`];
|
|
702
|
+
return [`-- ${source.title} (${source.publisher})`, `-- ${source.url}`];
|
|
703
|
+
}
|
|
704
|
+
function capitalise(text) {
|
|
705
|
+
return text.charAt(0).toUpperCase() + text.slice(1);
|
|
706
|
+
}
|
|
707
|
+
function byCode(a, b) {
|
|
708
|
+
return a.code < b.code ? -1 : a.code > b.code ? 1 : 0;
|
|
709
|
+
}
|
|
710
|
+
function text(value) {
|
|
711
|
+
if (value === null || value === undefined)
|
|
712
|
+
return 'null';
|
|
713
|
+
return `'${value.replace(/'/g, "''")}'`;
|
|
714
|
+
}
|
|
715
|
+
function date(value) {
|
|
716
|
+
return value === null || value === undefined ? 'null' : `date '${value}'`;
|
|
717
|
+
}
|
|
718
|
+
function number(value) {
|
|
719
|
+
return value === null || value === undefined ? 'null' : String(value);
|
|
720
|
+
}
|
|
721
|
+
/** A `text[]` literal, empty included, in the order the pack wrote it. */
|
|
722
|
+
/**
|
|
723
|
+
* The cadences a form is filed on. Its own helper rather than `array()`,
|
|
724
|
+
* because the column is an enum array and a text array will not cast itself
|
|
725
|
+
* into one silently.
|
|
726
|
+
*/
|
|
727
|
+
function periods(values) {
|
|
728
|
+
return `array[${values.map((value) => text(value)).join(', ')}]::declaration_period[]`;
|
|
729
|
+
}
|
|
730
|
+
function array(values) {
|
|
731
|
+
if (values.length === 0)
|
|
732
|
+
return `'{}'::text[]`;
|
|
733
|
+
return `array[${values.map((value) => text(value)).join(', ')}]::text[]`;
|
|
734
|
+
}
|
|
735
|
+
/** What a tax turns on, as the closed vocabulary the column is typed on. */
|
|
736
|
+
function conditions(values) {
|
|
737
|
+
if (values.length === 0)
|
|
738
|
+
return `'{}'::tax_condition[]`;
|
|
739
|
+
return `array[${values.map((value) => text(value)).join(', ')}]::tax_condition[]`;
|
|
740
|
+
}
|
|
741
|
+
/** A value of a PostgreSQL enum, or a null the column keeps as one. */
|
|
742
|
+
function enumeration(value, type) {
|
|
743
|
+
return value === null ? 'null' : `${text(value)}::${type}`;
|
|
744
|
+
}
|
|
745
|
+
function bool(value) {
|
|
746
|
+
return value ? 'true' : 'false';
|
|
747
|
+
}
|
|
748
|
+
/**
|
|
749
|
+
* The pack's value, or a literal `null`. For a column with no default, which
|
|
750
|
+
* is every column this release adds: nothing for the schema to fall back on,
|
|
751
|
+
* so "the pack said nothing" is written as the absence it is.
|
|
752
|
+
*/
|
|
753
|
+
function orNull(value, render) {
|
|
754
|
+
return value === null || value === undefined ? 'null' : render(value);
|
|
755
|
+
}
|
|
756
|
+
/**
|
|
757
|
+
* A `text[]` of formats, or null. An empty list and a missing section say the
|
|
758
|
+
* same thing here — this country has told us nothing about its banks — and
|
|
759
|
+
* one of them has to reach the column, so it is null rather than `{}`, which
|
|
760
|
+
* would read as "explicitly no format at all".
|
|
761
|
+
*/
|
|
762
|
+
function listOrNull(values) {
|
|
763
|
+
return values.length === 0 ? 'null' : array(values);
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* The value the pack declared, or the SQL keyword that lets the column decide.
|
|
767
|
+
* `default` in a `values` row is also what `excluded` carries into the upsert,
|
|
768
|
+
* so a pack that stops declaring something goes back to the schema's answer
|
|
769
|
+
* rather than keeping the last one it was given.
|
|
770
|
+
*/
|
|
771
|
+
function defaulted(value, render) {
|
|
772
|
+
return value === null || value === undefined ? 'default' : render(value);
|
|
773
|
+
}
|
|
774
|
+
/** A jsonb literal, with its keys in a fixed order so the output is stable. */
|
|
775
|
+
/**
|
|
776
|
+
* The source register, as one jsonb array.
|
|
777
|
+
*
|
|
778
|
+
* In the order the pack declares, not sorted: a register is a reading list,
|
|
779
|
+
* and the text a country's chart of accounts comes from belongs at the top of
|
|
780
|
+
* it. The fields are written out by name rather than stringified whole, so a
|
|
781
|
+
* field added to the format later is a field somebody decided to compile.
|
|
782
|
+
*/
|
|
783
|
+
function register(sources) {
|
|
784
|
+
const rows = sources.map((source) => ({
|
|
785
|
+
key: source.key,
|
|
786
|
+
title: source.title,
|
|
787
|
+
publisher: source.publisher,
|
|
788
|
+
url: source.url,
|
|
789
|
+
consulted_on: source.consulted_on,
|
|
790
|
+
kind: source.kind,
|
|
791
|
+
}));
|
|
792
|
+
return `${text(JSON.stringify(rows))}::jsonb`;
|
|
793
|
+
}
|
|
794
|
+
function json(value) {
|
|
795
|
+
const entries = Object.entries(value ?? {}).sort(([a], [b]) => (a < b ? -1 : 1));
|
|
796
|
+
const object = {};
|
|
797
|
+
for (const [key, item] of entries)
|
|
798
|
+
object[key] = item;
|
|
799
|
+
return `${text(JSON.stringify(object))}::jsonb`;
|
|
800
|
+
}
|
|
801
|
+
//# sourceMappingURL=compile.js.map
|