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,261 @@
|
|
|
1
|
+
-- Ekwo OS — a company has a face, and an invoice has to print it.
|
|
2
|
+
--
|
|
3
|
+
-- The core knew a company's name, its legal name, its address, its two
|
|
4
|
+
-- identifiers and its currency. An invoice needs more than that, and every
|
|
5
|
+
-- one of the missing pieces is a thing a renderer would otherwise have to be
|
|
6
|
+
-- configured with separately — which is the same fact written twice, in a
|
|
7
|
+
-- place the books cannot see.
|
|
8
|
+
--
|
|
9
|
+
-- **What is added, and why each one is a column rather than a setting.**
|
|
10
|
+
-- `trade_name` because a company trades under a name that is not its
|
|
11
|
+
-- statutory one and the invoice carries both. `logo_url` because a renderer
|
|
12
|
+
-- needs to find the image — a URL or a storage path, never the file: the core
|
|
13
|
+
-- keeps books, not binaries. `share_capital` with its own currency because
|
|
14
|
+
-- several European legal forms must state it on every document.
|
|
15
|
+
-- `activity_code` with the scheme it is written in, because NACE, APE and SIC
|
|
16
|
+
-- are different registers and a bare code is unreadable without the one it
|
|
17
|
+
-- came from. `default_bank_account_id` because an invoice without an IBAN
|
|
18
|
+
-- cannot be paid. `document_template` because a company that has chosen a
|
|
19
|
+
-- layout has chosen it once, not per invoice.
|
|
20
|
+
--
|
|
21
|
+
-- **What is deliberately not added.** No `registry_reference`:
|
|
22
|
+
-- `companies.registration_number` is already the number the commercial
|
|
23
|
+
-- register holds — a BCE number in Belgium, an RCS or SIRET in France — and a
|
|
24
|
+
-- second column for it would be two answers to one question, which is the one
|
|
25
|
+
-- thing this schema will not do.
|
|
26
|
+
--
|
|
27
|
+
-- **No literal default anywhere.** `share_capital_currency` falls back to the
|
|
28
|
+
-- company's own currency, in a trigger, and never to a currency written into
|
|
29
|
+
-- this file.
|
|
30
|
+
|
|
31
|
+
alter table companies
|
|
32
|
+
add column if not exists trade_name text,
|
|
33
|
+
add column if not exists logo_url text,
|
|
34
|
+
add column if not exists share_capital numeric(16, 2),
|
|
35
|
+
add column if not exists share_capital_currency char(3) references currencies(code),
|
|
36
|
+
add column if not exists activity_code text,
|
|
37
|
+
add column if not exists activity_scheme text,
|
|
38
|
+
add column if not exists default_bank_account_id uuid,
|
|
39
|
+
add column if not exists document_template text;
|
|
40
|
+
|
|
41
|
+
comment on column companies.trade_name is
|
|
42
|
+
'The name the company trades under, when it is not the statutory one. A renderer shows this and keeps legal_name for the footer.';
|
|
43
|
+
comment on column companies.logo_url is
|
|
44
|
+
'Where the logo is, as a URL or a storage path. The core stores no file: an accounting schema that held binaries would be backing up images with the ledger.';
|
|
45
|
+
comment on column companies.share_capital is
|
|
46
|
+
'Capital to be stated on documents where the law requires it. Null where it does not, which is not zero.';
|
|
47
|
+
comment on column companies.share_capital_currency is
|
|
48
|
+
'Currency the capital is expressed in. Filled with the company''s own currency when it is left empty, and never with a currency written into the schema.';
|
|
49
|
+
comment on column companies.activity_code is
|
|
50
|
+
'The company''s activity in the register of its country — NACE, APE, SIC. Text, because the registers are not numbers and not the same length.';
|
|
51
|
+
comment on column companies.activity_scheme is
|
|
52
|
+
'Which register activity_code belongs to. A code without its scheme cannot be looked up.';
|
|
53
|
+
comment on column companies.default_bank_account_id is
|
|
54
|
+
'The account a customer is asked to pay into. It fills documents.payee_iban (BT-84) when a sales document names none.';
|
|
55
|
+
comment on column companies.document_template is
|
|
56
|
+
'A code the renderer interprets. The core never reads it: what a document looks like is not an accounting question.';
|
|
57
|
+
|
|
58
|
+
alter table companies
|
|
59
|
+
add constraint companies_share_capital_positive
|
|
60
|
+
check (share_capital is null or share_capital >= 0),
|
|
61
|
+
add constraint companies_share_capital_has_currency
|
|
62
|
+
check (share_capital is null or share_capital_currency is not null),
|
|
63
|
+
add constraint companies_default_bank_account_fkey
|
|
64
|
+
foreign key (default_bank_account_id) references bank_accounts(id) on delete set null;
|
|
65
|
+
|
|
66
|
+
-- The account also has to belong to this company, and that is a trigger
|
|
67
|
+
-- rather than the composite key `(default_bank_account_id, id) references
|
|
68
|
+
-- bank_accounts(id, company_id)` that says it in one line. The composite key
|
|
69
|
+
-- works and its `on delete set null` does not: PostgreSQL nulls *every*
|
|
70
|
+
-- referencing column, so deleting a bank account would try to null
|
|
71
|
+
-- `companies.id`. A plain key keeps the friendly behaviour — withdrawing an
|
|
72
|
+
-- account clears the default instead of refusing the delete — and the trigger
|
|
73
|
+
-- keeps the guarantee.
|
|
74
|
+
|
|
75
|
+
create or replace function companies_default_bank_account_is_ours()
|
|
76
|
+
returns trigger
|
|
77
|
+
language plpgsql
|
|
78
|
+
as $$
|
|
79
|
+
begin
|
|
80
|
+
if new.default_bank_account_id is not null
|
|
81
|
+
and not exists (select 1 from bank_accounts b
|
|
82
|
+
where b.id = new.default_bank_account_id
|
|
83
|
+
and b.company_id = new.id) then
|
|
84
|
+
raise exception 'foreign_bank_account: that bank account belongs to another company'
|
|
85
|
+
using errcode = '23503';
|
|
86
|
+
end if;
|
|
87
|
+
return new;
|
|
88
|
+
end;
|
|
89
|
+
$$;
|
|
90
|
+
|
|
91
|
+
create trigger companies_default_bank_account_is_ours
|
|
92
|
+
before insert or update of default_bank_account_id on companies
|
|
93
|
+
for each row execute function companies_default_bank_account_is_ours();
|
|
94
|
+
|
|
95
|
+
-- ---------------------------------------------------------------------------
|
|
96
|
+
-- The currency of the capital, when nobody said
|
|
97
|
+
-- ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
create or replace function companies_default_capital_currency()
|
|
100
|
+
returns trigger
|
|
101
|
+
language plpgsql
|
|
102
|
+
as $$
|
|
103
|
+
begin
|
|
104
|
+
if new.share_capital is not null and new.share_capital_currency is null then
|
|
105
|
+
new.share_capital_currency := new.currency_code;
|
|
106
|
+
end if;
|
|
107
|
+
return new;
|
|
108
|
+
end;
|
|
109
|
+
$$;
|
|
110
|
+
|
|
111
|
+
create trigger companies_default_capital_currency
|
|
112
|
+
before insert or update of share_capital, share_capital_currency, currency_code on companies
|
|
113
|
+
for each row execute function companies_default_capital_currency();
|
|
114
|
+
|
|
115
|
+
comment on function companies_default_capital_currency() is
|
|
116
|
+
'A capital stated with no currency is stated in the company''s own. The alternative was a literal in the schema, which is one country''s answer given to every country.';
|
|
117
|
+
|
|
118
|
+
-- ---------------------------------------------------------------------------
|
|
119
|
+
-- The IBAN a sales document carries
|
|
120
|
+
--
|
|
121
|
+
-- A trigger and not a default on the column, for the reason the account of a
|
|
122
|
+
-- document line is resolved in a trigger: every client that inserts a
|
|
123
|
+
-- document has to get the same answer, and a client that did not know about
|
|
124
|
+
-- the company default would produce an invoice nobody can pay.
|
|
125
|
+
--
|
|
126
|
+
-- Only on what the company issues. On a purchase invoice the payee is the
|
|
127
|
+
-- supplier, and filling our own IBAN in there would be writing the wrong
|
|
128
|
+
-- fact — worse than an empty column, because it looks right.
|
|
129
|
+
-- ---------------------------------------------------------------------------
|
|
130
|
+
|
|
131
|
+
create or replace function documents_default_payee_iban()
|
|
132
|
+
returns trigger
|
|
133
|
+
language plpgsql
|
|
134
|
+
as $$
|
|
135
|
+
declare
|
|
136
|
+
v_iban text;
|
|
137
|
+
begin
|
|
138
|
+
if new.payee_iban is null and new.doc_type::text like 'sale%' then
|
|
139
|
+
select b.iban into v_iban
|
|
140
|
+
from companies c
|
|
141
|
+
join bank_accounts b on b.id = c.default_bank_account_id
|
|
142
|
+
where c.id = new.company_id;
|
|
143
|
+
new.payee_iban := v_iban;
|
|
144
|
+
end if;
|
|
145
|
+
return new;
|
|
146
|
+
end;
|
|
147
|
+
$$;
|
|
148
|
+
|
|
149
|
+
create trigger documents_default_payee_iban
|
|
150
|
+
before insert on documents
|
|
151
|
+
for each row execute function documents_default_payee_iban();
|
|
152
|
+
|
|
153
|
+
comment on function documents_default_payee_iban() is
|
|
154
|
+
'A sales document with no payee IBAN takes the company''s default bank account. A purchase document never does: the payee there is somebody else.';
|
|
155
|
+
|
|
156
|
+
-- ---------------------------------------------------------------------------
|
|
157
|
+
-- document_header — everything a renderer needs, in one read
|
|
158
|
+
--
|
|
159
|
+
-- The lines are `document_line_items`, the sentences are
|
|
160
|
+
-- `document_legal_mentions`, and the third read a renderer had to invent for
|
|
161
|
+
-- itself was the header: who issues, who receives, what is owed, where it is
|
|
162
|
+
-- paid, and what the country of the document requires of it. Three views,
|
|
163
|
+
-- three selects, and no client holding a copy of the company's letterhead.
|
|
164
|
+
--
|
|
165
|
+
-- `security_invoker`, like the other two: what a person may see through it is
|
|
166
|
+
-- what they may see under it.
|
|
167
|
+
-- ---------------------------------------------------------------------------
|
|
168
|
+
|
|
169
|
+
create or replace view document_header
|
|
170
|
+
with (security_invoker = true) as
|
|
171
|
+
select d.id as document_id,
|
|
172
|
+
d.company_id,
|
|
173
|
+
d.doc_type,
|
|
174
|
+
d.state,
|
|
175
|
+
d.payment_state,
|
|
176
|
+
d.number,
|
|
177
|
+
d.supplier_reference,
|
|
178
|
+
d.document_date,
|
|
179
|
+
d.accounting_date,
|
|
180
|
+
d.due_date,
|
|
181
|
+
d.delivery_date,
|
|
182
|
+
d.currency_code,
|
|
183
|
+
d.amount_untaxed,
|
|
184
|
+
d.amount_tax,
|
|
185
|
+
d.amount_total,
|
|
186
|
+
d.amount_paid,
|
|
187
|
+
d.amount_residual,
|
|
188
|
+
d.payment_terms,
|
|
189
|
+
d.payment_means_code,
|
|
190
|
+
d.payment_reference,
|
|
191
|
+
d.buyer_reference,
|
|
192
|
+
d.order_reference,
|
|
193
|
+
d.contract_reference,
|
|
194
|
+
d.project_reference,
|
|
195
|
+
d.note,
|
|
196
|
+
-- BT-84: what the document says, else what the company's default
|
|
197
|
+
-- bank account says. The trigger fills it on the way in, so the
|
|
198
|
+
-- coalesce is what keeps a document written before this migration
|
|
199
|
+
-- true as well.
|
|
200
|
+
coalesce(d.payee_iban, seller_bank.iban) as payee_iban,
|
|
201
|
+
seller_bank.bic as payee_bic,
|
|
202
|
+
|
|
203
|
+
-- The seller: this company.
|
|
204
|
+
coalesce(c.trade_name, c.name) as seller_name,
|
|
205
|
+
c.legal_name as seller_legal_name,
|
|
206
|
+
c.legal_form as seller_legal_form,
|
|
207
|
+
c.vat_number as seller_vat_number,
|
|
208
|
+
c.registration_number as seller_registration_number,
|
|
209
|
+
c.address_line1 as seller_address_line1,
|
|
210
|
+
c.address_line2 as seller_address_line2,
|
|
211
|
+
c.postal_code as seller_postal_code,
|
|
212
|
+
c.city as seller_city,
|
|
213
|
+
c.country as seller_country,
|
|
214
|
+
c.region as seller_region,
|
|
215
|
+
c.email as seller_email,
|
|
216
|
+
c.phone as seller_phone,
|
|
217
|
+
c.website as seller_website,
|
|
218
|
+
c.logo_url as seller_logo_url,
|
|
219
|
+
c.share_capital as seller_share_capital,
|
|
220
|
+
c.share_capital_currency as seller_share_capital_currency,
|
|
221
|
+
c.activity_code as seller_activity_code,
|
|
222
|
+
c.activity_scheme as seller_activity_scheme,
|
|
223
|
+
c.document_template,
|
|
224
|
+
|
|
225
|
+
-- The buyer: the contact the document is written against.
|
|
226
|
+
ct.id as buyer_id,
|
|
227
|
+
ct.name as buyer_name,
|
|
228
|
+
ct.vat_number as buyer_vat_number,
|
|
229
|
+
ct.registration_number as buyer_registration_number,
|
|
230
|
+
ct.address_line1 as buyer_address_line1,
|
|
231
|
+
ct.address_line2 as buyer_address_line2,
|
|
232
|
+
ct.postal_code as buyer_postal_code,
|
|
233
|
+
ct.city as buyer_city,
|
|
234
|
+
ct.country as buyer_country,
|
|
235
|
+
ct.region as buyer_region,
|
|
236
|
+
ct.email as buyer_email,
|
|
237
|
+
|
|
238
|
+
-- What the country of the document requires. Its country is the
|
|
239
|
+
-- company's fiscal_country — the country whose VAT rules apply — and
|
|
240
|
+
-- not its address, which is the same distinction
|
|
241
|
+
-- document_legal_mentions makes.
|
|
242
|
+
c.fiscal_country as country,
|
|
243
|
+
cd.number_format,
|
|
244
|
+
cd.numbering_gapless,
|
|
245
|
+
cd.legal_payment_days,
|
|
246
|
+
cd.late_payment_reference,
|
|
247
|
+
cd.tax_point_rule,
|
|
248
|
+
cd.einvoice_profile,
|
|
249
|
+
cd.einvoice_mandatory_from,
|
|
250
|
+
cd.party_scheme,
|
|
251
|
+
cd.vat_scheme
|
|
252
|
+
from documents d
|
|
253
|
+
join companies c on c.id = d.company_id
|
|
254
|
+
join contacts ct on ct.id = d.contact_id
|
|
255
|
+
left join bank_accounts seller_bank on seller_bank.id = c.default_bank_account_id
|
|
256
|
+
left join country_defaults cd on cd.country = c.fiscal_country;
|
|
257
|
+
|
|
258
|
+
comment on view document_header is
|
|
259
|
+
'One document with everything printed above its lines: the seller, the buyer, the amounts, where it is paid and what its country requires. The lines are document_line_items and the sentences are document_legal_mentions.';
|
|
260
|
+
|
|
261
|
+
revoke execute on all functions in schema public from public;
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
-- Ekwo OS — the number a country asked for, instead of the one we built.
|
|
2
|
+
--
|
|
3
|
+
-- P0-7 put `number_format` and `numbering_gapless` on the country model and
|
|
4
|
+
-- said, in as many words, that `next_entry_number()` did not read them: "a
|
|
5
|
+
-- numbering engine that consumes a format is its own piece of work, and both
|
|
6
|
+
-- packs declare exactly the pattern the engine already produces, so nothing
|
|
7
|
+
-- moves under anybody's feet the day it lands." This is that piece of work.
|
|
8
|
+
--
|
|
9
|
+
-- **The grammar is the one already published**, in the comment on the column
|
|
10
|
+
-- and in `packs/schema/pack.1.json`, and it is not reinvented here:
|
|
11
|
+
--
|
|
12
|
+
-- {CODE} the journal code
|
|
13
|
+
-- {YYYY} the year, four digits — {YY} for two
|
|
14
|
+
-- {MM} the month, two digits
|
|
15
|
+
-- {NNNN} the counter, padded to as many N as are written
|
|
16
|
+
--
|
|
17
|
+
-- **There is no fallback.** A pack that declares no format gets
|
|
18
|
+
-- `no_number_format`, naming `documents.number_format` — the field it has to
|
|
19
|
+
-- fill — rather than `CODE/YYYY/NNNN`, which is Belgium's and France's answer
|
|
20
|
+
-- handed to a country that has not spoken. It is the same rule `closing_style`
|
|
21
|
+
-- and `fiscal_year_default` already keep, and the reason is the one P0-8 gave:
|
|
22
|
+
-- a default is one country's law given to every country that has not said.
|
|
23
|
+
--
|
|
24
|
+
-- **The counter follows the pattern.** A pattern that carries the year
|
|
25
|
+
-- restarts with it, which is what the four numbering styles of the pack
|
|
26
|
+
-- format compile to; a pattern that carries none counts on for the life of
|
|
27
|
+
-- the journal, and that series is kept under year 0 in `journal_sequences` —
|
|
28
|
+
-- the counter is keyed by journal and period, and 0 is the period that is not
|
|
29
|
+
-- a year. A `{MM}` in the pattern **prints** the month and does not restart
|
|
30
|
+
-- the counter: a monthly series is a fifth style, no pack declares one, and
|
|
31
|
+
-- inventing the behaviour before a pack asks for it is how a guess becomes a
|
|
32
|
+
-- rule.
|
|
33
|
+
--
|
|
34
|
+
-- **`numbering_gapless` gets its reader too**, and it is `post_entry()`: where
|
|
35
|
+
-- the law forbids a hole in the sequence, an entry may not be posted with a
|
|
36
|
+
-- number chosen by hand, because a number that skips the counter is exactly
|
|
37
|
+
-- how a hole appears. The core has no path that imports entries under their
|
|
38
|
+
-- old numbers — an opening balance is a trial balance, not a journal — so
|
|
39
|
+
-- nothing that exists is refused by this. The day such an import is written,
|
|
40
|
+
-- this is the rule it will have to argue with.
|
|
41
|
+
|
|
42
|
+
-- ---------------------------------------------------------------------------
|
|
43
|
+
-- One reader for both columns
|
|
44
|
+
-- ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
create or replace function numbering_rules(
|
|
47
|
+
p_company_id uuid,
|
|
48
|
+
out number_format text,
|
|
49
|
+
out numbering_gapless boolean
|
|
50
|
+
)
|
|
51
|
+
language sql
|
|
52
|
+
stable
|
|
53
|
+
as $$
|
|
54
|
+
select d.number_format, coalesce(d.numbering_gapless, false)
|
|
55
|
+
from companies c
|
|
56
|
+
left join country_defaults d on d.country = c.fiscal_country
|
|
57
|
+
where c.id = p_company_id;
|
|
58
|
+
$$;
|
|
59
|
+
|
|
60
|
+
comment on function numbering_rules(uuid) is
|
|
61
|
+
'What the country of a company says about its document numbers: the pattern, and whether the law forbids a hole. The only function that reads either column.';
|
|
62
|
+
|
|
63
|
+
-- ---------------------------------------------------------------------------
|
|
64
|
+
-- Rendering one number
|
|
65
|
+
--
|
|
66
|
+
-- Separate from `next_entry_number()` so that the pattern can be tested
|
|
67
|
+
-- without burning a counter, and so a document series of its own — which is
|
|
68
|
+
-- not in this release — has the same renderer rather than a second one.
|
|
69
|
+
-- ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
create or replace function format_number(p_format text, p_code text, p_date date, p_number integer)
|
|
72
|
+
returns text
|
|
73
|
+
language plpgsql
|
|
74
|
+
immutable
|
|
75
|
+
as $$
|
|
76
|
+
declare
|
|
77
|
+
v_out text := p_format;
|
|
78
|
+
v_token text;
|
|
79
|
+
v_width integer;
|
|
80
|
+
v_digits text := p_number::text;
|
|
81
|
+
v_left text;
|
|
82
|
+
begin
|
|
83
|
+
if p_format is null or p_format = '' then
|
|
84
|
+
raise exception 'no_number_format: this country declares no documents.number_format, and there is no default to fall back on';
|
|
85
|
+
end if;
|
|
86
|
+
|
|
87
|
+
v_out := replace(v_out, '{CODE}', coalesce(p_code, ''));
|
|
88
|
+
v_out := replace(v_out, '{YYYY}', to_char(p_date, 'YYYY'));
|
|
89
|
+
v_out := replace(v_out, '{YY}', to_char(p_date, 'YY'));
|
|
90
|
+
v_out := replace(v_out, '{MM}', to_char(p_date, 'MM'));
|
|
91
|
+
|
|
92
|
+
v_token := (regexp_match(v_out, '\{N+\}'))[1];
|
|
93
|
+
if v_token is null then
|
|
94
|
+
raise exception 'number_format_without_counter: % carries no {N...} counter, so every document would be numbered the same', p_format;
|
|
95
|
+
end if;
|
|
96
|
+
|
|
97
|
+
-- Padded to the width the pattern asks for, and never truncated to it: a
|
|
98
|
+
-- counter that has outgrown its padding is a longer number, not a wrong one.
|
|
99
|
+
v_width := length(v_token) - 2;
|
|
100
|
+
v_out := replace(v_out, v_token,
|
|
101
|
+
case when length(v_digits) >= v_width then v_digits
|
|
102
|
+
else lpad(v_digits, v_width, '0') end);
|
|
103
|
+
|
|
104
|
+
v_left := (regexp_match(v_out, '\{[^}]*\}'))[1];
|
|
105
|
+
if v_left is not null then
|
|
106
|
+
raise exception 'unknown_number_token: % is not a token of the number format grammar ({CODE}, {YYYY}, {YY}, {MM}, {N...})', v_left;
|
|
107
|
+
end if;
|
|
108
|
+
|
|
109
|
+
return v_out;
|
|
110
|
+
end;
|
|
111
|
+
$$;
|
|
112
|
+
|
|
113
|
+
comment on function format_number(text, text, date, integer) is
|
|
114
|
+
'One document number, rendered from the pattern the country pack declares. Raises rather than guessing at a token it does not know.';
|
|
115
|
+
|
|
116
|
+
-- ---------------------------------------------------------------------------
|
|
117
|
+
-- next_entry_number, on the pattern
|
|
118
|
+
-- ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
create or replace function next_entry_number(p_journal_id uuid, p_date date)
|
|
121
|
+
returns text
|
|
122
|
+
language plpgsql
|
|
123
|
+
security definer
|
|
124
|
+
set search_path = public, pg_temp
|
|
125
|
+
as $$
|
|
126
|
+
declare
|
|
127
|
+
v_code text;
|
|
128
|
+
v_company uuid;
|
|
129
|
+
v_format text;
|
|
130
|
+
v_period smallint;
|
|
131
|
+
v_number integer;
|
|
132
|
+
begin
|
|
133
|
+
select j.code, j.company_id into v_code, v_company from journals j where j.id = p_journal_id;
|
|
134
|
+
if v_code is null then
|
|
135
|
+
raise exception 'unknown_journal: journal % does not exist', p_journal_id;
|
|
136
|
+
end if;
|
|
137
|
+
|
|
138
|
+
if auth.uid() is not null and not has_capability(v_company, 'entries.post') then
|
|
139
|
+
raise exception 'not_allowed: drawing a number in this company needs entries.post'
|
|
140
|
+
using errcode = '42501';
|
|
141
|
+
end if;
|
|
142
|
+
|
|
143
|
+
select number_format into v_format from numbering_rules(v_company);
|
|
144
|
+
if v_format is null or v_format = '' then
|
|
145
|
+
raise exception 'no_number_format: the country pack of this company declares no documents.number_format, and there is no default to fall back on';
|
|
146
|
+
end if;
|
|
147
|
+
|
|
148
|
+
-- A pattern that carries the year restarts with it; one that does not is a
|
|
149
|
+
-- single series, kept under the period that is not a year.
|
|
150
|
+
v_period := case when v_format ~ '\{(YYYY|YY)\}'
|
|
151
|
+
then extract(year from p_date)::smallint
|
|
152
|
+
else 0::smallint end;
|
|
153
|
+
|
|
154
|
+
insert into journal_sequences (journal_id, year, last_number)
|
|
155
|
+
values (p_journal_id, v_period, 1)
|
|
156
|
+
on conflict (journal_id, year)
|
|
157
|
+
do update set last_number = journal_sequences.last_number + 1
|
|
158
|
+
returning last_number into v_number;
|
|
159
|
+
|
|
160
|
+
return format_number(v_format, v_code, p_date, v_number);
|
|
161
|
+
end;
|
|
162
|
+
$$;
|
|
163
|
+
|
|
164
|
+
comment on function next_entry_number(uuid, date) is
|
|
165
|
+
'Next number for a journal, on the pattern the country pack declares. Atomic: the counter row is locked, not the journal. Definer, because the counter is infrastructure and nobody writes it by hand.';
|
|
166
|
+
|
|
167
|
+
comment on column journal_sequences.year is
|
|
168
|
+
'The period the counter belongs to: the year where the pattern carries one, and 0 where it does not and the series runs on.';
|
|
169
|
+
|
|
170
|
+
-- ---------------------------------------------------------------------------
|
|
171
|
+
-- post_entry, reading numbering_gapless
|
|
172
|
+
--
|
|
173
|
+
-- Unchanged but for the guard: everything else is the function as published.
|
|
174
|
+
-- ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
create or replace function post_entry(p_entry_id uuid)
|
|
177
|
+
returns entries
|
|
178
|
+
language plpgsql
|
|
179
|
+
as $$
|
|
180
|
+
declare
|
|
181
|
+
v_entry entries%rowtype;
|
|
182
|
+
v_lines integer;
|
|
183
|
+
v_gapless boolean;
|
|
184
|
+
begin
|
|
185
|
+
select * into v_entry from entries where id = p_entry_id for update;
|
|
186
|
+
if not found then
|
|
187
|
+
raise exception 'unknown_entry: entry % does not exist', p_entry_id;
|
|
188
|
+
end if;
|
|
189
|
+
if v_entry.state = 'posted' then
|
|
190
|
+
return v_entry;
|
|
191
|
+
end if;
|
|
192
|
+
if v_entry.state = 'cancelled' then
|
|
193
|
+
raise exception 'entry_cancelled: entry % cannot be posted', p_entry_id;
|
|
194
|
+
end if;
|
|
195
|
+
|
|
196
|
+
select count(*) into v_lines from entry_lines where entry_id = p_entry_id;
|
|
197
|
+
if v_lines = 0 then
|
|
198
|
+
raise exception 'entry_empty: entry % has no lines', p_entry_id;
|
|
199
|
+
end if;
|
|
200
|
+
|
|
201
|
+
-- Re-read the totals maintained by the line trigger.
|
|
202
|
+
select * into v_entry from entries where id = p_entry_id;
|
|
203
|
+
if v_entry.total_debit <> v_entry.total_credit then
|
|
204
|
+
raise exception 'entry_unbalanced: entry % has debit % and credit %',
|
|
205
|
+
p_entry_id, v_entry.total_debit, v_entry.total_credit;
|
|
206
|
+
end if;
|
|
207
|
+
|
|
208
|
+
-- Where the law forbids a hole, the number comes from the counter and from
|
|
209
|
+
-- nowhere else. A number chosen by hand is how a sequence acquires one.
|
|
210
|
+
if v_entry.number is not null then
|
|
211
|
+
select numbering_gapless into v_gapless from numbering_rules(v_entry.company_id);
|
|
212
|
+
if v_gapless then
|
|
213
|
+
raise exception 'numbering_gapless: this country forbids a hole in the sequence, so entry % may not be posted under a number chosen by hand. Leave entries.number empty and the journal counter draws it',
|
|
214
|
+
p_entry_id;
|
|
215
|
+
end if;
|
|
216
|
+
end if;
|
|
217
|
+
|
|
218
|
+
perform assert_period_open(v_entry.company_id, v_entry.entry_date, true);
|
|
219
|
+
|
|
220
|
+
update entries
|
|
221
|
+
set number = coalesce(number, next_entry_number(journal_id, entry_date)),
|
|
222
|
+
fiscal_year_id = coalesce(fiscal_year_id, fiscal_year_at(company_id, entry_date)),
|
|
223
|
+
state = 'posted',
|
|
224
|
+
posted_at = now()
|
|
225
|
+
where id = p_entry_id
|
|
226
|
+
returning * into v_entry;
|
|
227
|
+
|
|
228
|
+
return v_entry;
|
|
229
|
+
end;
|
|
230
|
+
$$;
|
|
231
|
+
|
|
232
|
+
comment on function post_entry(uuid) is
|
|
233
|
+
'Validates, numbers and posts an entry. Raises rather than warning: a swallowed error is a missing entry. Where the country forbids a hole in the sequence, it refuses a number chosen by hand.';
|
|
234
|
+
|
|
235
|
+
revoke execute on all functions in schema public from public;
|