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,41 @@
|
|
|
1
|
+
-- Ekwo module `assets` — its tables leave with the company.
|
|
2
|
+
--
|
|
3
|
+
-- The socle writes an archive of one company from a registry of tables, and
|
|
4
|
+
-- refuses to write one while a table that belongs to a company is
|
|
5
|
+
-- unclassified. A module answers for its own through `<schema>.archive_tables()`,
|
|
6
|
+
-- looked up by the socle the way `can_disable()` is — a function in this schema
|
|
7
|
+
-- and not rows in a table of the socle, because this migration may be applied
|
|
8
|
+
-- on a socle that does not have that table yet.
|
|
9
|
+
--
|
|
10
|
+
-- All three tables travel. The register and the schedule are the company's,
|
|
11
|
+
-- and a depreciation line points at the entry it posted: left behind, the
|
|
12
|
+
-- ledger would arrive with entries tagged `assets` that nothing explains.
|
|
13
|
+
-- `category_templates` and `country_rules` are reference data of the
|
|
14
|
+
-- installation and belong to no company.
|
|
15
|
+
--
|
|
16
|
+
-- `load_order` is relative to this module; the socle places every module after
|
|
17
|
+
-- its own tables.
|
|
18
|
+
|
|
19
|
+
create or replace function assets.archive_tables()
|
|
20
|
+
returns table (
|
|
21
|
+
table_name text,
|
|
22
|
+
disposition text,
|
|
23
|
+
reason text,
|
|
24
|
+
via_column text,
|
|
25
|
+
via_table text,
|
|
26
|
+
load_order integer
|
|
27
|
+
)
|
|
28
|
+
language sql
|
|
29
|
+
immutable
|
|
30
|
+
as $$
|
|
31
|
+
values
|
|
32
|
+
('assets'::text, 'exported'::text, null::text, null::text, null::text, 1),
|
|
33
|
+
('depreciation_lines', 'exported', null, null, null, 2),
|
|
34
|
+
('disposals', 'exported', null, null, null, 3);
|
|
35
|
+
$$;
|
|
36
|
+
|
|
37
|
+
comment on function assets.archive_tables() is
|
|
38
|
+
'What an archive of one company does with each table of this module. Read by `public.company_archive_tables()`.';
|
|
39
|
+
|
|
40
|
+
revoke execute on function assets.archive_tables() from public, anon;
|
|
41
|
+
grant execute on function assets.archive_tables() to authenticated, service_role;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ekwo.ai/schemas/module/1.json",
|
|
3
|
+
"code": "budgets",
|
|
4
|
+
"name": "Budgets",
|
|
5
|
+
"description": "A budget per financial year, its lines per account and period, and the variance against what the ledger actually holds. Reads the socle and writes nothing to it.",
|
|
6
|
+
"schema": "budgets",
|
|
7
|
+
"version": "1.0.0",
|
|
8
|
+
"status": "available",
|
|
9
|
+
"requires_socle_min": "20260913074512",
|
|
10
|
+
"posts": false,
|
|
11
|
+
"mcp": { "prefix": "budgets" }
|
|
12
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
-- Ekwo — the `budgets` module: what a company planned, against what it booked.
|
|
2
|
+
--
|
|
3
|
+
-- The second module, and the one that proves the mechanism holds for a module
|
|
4
|
+
-- that is not like the first. It has **no country data at all** — a budget is a
|
|
5
|
+
-- decision a business takes, not a rule a country writes — and it **never
|
|
6
|
+
-- writes to the ledger**: it reads `public.entry_lines` and compares. So there
|
|
7
|
+
-- is no pack section, no seed, no call to `post_module_entry()`, and
|
|
8
|
+
-- `module.json` says `"posts": false`, which a test holds it to.
|
|
9
|
+
--
|
|
10
|
+
-- It also deliberately writes **no `budgets.can_disable()`**. The convention
|
|
11
|
+
-- `disable_module()` follows is to look for that function and allow the
|
|
12
|
+
-- disable when there is none, and this is the module that exercises the
|
|
13
|
+
-- absence: turning budgets off takes nothing away, because the rows stay where
|
|
14
|
+
-- they are and come back the moment it is turned on again. Row level security
|
|
15
|
+
-- is what hides them in between, which is the whole meaning of
|
|
16
|
+
-- `module_enabled()` sitting in every policy.
|
|
17
|
+
--
|
|
18
|
+
-- **The sign.** A budget is stated the way a business says it out loud: an
|
|
19
|
+
-- income of 100 000 and a cost of 60 000 are both positive numbers. The ledger
|
|
20
|
+
-- does not work that way — an income account carries a credit balance — so the
|
|
21
|
+
-- comparison multiplies the ledger balance by −1 on an income account and by
|
|
22
|
+
-- +1 everywhere else. That is one rule, it comes from `accounts.internal_group`
|
|
23
|
+
-- rather than from a column somebody fills in, and it is what the eighteen
|
|
24
|
+
-- account types are for.
|
|
25
|
+
--
|
|
26
|
+
-- **What counts as actual.** Posted entries, and only the ones that are
|
|
27
|
+
-- `kind = 'normal'`. A closing entry is the mirror image of the year and an
|
|
28
|
+
-- appropriation entry moves its result; neither is something a budget planned,
|
|
29
|
+
-- and leaving them in would make a closed year read as a variance of exactly
|
|
30
|
+
-- minus the budget. That is the same exclusion `financial_statement()` makes on
|
|
31
|
+
-- an income statement, for the same reason.
|
|
32
|
+
|
|
33
|
+
create schema budgets;
|
|
34
|
+
|
|
35
|
+
comment on schema budgets is
|
|
36
|
+
'Ekwo module `budgets`: what a company planned, per account and period, against what its ledger holds. Reads the socle, writes nothing to it.';
|
|
37
|
+
|
|
38
|
+
create type budgets.budget_state as enum ('draft', 'approved', 'closed');
|
|
39
|
+
|
|
40
|
+
comment on type budgets.budget_state is
|
|
41
|
+
'draft while it is being written, approved once it is the plan, closed when the period it covers is behind. It gates nothing in this module: a variance is a reading, and reading a draft is useful.';
|
|
42
|
+
|
|
43
|
+
create table budgets.budgets (
|
|
44
|
+
id uuid primary key default gen_random_uuid(),
|
|
45
|
+
company_id uuid not null references public.companies(id) on delete cascade,
|
|
46
|
+
fiscal_year_id uuid references public.fiscal_years(id) on delete restrict,
|
|
47
|
+
code text not null,
|
|
48
|
+
name text not null,
|
|
49
|
+
state budgets.budget_state not null default 'draft',
|
|
50
|
+
notes text,
|
|
51
|
+
created_at timestamptz not null default now(),
|
|
52
|
+
updated_at timestamptz not null default now(),
|
|
53
|
+
unique (company_id, code),
|
|
54
|
+
foreign key (fiscal_year_id) references public.fiscal_years(id)
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
comment on table budgets.budgets is
|
|
58
|
+
'One budget of one company, usually for one financial year. A company may hold several — a plan and a revision are two budgets and not two columns.';
|
|
59
|
+
comment on column budgets.budgets.fiscal_year_id is
|
|
60
|
+
'The year this budget is for, where it is for one. Null on a rolling budget, whose lines carry their own periods anyway.';
|
|
61
|
+
|
|
62
|
+
create unique index budgets_id_company_idx on budgets.budgets (id, company_id);
|
|
63
|
+
|
|
64
|
+
create trigger budgets_set_updated_at
|
|
65
|
+
before update on budgets.budgets
|
|
66
|
+
for each row execute function public.set_updated_at();
|
|
67
|
+
|
|
68
|
+
create table budgets.lines (
|
|
69
|
+
id uuid primary key default gen_random_uuid(),
|
|
70
|
+
budget_id uuid not null references budgets.budgets(id) on delete cascade,
|
|
71
|
+
company_id uuid not null references public.companies(id) on delete cascade,
|
|
72
|
+
account_id uuid not null references public.accounts(id) on delete restrict,
|
|
73
|
+
period_start date not null,
|
|
74
|
+
period_end date not null,
|
|
75
|
+
amount numeric(16, 2) not null,
|
|
76
|
+
note text,
|
|
77
|
+
created_at timestamptz not null default now(),
|
|
78
|
+
updated_at timestamptz not null default now(),
|
|
79
|
+
-- One line per account and period. A second figure for the same month on the
|
|
80
|
+
-- same account is a revision, and a revision is a budget of its own.
|
|
81
|
+
unique (budget_id, account_id, period_start, period_end),
|
|
82
|
+
constraint budgets_lines_period check (period_end >= period_start),
|
|
83
|
+
foreign key (budget_id, company_id) references budgets.budgets(id, company_id) on delete cascade,
|
|
84
|
+
foreign key (account_id, company_id) references public.accounts(id, company_id)
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
comment on table budgets.lines is
|
|
88
|
+
'What one account is expected to carry over one period, in the sign a business says it: an income and a cost are both positive.';
|
|
89
|
+
|
|
90
|
+
create index budgets_lines_account_idx on budgets.lines (company_id, account_id, period_start);
|
|
91
|
+
|
|
92
|
+
create trigger budgets_lines_set_updated_at
|
|
93
|
+
before update on budgets.lines
|
|
94
|
+
for each row execute function public.set_updated_at();
|
|
95
|
+
|
|
96
|
+
-- ---------------------------------------------------------------------------
|
|
97
|
+
-- The reading
|
|
98
|
+
-- ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
create or replace function budgets.variance(
|
|
101
|
+
p_company_id uuid,
|
|
102
|
+
p_budget_id uuid,
|
|
103
|
+
p_from date,
|
|
104
|
+
p_to date
|
|
105
|
+
)
|
|
106
|
+
returns table (
|
|
107
|
+
account_code text,
|
|
108
|
+
account_name text,
|
|
109
|
+
budget numeric,
|
|
110
|
+
actual numeric,
|
|
111
|
+
variance numeric
|
|
112
|
+
)
|
|
113
|
+
language sql
|
|
114
|
+
stable
|
|
115
|
+
as $$
|
|
116
|
+
with planned as (
|
|
117
|
+
select l.account_id, sum(l.amount) as amount
|
|
118
|
+
from budgets.lines l
|
|
119
|
+
where l.budget_id = p_budget_id
|
|
120
|
+
and l.company_id = p_company_id
|
|
121
|
+
and l.period_start >= p_from
|
|
122
|
+
and l.period_end <= p_to
|
|
123
|
+
group by l.account_id
|
|
124
|
+
),
|
|
125
|
+
booked as (
|
|
126
|
+
select l.account_id,
|
|
127
|
+
sum((l.debit - l.credit) * case when a.internal_group = 'income' then -1 else 1 end) as amount
|
|
128
|
+
from public.entry_lines l
|
|
129
|
+
join public.entries e on e.id = l.entry_id
|
|
130
|
+
join public.accounts a on a.id = l.account_id
|
|
131
|
+
where l.company_id = p_company_id
|
|
132
|
+
and e.state = 'posted'
|
|
133
|
+
and e.kind = 'normal'
|
|
134
|
+
and e.entry_date between p_from and p_to
|
|
135
|
+
and l.account_id in (select account_id from planned)
|
|
136
|
+
group by l.account_id
|
|
137
|
+
)
|
|
138
|
+
select a.code,
|
|
139
|
+
a.name,
|
|
140
|
+
coalesce(p.amount, 0)::numeric(16, 2),
|
|
141
|
+
coalesce(b.amount, 0)::numeric(16, 2),
|
|
142
|
+
(coalesce(b.amount, 0) - coalesce(p.amount, 0))::numeric(16, 2)
|
|
143
|
+
from planned p
|
|
144
|
+
join public.accounts a on a.id = p.account_id
|
|
145
|
+
left join booked b on b.account_id = p.account_id
|
|
146
|
+
order by a.code;
|
|
147
|
+
$$;
|
|
148
|
+
|
|
149
|
+
comment on function budgets.variance(uuid, uuid, date, date) is
|
|
150
|
+
'Budget against ledger, per account, over a period. Both figures are in the sign a business states them in — an income account''s credit balance is flipped — and the variance is the actual less the plan. Reads posted entries of kind `normal` only: a closing or appropriation entry is not what a period earned.';
|
|
151
|
+
|
|
152
|
+
-- ---------------------------------------------------------------------------
|
|
153
|
+
-- Row level security
|
|
154
|
+
-- ---------------------------------------------------------------------------
|
|
155
|
+
|
|
156
|
+
alter table budgets.budgets enable row level security;
|
|
157
|
+
alter table budgets.lines enable row level security;
|
|
158
|
+
|
|
159
|
+
create policy budgets_select on budgets.budgets
|
|
160
|
+
for select using (public.module_enabled(company_id, 'budgets'));
|
|
161
|
+
create policy budgets_write on budgets.budgets
|
|
162
|
+
for all using (public.module_enabled(company_id, 'budgets') and public.can_write_company(company_id))
|
|
163
|
+
with check (public.module_enabled(company_id, 'budgets') and public.can_write_company(company_id));
|
|
164
|
+
|
|
165
|
+
create policy budgets_lines_select on budgets.lines
|
|
166
|
+
for select using (public.module_enabled(company_id, 'budgets'));
|
|
167
|
+
create policy budgets_lines_write on budgets.lines
|
|
168
|
+
for all using (public.module_enabled(company_id, 'budgets') and public.can_write_company(company_id))
|
|
169
|
+
with check (public.module_enabled(company_id, 'budgets') and public.can_write_company(company_id));
|
|
170
|
+
|
|
171
|
+
-- ---------------------------------------------------------------------------
|
|
172
|
+
-- Privileges — a module schema does its own, because `public` gets them from
|
|
173
|
+
-- Supabase's default privileges and a schema a migration created gets nothing.
|
|
174
|
+
-- ---------------------------------------------------------------------------
|
|
175
|
+
|
|
176
|
+
grant usage on schema budgets to anon, authenticated, service_role;
|
|
177
|
+
|
|
178
|
+
grant select on all tables in schema budgets to anon;
|
|
179
|
+
grant select, insert, update, delete on all tables in schema budgets to authenticated, service_role;
|
|
180
|
+
grant execute on all functions in schema budgets to authenticated, service_role;
|
|
181
|
+
|
|
182
|
+
alter default privileges in schema budgets grant select on tables to anon;
|
|
183
|
+
alter default privileges in schema budgets
|
|
184
|
+
grant select, insert, update, delete on tables to authenticated, service_role;
|
|
185
|
+
alter default privileges in schema budgets revoke execute on functions from public, anon;
|
|
186
|
+
alter default privileges in schema budgets grant execute on functions to authenticated, service_role;
|
|
187
|
+
|
|
188
|
+
revoke execute on all functions in schema budgets from public;
|
|
189
|
+
|
|
190
|
+
-- ---------------------------------------------------------------------------
|
|
191
|
+
-- The registry row
|
|
192
|
+
-- ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
insert into public.modules (code, name, description, schema_name, version, status, requires_socle_min)
|
|
195
|
+
values (
|
|
196
|
+
'budgets',
|
|
197
|
+
'Budgets',
|
|
198
|
+
'A budget per financial year, its lines per account and period, and the variance against what the ledger actually holds. No country data, and nothing written to the ledger.',
|
|
199
|
+
'budgets',
|
|
200
|
+
'1.0.0',
|
|
201
|
+
'available',
|
|
202
|
+
'20260913074512'
|
|
203
|
+
)
|
|
204
|
+
on conflict (code) do update set
|
|
205
|
+
name = excluded.name,
|
|
206
|
+
description = excluded.description,
|
|
207
|
+
schema_name = excluded.schema_name,
|
|
208
|
+
version = excluded.version,
|
|
209
|
+
status = excluded.status,
|
|
210
|
+
requires_socle_min = excluded.requires_socle_min;
|
package/dist/assets/modules/budgets/supabase/migrations/20260913104233_foreign_key_indexes.sql
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- Ekwo OS — a foreign key without an index is a sequential scan waiting for
|
|
2
|
+
-- a big enough table.
|
|
3
|
+
--
|
|
4
|
+
-- Postgres indexes the *referenced* side of a foreign key, because that side
|
|
5
|
+
-- is a primary key. It indexes nothing on the referencing side. Two things
|
|
6
|
+
-- then read the whole table:
|
|
7
|
+
--
|
|
8
|
+
-- * every delete or key update of the parent, which has to prove no child
|
|
9
|
+
-- points at the row — deleting one contact scans `entry_lines`;
|
|
10
|
+
-- * every join written the natural way, which for an accounting core is
|
|
11
|
+
-- most of them: a document to its lines, an entry to its lines, a line to
|
|
12
|
+
-- its account, its tax, its contact.
|
|
13
|
+
--
|
|
14
|
+
-- On the demo company nothing is slow, which is exactly why this survived 49
|
|
15
|
+
-- migrations. On a company with four years of books it is the difference
|
|
16
|
+
-- between a report and a timeout.
|
|
17
|
+
--
|
|
18
|
+
-- 3 indexes: every foreign key of the `budgets` module that had none, and the
|
|
19
|
+
-- `company_id` of the tables that had none either — which the sweep covers,
|
|
20
|
+
-- because `company_id` is itself a foreign key everywhere it appears.
|
|
21
|
+
--
|
|
22
|
+
-- Where a table has both a single-column key and a composite one leading with
|
|
23
|
+
-- the same column — `(account_id)` and `(account_id, company_id)`, the shape
|
|
24
|
+
-- this schema uses to keep a child in its parent's company — one index on the
|
|
25
|
+
-- composite serves both, and that is the one created.
|
|
26
|
+
--
|
|
27
|
+
-- The list is not maintained by hand. `tests/schema.test.ts` asks the
|
|
28
|
+
-- catalogue the same question and fails when the answer is not empty, so a
|
|
29
|
+
-- foreign key added later arrives with its index or the build says so.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
-- budgets.budgets
|
|
33
|
+
create index if not exists budgets_fiscal_year_idx on budgets.budgets (fiscal_year_id);
|
|
34
|
+
|
|
35
|
+
-- budgets.lines
|
|
36
|
+
create index if not exists lines_account_company_idx on budgets.lines (account_id, company_id);
|
|
37
|
+
create index if not exists lines_budget_company_idx on budgets.lines (budget_id, company_id);
|
package/dist/assets/modules/budgets/supabase/migrations/20260913104501_budgets_capabilities.sql
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
-- Ekwo module `budgets` — the module names what it lets somebody do.
|
|
2
|
+
--
|
|
3
|
+
-- Its policies asked `public.can_write_company()`, which is `entries.write`:
|
|
4
|
+
-- whoever may draft a journal entry could also write next year's budget. A
|
|
5
|
+
-- budget is not a book — it is written by whoever plans, read by whoever
|
|
6
|
+
-- reports, and it is the one thing in an installation that is deliberately
|
|
7
|
+
-- not the truth. Sharing a lock with the ledger is the wrong shape.
|
|
8
|
+
--
|
|
9
|
+
-- budgets.read — see the budgets and their variance.
|
|
10
|
+
-- budgets.write — create and change a budget and its lines.
|
|
11
|
+
--
|
|
12
|
+
-- There is no `budgets.post`: this module writes nothing to the ledger, which
|
|
13
|
+
-- its manifest says (`"posts": false`) and a test of the socle enforces.
|
|
14
|
+
--
|
|
15
|
+
-- The presets follow the socle's. The owner preset was filled with `select
|
|
16
|
+
-- 'owner', code from capabilities` at the socle's own migration time, so a
|
|
17
|
+
-- code added later has to name the owner itself.
|
|
18
|
+
|
|
19
|
+
insert into public.capabilities (code, area, description) values
|
|
20
|
+
('budgets.read', 'budgets', 'Read the budgets of a company and their variance against the ledger.'),
|
|
21
|
+
('budgets.write', 'budgets', 'Create and change a budget and its lines.')
|
|
22
|
+
on conflict (code) do update set area = excluded.area, description = excluded.description;
|
|
23
|
+
|
|
24
|
+
insert into public.role_capabilities (role, capability) values
|
|
25
|
+
('viewer'::public.member_role, 'budgets.read'),
|
|
26
|
+
('accountant'::public.member_role, 'budgets.read'),
|
|
27
|
+
('accountant'::public.member_role, 'budgets.write'),
|
|
28
|
+
('owner'::public.member_role, 'budgets.read'),
|
|
29
|
+
('owner'::public.member_role, 'budgets.write')
|
|
30
|
+
on conflict do nothing;
|
|
31
|
+
|
|
32
|
+
-- `module_enabled()` stays in front of every policy: a capability says what a
|
|
33
|
+
-- person may do, and the module being enabled says whether the company has
|
|
34
|
+
-- this at all.
|
|
35
|
+
|
|
36
|
+
drop policy budgets_select on budgets.budgets;
|
|
37
|
+
create policy budgets_select on budgets.budgets
|
|
38
|
+
for select using (
|
|
39
|
+
public.module_enabled(company_id, 'budgets')
|
|
40
|
+
and public.has_capability(company_id, 'budgets.read')
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
drop policy budgets_write on budgets.budgets;
|
|
44
|
+
create policy budgets_write on budgets.budgets
|
|
45
|
+
for all using (
|
|
46
|
+
public.module_enabled(company_id, 'budgets')
|
|
47
|
+
and public.has_capability(company_id, 'budgets.write')
|
|
48
|
+
)
|
|
49
|
+
with check (
|
|
50
|
+
public.module_enabled(company_id, 'budgets')
|
|
51
|
+
and public.has_capability(company_id, 'budgets.write')
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
drop policy budgets_lines_select on budgets.lines;
|
|
55
|
+
create policy budgets_lines_select on budgets.lines
|
|
56
|
+
for select using (
|
|
57
|
+
public.module_enabled(company_id, 'budgets')
|
|
58
|
+
and public.has_capability(company_id, 'budgets.read')
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
drop policy budgets_lines_write on budgets.lines;
|
|
62
|
+
create policy budgets_lines_write on budgets.lines
|
|
63
|
+
for all using (
|
|
64
|
+
public.module_enabled(company_id, 'budgets')
|
|
65
|
+
and public.has_capability(company_id, 'budgets.write')
|
|
66
|
+
)
|
|
67
|
+
with check (
|
|
68
|
+
public.module_enabled(company_id, 'budgets')
|
|
69
|
+
and public.has_capability(company_id, 'budgets.write')
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
revoke execute on all functions in schema budgets from public;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
-- Ekwo OS (budgets) — a plan and what was booked against it are written at
|
|
2
|
+
-- the decimals of the company's currency.
|
|
3
|
+
--
|
|
4
|
+
-- `budgets.variance()` cast its three figures to `numeric(16, 2)`, which is a
|
|
5
|
+
-- rounding to two decimals wearing the clothes of a type. A plan stated in a
|
|
6
|
+
-- currency with no decimals came back with two of them, and the variance
|
|
7
|
+
-- between a plan and a ledger that both hold whole units read as a fraction.
|
|
8
|
+
--
|
|
9
|
+
-- The three casts become `public.round_amount`, which asks the company what
|
|
10
|
+
-- its currency is. Nothing else about the reading changes: the sign a business
|
|
11
|
+
-- states a figure in, and the closing entries left out of it, are as they were.
|
|
12
|
+
|
|
13
|
+
create or replace function budgets.variance(
|
|
14
|
+
p_company_id uuid,
|
|
15
|
+
p_budget_id uuid,
|
|
16
|
+
p_from date,
|
|
17
|
+
p_to date
|
|
18
|
+
)
|
|
19
|
+
returns table (
|
|
20
|
+
account_code text,
|
|
21
|
+
account_name text,
|
|
22
|
+
budget numeric,
|
|
23
|
+
actual numeric,
|
|
24
|
+
variance numeric
|
|
25
|
+
)
|
|
26
|
+
language sql
|
|
27
|
+
stable
|
|
28
|
+
as $$
|
|
29
|
+
with planned as (
|
|
30
|
+
select l.account_id, sum(l.amount) as amount
|
|
31
|
+
from budgets.lines l
|
|
32
|
+
where l.budget_id = p_budget_id
|
|
33
|
+
and l.company_id = p_company_id
|
|
34
|
+
and l.period_start >= p_from
|
|
35
|
+
and l.period_end <= p_to
|
|
36
|
+
group by l.account_id
|
|
37
|
+
),
|
|
38
|
+
booked as (
|
|
39
|
+
select l.account_id,
|
|
40
|
+
sum((l.debit - l.credit) * case when a.internal_group = 'income' then -1 else 1 end) as amount
|
|
41
|
+
from public.entry_lines l
|
|
42
|
+
join public.entries e on e.id = l.entry_id
|
|
43
|
+
join public.accounts a on a.id = l.account_id
|
|
44
|
+
where l.company_id = p_company_id
|
|
45
|
+
and e.state = 'posted'
|
|
46
|
+
and e.kind = 'normal'
|
|
47
|
+
and e.entry_date between p_from and p_to
|
|
48
|
+
and l.account_id in (select account_id from planned)
|
|
49
|
+
group by l.account_id
|
|
50
|
+
)
|
|
51
|
+
select a.code,
|
|
52
|
+
a.name,
|
|
53
|
+
public.round_amount(coalesce(p.amount, 0), r.rounding),
|
|
54
|
+
public.round_amount(coalesce(b.amount, 0), r.rounding),
|
|
55
|
+
public.round_amount(coalesce(b.amount, 0) - coalesce(p.amount, 0), r.rounding)
|
|
56
|
+
from planned p
|
|
57
|
+
join public.accounts a on a.id = p.account_id
|
|
58
|
+
left join booked b on b.account_id = p.account_id
|
|
59
|
+
cross join (select public.rounding_of(p_company_id) as rounding) r
|
|
60
|
+
order by a.code;
|
|
61
|
+
$$;
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
comment on function budgets.variance(uuid, uuid, date, date) is
|
|
65
|
+
'Budget against ledger, per account, over a period, at the decimals of the company''s currency. Both figures are in the sign a business states them in — an income account''s credit balance is flipped — and the variance is the actual less the plan. Reads posted entries of kind `normal` only: a closing or appropriation entry is not what a period earned.';
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
revoke execute on all functions in schema budgets from public;
|
package/dist/assets/modules/budgets/supabase/migrations/20260914145048_budgets_accounts_in_use.sql
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
-- The `budgets` module answers which accounts of a company it holds.
|
|
2
|
+
--
|
|
3
|
+
-- Same convention as `assets`: `public.accounts_in_use()` asks each module the
|
|
4
|
+
-- company has enabled for `<schema>.accounts_in_use(uuid)` rather than naming
|
|
5
|
+
-- the schema itself, so the socle keeps ignoring what is built beside it.
|
|
6
|
+
--
|
|
7
|
+
-- A budget line is a planned amount on an account, and an account somebody has
|
|
8
|
+
-- budgeted for is an account they mean to use — which is the whole case for
|
|
9
|
+
-- counting it before anything is booked on it.
|
|
10
|
+
--
|
|
11
|
+
-- Security invoker: the policies of `budgets.lines` decide, and a caller who
|
|
12
|
+
-- is not a member of the company sees nothing.
|
|
13
|
+
|
|
14
|
+
create or replace function budgets.accounts_in_use(p_company_id uuid)
|
|
15
|
+
returns setof uuid
|
|
16
|
+
language sql
|
|
17
|
+
stable
|
|
18
|
+
as $$
|
|
19
|
+
select distinct l.account_id
|
|
20
|
+
from budgets.lines l
|
|
21
|
+
where l.company_id = p_company_id;
|
|
22
|
+
$$;
|
|
23
|
+
|
|
24
|
+
comment on function budgets.accounts_in_use(uuid) is
|
|
25
|
+
'The accounts this module points at for one company: every account a budget line plans an amount on. Read by public.accounts_in_use() through the module convention.';
|
|
26
|
+
|
|
27
|
+
revoke execute on function budgets.accounts_in_use(uuid) from public, anon;
|
|
28
|
+
grant execute on function budgets.accounts_in_use(uuid) to authenticated, service_role;
|
|
29
|
+
|
|
30
|
+
revoke execute on all functions in schema budgets from public;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
-- The `budgets` module grants its own rights, by name.
|
|
2
|
+
--
|
|
3
|
+
-- Same change as `20260914151207` for the socle and
|
|
4
|
+
-- `20260914151530` for `assets`: the privileges a module schema hands out are
|
|
5
|
+
-- written down, table by table, instead of being taken from a wildcard or
|
|
6
|
+
-- from the project's default privileges.
|
|
7
|
+
--
|
|
8
|
+
-- `20260913083012` granted `select on all tables in schema budgets to anon`.
|
|
9
|
+
-- Row level security answered every such read with an empty set, so nothing
|
|
10
|
+
-- leaked, and the doctrine of `20260911210131` still says the anonymous role
|
|
11
|
+
-- reaches the policy helpers and nothing else. Published migrations are never
|
|
12
|
+
-- edited, so it is corrected here.
|
|
13
|
+
|
|
14
|
+
revoke all on all tables in schema budgets from anon, authenticated, service_role;
|
|
15
|
+
revoke all on all sequences in schema budgets from anon, authenticated, service_role;
|
|
16
|
+
|
|
17
|
+
alter default privileges in schema budgets revoke all on tables from anon, authenticated, service_role;
|
|
18
|
+
alter default privileges in schema budgets revoke all on sequences from anon, authenticated, service_role;
|
|
19
|
+
alter default privileges in schema budgets revoke execute on functions from public, anon, authenticated, service_role;
|
|
20
|
+
|
|
21
|
+
-- Both tables carry a policy `for all`: a budget and its lines are written by
|
|
22
|
+
-- the people who plan the year, and read by everyone who may read the books.
|
|
23
|
+
grant select, insert, update, delete on table
|
|
24
|
+
budgets.budgets,
|
|
25
|
+
budgets.lines
|
|
26
|
+
to authenticated, service_role;
|
|
27
|
+
|
|
28
|
+
-- `budgets.variance` keeps the grant its own migration gave it. The schema
|
|
29
|
+
-- has no trigger function and no sequence.
|
|
30
|
+
|
|
31
|
+
revoke execute on all functions in schema budgets from public;
|
package/dist/assets/modules/budgets/supabase/migrations/20260918113954_budgets_client_reads.sql
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- Ekwo module `budgets` — the client preset reads this module too.
|
|
2
|
+
--
|
|
3
|
+
-- The socle's `client` preset holds what `viewer` holds, and it was filled
|
|
4
|
+
-- from `role_capabilities` when the socle's migration ran: on an installation
|
|
5
|
+
-- that already carried this module, `budgets.read` was copied then. On a fresh
|
|
6
|
+
-- one the socle is applied before any module, so the row has to come from
|
|
7
|
+
-- here.
|
|
8
|
+
--
|
|
9
|
+
-- The label is read from the catalogue rather than written as a literal. A
|
|
10
|
+
-- module may be applied on a socle older than the preset, where
|
|
11
|
+
-- `'client'::member_role` does not parse; there this inserts nothing, and the
|
|
12
|
+
-- socle's migration copies the row the day it arrives.
|
|
13
|
+
|
|
14
|
+
insert into public.role_capabilities (role, capability)
|
|
15
|
+
select e.enumlabel::text::public.member_role, 'budgets.read'
|
|
16
|
+
from pg_catalog.pg_enum e
|
|
17
|
+
join pg_catalog.pg_type t on t.oid = e.enumtypid
|
|
18
|
+
join pg_catalog.pg_namespace n on n.oid = t.typnamespace
|
|
19
|
+
where n.nspname = 'public' and t.typname = 'member_role' and e.enumlabel = 'client'
|
|
20
|
+
on conflict do nothing;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- Ekwo module `budgets` — its tables leave with the company.
|
|
2
|
+
--
|
|
3
|
+
-- The socle writes an archive of one company from a registry of tables, and
|
|
4
|
+
-- refuses to write one while a table that belongs to a company is
|
|
5
|
+
-- unclassified. A module answers for its own through `<schema>.archive_tables()`,
|
|
6
|
+
-- looked up by the socle the way `can_disable()` is — a function in this schema
|
|
7
|
+
-- and not rows in a table of the socle, because this migration may be applied
|
|
8
|
+
-- on a socle that does not have that table yet.
|
|
9
|
+
--
|
|
10
|
+
-- A budget is a plan the company wrote against its own chart and its own
|
|
11
|
+
-- financial years. It posts nothing, and it is still the company's.
|
|
12
|
+
--
|
|
13
|
+
-- `load_order` is relative to this module; the socle places every module after
|
|
14
|
+
-- its own tables.
|
|
15
|
+
|
|
16
|
+
create or replace function budgets.archive_tables()
|
|
17
|
+
returns table (
|
|
18
|
+
table_name text,
|
|
19
|
+
disposition text,
|
|
20
|
+
reason text,
|
|
21
|
+
via_column text,
|
|
22
|
+
via_table text,
|
|
23
|
+
load_order integer
|
|
24
|
+
)
|
|
25
|
+
language sql
|
|
26
|
+
immutable
|
|
27
|
+
as $$
|
|
28
|
+
values
|
|
29
|
+
('budgets'::text, 'exported'::text, null::text, null::text, null::text, 1),
|
|
30
|
+
('lines', 'exported', null, null, null, 2);
|
|
31
|
+
$$;
|
|
32
|
+
|
|
33
|
+
comment on function budgets.archive_tables() is
|
|
34
|
+
'What an archive of one company does with each table of this module. Read by `public.company_archive_tables()`.';
|
|
35
|
+
|
|
36
|
+
revoke execute on function budgets.archive_tables() from public, anon;
|
|
37
|
+
grant execute on function budgets.archive_tables() to authenticated, service_role;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://ekwo.ai/schemas/module/1.json",
|
|
4
|
+
"title": "Ekwo module manifest, version 1",
|
|
5
|
+
"description": "What a module of Ekwo OS says about itself. One file, `modules/<code>/module.json`, read by `ekwo module list|enable|migrate`, by the MCP loader and by the guard tests. Everything a module *does* is in its migrations; this says where it lives and what it is allowed to reach.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["code", "name", "schema", "version", "status"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"$schema": { "type": "string" },
|
|
11
|
+
"code": {
|
|
12
|
+
"$ref": "#/$defs/identifier",
|
|
13
|
+
"description": "The module's identifier, and the key of `public.modules`. Immutable once published: it is written on every entry the module posts."
|
|
14
|
+
},
|
|
15
|
+
"name": { "type": "string", "minLength": 1, "maxLength": 120 },
|
|
16
|
+
"description": { "type": "string", "minLength": 1, "maxLength": 400 },
|
|
17
|
+
"schema": {
|
|
18
|
+
"$ref": "#/$defs/identifier",
|
|
19
|
+
"description": "The Postgres schema the module lives in. One schema per module, never `public`, and PostgREST only serves it once it is listed in the project's exposed schemas."
|
|
20
|
+
},
|
|
21
|
+
"version": {
|
|
22
|
+
"type": "string",
|
|
23
|
+
"pattern": "^(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)$",
|
|
24
|
+
"description": "Semver, on the same scale a pack uses: a label is a patch, a table or a column is a minor, a change that asks a company to do something is a major."
|
|
25
|
+
},
|
|
26
|
+
"status": {
|
|
27
|
+
"enum": ["draft", "available", "deprecated"],
|
|
28
|
+
"description": "draft: installed and refused by enable_module(). available: enableable. deprecated: the companies that hold it keep working and no new one may enable it."
|
|
29
|
+
},
|
|
30
|
+
"requires_socle_min": {
|
|
31
|
+
"type": "string",
|
|
32
|
+
"pattern": "^[0-9]{14}$",
|
|
33
|
+
"description": "The oldest socle migration this module needs, by its timestamp version. `ekwo module enable` compares it against the migration history and refuses rather than letting a foreign key fail halfway."
|
|
34
|
+
},
|
|
35
|
+
"posts": {
|
|
36
|
+
"type": "boolean",
|
|
37
|
+
"description": "Whether this module writes to the ledger, always through post_module_entry(). A module that says false is held to it: a test refuses post_module_entry in its migrations."
|
|
38
|
+
},
|
|
39
|
+
"mcp": {
|
|
40
|
+
"type": "object",
|
|
41
|
+
"required": ["prefix"],
|
|
42
|
+
"additionalProperties": false,
|
|
43
|
+
"properties": {
|
|
44
|
+
"prefix": {
|
|
45
|
+
"$ref": "#/$defs/identifier",
|
|
46
|
+
"description": "Every MCP tool of this module is named `<prefix>_<verb>`, so a model reading a tool list can tell which module answers for what."
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"pack": {
|
|
51
|
+
"type": "object",
|
|
52
|
+
"description": "The country data this module reads, if any. A module with no `pack` section has no country rule at all — which is most of them.",
|
|
53
|
+
"required": ["section"],
|
|
54
|
+
"additionalProperties": false,
|
|
55
|
+
"properties": {
|
|
56
|
+
"section": {
|
|
57
|
+
"$ref": "#/$defs/identifier",
|
|
58
|
+
"description": "The file a country pack carries for this module: `packs/<cc>/<section>.json`, compiled by `ekwo pack build` into `supabase/seed/modules/<code>/`."
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"$defs": {
|
|
64
|
+
"identifier": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"pattern": "^[a-z][a-z0-9_]{1,30}$"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
-- Ekwo OS — currencies. ISO 4217, the handful a European ledger meets.
|
|
2
|
+
|
|
3
|
+
insert into currencies (code, name, symbol, decimal_places) values
|
|
4
|
+
('EUR', 'Euro', E'€', 2),
|
|
5
|
+
('USD', 'US dollar', '$', 2),
|
|
6
|
+
('GBP', 'Pound sterling', E'£', 2),
|
|
7
|
+
('CHF', 'Swiss franc', 'CHF', 2),
|
|
8
|
+
('SEK', 'Swedish krona', 'kr', 2),
|
|
9
|
+
('DKK', 'Danish krone', 'kr', 2),
|
|
10
|
+
('NOK', 'Norwegian krone', 'kr', 2),
|
|
11
|
+
('PLN', 'Polish zloty', E'z\u0142', 2),
|
|
12
|
+
('CZK', 'Czech koruna', E'Kč', 2),
|
|
13
|
+
('CAD', 'Canadian dollar', '$', 2),
|
|
14
|
+
('JPY', 'Japanese yen', E'¥', 0)
|
|
15
|
+
on conflict (code) do nothing;
|