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,227 @@
|
|
|
1
|
+
-- Ekwo OS — what moved after it went.
|
|
2
|
+
--
|
|
3
|
+
-- A declaration leaves, and the period it covers stays open. That is not a
|
|
4
|
+
-- defect: a supplier invoice arrives late, an accountant adjusts an entry, a
|
|
5
|
+
-- correction lands. Every one of those is legitimate, and refusing them would
|
|
6
|
+
-- be refusing bookkeeping. **What is not legitimate is that nobody is told.**
|
|
7
|
+
--
|
|
8
|
+
-- D1 froze the figures and gave `filing_drift()` — box by box, what the ledger
|
|
9
|
+
-- says now against what was sent. This file adds the two things that were
|
|
10
|
+
-- missing around it:
|
|
11
|
+
--
|
|
12
|
+
-- filings_touched_since() which declarations the ledger disturbed after
|
|
13
|
+
-- they went, how many entries, and how many of
|
|
14
|
+
-- their figures moved
|
|
15
|
+
-- lock_filed_period() a company that wants the period shut says so
|
|
16
|
+
-- once, after the books for it are clear
|
|
17
|
+
--
|
|
18
|
+
-- and it makes the corrective settle **the difference**, which is the part D4
|
|
19
|
+
-- left named and undone.
|
|
20
|
+
--
|
|
21
|
+
-- On the lock: the database does not propose anything, it answers. The
|
|
22
|
+
-- proposal belongs to whatever is at the keyboard — the CLI, an agent, a
|
|
23
|
+
-- person — and what the schema owes it is a way of saying yes that cannot be
|
|
24
|
+
-- got wrong. So the lock is its own function, it moves forward only, and it
|
|
25
|
+
-- refuses while the period still has books to clear. A company that never
|
|
26
|
+
-- calls it keeps an open period and a reading that says what fell into it.
|
|
27
|
+
--
|
|
28
|
+
-- What is deliberately not here: **what a country does with a correction.**
|
|
29
|
+
-- Some want a full replacement return, some an adjustment carried on the next
|
|
30
|
+
-- period, most with a threshold below which nothing is filed at all. That is
|
|
31
|
+
-- pack data, nobody has read the texts yet, and a mechanism invented now would
|
|
32
|
+
-- be one country's habit given to five others. `supersede_filing()` does the
|
|
33
|
+
-- one thing that is true everywhere: what was sent stays sent, and the new
|
|
34
|
+
-- declaration points at it.
|
|
35
|
+
|
|
36
|
+
-- ---------------------------------------------------------------------------
|
|
37
|
+
-- filing_tax_movements, net of what an earlier settlement already carried
|
|
38
|
+
--
|
|
39
|
+
-- A corrective covers the same period as the declaration it replaces, and that
|
|
40
|
+
-- period's tax accounts were already cleared once. Left as it was, this would
|
|
41
|
+
-- hand `settle_filing()` the whole period again and book the debt twice.
|
|
42
|
+
--
|
|
43
|
+
-- So the movements of the window are netted against the settlement entries of
|
|
44
|
+
-- the declarations that came before it on the same period — restricted to the
|
|
45
|
+
-- accounts the window itself moved, which is what leaves out their
|
|
46
|
+
-- counterpart line on the debt account. What comes back is exactly what has
|
|
47
|
+
-- not been settled yet: nothing at all when nothing moved, and the difference
|
|
48
|
+
-- when something did.
|
|
49
|
+
-- ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
create or replace function filing_tax_movements(p_filing_id uuid)
|
|
52
|
+
returns table (account_id uuid, balance numeric)
|
|
53
|
+
language sql
|
|
54
|
+
stable
|
|
55
|
+
security invoker
|
|
56
|
+
as $$
|
|
57
|
+
with f as (
|
|
58
|
+
select * from tax_filings where id = p_filing_id
|
|
59
|
+
),
|
|
60
|
+
moved as (
|
|
61
|
+
select l.account_id, sum(l.balance) as balance
|
|
62
|
+
from f
|
|
63
|
+
join entry_lines l on l.company_id = f.company_id
|
|
64
|
+
join entries e on e.id = l.entry_id
|
|
65
|
+
where e.state = 'posted'
|
|
66
|
+
and l.tax_line
|
|
67
|
+
and coalesce(l.tax_point_date, e.entry_date) between f.period_start and f.period_end
|
|
68
|
+
group by l.account_id
|
|
69
|
+
),
|
|
70
|
+
already as (
|
|
71
|
+
select l.account_id, sum(l.balance) as balance
|
|
72
|
+
from f
|
|
73
|
+
join tax_filings p
|
|
74
|
+
on p.company_id = f.company_id
|
|
75
|
+
and p.report_code = f.report_code
|
|
76
|
+
and p.period_start = f.period_start
|
|
77
|
+
and p.period_end = f.period_end
|
|
78
|
+
and p.id <> f.id
|
|
79
|
+
and p.settlement_entry_id is not null
|
|
80
|
+
join entry_lines l on l.entry_id = p.settlement_entry_id
|
|
81
|
+
join moved m on m.account_id = l.account_id
|
|
82
|
+
group by l.account_id
|
|
83
|
+
)
|
|
84
|
+
select m.account_id,
|
|
85
|
+
round_amount(m.balance + coalesce(a.balance, 0), rounding_of(f.company_id))
|
|
86
|
+
from f, moved m
|
|
87
|
+
left join already a on a.account_id = m.account_id
|
|
88
|
+
where round_amount(m.balance + coalesce(a.balance, 0), rounding_of(f.company_id)) <> 0;
|
|
89
|
+
$$;
|
|
90
|
+
|
|
91
|
+
comment on function filing_tax_movements(uuid) is
|
|
92
|
+
'The tax accounts a declared period moved and by how much, on the same window and the same tax-point rule the return read, net of what an earlier settlement of the same period already carried. What settle_filing() clears — the whole period the first time, the difference on a corrective — and what anybody can read before it does.';
|
|
93
|
+
|
|
94
|
+
revoke execute on function filing_tax_movements(uuid) from public, anon;
|
|
95
|
+
grant execute on function filing_tax_movements(uuid) to authenticated, service_role;
|
|
96
|
+
|
|
97
|
+
-- ---------------------------------------------------------------------------
|
|
98
|
+
-- filings_touched_since
|
|
99
|
+
--
|
|
100
|
+
-- The reading the whole freeze was for. A declaration is listed when the
|
|
101
|
+
-- ledger moved inside its period **after** it was filed — not when the figures
|
|
102
|
+
-- happen to differ, which is `filing_drift()`'s question, because an entry
|
|
103
|
+
-- that nets to nothing in every box is still an entry somebody posted into a
|
|
104
|
+
-- period that had gone.
|
|
105
|
+
--
|
|
106
|
+
-- What counts as moving it is a line that names a box, which is the same test
|
|
107
|
+
-- the tax lock uses to decide what it protects. Two readings of "an entry
|
|
108
|
+
-- that concerns the declaration" would eventually disagree, and a payroll
|
|
109
|
+
-- entry landing in a declared quarter is not news. It also leaves out the
|
|
110
|
+
-- settlement of the declaration itself, which is posted at the end of the
|
|
111
|
+
-- period and after the filing, and is the opposite of a disturbance.
|
|
112
|
+
--
|
|
113
|
+
-- Read-only, and it names nothing to do: whether a change is a corrective to
|
|
114
|
+
-- file, an entry in the wrong period, or a legitimate movement that changes no
|
|
115
|
+
-- figure, is a judgement, and the database's job is to stop it being invisible.
|
|
116
|
+
-- ---------------------------------------------------------------------------
|
|
117
|
+
|
|
118
|
+
create or replace function filings_touched_since(
|
|
119
|
+
p_company_id uuid,
|
|
120
|
+
p_from date default null,
|
|
121
|
+
p_to date default null
|
|
122
|
+
)
|
|
123
|
+
returns table (
|
|
124
|
+
filing_id uuid,
|
|
125
|
+
report_code text,
|
|
126
|
+
period_start date,
|
|
127
|
+
period_end date,
|
|
128
|
+
state tax_filing_state,
|
|
129
|
+
filed_at timestamptz,
|
|
130
|
+
entries integer,
|
|
131
|
+
last_entry_at timestamptz,
|
|
132
|
+
boxes_moved integer
|
|
133
|
+
)
|
|
134
|
+
language sql
|
|
135
|
+
stable
|
|
136
|
+
security invoker
|
|
137
|
+
as $$
|
|
138
|
+
select f.id, f.report_code, f.period_start, f.period_end, f.state, f.filed_at,
|
|
139
|
+
t.entries::integer, t.last_entry_at,
|
|
140
|
+
(select count(*) from filing_drift(f.id))::integer
|
|
141
|
+
from tax_filings f
|
|
142
|
+
cross join lateral (
|
|
143
|
+
select count(distinct e.id) as entries, max(e.posted_at) as last_entry_at
|
|
144
|
+
from entry_lines l
|
|
145
|
+
join entries e on e.id = l.entry_id
|
|
146
|
+
where l.company_id = f.company_id
|
|
147
|
+
and e.state = 'posted'
|
|
148
|
+
and e.posted_at > f.filed_at
|
|
149
|
+
and l.declaration_box is not null
|
|
150
|
+
and coalesce(l.tax_point_date, e.entry_date) between f.period_start and f.period_end
|
|
151
|
+
) t
|
|
152
|
+
where f.company_id = p_company_id
|
|
153
|
+
and f.filed_at is not null
|
|
154
|
+
and f.state <> 'superseded'
|
|
155
|
+
and (p_from is null or f.period_end >= p_from)
|
|
156
|
+
and (p_to is null or f.period_start <= p_to)
|
|
157
|
+
and t.entries > 0
|
|
158
|
+
order by f.period_start, f.report_code;
|
|
159
|
+
$$;
|
|
160
|
+
|
|
161
|
+
comment on function filings_touched_since(uuid, date, date) is
|
|
162
|
+
'Declarations that have gone and whose period the ledger moved afterwards: how many entries carrying a declaration box landed in it, when the last one did, and how many of the filed figures now disagree. An entry that changes no figure is still listed — it was posted into a period that had been declared, and that is the fact being reported.';
|
|
163
|
+
|
|
164
|
+
revoke execute on function filings_touched_since(uuid, date, date) from public, anon;
|
|
165
|
+
grant execute on function filings_touched_since(uuid, date, date) to authenticated, service_role;
|
|
166
|
+
|
|
167
|
+
-- ---------------------------------------------------------------------------
|
|
168
|
+
-- lock_filed_period
|
|
169
|
+
--
|
|
170
|
+
-- The other half of "signalled, not refused": a company that does want the
|
|
171
|
+
-- period shut says so, once, and the schema makes saying it safe.
|
|
172
|
+
--
|
|
173
|
+
-- It is a function of its own and not a flag on `file_filing()`, and the test
|
|
174
|
+
-- is what settled that. The tax lock is checked by `post_entry()` for every
|
|
175
|
+
-- entry it posts, whatever the entry names — so locking at the moment of
|
|
176
|
+
-- filing locks the declaration's own settlement out of its period, and the
|
|
177
|
+
-- books can never be cleared. The order that works is the order the work
|
|
178
|
+
-- actually has: file, hear back, settle, then shut.
|
|
179
|
+
--
|
|
180
|
+
-- So this refuses to shut a period whose settlement is still possible and not
|
|
181
|
+
-- done, and says which. It moves the lock forward only: a company filing an
|
|
182
|
+
-- old period late did not ask to reopen everything it had already closed.
|
|
183
|
+
-- ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
create or replace function lock_filed_period(p_filing_id uuid)
|
|
186
|
+
returns date
|
|
187
|
+
language plpgsql
|
|
188
|
+
volatile
|
|
189
|
+
security invoker
|
|
190
|
+
as $$
|
|
191
|
+
declare
|
|
192
|
+
v_filing tax_filings;
|
|
193
|
+
v_lock date;
|
|
194
|
+
begin
|
|
195
|
+
select * into v_filing from tax_filings f where f.id = p_filing_id;
|
|
196
|
+
if not found then
|
|
197
|
+
raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
|
|
198
|
+
end if;
|
|
199
|
+
if v_filing.state in ('draft', 'ready') then
|
|
200
|
+
raise exception 'filing_not_sent: a period is shut behind a declaration that has gone, and this one is %',
|
|
201
|
+
v_filing.state;
|
|
202
|
+
end if;
|
|
203
|
+
if v_filing.state = 'superseded' then
|
|
204
|
+
raise exception 'filing_superseded: % was replaced, and it is the declaration that replaced it that shuts the period',
|
|
205
|
+
p_filing_id;
|
|
206
|
+
end if;
|
|
207
|
+
|
|
208
|
+
if v_filing.settlement_entry_id is null
|
|
209
|
+
and exists (select 1 from filing_tax_movements(p_filing_id)) then
|
|
210
|
+
raise exception 'settle_first: this period still has tax accounts to clear, and the lock would refuse the entry that clears them';
|
|
211
|
+
end if;
|
|
212
|
+
|
|
213
|
+
update companies
|
|
214
|
+
set tax_lock_date = v_filing.period_end
|
|
215
|
+
where id = v_filing.company_id
|
|
216
|
+
and (tax_lock_date is null or tax_lock_date < v_filing.period_end);
|
|
217
|
+
|
|
218
|
+
select c.tax_lock_date into v_lock from companies c where c.id = v_filing.company_id;
|
|
219
|
+
return v_lock;
|
|
220
|
+
end;
|
|
221
|
+
$$;
|
|
222
|
+
|
|
223
|
+
comment on function lock_filed_period(uuid) is
|
|
224
|
+
'Carries the tax lock to the end of the period a declaration covers, so nothing more falls into it. Forward only, refused on a declaration that has not gone, and refused while the period still has tax accounts to clear — because the lock would then refuse the very entry that clears them. Returns the lock date the company ends up with. Nobody is obliged to call it: a period left open is a period filings_touched_since() reports on.';
|
|
225
|
+
|
|
226
|
+
revoke execute on function lock_filed_period(uuid) from public, anon;
|
|
227
|
+
grant execute on function lock_filed_period(uuid) to authenticated, service_role;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- Ekwo OS — a form names the file it is deposited as.
|
|
2
|
+
--
|
|
3
|
+
-- Seven bricks write legal files today and not one of them writes a periodic
|
|
4
|
+
-- VAT return: `intra-consignment`, `des`, `vd` and `ecdf` carry recapitulative
|
|
5
|
+
-- statements, `xbrl-cbso` the annual accounts, `fec` and `factur-x` are
|
|
6
|
+
-- neither. The declaration a company actually files every month — the one D1
|
|
7
|
+
-- freezes and D4 settles — existed here as figures and never as a file.
|
|
8
|
+
--
|
|
9
|
+
-- `@ekwo-ai/vat-consignment` is the first, for the XML Intervat takes. This
|
|
10
|
+
-- column is what says a form has one, and it is a **name of a format** and not
|
|
11
|
+
-- of a country: the day two countries deposit the same shape of file, they
|
|
12
|
+
-- name the same brick, and the day one country changes form it names another.
|
|
13
|
+
--
|
|
14
|
+
-- Null is the ordinary answer, for now and on purpose. A form nobody can write
|
|
15
|
+
-- is still a form a company files by hand on a portal, and a column that had
|
|
16
|
+
-- to be filled would be filled with something invented.
|
|
17
|
+
--
|
|
18
|
+
-- What is not here: **the guard that refuses a pack naming a format nobody can
|
|
19
|
+
-- write.** It belongs with the one owed to `bank_statement_formats` and
|
|
20
|
+
-- `payment_formats`, which are declared with no reader for the same reason,
|
|
21
|
+
-- and it is one guard over the three of them rather than three.
|
|
22
|
+
|
|
23
|
+
alter table tax_report_templates
|
|
24
|
+
add column if not exists file_format text;
|
|
25
|
+
|
|
26
|
+
comment on column tax_report_templates.file_format is
|
|
27
|
+
'The file this form is deposited as, by the name of the brick that writes it — vat-consignment for the Belgian XML Intervat takes. Named after the format and never after the country. Null where no brick writes the form, which is most of them: filing by hand on a portal is how it is done until one exists.';
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
-- Ekwo OS — a deposit, and what came back.
|
|
2
|
+
--
|
|
3
|
+
-- Filing was one act with one date: `filed_at`, `reference`, and a state that
|
|
4
|
+
-- went `filed` then `accepted` or `rejected`. That is the happy path and it is
|
|
5
|
+
-- the only one the schema could describe. A declaration is refused for a
|
|
6
|
+
-- malformed file, for a certificate that expired mid-session, for a figure the
|
|
7
|
+
-- administration disputes — and then it is **sent again**, which the schema had
|
|
8
|
+
-- no way of saying: `file_filing()` refused everything that was not a draft, so
|
|
9
|
+
-- a rejected declaration was a dead end whose only exit was a corrective. A
|
|
10
|
+
-- corrective replaces a declaration the administration **accepted**. One it
|
|
11
|
+
-- never received has nothing to correct.
|
|
12
|
+
--
|
|
13
|
+
-- So a deposit becomes an object of its own:
|
|
14
|
+
--
|
|
15
|
+
-- tax_filing_deposits one row per send, with what came back
|
|
16
|
+
-- file_filing() records a send, and accepts a rejected declaration
|
|
17
|
+
-- reopen_filing() a rejected declaration goes back to draft to be redone
|
|
18
|
+
--
|
|
19
|
+
-- **Why this is in the open core.** Sending the file is the operated side —
|
|
20
|
+
-- credentials, a certificate, a portal session, somebody answerable when a
|
|
21
|
+
-- return is late — and it stays in `ee/`, on the same line as the bank. What
|
|
22
|
+
-- comes back does not: the deposit number, the acknowledgement, the words the
|
|
23
|
+
-- administration used, and the file that was sent are the company's proof that
|
|
24
|
+
-- it filed. A company that stops paying for the transmission keeps every one of
|
|
25
|
+
-- them, in its own database, under the same policies as the rest of its books.
|
|
26
|
+
-- That is the whole argument for installing this.
|
|
27
|
+
--
|
|
28
|
+
-- The channel is two words and not a list of providers. `portal` is a person
|
|
29
|
+
-- uploading the file themselves, which is complete and free and is how most
|
|
30
|
+
-- installations will do it; `service` is a transmission held by somebody, whose
|
|
31
|
+
-- name is recorded as text. A closed list of providers here would be the
|
|
32
|
+
-- coupling this project refuses — one interface, one provider at a time, and
|
|
33
|
+
-- the core never knows which.
|
|
34
|
+
|
|
35
|
+
create type filing_channel as enum ('portal', 'service');
|
|
36
|
+
|
|
37
|
+
comment on type filing_channel is
|
|
38
|
+
'How a declaration was sent: portal — a person uploaded it themselves — or service, a transmission somebody operates. Two words, because the core describes the act and never the provider.';
|
|
39
|
+
|
|
40
|
+
create table tax_filing_deposits (
|
|
41
|
+
id uuid primary key default gen_random_uuid(),
|
|
42
|
+
filing_id uuid not null references tax_filings(id) on delete cascade,
|
|
43
|
+
-- 1, 2, 3: a rejected declaration is sent again, and each send is a row.
|
|
44
|
+
sequence integer not null,
|
|
45
|
+
channel filing_channel not null default 'portal',
|
|
46
|
+
-- The name of the service, where one was used. Free text on purpose: the
|
|
47
|
+
-- core records what was used and never holds a list of what may be.
|
|
48
|
+
service text,
|
|
49
|
+
sent_at timestamptz not null default now(),
|
|
50
|
+
sent_by uuid,
|
|
51
|
+
-- What the administration gave back for **this** send.
|
|
52
|
+
reference text,
|
|
53
|
+
outcome tax_filing_state,
|
|
54
|
+
outcome_at timestamptz,
|
|
55
|
+
-- Its own words. A refusal summarised is a refusal half-read, and the
|
|
56
|
+
-- sentence is what a person needs to know what to change.
|
|
57
|
+
message text,
|
|
58
|
+
-- The two files of a deposit, where they were kept: what was sent, and the
|
|
59
|
+
-- receipt. Both are rows of `attachments` on this declaration; naming them
|
|
60
|
+
-- here is what tells them apart.
|
|
61
|
+
sent_file_id uuid references attachments(id) on delete set null,
|
|
62
|
+
acknowledgement_id uuid references attachments(id) on delete set null,
|
|
63
|
+
created_at timestamptz not null default now(),
|
|
64
|
+
unique (filing_id, sequence),
|
|
65
|
+
constraint tax_filing_deposits_outcome check (
|
|
66
|
+
outcome is null or outcome in ('accepted', 'rejected', 'paid')
|
|
67
|
+
),
|
|
68
|
+
constraint tax_filing_deposits_outcome_has_a_date check (
|
|
69
|
+
(outcome is null) = (outcome_at is null)
|
|
70
|
+
)
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
comment on table tax_filing_deposits is
|
|
74
|
+
'One row per send of a declaration, with what came back: the deposit number, the outcome, the administration''s own words, and the two files — what was sent and the receipt. A rejected declaration is sent again, so a declaration has as many deposits as it took.';
|
|
75
|
+
comment on column tax_filing_deposits.service is
|
|
76
|
+
'The name of the transmission service, where one was used. Free text: the core records what was used and holds no list of what may be, which is what keeps it uncoupled from any provider.';
|
|
77
|
+
comment on column tax_filing_deposits.message is
|
|
78
|
+
'What the administration answered, in its own words. Not summarised: a refusal is read to know what to change.';
|
|
79
|
+
comment on column tax_filing_deposits.sent_file_id is
|
|
80
|
+
'The file that was sent, kept as an attachment of this declaration. Keeping it is what makes reopening a rejected declaration lossless — the core makes it possible and cannot impose it.';
|
|
81
|
+
|
|
82
|
+
create index tax_filing_deposits_filing_idx on tax_filing_deposits (filing_id, sequence desc);
|
|
83
|
+
-- The two files, indexed like every other foreign key of this schema: a
|
|
84
|
+
-- deleted attachment has to find the deposits that point at it without a scan.
|
|
85
|
+
create index tax_filing_deposits_sent_file_idx on tax_filing_deposits (sent_file_id);
|
|
86
|
+
create index tax_filing_deposits_acknowledgement_idx on tax_filing_deposits (acknowledgement_id);
|
|
87
|
+
|
|
88
|
+
alter table tax_filing_deposits enable row level security;
|
|
89
|
+
|
|
90
|
+
create policy tax_filing_deposits_select on tax_filing_deposits
|
|
91
|
+
for select using (exists (
|
|
92
|
+
select 1 from tax_filings f
|
|
93
|
+
where f.id = tax_filing_deposits.filing_id
|
|
94
|
+
and has_capability(f.company_id, 'filings.read')));
|
|
95
|
+
create policy tax_filing_deposits_write on tax_filing_deposits
|
|
96
|
+
for all using (exists (
|
|
97
|
+
select 1 from tax_filings f
|
|
98
|
+
where f.id = tax_filing_deposits.filing_id
|
|
99
|
+
and has_capability(f.company_id, 'filings.write')))
|
|
100
|
+
with check (exists (
|
|
101
|
+
select 1 from tax_filings f
|
|
102
|
+
where f.id = tax_filing_deposits.filing_id
|
|
103
|
+
and has_capability(f.company_id, 'filings.write')));
|
|
104
|
+
|
|
105
|
+
comment on policy tax_filing_deposits_select on tax_filing_deposits is
|
|
106
|
+
'filings.read: what was sent and what came back is proof of filing, and it is read by whoever reads the declarations.';
|
|
107
|
+
|
|
108
|
+
grant select, insert, update, delete on table tax_filing_deposits to authenticated, service_role;
|
|
109
|
+
|
|
110
|
+
-- ---------------------------------------------------------------------------
|
|
111
|
+
-- file_filing, which now records the send
|
|
112
|
+
--
|
|
113
|
+
-- Replaces the three-argument function rather than sitting beside it: two
|
|
114
|
+
-- overloads with defaults would make the call everybody writes ambiguous.
|
|
115
|
+
-- ---------------------------------------------------------------------------
|
|
116
|
+
|
|
117
|
+
drop function if exists file_filing(uuid, text, timestamptz);
|
|
118
|
+
|
|
119
|
+
create or replace function file_filing(
|
|
120
|
+
p_filing_id uuid,
|
|
121
|
+
p_reference text default null,
|
|
122
|
+
p_filed_at timestamptz default null,
|
|
123
|
+
p_channel filing_channel default 'portal',
|
|
124
|
+
p_service text default null
|
|
125
|
+
)
|
|
126
|
+
returns tax_filings
|
|
127
|
+
language plpgsql
|
|
128
|
+
volatile
|
|
129
|
+
security invoker
|
|
130
|
+
as $$
|
|
131
|
+
declare
|
|
132
|
+
v_filing tax_filings;
|
|
133
|
+
v_at timestamptz;
|
|
134
|
+
v_next integer;
|
|
135
|
+
begin
|
|
136
|
+
select * into v_filing from tax_filings f where f.id = p_filing_id;
|
|
137
|
+
if not found then
|
|
138
|
+
raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
|
|
139
|
+
end if;
|
|
140
|
+
|
|
141
|
+
-- Rejected is a state a declaration is sent again from. It was never
|
|
142
|
+
-- received, so there is nothing to correct and no corrective to open.
|
|
143
|
+
if v_filing.state not in ('draft', 'ready', 'rejected') then
|
|
144
|
+
raise exception 'filing_already_%: this declaration has already gone', v_filing.state;
|
|
145
|
+
end if;
|
|
146
|
+
|
|
147
|
+
if v_filing.prepared_at is null then
|
|
148
|
+
raise exception 'filing_not_prepared: compute the figures before filing them';
|
|
149
|
+
end if;
|
|
150
|
+
|
|
151
|
+
v_at := coalesce(p_filed_at, now());
|
|
152
|
+
|
|
153
|
+
select coalesce(max(d.sequence), 0) + 1 into v_next
|
|
154
|
+
from tax_filing_deposits d where d.filing_id = p_filing_id;
|
|
155
|
+
|
|
156
|
+
insert into tax_filing_deposits (filing_id, sequence, channel, service, sent_at, sent_by, reference)
|
|
157
|
+
values (p_filing_id, v_next, p_channel, p_service, v_at, auth.uid(), p_reference);
|
|
158
|
+
|
|
159
|
+
update tax_filings
|
|
160
|
+
set state = 'filed',
|
|
161
|
+
reference = coalesce(p_reference, reference),
|
|
162
|
+
filed_at = v_at,
|
|
163
|
+
filed_by = auth.uid()
|
|
164
|
+
where id = p_filing_id
|
|
165
|
+
returning * into v_filing;
|
|
166
|
+
|
|
167
|
+
return v_filing;
|
|
168
|
+
end;
|
|
169
|
+
$$;
|
|
170
|
+
|
|
171
|
+
comment on function file_filing(uuid, text, timestamptz, filing_channel, text) is
|
|
172
|
+
'Records that a declaration has gone, with the reference the administration gave back, and keeps the send as a row of tax_filing_deposits. It asks that the figures were computed, not that there are any: a period where nothing happened is filed nil. A rejected declaration may be sent again — it was never received — and the second send is a second deposit, not a corrective.';
|
|
173
|
+
|
|
174
|
+
revoke execute on function file_filing(uuid, text, timestamptz, filing_channel, text) from public, anon;
|
|
175
|
+
grant execute on function file_filing(uuid, text, timestamptz, filing_channel, text) to authenticated, service_role;
|
|
176
|
+
|
|
177
|
+
-- ---------------------------------------------------------------------------
|
|
178
|
+
-- record_filing_outcome, which writes on the deposit it answers
|
|
179
|
+
-- ---------------------------------------------------------------------------
|
|
180
|
+
|
|
181
|
+
drop function if exists record_filing_outcome(uuid, tax_filing_state, text);
|
|
182
|
+
|
|
183
|
+
create or replace function record_filing_outcome(
|
|
184
|
+
p_filing_id uuid,
|
|
185
|
+
p_state tax_filing_state,
|
|
186
|
+
p_reference text default null,
|
|
187
|
+
p_message text default null
|
|
188
|
+
)
|
|
189
|
+
returns tax_filings
|
|
190
|
+
language plpgsql
|
|
191
|
+
volatile
|
|
192
|
+
security invoker
|
|
193
|
+
as $$
|
|
194
|
+
declare
|
|
195
|
+
v_filing tax_filings;
|
|
196
|
+
begin
|
|
197
|
+
select * into v_filing from tax_filings f where f.id = p_filing_id;
|
|
198
|
+
if not found then
|
|
199
|
+
raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
|
|
200
|
+
end if;
|
|
201
|
+
|
|
202
|
+
if p_state not in ('accepted', 'rejected', 'paid') then
|
|
203
|
+
raise exception 'not_an_outcome: % is not something an administration answers', p_state;
|
|
204
|
+
end if;
|
|
205
|
+
if v_filing.state = 'draft' or v_filing.state = 'ready' then
|
|
206
|
+
raise exception 'filing_not_sent: a declaration that has not gone cannot come back %', p_state;
|
|
207
|
+
end if;
|
|
208
|
+
if p_state = 'paid' and v_filing.state <> 'accepted' then
|
|
209
|
+
raise exception 'filing_not_accepted: paying follows acceptance, and this one is %', v_filing.state;
|
|
210
|
+
end if;
|
|
211
|
+
|
|
212
|
+
-- On the last send, because that is the one being answered.
|
|
213
|
+
update tax_filing_deposits d
|
|
214
|
+
set outcome = p_state,
|
|
215
|
+
outcome_at = now(),
|
|
216
|
+
reference = coalesce(p_reference, d.reference),
|
|
217
|
+
message = coalesce(p_message, d.message)
|
|
218
|
+
where d.filing_id = p_filing_id
|
|
219
|
+
and d.sequence = (select max(x.sequence) from tax_filing_deposits x where x.filing_id = p_filing_id);
|
|
220
|
+
|
|
221
|
+
update tax_filings
|
|
222
|
+
set state = p_state,
|
|
223
|
+
reference = coalesce(p_reference, reference)
|
|
224
|
+
where id = p_filing_id
|
|
225
|
+
returning * into v_filing;
|
|
226
|
+
return v_filing;
|
|
227
|
+
end;
|
|
228
|
+
$$;
|
|
229
|
+
|
|
230
|
+
comment on function record_filing_outcome(uuid, tax_filing_state, text, text) is
|
|
231
|
+
'What came back: accepted, rejected, or paid, written on the declaration and on the send it answers — with the administration''s own words where it gave any. Paying follows acceptance, and a declaration that never went cannot come back at all.';
|
|
232
|
+
|
|
233
|
+
revoke execute on function record_filing_outcome(uuid, tax_filing_state, text, text) from public, anon;
|
|
234
|
+
grant execute on function record_filing_outcome(uuid, tax_filing_state, text, text) to authenticated, service_role;
|
|
235
|
+
|
|
236
|
+
-- ---------------------------------------------------------------------------
|
|
237
|
+
-- reopen_filing
|
|
238
|
+
--
|
|
239
|
+
-- A rejection on the substance is not answered by sending the same file again:
|
|
240
|
+
-- the figures have to be worked out afresh, and `prepare_filing()` refuses
|
|
241
|
+
-- anything that has left draft. So this is the door back, and it opens for one
|
|
242
|
+
-- state only.
|
|
243
|
+
--
|
|
244
|
+
-- It is safe because of the table above: what was sent is a deposit, with its
|
|
245
|
+
-- reference, its answer and — where somebody kept it — the file itself. The
|
|
246
|
+
-- core cannot force an installation to archive that file; it can make keeping
|
|
247
|
+
-- it the thing that makes this lossless, and say so.
|
|
248
|
+
-- ---------------------------------------------------------------------------
|
|
249
|
+
|
|
250
|
+
create or replace function reopen_filing(p_filing_id uuid)
|
|
251
|
+
returns tax_filings
|
|
252
|
+
language plpgsql
|
|
253
|
+
volatile
|
|
254
|
+
security invoker
|
|
255
|
+
as $$
|
|
256
|
+
declare
|
|
257
|
+
v_filing tax_filings;
|
|
258
|
+
begin
|
|
259
|
+
select * into v_filing from tax_filings f where f.id = p_filing_id;
|
|
260
|
+
if not found then
|
|
261
|
+
raise exception 'not_found: filing %', p_filing_id using errcode = 'no_data_found';
|
|
262
|
+
end if;
|
|
263
|
+
if v_filing.state <> 'rejected' then
|
|
264
|
+
raise exception 'filing_not_rejected: a % declaration is not redone, it is superseded by a corrective',
|
|
265
|
+
v_filing.state;
|
|
266
|
+
end if;
|
|
267
|
+
|
|
268
|
+
-- The declaration goes back to being prepared. `filed_at` empties with it —
|
|
269
|
+
-- the schema's own rule that a draft carries no filing date — and nothing is
|
|
270
|
+
-- lost, because the send that was refused is a row of tax_filing_deposits.
|
|
271
|
+
update tax_filings
|
|
272
|
+
set state = 'draft',
|
|
273
|
+
filed_at = null,
|
|
274
|
+
filed_by = null,
|
|
275
|
+
reference = null
|
|
276
|
+
where id = p_filing_id
|
|
277
|
+
returning * into v_filing;
|
|
278
|
+
return v_filing;
|
|
279
|
+
end;
|
|
280
|
+
$$;
|
|
281
|
+
|
|
282
|
+
comment on function reopen_filing(uuid) is
|
|
283
|
+
'Takes a rejected declaration back to draft so its figures can be worked out again. Only a rejected one: a declaration the administration accepted is replaced by a corrective, never rewritten. What was sent stays in tax_filing_deposits.';
|
|
284
|
+
|
|
285
|
+
revoke execute on function reopen_filing(uuid) from public, anon;
|
|
286
|
+
grant execute on function reopen_filing(uuid) to authenticated, service_role;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- Ekwo OS — a fourth preset is named, and nothing else happens here.
|
|
2
|
+
--
|
|
3
|
+
-- `member_role` gains `client`: the person whose company this is, inside an
|
|
4
|
+
-- installation that belongs to whoever keeps their books. What the preset
|
|
5
|
+
-- holds is the next migration — a new enum value cannot be used in the
|
|
6
|
+
-- transaction that adds it, which is the reason `instance_admin` was added in
|
|
7
|
+
-- a file of its own on the first day, and the reason this one is.
|
|
8
|
+
|
|
9
|
+
alter type member_role add value if not exists 'client';
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
-- Ekwo OS — a client reads, and hands over a piece. It writes nothing.
|
|
2
|
+
--
|
|
3
|
+
-- Several companies inside one installation is the normal case: a firm and
|
|
4
|
+
-- the companies it keeps the books of. The person who runs one of those
|
|
5
|
+
-- companies had no preset to sit on. `viewer` reads and cannot hand anything
|
|
6
|
+
-- over; `accountant` hands over and can also draft an invoice, which is the
|
|
7
|
+
-- one thing the firm is there to do.
|
|
8
|
+
--
|
|
9
|
+
-- **What was missing was not a preset.** Filing a piece is an insert into
|
|
10
|
+
-- `attachments`, and the only policy that admits one tests `documents.write`
|
|
11
|
+
-- — the capability that also creates and changes draft documents and their
|
|
12
|
+
-- lines. There was no way to say "may give us the receipt" without saying
|
|
13
|
+
-- "may write the purchase invoice". So this migration adds one capability,
|
|
14
|
+
-- `documents.deposit`, and the narrowest policy that can carry it:
|
|
15
|
+
--
|
|
16
|
+
-- * INSERT only. There is no update and no delete behind it: a piece that
|
|
17
|
+
-- was handed over is the firm's to file, to re-point at the document it
|
|
18
|
+
-- becomes, or to discard, and all three are `documents.write`.
|
|
19
|
+
-- * On the company itself — `entity_type = 'company'`, `entity_id` the
|
|
20
|
+
-- company. `attachments` is polymorphic, and a capability that attached
|
|
21
|
+
-- to any row would let its holder pin a file of their own making on a
|
|
22
|
+
-- filed declaration, beside the receipt the administration sent. The
|
|
23
|
+
-- company row is the one thing a depositor is certain to be able to name,
|
|
24
|
+
-- and a file sitting on it is what an in-tray is.
|
|
25
|
+
-- * Signed. `uploaded_by` must be the caller, and the column now defaults
|
|
26
|
+
-- to it, so the firm reads who handed over what without trusting the
|
|
27
|
+
-- application to have said so.
|
|
28
|
+
--
|
|
29
|
+
-- **Reading back what one handed over.** `insert … returning` is checked
|
|
30
|
+
-- against the SELECT policies, and the one on `attachments` tests
|
|
31
|
+
-- `documents.read`. A client holds it; a member or a key granted
|
|
32
|
+
-- `documents.deposit` alone — a scanner in a hallway — does not, and would
|
|
33
|
+
-- have its insert refused for a row it is allowed to write. A second SELECT
|
|
34
|
+
-- policy lets a depositor read their own deposits. It reads no table, so it
|
|
35
|
+
-- cannot recurse.
|
|
36
|
+
--
|
|
37
|
+
-- **The preset.** `client` holds what `viewer` holds, read from
|
|
38
|
+
-- `role_capabilities` rather than listed again, plus `documents.deposit`. The
|
|
39
|
+
-- ledger is included on purpose: the books are the client's, the firm is the
|
|
40
|
+
-- guest, and a preset that hid a company's own entries from the person
|
|
41
|
+
-- answerable for them would be describing a different relationship. A firm
|
|
42
|
+
-- that wants less revokes it, per member — that is what
|
|
43
|
+
-- `capabilities_revoked` is for.
|
|
44
|
+
--
|
|
45
|
+
-- There is no approval in this preset because there is no act of approval in
|
|
46
|
+
-- the schema that is not also a write: `tax_filings.state = 'ready'` is
|
|
47
|
+
-- reached through `filings.write`. `docs/decisions.md` says so under this
|
|
48
|
+
-- date.
|
|
49
|
+
|
|
50
|
+
insert into capabilities (code, area, description) values
|
|
51
|
+
('documents.deposit', 'documents',
|
|
52
|
+
'Hand a file over to the company: an attachment on the company itself, signed by whoever dropped it. Insert only — filing it, moving it and deleting it are documents.write.')
|
|
53
|
+
on conflict (code) do nothing;
|
|
54
|
+
|
|
55
|
+
-- Whoever may write a document may already file an attachment anywhere, so
|
|
56
|
+
-- the two working presets hold the narrower act as well: an interface asks
|
|
57
|
+
-- one question — may this person drop a file here — and gets one answer.
|
|
58
|
+
insert into role_capabilities (role, capability) values
|
|
59
|
+
('owner'::member_role, 'documents.deposit'),
|
|
60
|
+
('accountant'::member_role, 'documents.deposit'),
|
|
61
|
+
('client'::member_role, 'documents.deposit')
|
|
62
|
+
on conflict do nothing;
|
|
63
|
+
|
|
64
|
+
insert into role_capabilities (role, capability)
|
|
65
|
+
select 'client'::member_role, rc.capability
|
|
66
|
+
from role_capabilities rc
|
|
67
|
+
where rc.role = 'viewer'
|
|
68
|
+
on conflict do nothing;
|
|
69
|
+
|
|
70
|
+
alter table attachments alter column uploaded_by set default auth.uid();
|
|
71
|
+
|
|
72
|
+
comment on column attachments.uploaded_by is
|
|
73
|
+
'Who filed it. Defaults to the caller; a deposit is refused unless it says the caller.';
|
|
74
|
+
|
|
75
|
+
create policy attachments_deposit on attachments
|
|
76
|
+
for insert with check (
|
|
77
|
+
has_capability(company_id, 'documents.deposit')
|
|
78
|
+
and entity_type = 'company'
|
|
79
|
+
and entity_id = company_id
|
|
80
|
+
and uploaded_by is not distinct from auth.uid()
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
comment on policy attachments_deposit on attachments is
|
|
84
|
+
'A piece handed over: on the company itself, signed by the caller, insert only. Everything else on this table is documents.write.';
|
|
85
|
+
|
|
86
|
+
create policy attachments_select_own_deposit on attachments
|
|
87
|
+
for select using (
|
|
88
|
+
uploaded_by = auth.uid()
|
|
89
|
+
and has_capability(company_id, 'documents.deposit')
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
comment on policy attachments_select_own_deposit on attachments is
|
|
93
|
+
'A depositor reads back what they handed over, so that `insert … returning` answers for somebody who holds documents.deposit and not documents.read.';
|
|
94
|
+
|
|
95
|
+
comment on table company_members is
|
|
96
|
+
'Who may read or write a company. A role is a preset resolved through `role_capabilities`: `owner` administers, `accountant` books, `viewer` reads, `client` reads and hands pieces over.';
|