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