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,564 @@
|
|
|
1
|
+
-- Ekwo OS (assets) — a depreciation is written at the decimals of the
|
|
2
|
+
-- company's currency, like every other amount.
|
|
3
|
+
--
|
|
4
|
+
-- The module rounded to the cent in nine places: the cost and the residual
|
|
5
|
+
-- value of an asset, the annuity of both methods, the declining cap, the
|
|
6
|
+
-- monthly share of a yearly annuity, the proceeds of a disposal, and the two
|
|
7
|
+
-- readings. Two decimals is what the euro has; a company keeping its books in
|
|
8
|
+
-- yen would have had a schedule that never summed to the cost, because the
|
|
9
|
+
-- last line takes the remainder and the remainder was in a decimal the
|
|
10
|
+
-- currency does not have.
|
|
11
|
+
--
|
|
12
|
+
-- Every one of them now asks `public.rounding_of(company)` once and passes the
|
|
13
|
+
-- answer to `public.round_amount`. The rule that made the schedule tie out is
|
|
14
|
+
-- unchanged — everything is rounded as it goes and the last line takes what is
|
|
15
|
+
-- left — it simply ties out in the currency's own unit now.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
create or replace function assets.create_asset(
|
|
19
|
+
p_company_id uuid,
|
|
20
|
+
p_code text,
|
|
21
|
+
p_name text,
|
|
22
|
+
p_acquisition_date date,
|
|
23
|
+
p_cost numeric,
|
|
24
|
+
p_asset_account text,
|
|
25
|
+
p_depreciation_account text,
|
|
26
|
+
p_expense_account text,
|
|
27
|
+
p_category_code text default null,
|
|
28
|
+
p_duration_months integer default null,
|
|
29
|
+
p_method assets.depreciation_method default null,
|
|
30
|
+
p_coefficient numeric default null,
|
|
31
|
+
p_residual_value numeric default 0,
|
|
32
|
+
p_in_service_date date default null,
|
|
33
|
+
p_document_line_id uuid default null,
|
|
34
|
+
p_contact_id uuid default null,
|
|
35
|
+
p_description text default null
|
|
36
|
+
)
|
|
37
|
+
returns uuid
|
|
38
|
+
language plpgsql
|
|
39
|
+
as $$
|
|
40
|
+
declare
|
|
41
|
+
v_country char(2);
|
|
42
|
+
v_category assets.category_templates%rowtype;
|
|
43
|
+
v_method assets.depreciation_method;
|
|
44
|
+
v_duration integer;
|
|
45
|
+
v_coef numeric(7, 3);
|
|
46
|
+
v_id uuid;
|
|
47
|
+
v_asset uuid;
|
|
48
|
+
v_deprec uuid;
|
|
49
|
+
v_round public.money_rounding;
|
|
50
|
+
v_expense uuid;
|
|
51
|
+
begin
|
|
52
|
+
if not public.module_is_enabled(p_company_id, 'assets') then
|
|
53
|
+
raise exception 'module_not_enabled: assets is not enabled on this company'
|
|
54
|
+
using errcode = '55006';
|
|
55
|
+
end if;
|
|
56
|
+
|
|
57
|
+
select country into v_country from public.companies where id = p_company_id;
|
|
58
|
+
if v_country is null then
|
|
59
|
+
raise exception 'unknown_company: %', p_company_id;
|
|
60
|
+
end if;
|
|
61
|
+
|
|
62
|
+
if p_category_code is not null then
|
|
63
|
+
select * into v_category
|
|
64
|
+
from assets.category_templates
|
|
65
|
+
where country = v_country and code = p_category_code;
|
|
66
|
+
if not found then
|
|
67
|
+
raise exception 'unknown_asset_category: % is not a category of the % pack', p_category_code, v_country;
|
|
68
|
+
end if;
|
|
69
|
+
end if;
|
|
70
|
+
|
|
71
|
+
-- The category is a suggestion, so anything the caller gave wins over it.
|
|
72
|
+
-- Same rule as a product pre-filling a document line.
|
|
73
|
+
v_method := coalesce(p_method, v_category.method, 'straight_line');
|
|
74
|
+
v_duration := coalesce(p_duration_months, v_category.duration_months);
|
|
75
|
+
v_coef := coalesce(p_coefficient, v_category.coefficient);
|
|
76
|
+
|
|
77
|
+
if v_duration is null then
|
|
78
|
+
raise exception 'no_duration: name a duration in months, or a category of the % pack that carries one', v_country;
|
|
79
|
+
end if;
|
|
80
|
+
|
|
81
|
+
v_round := public.rounding_of(p_company_id);
|
|
82
|
+
v_asset := public.account_id_by_code(p_company_id, p_asset_account);
|
|
83
|
+
v_deprec := public.account_id_by_code(p_company_id, p_depreciation_account);
|
|
84
|
+
v_expense := public.account_id_by_code(p_company_id, p_expense_account);
|
|
85
|
+
if v_asset is null then
|
|
86
|
+
raise exception 'unknown_account: % is not an account of this company', p_asset_account;
|
|
87
|
+
end if;
|
|
88
|
+
if v_deprec is null then
|
|
89
|
+
raise exception 'unknown_account: % is not an account of this company', p_depreciation_account;
|
|
90
|
+
end if;
|
|
91
|
+
if v_expense is null then
|
|
92
|
+
raise exception 'unknown_account: % is not an account of this company', p_expense_account;
|
|
93
|
+
end if;
|
|
94
|
+
|
|
95
|
+
insert into assets.assets (
|
|
96
|
+
company_id, code, name, description, category_code, document_line_id, contact_id,
|
|
97
|
+
acquisition_date, in_service_date, cost, residual_value, method, duration_months,
|
|
98
|
+
coefficient, prorata, asset_account_id, depreciation_account_id, expense_account_id, state
|
|
99
|
+
)
|
|
100
|
+
values (
|
|
101
|
+
p_company_id, p_code, p_name, p_description, p_category_code, p_document_line_id, p_contact_id,
|
|
102
|
+
p_acquisition_date, p_in_service_date,
|
|
103
|
+
public.round_amount(p_cost, v_round),
|
|
104
|
+
public.round_amount(coalesce(p_residual_value, 0), v_round),
|
|
105
|
+
v_method, v_duration, v_coef, v_category.prorata, v_asset, v_deprec, v_expense, 'active'
|
|
106
|
+
)
|
|
107
|
+
returning id into v_id;
|
|
108
|
+
|
|
109
|
+
perform assets.generate_schedule(v_id);
|
|
110
|
+
return v_id;
|
|
111
|
+
end;
|
|
112
|
+
$$;
|
|
113
|
+
|
|
114
|
+
create or replace function assets.generate_schedule(p_asset_id uuid)
|
|
115
|
+
returns integer
|
|
116
|
+
language plpgsql
|
|
117
|
+
as $$
|
|
118
|
+
declare
|
|
119
|
+
v_asset assets.assets%rowtype;
|
|
120
|
+
v_rules assets.country_rules%rowtype;
|
|
121
|
+
v_prorata assets.prorata_rule;
|
|
122
|
+
v_start date;
|
|
123
|
+
v_year public.fiscal_years%rowtype;
|
|
124
|
+
v_base numeric;
|
|
125
|
+
v_rate numeric;
|
|
126
|
+
v_period_start date;
|
|
127
|
+
v_period_end date;
|
|
128
|
+
v_fraction numeric;
|
|
129
|
+
v_amount numeric;
|
|
130
|
+
v_linear numeric;
|
|
131
|
+
v_cap numeric;
|
|
132
|
+
v_accum numeric := 0;
|
|
133
|
+
-- A schedule is a ledger figure, so each annuity is written at the decimals
|
|
134
|
+
-- of the currency the company keeps its books in.
|
|
135
|
+
v_round public.money_rounding;
|
|
136
|
+
v_sequence integer := 0;
|
|
137
|
+
v_periods integer;
|
|
138
|
+
v_left integer;
|
|
139
|
+
v_guard integer := 0;
|
|
140
|
+
begin
|
|
141
|
+
select * into v_asset from assets.assets where id = p_asset_id;
|
|
142
|
+
if not found then
|
|
143
|
+
raise exception 'unknown_asset: %', p_asset_id;
|
|
144
|
+
end if;
|
|
145
|
+
if v_asset.state = 'disposed' then
|
|
146
|
+
raise exception 'asset_disposed: % has left the books', v_asset.code;
|
|
147
|
+
end if;
|
|
148
|
+
if v_asset.method = 'units_of_production' then
|
|
149
|
+
raise exception 'units_of_production_unsupported: a schedule by output needs the units of each period, which this module does not record. Name a duration and a straight line, or keep the schedule outside Ekwo.';
|
|
150
|
+
end if;
|
|
151
|
+
|
|
152
|
+
-- A schedule is generated once. Once a line has been booked, the schedule is
|
|
153
|
+
-- what the ledger says happened, and regenerating it would leave the two
|
|
154
|
+
-- disagreeing without anything looking wrong — which is the failure the
|
|
155
|
+
-- column `posted_at` exists to make visible.
|
|
156
|
+
if exists (select 1 from assets.depreciation_lines l
|
|
157
|
+
where l.asset_id = p_asset_id and l.posted_at is not null) then
|
|
158
|
+
raise exception 'schedule_already_posted: % has depreciation already booked; a schedule is not rewritten under the ledger', v_asset.code
|
|
159
|
+
using errcode = '55006';
|
|
160
|
+
end if;
|
|
161
|
+
delete from assets.depreciation_lines where asset_id = p_asset_id;
|
|
162
|
+
|
|
163
|
+
v_round := public.rounding_of(v_asset.company_id);
|
|
164
|
+
v_start := coalesce(v_asset.in_service_date, v_asset.acquisition_date);
|
|
165
|
+
v_base := v_asset.cost - v_asset.residual_value;
|
|
166
|
+
|
|
167
|
+
v_rules := assets.rules(v_asset.company_id);
|
|
168
|
+
v_prorata := coalesce(
|
|
169
|
+
v_asset.prorata,
|
|
170
|
+
case when v_asset.method = 'declining_balance' then v_rules.prorata_declining
|
|
171
|
+
else v_rules.prorata_straight_line end);
|
|
172
|
+
if v_prorata is null then
|
|
173
|
+
raise exception 'no_assets_country_rules: the pack of this company''s country says nothing about how a first period is prorated. Add an assets section to the pack, or set assets.prorata on this asset.'
|
|
174
|
+
using errcode = '55006';
|
|
175
|
+
end if;
|
|
176
|
+
|
|
177
|
+
select * into v_year
|
|
178
|
+
from public.fiscal_years f
|
|
179
|
+
where f.company_id = v_asset.company_id
|
|
180
|
+
and v_start between f.start_date and f.end_date
|
|
181
|
+
limit 1;
|
|
182
|
+
if not found then
|
|
183
|
+
raise exception 'no_fiscal_year: % entered service on %, which falls in no financial year of this company', v_asset.code, v_start;
|
|
184
|
+
end if;
|
|
185
|
+
|
|
186
|
+
v_period_start := v_year.start_date;
|
|
187
|
+
v_rate := 12::numeric / v_asset.duration_months;
|
|
188
|
+
-- How many annuities the duration is worth. A declining balance measures
|
|
189
|
+
-- what is left to run in periods and not in days, because that is what the
|
|
190
|
+
-- rule it comes from says: at the start of the second year of a five-year
|
|
191
|
+
-- asset, four annuities remain, whatever day of the first year it was
|
|
192
|
+
-- bought on.
|
|
193
|
+
v_periods := ceil(v_asset.duration_months / 12.0)::integer;
|
|
194
|
+
if v_rules.declining_cap_percent is not null then
|
|
195
|
+
v_cap := public.round_amount(v_asset.cost * v_rules.declining_cap_percent / 100, v_round);
|
|
196
|
+
end if;
|
|
197
|
+
|
|
198
|
+
while v_accum < v_base loop
|
|
199
|
+
v_guard := v_guard + 1;
|
|
200
|
+
if v_guard > 1200 then
|
|
201
|
+
raise exception 'schedule_runaway: % produced more than 1200 periods; check its duration and its coefficient', v_asset.code;
|
|
202
|
+
end if;
|
|
203
|
+
|
|
204
|
+
v_period_end := assets.period_end_for(v_asset.company_id, v_period_start);
|
|
205
|
+
v_fraction := assets.prorata_fraction(v_prorata, v_rules.day_count, v_start, v_period_start, v_period_end);
|
|
206
|
+
|
|
207
|
+
if v_asset.method = 'declining_balance' then
|
|
208
|
+
v_amount := public.round_amount(
|
|
209
|
+
(v_base - v_accum) * v_rate * v_asset.coefficient * v_fraction, v_round);
|
|
210
|
+
if v_cap is not null then
|
|
211
|
+
v_amount := least(v_amount, public.round_amount(v_cap * v_fraction, v_round));
|
|
212
|
+
end if;
|
|
213
|
+
-- The straight line over what is left to run. A declining balance is a
|
|
214
|
+
-- front-loaded schedule that never reaches zero on its own, and the
|
|
215
|
+
-- switch is what makes it end on the last period rather than never.
|
|
216
|
+
-- The last period is `v_left = 1`, where the straight line is the whole
|
|
217
|
+
-- remaining value.
|
|
218
|
+
v_left := greatest(1, v_periods - v_sequence);
|
|
219
|
+
if coalesce(v_rules.declining_switch_to_linear, true) then
|
|
220
|
+
v_linear := public.round_amount((v_base - v_accum) / v_left * v_fraction, v_round);
|
|
221
|
+
v_amount := greatest(v_amount, v_linear);
|
|
222
|
+
end if;
|
|
223
|
+
else
|
|
224
|
+
v_amount := public.round_amount(v_base * v_rate * v_fraction, v_round);
|
|
225
|
+
end if;
|
|
226
|
+
|
|
227
|
+
if v_amount <= 0 then
|
|
228
|
+
v_amount := v_base - v_accum;
|
|
229
|
+
end if;
|
|
230
|
+
-- The last period takes the remainder, whatever the rounding did on the
|
|
231
|
+
-- way. This is the line that makes the schedule sum to the cent.
|
|
232
|
+
if v_accum + v_amount > v_base then
|
|
233
|
+
v_amount := v_base - v_accum;
|
|
234
|
+
end if;
|
|
235
|
+
|
|
236
|
+
v_accum := v_accum + v_amount;
|
|
237
|
+
v_sequence := v_sequence + 1;
|
|
238
|
+
|
|
239
|
+
insert into assets.depreciation_lines
|
|
240
|
+
(asset_id, company_id, sequence, period_start, period_end, amount, accumulated, net_book_value)
|
|
241
|
+
values (p_asset_id, v_asset.company_id, v_sequence, v_period_start, v_period_end,
|
|
242
|
+
v_amount, v_accum, v_asset.cost - v_accum);
|
|
243
|
+
|
|
244
|
+
v_period_start := v_period_end + 1;
|
|
245
|
+
end loop;
|
|
246
|
+
|
|
247
|
+
if assets.period_is_monthly(v_asset.company_id) then
|
|
248
|
+
perform assets.split_into_months(p_asset_id);
|
|
249
|
+
end if;
|
|
250
|
+
|
|
251
|
+
update assets.assets
|
|
252
|
+
set state = case when state = 'draft' then 'active' else state end
|
|
253
|
+
where id = p_asset_id;
|
|
254
|
+
|
|
255
|
+
return (select count(*)::integer from assets.depreciation_lines where asset_id = p_asset_id);
|
|
256
|
+
end;
|
|
257
|
+
$$;
|
|
258
|
+
|
|
259
|
+
create or replace function assets.split_into_months(p_asset_id uuid)
|
|
260
|
+
returns integer
|
|
261
|
+
language plpgsql
|
|
262
|
+
as $$
|
|
263
|
+
declare
|
|
264
|
+
v_line assets.depreciation_lines%rowtype;
|
|
265
|
+
v_asset assets.assets%rowtype;
|
|
266
|
+
v_months integer;
|
|
267
|
+
v_start date;
|
|
268
|
+
v_end date;
|
|
269
|
+
v_share numeric;
|
|
270
|
+
v_done numeric;
|
|
271
|
+
v_accum numeric := 0;
|
|
272
|
+
v_round public.money_rounding;
|
|
273
|
+
v_sequence integer := 0;
|
|
274
|
+
v_index integer;
|
|
275
|
+
v_new jsonb := '[]'::jsonb;
|
|
276
|
+
begin
|
|
277
|
+
select * into v_asset from assets.assets where id = p_asset_id;
|
|
278
|
+
v_round := public.rounding_of(v_asset.company_id);
|
|
279
|
+
|
|
280
|
+
for v_line in
|
|
281
|
+
select * from assets.depreciation_lines where asset_id = p_asset_id order by sequence
|
|
282
|
+
loop
|
|
283
|
+
v_months := assets.months_inclusive(v_line.period_start, v_line.period_end);
|
|
284
|
+
v_done := 0;
|
|
285
|
+
for v_index in 1 .. v_months loop
|
|
286
|
+
v_start := (date_trunc('month', v_line.period_start) + make_interval(months => v_index - 1))::date;
|
|
287
|
+
v_end := (date_trunc('month', v_start) + interval '1 month' - interval '1 day')::date;
|
|
288
|
+
if v_index = 1 then v_start := v_line.period_start; end if;
|
|
289
|
+
if v_index = v_months then v_end := v_line.period_end; end if;
|
|
290
|
+
|
|
291
|
+
v_share := case when v_index = v_months
|
|
292
|
+
then v_line.amount - v_done
|
|
293
|
+
else public.round_amount(v_line.amount / v_months, v_round) end;
|
|
294
|
+
v_done := v_done + v_share;
|
|
295
|
+
if v_share = 0 then continue; end if;
|
|
296
|
+
|
|
297
|
+
v_accum := v_accum + v_share;
|
|
298
|
+
v_sequence := v_sequence + 1;
|
|
299
|
+
v_new := v_new || jsonb_build_object(
|
|
300
|
+
'sequence', v_sequence, 'period_start', v_start, 'period_end', v_end,
|
|
301
|
+
'amount', v_share, 'accumulated', v_accum,
|
|
302
|
+
'net_book_value', v_asset.cost - v_accum);
|
|
303
|
+
end loop;
|
|
304
|
+
end loop;
|
|
305
|
+
|
|
306
|
+
delete from assets.depreciation_lines where asset_id = p_asset_id;
|
|
307
|
+
insert into assets.depreciation_lines
|
|
308
|
+
(asset_id, company_id, sequence, period_start, period_end, amount, accumulated, net_book_value)
|
|
309
|
+
select p_asset_id, v_asset.company_id,
|
|
310
|
+
(row ->> 'sequence')::integer, (row ->> 'period_start')::date, (row ->> 'period_end')::date,
|
|
311
|
+
(row ->> 'amount')::numeric, (row ->> 'accumulated')::numeric, (row ->> 'net_book_value')::numeric
|
|
312
|
+
from jsonb_array_elements(v_new) as row;
|
|
313
|
+
|
|
314
|
+
return v_sequence;
|
|
315
|
+
end;
|
|
316
|
+
$$;
|
|
317
|
+
|
|
318
|
+
create or replace function assets.dispose_asset(
|
|
319
|
+
p_asset_id uuid,
|
|
320
|
+
p_date date,
|
|
321
|
+
p_proceeds numeric default 0,
|
|
322
|
+
p_counterpart_account text default null,
|
|
323
|
+
p_contact_id uuid default null
|
|
324
|
+
)
|
|
325
|
+
returns uuid
|
|
326
|
+
language plpgsql
|
|
327
|
+
as $$
|
|
328
|
+
declare
|
|
329
|
+
v_asset assets.assets%rowtype;
|
|
330
|
+
v_rules assets.country_rules%rowtype;
|
|
331
|
+
v_defaults public.country_defaults%rowtype;
|
|
332
|
+
v_country char(2);
|
|
333
|
+
v_accum numeric;
|
|
334
|
+
v_nbv numeric;
|
|
335
|
+
v_result numeric;
|
|
336
|
+
v_round public.money_rounding;
|
|
337
|
+
v_proceeds numeric;
|
|
338
|
+
v_counter uuid;
|
|
339
|
+
v_account uuid;
|
|
340
|
+
v_lines jsonb := '[]'::jsonb;
|
|
341
|
+
v_entry uuid;
|
|
342
|
+
begin
|
|
343
|
+
select * into v_asset from assets.assets where id = p_asset_id for update;
|
|
344
|
+
if not found then
|
|
345
|
+
raise exception 'unknown_asset: %', p_asset_id;
|
|
346
|
+
end if;
|
|
347
|
+
if v_asset.state = 'disposed' then
|
|
348
|
+
raise exception 'asset_disposed: % has already left the books', v_asset.code;
|
|
349
|
+
end if;
|
|
350
|
+
if p_date < coalesce(v_asset.in_service_date, v_asset.acquisition_date) then
|
|
351
|
+
raise exception 'disposal_before_service: % cannot leave the books before it entered them', v_asset.code;
|
|
352
|
+
end if;
|
|
353
|
+
|
|
354
|
+
-- Everything the schedule planned for a period that has already ended has to
|
|
355
|
+
-- be booked before the asset goes: a disposal reads the accumulated
|
|
356
|
+
-- depreciation off the ledger, and a period left unbooked would make it read
|
|
357
|
+
-- a figure the accounts do not carry.
|
|
358
|
+
if exists (select 1 from assets.depreciation_lines l
|
|
359
|
+
where l.asset_id = p_asset_id and l.posted_at is null and l.period_end < p_date) then
|
|
360
|
+
raise exception 'depreciation_pending: run assets.run_depreciation(company, %) before disposing of %',
|
|
361
|
+
p_date, v_asset.code
|
|
362
|
+
using errcode = '55006';
|
|
363
|
+
end if;
|
|
364
|
+
|
|
365
|
+
select country into v_country from public.companies where id = v_asset.company_id;
|
|
366
|
+
v_round := public.rounding_of(v_asset.company_id);
|
|
367
|
+
v_proceeds := public.round_amount(coalesce(p_proceeds, 0), v_round);
|
|
368
|
+
v_rules := assets.rules(v_asset.company_id);
|
|
369
|
+
if v_rules.disposal_style is null then
|
|
370
|
+
raise exception 'no_disposal_style: the pack of % says nothing about how an asset leaves the books. Set assets.disposal, and the roles it needs, in the pack.', v_country
|
|
371
|
+
using errcode = '55006';
|
|
372
|
+
end if;
|
|
373
|
+
select * into v_defaults from public.country_defaults where country = v_country;
|
|
374
|
+
|
|
375
|
+
select coalesce(sum(l.amount), 0) into v_accum
|
|
376
|
+
from assets.depreciation_lines l
|
|
377
|
+
where l.asset_id = p_asset_id and l.posted_at is not null;
|
|
378
|
+
v_nbv := v_asset.cost - v_accum;
|
|
379
|
+
v_result := v_proceeds - v_nbv;
|
|
380
|
+
|
|
381
|
+
-- What the schedule still planned never happened. Deleting only the unposted
|
|
382
|
+
-- lines keeps everything the ledger knows about.
|
|
383
|
+
delete from assets.depreciation_lines
|
|
384
|
+
where asset_id = p_asset_id and posted_at is null;
|
|
385
|
+
|
|
386
|
+
-- 1. The asset leaves, and its accumulated depreciation with it.
|
|
387
|
+
if v_accum > 0 then
|
|
388
|
+
v_lines := v_lines || jsonb_build_object(
|
|
389
|
+
'account_id', v_asset.depreciation_account_id,
|
|
390
|
+
'label', v_asset.code || ' — accumulated depreciation', 'debit', v_accum);
|
|
391
|
+
end if;
|
|
392
|
+
v_lines := v_lines || jsonb_build_object(
|
|
393
|
+
'account_id', v_asset.asset_account_id,
|
|
394
|
+
'label', v_asset.code || ' — ' || v_asset.name, 'credit', v_asset.cost);
|
|
395
|
+
|
|
396
|
+
-- 2. What the buyer owes, if anything.
|
|
397
|
+
if v_proceeds > 0 then
|
|
398
|
+
v_counter := case
|
|
399
|
+
when p_counterpart_account is not null
|
|
400
|
+
then public.account_id_by_code(v_asset.company_id, p_counterpart_account)
|
|
401
|
+
else (select receivable_account_id from public.companies where id = v_asset.company_id)
|
|
402
|
+
end;
|
|
403
|
+
if v_counter is null then
|
|
404
|
+
raise exception 'no_counterpart_account: name the account the proceeds of % land on', v_asset.code;
|
|
405
|
+
end if;
|
|
406
|
+
v_lines := v_lines || jsonb_build_object(
|
|
407
|
+
'account_id', v_counter, 'label', v_asset.code || ' — proceeds',
|
|
408
|
+
'debit', v_proceeds, 'contact_id', p_contact_id);
|
|
409
|
+
end if;
|
|
410
|
+
|
|
411
|
+
-- 3. The result, the way the country presents it.
|
|
412
|
+
if v_rules.disposal_style = 'gross' then
|
|
413
|
+
-- The net book value is a charge and the proceeds an income, both in full.
|
|
414
|
+
-- The two lines above already cleared the asset and its depreciation, so
|
|
415
|
+
-- what is left to write is the charge; the proceeds line of the income
|
|
416
|
+
-- account replaces nothing, it is the other half of the same presentation.
|
|
417
|
+
v_account := public.account_id_by_code(v_asset.company_id, v_defaults.asset_disposal_value_code);
|
|
418
|
+
if v_account is null then
|
|
419
|
+
raise exception 'no_asset_disposal_value_account: % files a gross disposal and its pack names no account for the value of what was sold', v_country;
|
|
420
|
+
end if;
|
|
421
|
+
if v_nbv > 0 then
|
|
422
|
+
v_lines := v_lines || jsonb_build_object(
|
|
423
|
+
'account_id', v_account, 'label', v_asset.code || ' — net book value sold',
|
|
424
|
+
'debit', v_nbv);
|
|
425
|
+
end if;
|
|
426
|
+
if v_proceeds > 0 then
|
|
427
|
+
v_account := public.account_id_by_code(v_asset.company_id, v_defaults.asset_disposal_proceeds_code);
|
|
428
|
+
if v_account is null then
|
|
429
|
+
raise exception 'no_asset_disposal_proceeds_account: % files a gross disposal and its pack names no account for the proceeds', v_country;
|
|
430
|
+
end if;
|
|
431
|
+
v_lines := v_lines || jsonb_build_object(
|
|
432
|
+
'account_id', v_account, 'label', v_asset.code || ' — proceeds of disposal',
|
|
433
|
+
'credit', v_proceeds);
|
|
434
|
+
end if;
|
|
435
|
+
-- The two proceeds lines are the two halves of one movement and not a
|
|
436
|
+
-- double count: the buyer is debited above, the income account is credited
|
|
437
|
+
-- here. What the gross style adds to the net-result one is the charge, and
|
|
438
|
+
-- an income statement that prints both is what France asks for.
|
|
439
|
+
elsif v_result <> 0 then
|
|
440
|
+
v_account := public.account_id_by_code(
|
|
441
|
+
v_asset.company_id,
|
|
442
|
+
case when v_result > 0 then v_defaults.asset_disposal_gain_code
|
|
443
|
+
else coalesce(v_defaults.asset_disposal_loss_code, v_defaults.asset_disposal_gain_code) end);
|
|
444
|
+
if v_account is null then
|
|
445
|
+
raise exception 'no_asset_disposal_account: the pack of % names no account for a % on the disposal of a fixed asset',
|
|
446
|
+
v_country, case when v_result > 0 then 'gain' else 'loss' end;
|
|
447
|
+
end if;
|
|
448
|
+
v_lines := v_lines || jsonb_build_object(
|
|
449
|
+
'account_id', v_account,
|
|
450
|
+
'label', v_asset.code || ' — ' || case when v_result > 0 then 'gain' else 'loss' end || ' on disposal',
|
|
451
|
+
'debit', case when v_result < 0 then -v_result else 0 end,
|
|
452
|
+
'credit', case when v_result > 0 then v_result else 0 end);
|
|
453
|
+
end if;
|
|
454
|
+
|
|
455
|
+
v_entry := public.post_module_entry(
|
|
456
|
+
v_asset.company_id, 'assets', 'disposal:' || v_asset.code, p_date,
|
|
457
|
+
'Disposal of ' || v_asset.code || ' — ' || v_asset.name, v_lines);
|
|
458
|
+
|
|
459
|
+
insert into assets.disposals
|
|
460
|
+
(asset_id, company_id, disposal_date, proceeds, counterpart_account_id, contact_id,
|
|
461
|
+
cost, accumulated, net_book_value, result, entry_id)
|
|
462
|
+
values (p_asset_id, v_asset.company_id, p_date, v_proceeds, v_counter, p_contact_id,
|
|
463
|
+
v_asset.cost, v_accum, v_nbv, v_result, v_entry);
|
|
464
|
+
|
|
465
|
+
update assets.assets set state = 'disposed' where id = p_asset_id;
|
|
466
|
+
return v_entry;
|
|
467
|
+
end;
|
|
468
|
+
$$;
|
|
469
|
+
|
|
470
|
+
create or replace function assets.register(p_company_id uuid, p_at date)
|
|
471
|
+
returns table (
|
|
472
|
+
code text,
|
|
473
|
+
name text,
|
|
474
|
+
category_code text,
|
|
475
|
+
acquisition_date date,
|
|
476
|
+
cost numeric,
|
|
477
|
+
accumulated numeric,
|
|
478
|
+
net_book_value numeric,
|
|
479
|
+
state text
|
|
480
|
+
)
|
|
481
|
+
language sql
|
|
482
|
+
stable
|
|
483
|
+
as $$
|
|
484
|
+
select a.code,
|
|
485
|
+
a.name,
|
|
486
|
+
a.category_code,
|
|
487
|
+
a.acquisition_date,
|
|
488
|
+
a.cost,
|
|
489
|
+
public.round_amount(coalesce(d.accumulated, 0), public.rounding_of(p_company_id)),
|
|
490
|
+
public.round_amount(a.cost - coalesce(d.accumulated, 0), public.rounding_of(p_company_id)),
|
|
491
|
+
a.state::text
|
|
492
|
+
from assets.assets a
|
|
493
|
+
left join lateral (
|
|
494
|
+
select coalesce(sum(l.amount), 0) as accumulated
|
|
495
|
+
from assets.depreciation_lines l
|
|
496
|
+
where l.asset_id = a.id and l.posted_at is not null and l.period_end <= p_at
|
|
497
|
+
) d on true
|
|
498
|
+
where a.company_id = p_company_id
|
|
499
|
+
and a.acquisition_date <= p_at
|
|
500
|
+
and not exists (
|
|
501
|
+
select 1 from assets.disposals x
|
|
502
|
+
where x.asset_id = a.id and x.disposal_date <= p_at
|
|
503
|
+
)
|
|
504
|
+
order by a.code;
|
|
505
|
+
$$;
|
|
506
|
+
|
|
507
|
+
create or replace function assets.movements(p_company_id uuid, p_from date, p_to date)
|
|
508
|
+
returns table (
|
|
509
|
+
code text,
|
|
510
|
+
name text,
|
|
511
|
+
opening_cost numeric,
|
|
512
|
+
additions numeric,
|
|
513
|
+
disposals_cost numeric,
|
|
514
|
+
closing_cost numeric,
|
|
515
|
+
opening_accumulated numeric,
|
|
516
|
+
depreciation numeric,
|
|
517
|
+
disposals_accumulated numeric,
|
|
518
|
+
closing_accumulated numeric,
|
|
519
|
+
net_book_value numeric
|
|
520
|
+
)
|
|
521
|
+
language sql
|
|
522
|
+
stable
|
|
523
|
+
as $$
|
|
524
|
+
with movement as (
|
|
525
|
+
select a.id, a.code, a.name, a.cost,
|
|
526
|
+
a.acquisition_date < p_from as held_before,
|
|
527
|
+
a.acquisition_date between p_from and p_to as added,
|
|
528
|
+
x.disposal_date, x.accumulated as disposed_accumulated,
|
|
529
|
+
coalesce((select sum(l.amount) from assets.depreciation_lines l
|
|
530
|
+
where l.asset_id = a.id and l.posted_at is not null
|
|
531
|
+
and l.period_end < p_from), 0) as accum_before,
|
|
532
|
+
coalesce((select sum(l.amount) from assets.depreciation_lines l
|
|
533
|
+
where l.asset_id = a.id and l.posted_at is not null
|
|
534
|
+
and l.period_end between p_from and p_to), 0) as accum_period
|
|
535
|
+
from assets.assets a
|
|
536
|
+
left join assets.disposals x on x.asset_id = a.id
|
|
537
|
+
where a.company_id = p_company_id
|
|
538
|
+
and a.acquisition_date <= p_to
|
|
539
|
+
)
|
|
540
|
+
select m.code, m.name,
|
|
541
|
+
public.round_amount(case when m.held_before then m.cost else 0 end, r.rounding),
|
|
542
|
+
public.round_amount(case when m.added then m.cost else 0 end, r.rounding),
|
|
543
|
+
public.round_amount(case when m.disposal_date between p_from and p_to
|
|
544
|
+
then m.cost else 0 end, r.rounding),
|
|
545
|
+
public.round_amount(case when m.disposal_date is not null and m.disposal_date <= p_to
|
|
546
|
+
then 0 else m.cost end, r.rounding),
|
|
547
|
+
public.round_amount(m.accum_before, r.rounding),
|
|
548
|
+
public.round_amount(m.accum_period, r.rounding),
|
|
549
|
+
public.round_amount(case when m.disposal_date between p_from and p_to
|
|
550
|
+
then coalesce(m.disposed_accumulated, 0) else 0 end, r.rounding),
|
|
551
|
+
public.round_amount(case when m.disposal_date is not null and m.disposal_date <= p_to
|
|
552
|
+
then 0 else m.accum_before + m.accum_period end, r.rounding),
|
|
553
|
+
public.round_amount(case when m.disposal_date is not null and m.disposal_date <= p_to
|
|
554
|
+
then 0 else m.cost - m.accum_before - m.accum_period end, r.rounding)
|
|
555
|
+
from movement m
|
|
556
|
+
cross join (select public.rounding_of(p_company_id) as rounding) r
|
|
557
|
+
order by m.code;
|
|
558
|
+
$$;
|
|
559
|
+
|
|
560
|
+
comment on function assets.generate_schedule(uuid) is
|
|
561
|
+
'Writes the depreciation schedule of an asset, period by period, rounded at the decimals of the company''s currency with the last line taking the remainder. Refuses to rewrite a schedule whose lines are already booked.';
|
|
562
|
+
|
|
563
|
+
|
|
564
|
+
revoke execute on all functions in schema assets from public;
|
package/dist/assets/modules/assets/supabase/migrations/20260914145012_assets_accounts_in_use.sql
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
-- The `assets` module answers which accounts of a company it holds.
|
|
2
|
+
--
|
|
3
|
+
-- `public.accounts_in_use()` builds the working chart of a company and has to
|
|
4
|
+
-- count the accounts a module points at — an asset booked on 240000 belongs
|
|
5
|
+
-- in the list whether or not a depreciation has been run yet. It does not name
|
|
6
|
+
-- this schema to find out: it asks, by the convention `disable_module()`
|
|
7
|
+
-- already reads, `<schema>.accounts_in_use(uuid)`. A module that references no
|
|
8
|
+
-- account writes no such function and `to_regprocedure` answers null.
|
|
9
|
+
--
|
|
10
|
+
-- Four columns of this schema carry an account: the three an asset is booked
|
|
11
|
+
-- on, and the counterpart a disposal is settled against.
|
|
12
|
+
--
|
|
13
|
+
-- Security invoker, so row level security answers: a caller who is not a
|
|
14
|
+
-- member of the company, or a company that has not enabled this module, gets
|
|
15
|
+
-- an empty set from the policies rather than a refusal from here.
|
|
16
|
+
|
|
17
|
+
create or replace function assets.accounts_in_use(p_company_id uuid)
|
|
18
|
+
returns setof uuid
|
|
19
|
+
language sql
|
|
20
|
+
stable
|
|
21
|
+
as $$
|
|
22
|
+
select unnest(array[a.asset_account_id, a.depreciation_account_id, a.expense_account_id])
|
|
23
|
+
from assets.assets a
|
|
24
|
+
where a.company_id = p_company_id
|
|
25
|
+
union
|
|
26
|
+
select d.counterpart_account_id
|
|
27
|
+
from assets.disposals d
|
|
28
|
+
where d.company_id = p_company_id and d.counterpart_account_id is not null;
|
|
29
|
+
$$;
|
|
30
|
+
|
|
31
|
+
comment on function assets.accounts_in_use(uuid) is
|
|
32
|
+
'The accounts this module points at for one company: what its assets are booked, depreciated and charged on, and what a disposal was settled against. Read by public.accounts_in_use() through the module convention.';
|
|
33
|
+
|
|
34
|
+
revoke execute on function assets.accounts_in_use(uuid) from public, anon;
|
|
35
|
+
grant execute on function assets.accounts_in_use(uuid) to authenticated, service_role;
|
|
36
|
+
|
|
37
|
+
revoke execute on all functions in schema assets from public;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
-- The `assets` module grants its own rights, by name.
|
|
2
|
+
--
|
|
3
|
+
-- The socle stopped borrowing the project's default privileges in
|
|
4
|
+
-- `20260914151207`; this is the same change for a module schema, where the
|
|
5
|
+
-- starting point was different and wrong in its own way.
|
|
6
|
+
--
|
|
7
|
+
-- `20260913081447` opened the schema with three wildcards:
|
|
8
|
+
--
|
|
9
|
+
-- grant select on all tables in schema assets to anon;
|
|
10
|
+
-- grant select, insert, update, delete on all tables in schema assets
|
|
11
|
+
-- to authenticated, service_role;
|
|
12
|
+
--
|
|
13
|
+
-- The first contradicts the doctrine of `20260911210131` — `anon` reaches the
|
|
14
|
+
-- policy helpers and nothing else — and it was the only place in the whole
|
|
15
|
+
-- schema where the anonymous role held a privilege on a table. Row level
|
|
16
|
+
-- security answered every one of those reads with an empty set, so nothing
|
|
17
|
+
-- leaked; the surface should be closed rather than merely empty, which is the
|
|
18
|
+
-- same sentence that migration wrote about functions.
|
|
19
|
+
--
|
|
20
|
+
-- The second is too wide by two tables. `category_templates` and
|
|
21
|
+
-- `country_rules` are what the module's pack section installs — the
|
|
22
|
+
-- depreciation categories of a country and the rules that pick a method —
|
|
23
|
+
-- and their only policy is a SELECT. A grant that offers three verbs no
|
|
24
|
+
-- policy will ever accept is a grant that describes the schema incorrectly,
|
|
25
|
+
-- and from here the inventory test reads it as the declaration it is.
|
|
26
|
+
--
|
|
27
|
+
-- Published migrations are never edited, so the wildcards stand in their file
|
|
28
|
+
-- and are corrected here.
|
|
29
|
+
|
|
30
|
+
-- `usage` stays as it is for all three roles. A schema nobody holds an object
|
|
31
|
+
-- privilege in reaches nothing, and PostgREST needs it for the roles that do.
|
|
32
|
+
|
|
33
|
+
revoke all on all tables in schema assets from anon, authenticated, service_role;
|
|
34
|
+
revoke all on all sequences in schema assets from anon, authenticated, service_role;
|
|
35
|
+
|
|
36
|
+
alter default privileges in schema assets revoke all on tables from anon, authenticated, service_role;
|
|
37
|
+
alter default privileges in schema assets revoke all on sequences from anon, authenticated, service_role;
|
|
38
|
+
alter default privileges in schema assets revoke execute on functions from public, anon, authenticated, service_role;
|
|
39
|
+
|
|
40
|
+
-- The register and what it produces: a policy `for all` governs each, so the
|
|
41
|
+
-- four verbs are the ones row level security is prepared to judge. Posting is
|
|
42
|
+
-- guarded by `assets.assert_may_post`, not by a withheld privilege, so DELETE
|
|
43
|
+
-- on a draft line stays possible and DELETE on a posted one stays refused.
|
|
44
|
+
grant select, insert, update, delete on table
|
|
45
|
+
assets.assets,
|
|
46
|
+
assets.depreciation_lines,
|
|
47
|
+
assets.disposals
|
|
48
|
+
to authenticated, service_role;
|
|
49
|
+
|
|
50
|
+
-- What the country pack installs. SELECT only, on both sides of the grant and
|
|
51
|
+
-- of the policy.
|
|
52
|
+
grant select on table
|
|
53
|
+
assets.category_templates,
|
|
54
|
+
assets.country_rules
|
|
55
|
+
to authenticated, service_role;
|
|
56
|
+
|
|
57
|
+
-- The fourteen callable functions keep the nominative grants their own
|
|
58
|
+
-- migration gave them. The trigger body does not: it is fired by the table,
|
|
59
|
+
-- never called, and PostgreSQL checks EXECUTE when a trigger is created
|
|
60
|
+
-- rather than when it fires.
|
|
61
|
+
revoke execute on function assets.assert_may_post() from public, anon, authenticated, service_role;
|
|
62
|
+
|
|
63
|
+
revoke execute on all functions in schema assets from public;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
-- Ekwo module `assets` — 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, `assets.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, 'assets.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;
|