shutro-erp 0.3.5 → 0.3.7
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/dist/account_asset/.kosh/bindings.js +15 -0
- package/dist/{mrp_account → account_asset}/.kosh/schema.d.ts +785 -614
- package/dist/account_asset/board.d.ts +61 -0
- package/dist/account_asset/board.js +86 -0
- package/dist/account_asset/books.d.ts +42 -0
- package/dist/account_asset/books.js +398 -0
- package/dist/account_asset/demo.d.ts +7 -0
- package/dist/account_asset/demo.js +72 -0
- package/dist/account_asset/i18n/bn.d.ts +2 -0
- package/dist/account_asset/i18n/bn.js +97 -0
- package/dist/account_asset/index.d.ts +9 -0
- package/dist/account_asset/index.js +337 -0
- package/dist/account_asset/module.d.ts +11076 -0
- package/dist/account_asset/module.js +230 -0
- package/dist/account_budget/.kosh/bindings.js +15 -0
- package/dist/account_budget/.kosh/schema.d.ts +2841 -0
- package/dist/account_budget/budget.d.ts +5 -0
- package/dist/account_budget/budget.js +142 -0
- package/dist/account_budget/demo.d.ts +6 -0
- package/dist/account_budget/demo.js +30 -0
- package/dist/account_budget/i18n/bn.d.ts +2 -0
- package/dist/account_budget/i18n/bn.js +63 -0
- package/dist/account_budget/index.d.ts +4 -0
- package/dist/account_budget/index.js +237 -0
- package/dist/account_budget/module.d.ts +27289 -0
- package/dist/account_budget/module.js +182 -0
- package/dist/account_portal/.kosh/bindings.js +1 -1
- package/dist/account_portal/.kosh/schema.d.ts +651 -10
- package/dist/account_portal/i18n/bn.d.ts +2 -0
- package/dist/account_portal/i18n/bn.js +6 -0
- package/dist/account_portal/index.js +2 -0
- package/dist/account_portal/module.d.ts +5212 -1592
- package/dist/accounting/.kosh/bindings.js +1 -1
- package/dist/accounting/.kosh/schema.d.ts +651 -10
- package/dist/accounting/closing.js +1 -0
- package/dist/accounting/fiscal.d.ts +578 -0
- package/dist/accounting/fiscal.js +401 -0
- package/dist/accounting/followups.d.ts +618 -0
- package/dist/accounting/followups.js +420 -0
- package/dist/accounting/i18n/bn.d.ts +2 -0
- package/dist/accounting/i18n/bn.js +425 -0
- package/dist/accounting/index.d.ts +3 -1
- package/dist/accounting/index.js +780 -5
- package/dist/accounting/module.d.ts +5386 -1543
- package/dist/accounting/module.js +155 -18
- package/dist/accounting/statements.d.ts +878 -0
- package/dist/accounting/statements.js +728 -0
- package/dist/accounting/web/BankReconcilePage.d.ts +9 -0
- package/dist/accounting/web/BankReconcilePage.js +138 -0
- package/dist/accounting/web/FollowupsPage.d.ts +8 -0
- package/dist/accounting/web/FollowupsPage.js +54 -0
- package/dist/accounting/web/PostingPreview.d.ts +8 -0
- package/dist/accounting/web/PostingPreview.js +26 -0
- package/dist/accounting/web/index.d.ts +3 -0
- package/dist/accounting/web/index.js +9 -0
- package/dist/accounting/web/messages.js +140 -0
- package/dist/approvals/.kosh/bindings.d.ts +22 -0
- package/dist/approvals/.kosh/bindings.js +15 -0
- package/dist/approvals/.kosh/schema.d.ts +879 -0
- package/dist/approvals/demo.d.ts +7 -0
- package/dist/approvals/demo.js +41 -0
- package/dist/approvals/i18n/bn.d.ts +2 -0
- package/dist/approvals/i18n/bn.js +133 -0
- package/dist/approvals/index.d.ts +36 -0
- package/dist/approvals/index.js +772 -0
- package/dist/approvals/mails.d.ts +30 -0
- package/dist/approvals/mails.js +87 -0
- package/dist/approvals/module.d.ts +2930 -0
- package/dist/approvals/module.js +473 -0
- package/dist/approvals/web/Config.d.ts +12 -0
- package/dist/approvals/web/Config.js +60 -0
- package/dist/approvals/web/History.d.ts +4 -0
- package/dist/approvals/web/History.js +13 -0
- package/dist/approvals/web/Inbox.d.ts +9 -0
- package/dist/approvals/web/Inbox.js +114 -0
- package/dist/approvals/web/Matrix.d.ts +9 -0
- package/dist/approvals/web/Matrix.js +26 -0
- package/dist/approvals/web/Mine.d.ts +7 -0
- package/dist/approvals/web/Mine.js +27 -0
- package/dist/approvals/web/index.d.ts +15 -0
- package/dist/approvals/web/index.js +29 -0
- package/dist/approvals/web/messages.d.ts +2 -0
- package/dist/approvals/web/messages.js +211 -0
- package/dist/approvals/web/shared.d.ts +59 -0
- package/dist/approvals/web/shared.js +46 -0
- package/dist/approvals_purchase/.kosh/bindings.d.ts +22 -0
- package/dist/approvals_purchase/.kosh/bindings.js +15 -0
- package/dist/approvals_purchase/.kosh/schema.d.ts +1997 -0
- package/dist/approvals_purchase/i18n/bn.d.ts +2 -0
- package/dist/approvals_purchase/i18n/bn.js +5 -0
- package/dist/approvals_purchase/index.d.ts +3 -0
- package/dist/approvals_purchase/index.js +47 -0
- package/dist/approvals_purchase/module.d.ts +12670 -0
- package/dist/approvals_purchase/module.js +84 -0
- package/dist/attendance/.kosh/bindings.d.ts +22 -0
- package/dist/attendance/.kosh/bindings.js +15 -0
- package/dist/attendance/.kosh/schema.d.ts +1106 -0
- package/dist/attendance/days.d.ts +96 -0
- package/dist/attendance/days.js +430 -0
- package/dist/attendance/demo.d.ts +7 -0
- package/dist/attendance/demo.js +58 -0
- package/dist/attendance/i18n/bn.d.ts +2 -0
- package/dist/attendance/i18n/bn.js +147 -0
- package/dist/attendance/index.d.ts +4 -0
- package/dist/attendance/index.js +515 -0
- package/dist/attendance/module.d.ts +4413 -0
- package/dist/attendance/module.js +412 -0
- package/dist/attendance/routes.d.ts +16 -0
- package/dist/attendance/routes.js +266 -0
- package/dist/attendance/web/Check.d.ts +8 -0
- package/dist/attendance/web/Check.js +40 -0
- package/dist/attendance/web/Kiosk.d.ts +8 -0
- package/dist/attendance/web/Kiosk.js +62 -0
- package/dist/attendance/web/PinButton.d.ts +7 -0
- package/dist/attendance/web/PinButton.js +29 -0
- package/dist/attendance/web/index.d.ts +13 -0
- package/dist/attendance/web/index.js +22 -0
- package/dist/attendance/web/messages.d.ts +2 -0
- package/dist/attendance/web/messages.js +61 -0
- package/dist/automation/engine.js +1 -1
- package/dist/calendar/.kosh/bindings.js +1 -1
- package/dist/calendar/.kosh/schema.d.ts +2 -1
- package/dist/calendar/i18n/bn.d.ts +2 -0
- package/dist/calendar/i18n/bn.js +80 -0
- package/dist/calendar/index.js +77 -0
- package/dist/calendar/module.d.ts +26 -0
- package/dist/calendar/module.js +14 -4
- package/dist/cli/apps.js +25 -5
- package/dist/cli/init.js +46 -4
- package/dist/cli/templates.d.ts +1 -1
- package/dist/cli/templates.js +123 -5
- package/dist/contacts/.kosh/bindings.js +1 -1
- package/dist/contacts/.kosh/schema.d.ts +2 -1
- package/dist/contacts/i18n/bn.d.ts +2 -0
- package/dist/contacts/i18n/bn.js +67 -0
- package/dist/contacts/index.js +3 -0
- package/dist/contacts/module.d.ts +42 -0
- package/dist/contacts/module.js +19 -3
- package/dist/crm/.kosh/bindings.js +1 -1
- package/dist/crm/.kosh/schema.d.ts +3 -1
- package/dist/crm/i18n/bn.d.ts +2 -0
- package/dist/crm/i18n/bn.js +80 -0
- package/dist/crm/index.js +22 -0
- package/dist/crm/module.d.ts +22 -0
- package/dist/crm/module.js +6 -3
- package/dist/documents/.kosh/bindings.d.ts +22 -0
- package/dist/documents/.kosh/bindings.js +15 -0
- package/dist/documents/.kosh/schema.d.ts +863 -0
- package/dist/documents/demo.d.ts +6 -0
- package/dist/documents/demo.js +22 -0
- package/dist/documents/documents.d.ts +98 -0
- package/dist/documents/documents.js +318 -0
- package/dist/documents/i18n/bn.d.ts +2 -0
- package/dist/documents/i18n/bn.js +83 -0
- package/dist/documents/index.d.ts +30 -0
- package/dist/documents/index.js +569 -0
- package/dist/documents/module.d.ts +2801 -0
- package/dist/documents/module.js +215 -0
- package/dist/documents/web/Versions.d.ts +7 -0
- package/dist/documents/web/Versions.js +45 -0
- package/dist/documents/web/index.d.ts +11 -0
- package/dist/documents/web/index.js +16 -0
- package/dist/documents/web/messages.d.ts +2 -0
- package/dist/documents/web/messages.js +21 -0
- package/dist/event/.kosh/bindings.js +1 -1
- package/dist/event/.kosh/schema.d.ts +2 -1
- package/dist/event/i18n/bn.d.ts +2 -0
- package/dist/event/i18n/bn.js +101 -0
- package/dist/event/index.js +66 -0
- package/dist/event/module.d.ts +20 -0
- package/dist/event/module.js +8 -1
- package/dist/event_account/.kosh/bindings.js +1 -1
- package/dist/event_account/.kosh/schema.d.ts +651 -10
- package/dist/event_account/i18n/bn.d.ts +2 -0
- package/dist/event_account/i18n/bn.js +5 -0
- package/dist/event_account/index.js +2 -0
- package/dist/event_account/module.d.ts +5133 -1513
- package/dist/expense/.kosh/bindings.d.ts +22 -0
- package/dist/expense/.kosh/bindings.js +15 -0
- package/dist/expense/.kosh/schema.d.ts +1620 -0
- package/dist/expense/demo.d.ts +7 -0
- package/dist/expense/demo.js +69 -0
- package/dist/expense/i18n/bn.d.ts +2 -0
- package/dist/expense/i18n/bn.js +155 -0
- package/dist/expense/index.d.ts +3 -0
- package/dist/expense/index.js +502 -0
- package/dist/expense/module.d.ts +6768 -0
- package/dist/expense/module.js +649 -0
- package/dist/expense_account/.kosh/bindings.d.ts +22 -0
- package/dist/expense_account/.kosh/bindings.js +15 -0
- package/dist/expense_account/.kosh/schema.d.ts +2515 -0
- package/dist/expense_account/i18n/bn.d.ts +2 -0
- package/dist/expense_account/i18n/bn.js +15 -0
- package/dist/expense_account/index.d.ts +3 -0
- package/dist/expense_account/index.js +76 -0
- package/dist/expense_account/module.d.ts +14628 -0
- package/dist/expense_account/module.js +318 -0
- package/dist/helpdesk/.kosh/bindings.d.ts +22 -0
- package/dist/helpdesk/.kosh/bindings.js +15 -0
- package/dist/helpdesk/.kosh/schema.d.ts +886 -0
- package/dist/helpdesk/demo.d.ts +7 -0
- package/dist/helpdesk/demo.js +65 -0
- package/dist/helpdesk/hours.d.ts +17 -0
- package/dist/helpdesk/hours.js +94 -0
- package/dist/helpdesk/i18n/bn.d.ts +2 -0
- package/dist/helpdesk/i18n/bn.js +127 -0
- package/dist/helpdesk/index.d.ts +10 -0
- package/dist/helpdesk/index.js +463 -0
- package/dist/helpdesk/module.d.ts +2960 -0
- package/dist/helpdesk/module.js +326 -0
- package/dist/helpdesk/routes.d.ts +12 -0
- package/dist/helpdesk/routes.js +132 -0
- package/dist/helpdesk/sla.d.ts +37 -0
- package/dist/helpdesk/sla.js +226 -0
- package/dist/hr/.kosh/bindings.js +1 -1
- package/dist/hr/.kosh/schema.d.ts +59 -1
- package/dist/hr/demo.js +11 -1
- package/dist/hr/i18n/bn.d.ts +2 -0
- package/dist/hr/i18n/bn.js +126 -0
- package/dist/hr/index.d.ts +1 -1
- package/dist/hr/index.js +158 -12
- package/dist/hr/module.d.ts +932 -59
- package/dist/hr/module.js +114 -17
- package/dist/indent/.kosh/bindings.d.ts +22 -0
- package/dist/indent/.kosh/bindings.js +15 -0
- package/dist/indent/.kosh/schema.d.ts +2084 -0
- package/dist/indent/demo.d.ts +7 -0
- package/dist/indent/demo.js +48 -0
- package/dist/indent/i18n/bn.d.ts +2 -0
- package/dist/indent/i18n/bn.js +143 -0
- package/dist/indent/index.d.ts +3 -0
- package/dist/indent/index.js +450 -0
- package/dist/indent/module.d.ts +13424 -0
- package/dist/indent/module.js +792 -0
- package/dist/indent_purchase/.kosh/bindings.d.ts +22 -0
- package/dist/indent_purchase/.kosh/bindings.js +15 -0
- package/dist/indent_purchase/.kosh/schema.d.ts +2710 -0
- package/dist/indent_purchase/i18n/bn.d.ts +2 -0
- package/dist/indent_purchase/i18n/bn.js +13 -0
- package/dist/indent_purchase/index.d.ts +3 -0
- package/dist/indent_purchase/index.js +61 -0
- package/dist/indent_purchase/module.d.ts +27950 -0
- package/dist/indent_purchase/module.js +163 -0
- package/dist/l10n_bd/.kosh/bindings.js +1 -1
- package/dist/l10n_bd/.kosh/schema.d.ts +651 -10
- package/dist/l10n_bd/i18n/bn.d.ts +2 -0
- package/dist/l10n_bd/i18n/bn.js +69 -0
- package/dist/l10n_bd/index.js +2 -0
- package/dist/l10n_bd/module.d.ts +5212 -1592
- package/dist/l10n_bd_payroll/.kosh/bindings.d.ts +22 -0
- package/dist/l10n_bd_payroll/.kosh/bindings.js +15 -0
- package/dist/l10n_bd_payroll/.kosh/schema.d.ts +2891 -0
- package/dist/l10n_bd_payroll/data.d.ts +47 -0
- package/dist/l10n_bd_payroll/data.js +347 -0
- package/dist/l10n_bd_payroll/i18n/bn.d.ts +2 -0
- package/dist/l10n_bd_payroll/i18n/bn.js +4 -0
- package/dist/l10n_bd_payroll/index.d.ts +4 -0
- package/dist/l10n_bd_payroll/index.js +26 -0
- package/dist/l10n_bd_payroll/module.d.ts +17294 -0
- package/dist/l10n_bd_payroll/module.js +86 -0
- package/dist/l10n_bd_pos/.kosh/bindings.d.ts +22 -0
- package/dist/{mrp_account → l10n_bd_pos}/.kosh/bindings.js +2 -2
- package/dist/l10n_bd_pos/.kosh/schema.d.ts +2341 -0
- package/dist/l10n_bd_pos/i18n/bn.d.ts +2 -0
- package/dist/l10n_bd_pos/i18n/bn.js +32 -0
- package/dist/l10n_bd_pos/index.d.ts +3 -0
- package/dist/l10n_bd_pos/index.js +111 -0
- package/dist/{mrp_account → l10n_bd_pos}/module.d.ts +9047 -6981
- package/dist/l10n_bd_pos/module.js +43 -0
- package/dist/l10n_in/.kosh/bindings.js +1 -1
- package/dist/l10n_in/.kosh/schema.d.ts +651 -10
- package/dist/l10n_in/i18n/bn.d.ts +2 -0
- package/dist/l10n_in/i18n/bn.js +95 -0
- package/dist/l10n_in/index.js +2 -0
- package/dist/l10n_in/module.d.ts +6778 -1580
- package/dist/l10n_in/module.js +25 -5
- package/dist/l10n_in/web/GstSettings.js +2 -2
- package/dist/mail/inbound/handle.js +8 -5
- package/dist/mail/inbound/models.js +5 -3
- package/dist/mail/index.d.ts +12 -1
- package/dist/mail/index.js +139 -2
- package/dist/mail/send.d.ts +56 -0
- package/dist/mail/send.js +394 -0
- package/dist/mail/web/SendButton.d.ts +9 -0
- package/dist/mail/web/SendButton.js +71 -0
- package/dist/mail/web/SendSelected.d.ts +8 -0
- package/dist/mail/web/SendSelected.js +33 -0
- package/dist/mail/web/index.d.ts +2 -0
- package/dist/mail/web/index.js +6 -0
- package/dist/mail/web/messages.js +36 -0
- package/dist/maintenance/.kosh/bindings.js +1 -1
- package/dist/maintenance/.kosh/schema.d.ts +2 -1
- package/dist/maintenance/i18n/bn.d.ts +2 -0
- package/dist/maintenance/i18n/bn.js +132 -0
- package/dist/maintenance/index.js +85 -0
- package/dist/maintenance/module.d.ts +25 -0
- package/dist/maintenance/module.js +27 -9
- package/dist/maintenance_stock/.kosh/bindings.js +1 -1
- package/dist/maintenance_stock/.kosh/schema.d.ts +374 -3
- package/dist/maintenance_stock/i18n/bn.d.ts +2 -0
- package/dist/maintenance_stock/i18n/bn.js +21 -0
- package/dist/maintenance_stock/index.js +2 -0
- package/dist/maintenance_stock/module.d.ts +6095 -1327
- package/dist/maintenance_stock/module.js +1 -0
- package/dist/modules-sdk/apps.d.ts +10 -0
- package/dist/modules-sdk/apps.js +32 -0
- package/dist/modules-sdk/index.d.ts +1 -1
- package/dist/modules-sdk/manifest.d.ts +105 -2
- package/dist/payment/.kosh/bindings.js +1 -1
- package/dist/payment/.kosh/schema.d.ts +653 -11
- package/dist/payment/i18n/bn.d.ts +2 -0
- package/dist/payment/i18n/bn.js +59 -0
- package/dist/payment/index.d.ts +41 -0
- package/dist/payment/index.js +216 -74
- package/dist/payment/module.d.ts +5353 -1675
- package/dist/payment/module.js +36 -6
- package/dist/payment/settle.js +11 -1
- package/dist/payroll/.kosh/bindings.d.ts +22 -0
- package/dist/payroll/.kosh/bindings.js +15 -0
- package/dist/payroll/.kosh/schema.d.ts +1448 -0
- package/dist/payroll/compute.d.ts +35 -0
- package/dist/payroll/compute.js +398 -0
- package/dist/payroll/demo.d.ts +55 -0
- package/dist/payroll/demo.js +121 -0
- package/dist/payroll/formula.d.ts +62 -0
- package/dist/payroll/formula.js +320 -0
- package/dist/payroll/i18n/bn.d.ts +2 -0
- package/dist/payroll/i18n/bn.js +206 -0
- package/dist/payroll/index.d.ts +230 -0
- package/dist/payroll/index.js +715 -0
- package/dist/{mrp → payroll}/module.d.ts +4294 -2624
- package/dist/payroll/module.js +606 -0
- package/dist/payroll/routes.d.ts +10 -0
- package/dist/payroll/routes.js +112 -0
- package/dist/payroll_account/.kosh/bindings.d.ts +22 -0
- package/dist/payroll_account/.kosh/bindings.js +15 -0
- package/dist/payroll_account/.kosh/schema.d.ts +2887 -0
- package/dist/payroll_account/i18n/bn.d.ts +2 -0
- package/dist/payroll_account/i18n/bn.js +10 -0
- package/dist/payroll_account/index.d.ts +3 -0
- package/dist/payroll_account/index.js +42 -0
- package/dist/payroll_account/module.d.ts +16738 -0
- package/dist/payroll_account/module.js +256 -0
- package/dist/permissions/approvals.d.ts +227 -12
- package/dist/permissions/approvals.js +1421 -67
- package/dist/permissions/index.d.ts +1 -1
- package/dist/permissions/index.js +2 -2
- package/dist/permissions/models.js +364 -13
- package/dist/permissions/routes.d.ts +9 -1
- package/dist/permissions/routes.js +72 -5
- package/dist/permissions/web/ApprovalPanel.d.ts +5 -4
- package/dist/permissions/web/ApprovalPanel.js +75 -11
- package/dist/permissions/web/index.d.ts +6 -4
- package/dist/permissions/web/index.js +13 -4
- package/dist/permissions/web/messages.d.ts +2 -0
- package/dist/permissions/web/messages.js +95 -0
- package/dist/portal/.kosh/bindings.js +1 -1
- package/dist/portal/.kosh/schema.d.ts +2 -1
- package/dist/portal/documents.d.ts +3 -0
- package/dist/portal/documents.js +21 -1
- package/dist/portal/i18n/bn.d.ts +2 -0
- package/dist/portal/i18n/bn.js +33 -0
- package/dist/portal/index.js +3 -1
- package/dist/portal/module.d.ts +20 -0
- package/dist/portal/routes-documents.js +76 -0
- package/dist/pos/.kosh/bindings.js +1 -1
- package/dist/pos/.kosh/schema.d.ts +652 -10
- package/dist/pos/i18n/bn.d.ts +2 -0
- package/dist/pos/i18n/bn.js +133 -0
- package/dist/pos/index.js +87 -1
- package/dist/pos/module.d.ts +6967 -1709
- package/dist/pos/module.js +49 -5
- package/dist/pos/routes.js +80 -7
- package/dist/pos/web/Register.js +2 -5
- package/dist/pos_stock/.kosh/bindings.js +1 -1
- package/dist/pos_stock/.kosh/schema.d.ts +822 -12
- package/dist/pos_stock/i18n/bn.d.ts +2 -0
- package/dist/pos_stock/i18n/bn.js +6 -0
- package/dist/pos_stock/index.js +2 -0
- package/dist/pos_stock/module.d.ts +11241 -2851
- package/dist/product/.kosh/bindings.js +1 -1
- package/dist/product/.kosh/schema.d.ts +203 -1
- package/dist/product/i18n/bn.d.ts +2 -0
- package/dist/product/i18n/bn.js +175 -0
- package/dist/product/index.d.ts +1 -0
- package/dist/product/index.js +301 -1
- package/dist/product/module.d.ts +1727 -0
- package/dist/product/module.js +67 -6
- package/dist/product/pricelist.d.ts +575 -0
- package/dist/product/pricelist.js +257 -0
- package/dist/product/variants.d.ts +908 -0
- package/dist/product/variants.js +251 -0
- package/dist/production/.kosh/bindings.d.ts +22 -0
- package/dist/production/.kosh/bindings.js +15 -0
- package/dist/production/.kosh/schema.d.ts +2345 -0
- package/dist/{mrp → production}/demo.d.ts +1 -1
- package/dist/{mrp → production}/demo.js +5 -5
- package/dist/production/depth.d.ts +790 -0
- package/dist/production/depth.js +262 -0
- package/dist/production/i18n/bn.d.ts +2 -0
- package/dist/production/i18n/bn.js +350 -0
- package/dist/production/index.d.ts +6 -0
- package/dist/production/index.js +1256 -0
- package/dist/production/module.d.ts +17327 -0
- package/dist/production/module.js +1333 -0
- package/dist/production/plan.d.ts +921 -0
- package/dist/production/plan.js +511 -0
- package/dist/production/schedule.d.ts +656 -0
- package/dist/production/schedule.js +309 -0
- package/dist/production_account/.kosh/bindings.d.ts +22 -0
- package/dist/production_account/.kosh/bindings.js +15 -0
- package/dist/production_account/.kosh/schema.d.ts +3496 -0
- package/dist/production_account/i18n/bn.d.ts +2 -0
- package/dist/production_account/i18n/bn.js +93 -0
- package/dist/production_account/index.d.ts +4 -0
- package/dist/production_account/index.js +246 -0
- package/dist/production_account/module.d.ts +1192 -0
- package/dist/{mrp_account → production_account}/module.js +72 -18
- package/dist/production_account/overhead.d.ts +1195 -0
- package/dist/production_account/overhead.js +554 -0
- package/dist/project/.kosh/bindings.js +1 -1
- package/dist/project/.kosh/schema.d.ts +735 -1
- package/dist/project/i18n/bn.d.ts +2 -0
- package/dist/project/i18n/bn.js +383 -0
- package/dist/project/index.d.ts +3 -1
- package/dist/project/index.js +1024 -14
- package/dist/project/module.d.ts +9912 -390
- package/dist/project/module.js +1590 -14
- package/dist/project/routes.d.ts +44 -0
- package/dist/project/routes.js +314 -0
- package/dist/project/schedule.d.ts +72 -0
- package/dist/project/schedule.js +192 -0
- package/dist/project/web/Refresh.d.ts +8 -0
- package/dist/project/web/Refresh.js +29 -0
- package/dist/project/web/Schedule.d.ts +9 -0
- package/dist/project/web/Schedule.js +93 -0
- package/dist/project/web/Structure.d.ts +8 -0
- package/dist/project/web/Structure.js +86 -0
- package/dist/project/web/Workload.d.ts +8 -0
- package/dist/project/web/Workload.js +43 -0
- package/dist/project/web/index.d.ts +14 -0
- package/dist/project/web/index.js +25 -0
- package/dist/project/web/messages.d.ts +2 -0
- package/dist/project/web/messages.js +85 -0
- package/dist/project_account/.kosh/bindings.d.ts +22 -0
- package/dist/project_account/.kosh/bindings.js +15 -0
- package/dist/project_account/.kosh/schema.d.ts +2763 -0
- package/dist/project_account/i18n/bn.d.ts +2 -0
- package/dist/project_account/i18n/bn.js +6 -0
- package/dist/project_account/index.d.ts +3 -0
- package/dist/project_account/index.js +29 -0
- package/dist/project_account/module.d.ts +15013 -0
- package/dist/project_account/module.js +76 -0
- package/dist/project_portal/.kosh/bindings.js +1 -1
- package/dist/project_portal/.kosh/schema.d.ts +735 -1
- package/dist/project_portal/i18n/bn.d.ts +2 -0
- package/dist/project_portal/i18n/bn.js +6 -0
- package/dist/project_portal/index.js +2 -0
- package/dist/project_portal/module.d.ts +4796 -87
- package/dist/project_purchase/.kosh/bindings.d.ts +22 -0
- package/dist/project_purchase/.kosh/bindings.js +15 -0
- package/dist/project_purchase/.kosh/schema.d.ts +3393 -0
- package/dist/project_purchase/i18n/bn.d.ts +2 -0
- package/dist/project_purchase/i18n/bn.js +4 -0
- package/dist/project_purchase/index.d.ts +9 -0
- package/dist/project_purchase/index.js +24 -0
- package/dist/project_purchase/module.d.ts +38613 -0
- package/dist/project_purchase/module.js +56 -0
- package/dist/purchase/.kosh/bindings.js +1 -1
- package/dist/purchase/.kosh/schema.d.ts +908 -5
- package/dist/purchase/comparison.d.ts +993 -0
- package/dist/purchase/comparison.js +415 -0
- package/dist/purchase/extras.d.ts +1509 -0
- package/dist/purchase/extras.js +445 -0
- package/dist/purchase/i18n/bn.d.ts +2 -0
- package/dist/purchase/i18n/bn.js +413 -0
- package/dist/purchase/index.d.ts +5 -0
- package/dist/purchase/index.js +1280 -4
- package/dist/purchase/module.d.ts +7147 -395
- package/dist/purchase/module.js +303 -10
- package/dist/purchase/request.d.ts +696 -0
- package/dist/purchase/request.js +446 -0
- package/dist/purchase/tender.d.ts +1208 -0
- package/dist/purchase/tender.js +507 -0
- package/dist/purchase/vendorprice.d.ts +340 -0
- package/dist/purchase/vendorprice.js +153 -0
- package/dist/purchase_account/.kosh/bindings.js +1 -1
- package/dist/purchase_account/.kosh/schema.d.ts +1209 -16
- package/dist/purchase_account/i18n/bn.d.ts +2 -0
- package/dist/purchase_account/i18n/bn.js +22 -0
- package/dist/purchase_account/index.js +20 -0
- package/dist/purchase_account/module.d.ts +12232 -2028
- package/dist/purchase_account/module.js +252 -9
- package/dist/purchase_portal/.kosh/bindings.js +1 -1
- package/dist/purchase_portal/.kosh/schema.d.ts +908 -5
- package/dist/purchase_portal/i18n/bn.d.ts +2 -0
- package/dist/purchase_portal/i18n/bn.js +5 -0
- package/dist/purchase_portal/index.js +2 -0
- package/dist/purchase_portal/module.d.ts +6853 -352
- package/dist/purchase_stock/.kosh/bindings.js +1 -1
- package/dist/purchase_stock/.kosh/schema.d.ts +1096 -21
- package/dist/purchase_stock/i18n/bn.d.ts +2 -0
- package/dist/purchase_stock/i18n/bn.js +19 -0
- package/dist/purchase_stock/index.d.ts +2 -2
- package/dist/purchase_stock/index.js +38 -2
- package/dist/purchase_stock/module.d.ts +12930 -1612
- package/dist/purchase_stock/module.js +317 -9
- package/dist/purchase_stock/scorecard.d.ts +22 -0
- package/dist/purchase_stock/scorecard.js +77 -0
- package/dist/purchase_stock/web/Scorecard.d.ts +7 -0
- package/dist/purchase_stock/web/Scorecard.js +22 -0
- package/dist/purchase_stock/web/index.d.ts +11 -0
- package/dist/purchase_stock/web/index.js +16 -0
- package/dist/purchase_stock/web/messages.d.ts +2 -0
- package/dist/purchase_stock/web/messages.js +27 -0
- package/dist/purchase_stock_account/.kosh/bindings.js +1 -1
- package/dist/purchase_stock_account/.kosh/schema.d.ts +1444 -20
- package/dist/purchase_stock_account/i18n/bn.d.ts +2 -0
- package/dist/purchase_stock_account/i18n/bn.js +8 -0
- package/dist/purchase_stock_account/index.js +18 -0
- package/dist/purchase_stock_account/module.d.ts +7 -35346
- package/dist/purchase_stock_account/module.js +35 -8
- package/dist/recruitment/.kosh/bindings.js +1 -1
- package/dist/recruitment/.kosh/schema.d.ts +60 -1
- package/dist/recruitment/i18n/bn.d.ts +2 -0
- package/dist/recruitment/i18n/bn.js +93 -0
- package/dist/recruitment/index.js +64 -0
- package/dist/recruitment/module.d.ts +460 -8
- package/dist/recruitment/module.js +13 -4
- package/dist/reporting/documents.d.ts +5 -0
- package/dist/reporting/documents.js +11 -1
- package/dist/reporting/index.d.ts +1 -1
- package/dist/reporting/index.js +1 -1
- package/dist/reporting/web/CalendarView.d.ts +18 -5
- package/dist/reporting/web/CalendarView.js +163 -29
- package/dist/reporting/web/messages.js +18 -0
- package/dist/sale_account/.kosh/bindings.js +1 -1
- package/dist/sale_account/.kosh/schema.d.ts +763 -18
- package/dist/sale_account/i18n/bn.d.ts +2 -0
- package/dist/sale_account/i18n/bn.js +48 -0
- package/dist/sale_account/index.d.ts +1 -1
- package/dist/sale_account/index.js +102 -4
- package/dist/sale_account/module.d.ts +8241 -2052
- package/dist/sale_account/module.js +535 -46
- package/dist/sale_crm/.kosh/bindings.d.ts +22 -0
- package/dist/sale_crm/.kosh/bindings.js +15 -0
- package/dist/{mrp → sale_crm}/.kosh/schema.d.ts +391 -494
- package/dist/sale_crm/i18n/bn.d.ts +2 -0
- package/dist/sale_crm/i18n/bn.js +9 -0
- package/dist/sale_crm/index.d.ts +7 -0
- package/dist/sale_crm/index.js +104 -0
- package/dist/sale_crm/module.d.ts +5609 -0
- package/dist/sale_crm/module.js +73 -0
- package/dist/sale_portal/.kosh/bindings.js +1 -1
- package/dist/sale_portal/.kosh/schema.d.ts +292 -4
- package/dist/sale_portal/i18n/bn.d.ts +2 -0
- package/dist/sale_portal/i18n/bn.js +7 -0
- package/dist/sale_portal/index.js +10 -0
- package/dist/sale_portal/module.d.ts +2563 -292
- package/dist/sale_project/.kosh/bindings.js +1 -1
- package/dist/sale_project/.kosh/schema.d.ts +1025 -4
- package/dist/sale_project/i18n/bn.d.ts +2 -0
- package/dist/sale_project/i18n/bn.js +12 -0
- package/dist/sale_project/index.js +2 -0
- package/dist/sale_project/module.d.ts +9280 -762
- package/dist/sale_project/module.js +17 -1
- package/dist/sale_stock/.kosh/bindings.js +1 -1
- package/dist/sale_stock/.kosh/schema.d.ts +463 -6
- package/dist/sale_stock/i18n/bn.d.ts +2 -0
- package/dist/sale_stock/i18n/bn.js +15 -0
- package/dist/sale_stock/index.js +2 -0
- package/dist/sale_stock/module.d.ts +8355 -1334
- package/dist/sale_stock/module.js +20 -3
- package/dist/sales/.kosh/bindings.js +1 -1
- package/dist/sales/.kosh/schema.d.ts +292 -4
- package/dist/sales/i18n/bn.d.ts +2 -0
- package/dist/sales/i18n/bn.js +128 -0
- package/dist/sales/index.js +124 -5
- package/dist/sales/module.d.ts +3568 -633
- package/dist/sales/module.js +306 -11
- package/dist/server/api/duplicate.d.ts +14 -0
- package/dist/server/api/duplicate.js +75 -0
- package/dist/server/api/explain.d.ts +70 -0
- package/dist/server/api/explain.js +111 -0
- package/dist/server/api/metadata.d.ts +2 -0
- package/dist/server/api/metadata.js +9 -0
- package/dist/server/api/openapi.js +15 -0
- package/dist/server/api/rest.d.ts +1 -0
- package/dist/server/api/rest.js +76 -12
- package/dist/server/api/views.js +1 -1
- package/dist/server/apps.d.ts +16 -1
- package/dist/server/apps.js +164 -2
- package/dist/server/auth/base.js +2 -0
- package/dist/server/auth/service.d.ts +8 -0
- package/dist/server/auth/service.js +19 -0
- package/dist/server/events.d.ts +13 -0
- package/dist/server/events.js +22 -0
- package/dist/server/i18n.d.ts +27 -0
- package/dist/server/i18n.js +143 -0
- package/dist/server/index.d.ts +2 -0
- package/dist/server/index.js +2 -0
- package/dist/server/server.js +6 -0
- package/dist/settings/company.d.ts +3 -1
- package/dist/settings/company.js +26 -5
- package/dist/settings/routes.d.ts +10 -0
- package/dist/settings/routes.js +19 -3
- package/dist/settings/web/AppsPage.d.ts +3 -2
- package/dist/settings/web/AppsPage.js +26 -8
- package/dist/settings/web/CompaniesPage.js +91 -6
- package/dist/settings/web/messages.js +64 -0
- package/dist/sign/.kosh/bindings.d.ts +22 -0
- package/dist/{mrp → sign}/.kosh/bindings.js +2 -2
- package/dist/sign/.kosh/schema.d.ts +991 -0
- package/dist/sign/i18n/bn.d.ts +2 -0
- package/dist/sign/i18n/bn.js +97 -0
- package/dist/sign/index.d.ts +29 -0
- package/dist/sign/index.js +478 -0
- package/dist/sign/module.d.ts +4518 -0
- package/dist/sign/module.js +167 -0
- package/dist/sign/sign.d.ts +82 -0
- package/dist/sign/sign.js +474 -0
- package/dist/stock/.kosh/bindings.js +1 -1
- package/dist/stock/.kosh/schema.d.ts +374 -3
- package/dist/stock/demo.d.ts +2 -1
- package/dist/stock/demo.js +28 -1
- package/dist/stock/i18n/bn.d.ts +2 -0
- package/dist/stock/i18n/bn.js +311 -0
- package/dist/stock/index.d.ts +4 -1
- package/dist/stock/index.js +674 -6
- package/dist/stock/ledger.d.ts +78 -0
- package/dist/stock/ledger.js +582 -0
- package/dist/stock/module.d.ts +6142 -1380
- package/dist/stock/module.js +780 -26
- package/dist/stock/replenish.d.ts +38 -0
- package/dist/stock/replenish.js +191 -0
- package/dist/stock/returns.d.ts +157 -0
- package/dist/stock/returns.js +163 -0
- package/dist/stock/web/Ledger.d.ts +9 -0
- package/dist/stock/web/Ledger.js +108 -0
- package/dist/stock/web/Operations.d.ts +8 -0
- package/dist/stock/web/Operations.js +33 -0
- package/dist/stock/web/Replenishment.d.ts +8 -0
- package/dist/stock/web/Replenishment.js +84 -0
- package/dist/stock/web/ReturnButton.d.ts +7 -0
- package/dist/stock/web/ReturnButton.js +62 -0
- package/dist/stock/web/index.d.ts +7 -2
- package/dist/stock/web/index.js +15 -2
- package/dist/stock/web/messages.js +206 -0
- package/dist/stock_account/.kosh/bindings.js +1 -1
- package/dist/stock_account/.kosh/schema.d.ts +878 -12
- package/dist/stock_account/i18n/bn.d.ts +2 -0
- package/dist/stock_account/i18n/bn.js +73 -0
- package/dist/stock_account/index.d.ts +1 -0
- package/dist/stock_account/index.js +141 -3
- package/dist/stock_account/landed.d.ts +429 -0
- package/dist/stock_account/landed.js +335 -0
- package/dist/stock_account/module.d.ts +12143 -3391
- package/dist/stock_account/module.js +135 -6
- package/dist/stock_account/web/LandedPreview.d.ts +7 -0
- package/dist/stock_account/web/LandedPreview.js +23 -0
- package/dist/stock_account/web/index.d.ts +11 -0
- package/dist/stock_account/web/index.js +16 -0
- package/dist/stock_account/web/messages.d.ts +2 -0
- package/dist/stock_account/web/messages.js +27 -0
- package/dist/todo/i18n/bn.d.ts +2 -0
- package/dist/todo/i18n/bn.js +66 -0
- package/dist/todo/index.js +59 -0
- package/dist/todo/module.d.ts +5 -0
- package/dist/todo/module.js +5 -3
- package/dist/trade_finance/.kosh/bindings.js +1 -1
- package/dist/trade_finance/.kosh/schema.d.ts +2 -1
- package/dist/trade_finance/i18n/bn.d.ts +2 -0
- package/dist/trade_finance/i18n/bn.js +162 -0
- package/dist/trade_finance/index.js +95 -0
- package/dist/trade_finance/module.d.ts +26 -0
- package/dist/trade_finance/module.js +29 -8
- package/dist/trade_finance_account/.kosh/bindings.js +1 -1
- package/dist/trade_finance_account/.kosh/schema.d.ts +651 -10
- package/dist/trade_finance_account/i18n/bn.d.ts +2 -0
- package/dist/trade_finance_account/i18n/bn.js +27 -0
- package/dist/trade_finance_account/index.js +3 -0
- package/dist/trade_finance_account/module.d.ts +5129 -1509
- package/dist/trade_finance_account/module.js +2 -0
- package/dist/trade_finance_purchase/.kosh/bindings.js +1 -1
- package/dist/trade_finance_purchase/.kosh/schema.d.ts +908 -5
- package/dist/trade_finance_purchase/i18n/bn.d.ts +2 -0
- package/dist/trade_finance_purchase/i18n/bn.js +6 -0
- package/dist/trade_finance_purchase/index.js +2 -0
- package/dist/trade_finance_purchase/module.d.ts +6909 -408
- package/dist/trade_finance_sale/.kosh/bindings.js +1 -1
- package/dist/trade_finance_sale/.kosh/schema.d.ts +292 -4
- package/dist/trade_finance_sale/i18n/bn.d.ts +2 -0
- package/dist/trade_finance_sale/i18n/bn.js +6 -0
- package/dist/trade_finance_sale/index.js +2 -0
- package/dist/trade_finance_sale/module.d.ts +2602 -331
- package/dist/ui/api.d.ts +2 -0
- package/dist/ui/app.d.ts +9 -0
- package/dist/ui/app.js +47 -15
- package/dist/ui/components/AppIcon.js +9 -2
- package/dist/ui/components/Explain.d.ts +69 -0
- package/dist/ui/components/Explain.js +62 -0
- package/dist/ui/context.d.ts +8 -1
- package/dist/ui/context.js +34 -1
- package/dist/ui/i18n/messages.d.ts +40 -0
- package/dist/ui/i18n/messages.js +80 -0
- package/dist/ui/index.d.ts +2 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/registry.d.ts +14 -0
- package/dist/ui/registry.js +10 -0
- package/dist/ui/views/FormView.d.ts +6 -0
- package/dist/ui/views/FormView.js +51 -12
- package/dist/ui/views/KanbanView.js +9 -1
- package/dist/ui/views/ListView.d.ts +8 -0
- package/dist/ui/views/ListView.js +14 -5
- package/dist/ui/views/StatsRow.js +2 -1
- package/dist/ui/views/widgets.js +7 -1
- package/dist/views/extend.js +12 -0
- package/dist/views/types.d.ts +13 -2
- package/dist/views/validate.js +8 -1
- package/dist/website/.kosh/bindings.js +1 -1
- package/dist/website/.kosh/schema.d.ts +2 -1
- package/dist/website/i18n/bn.d.ts +2 -0
- package/dist/website/i18n/bn.js +113 -0
- package/dist/website/index.js +37 -0
- package/dist/website/module.d.ts +20 -0
- package/dist/website/module.js +30 -5
- package/dist/website_crm/.kosh/bindings.js +1 -1
- package/dist/website_crm/.kosh/schema.d.ts +3 -1
- package/dist/website_crm/i18n/bn.d.ts +2 -0
- package/dist/website_crm/i18n/bn.js +5 -0
- package/dist/website_crm/index.js +2 -0
- package/dist/website_crm/module.d.ts +41 -0
- package/dist/website_sale/.kosh/bindings.d.ts +22 -0
- package/dist/website_sale/.kosh/bindings.js +15 -0
- package/dist/website_sale/.kosh/schema.d.ts +2909 -0
- package/dist/website_sale/couriers.d.ts +50 -0
- package/dist/website_sale/couriers.js +59 -0
- package/dist/website_sale/demo.d.ts +7 -0
- package/dist/website_sale/demo.js +90 -0
- package/dist/website_sale/growth.d.ts +31 -0
- package/dist/website_sale/growth.js +163 -0
- package/dist/website_sale/i18n/bn.d.ts +2 -0
- package/dist/website_sale/i18n/bn.js +132 -0
- package/dist/website_sale/index.d.ts +49 -0
- package/dist/website_sale/index.js +620 -0
- package/dist/website_sale/module.d.ts +35182 -0
- package/dist/website_sale/module.js +419 -0
- package/dist/website_sale/shipments.d.ts +16 -0
- package/dist/website_sale/shipments.js +113 -0
- package/dist/website_sale/shop.d.ts +81 -0
- package/dist/website_sale/shop.js +662 -0
- package/dist/website_sale_stock/.kosh/bindings.d.ts +22 -0
- package/dist/website_sale_stock/.kosh/bindings.js +15 -0
- package/dist/website_sale_stock/.kosh/schema.d.ts +3400 -0
- package/dist/website_sale_stock/i18n/bn.d.ts +2 -0
- package/dist/website_sale_stock/i18n/bn.js +4 -0
- package/dist/website_sale_stock/index.d.ts +9 -0
- package/dist/website_sale_stock/index.js +22 -0
- package/dist/website_sale_stock/module.d.ts +11 -0
- package/dist/website_sale_stock/module.js +32 -0
- package/package.json +117 -8
- package/dist/mrp/index.d.ts +0 -3
- package/dist/mrp/index.js +0 -392
- package/dist/mrp/module.js +0 -695
- package/dist/mrp_account/index.d.ts +0 -3
- package/dist/mrp_account/index.js +0 -22
- /package/dist/{mrp → account_asset}/.kosh/bindings.d.ts +0 -0
- /package/dist/{mrp_account → account_budget}/.kosh/bindings.d.ts +0 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { HttpProblem } from '../errors.js';
|
|
2
|
+
/** Which module declares each model and field, from the loaded modules' Shutro definitions. */
|
|
3
|
+
function provenance(registry) {
|
|
4
|
+
const owner = new Map();
|
|
5
|
+
const fieldOwner = new Map();
|
|
6
|
+
const extenders = new Map();
|
|
7
|
+
for (const m of registry.loaded.modules) {
|
|
8
|
+
const shutro = m.shutro;
|
|
9
|
+
for (const model of shutro?.models ?? []) {
|
|
10
|
+
owner.set(model.name, m.name);
|
|
11
|
+
for (const key of Object.keys(model.fields ?? {}))
|
|
12
|
+
fieldOwner.set(`${model.name}.${key}`, m.name);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
for (const m of registry.loaded.modules) {
|
|
16
|
+
const shutro = m.shutro;
|
|
17
|
+
for (const ext of shutro?.extensions ?? []) {
|
|
18
|
+
const set = extenders.get(ext.name) ?? new Set();
|
|
19
|
+
set.add(m.name);
|
|
20
|
+
extenders.set(ext.name, set);
|
|
21
|
+
for (const key of Object.keys(ext.fields ?? {}))
|
|
22
|
+
fieldOwner.set(`${ext.name}.${key}`, m.name);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return { owner, fieldOwner, extenders };
|
|
26
|
+
}
|
|
27
|
+
const show = (value) => {
|
|
28
|
+
if (value === undefined || value === null || typeof value === 'function')
|
|
29
|
+
return null;
|
|
30
|
+
if (typeof value === 'string')
|
|
31
|
+
return value;
|
|
32
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
33
|
+
return String(value);
|
|
34
|
+
try {
|
|
35
|
+
return JSON.stringify(value);
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
/** A model explained, from the registry and the API metadata. */
|
|
42
|
+
export function explainModel(registry, models, name) {
|
|
43
|
+
const model = models.get(name);
|
|
44
|
+
if (model === undefined)
|
|
45
|
+
return null;
|
|
46
|
+
const { owner, fieldOwner, extenders } = provenance(registry);
|
|
47
|
+
const descriptors = registry.registry.get(name).model.fields;
|
|
48
|
+
const description = registry.registry.describe(name).fields;
|
|
49
|
+
const fields = model.fields.map((f) => {
|
|
50
|
+
const raw = descriptors[f.key] ?? {};
|
|
51
|
+
const d = description[f.key] ?? {};
|
|
52
|
+
const depends = Array.isArray(d['depends']) ? d['depends'].map(String) : [];
|
|
53
|
+
const defaultValue = raw['default'];
|
|
54
|
+
return {
|
|
55
|
+
key: f.key,
|
|
56
|
+
label: f.label,
|
|
57
|
+
kind: f.kind,
|
|
58
|
+
help: f.help,
|
|
59
|
+
target: f.target,
|
|
60
|
+
inverse: typeof raw['inverseField'] === 'string' ? raw['inverseField'] : null,
|
|
61
|
+
required: f.required,
|
|
62
|
+
readonly: f.readonly,
|
|
63
|
+
stored: f.stored,
|
|
64
|
+
computed: d['computed'] === true,
|
|
65
|
+
depends,
|
|
66
|
+
hasDefault: defaultValue !== undefined,
|
|
67
|
+
default: typeof defaultValue === 'function' ? '(computed when created)' : show(defaultValue),
|
|
68
|
+
ondelete: typeof raw['ondelete'] === 'string' ? raw['ondelete'] : null,
|
|
69
|
+
translate: f.translate,
|
|
70
|
+
index: raw['index'] !== undefined && raw['index'] !== false,
|
|
71
|
+
options: f.options,
|
|
72
|
+
module: fieldOwner.get(`${name}.${f.key}`) ?? owner.get(name) ?? null,
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
const incoming = [];
|
|
76
|
+
for (const other of models.values())
|
|
77
|
+
for (const f of other.fields)
|
|
78
|
+
if (f.target === name && f.kind !== 'one2many')
|
|
79
|
+
incoming.push({ model: other.name, field: f.key, label: f.label, kind: f.kind });
|
|
80
|
+
return {
|
|
81
|
+
model: name,
|
|
82
|
+
label: model.label,
|
|
83
|
+
module: owner.get(name) ?? null,
|
|
84
|
+
extendedBy: [...(extenders.get(name) ?? [])].sort(),
|
|
85
|
+
fields,
|
|
86
|
+
transitions: model.transitionSpecs.map((t) => ({
|
|
87
|
+
name: t.name,
|
|
88
|
+
field: t.field,
|
|
89
|
+
from: t.from,
|
|
90
|
+
to: t.to,
|
|
91
|
+
guarded: t.guard !== null,
|
|
92
|
+
message: t.message,
|
|
93
|
+
requires: t.requires,
|
|
94
|
+
})),
|
|
95
|
+
incoming: incoming.sort((a, b) => a.model.localeCompare(b.model) || a.field.localeCompare(b.field)),
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** The group that may read explanations besides administrators. */
|
|
99
|
+
export const DEVELOPER_GROUP = 'base.developer';
|
|
100
|
+
export function registerExplain(app, options) {
|
|
101
|
+
app.get('/api/v1/meta/explain/:model', async (request) => request.env(async (raw) => {
|
|
102
|
+
const env = raw;
|
|
103
|
+
if (!env.administrator && !env.su && !env.groups.has(DEVELOPER_GROUP))
|
|
104
|
+
throw new HttpProblem(403, 'not-developer', 'Developers only', 'Explanations are for administrators and developers.', 'Ask an administrator for the Developer role.');
|
|
105
|
+
const name = String(request.params.model ?? '');
|
|
106
|
+
const explained = explainModel(options.registry, options.models, name);
|
|
107
|
+
if (explained === null)
|
|
108
|
+
throw new HttpProblem(404, 'unknown-model', 'Unknown model', `No installed model is named '${name}'.`);
|
|
109
|
+
return explained;
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
@@ -51,6 +51,8 @@ export interface ApiModel {
|
|
|
51
51
|
readonly onchange: readonly string[];
|
|
52
52
|
/** The stored field names are searched by (`name` by default), or `null`. */
|
|
53
53
|
readonly recName: string | null;
|
|
54
|
+
/** The field sets of the model's unique constraints (a copy leaves them out, RFC 0023 §3.2). */
|
|
55
|
+
readonly unique: readonly (readonly string[])[];
|
|
54
56
|
}
|
|
55
57
|
/** `res.partner` → `ResPartner`. */
|
|
56
58
|
export declare function schemaName(model: string): string;
|
|
@@ -71,10 +71,19 @@ export function apiModels(registry) {
|
|
|
71
71
|
transitionSpecs,
|
|
72
72
|
onchange: [...description.onchange],
|
|
73
73
|
recName: recNameOf(registry, name, fields),
|
|
74
|
+
unique: uniqueOf(registry, name),
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
77
|
return out;
|
|
77
78
|
}
|
|
79
|
+
function uniqueOf(registry, name) {
|
|
80
|
+
// Shutro resolves a model's constraints to `{ kind, fields }` (the definition's object form also accepted).
|
|
81
|
+
const raw = registry.get(name).model.sqlConstraints;
|
|
82
|
+
const list = Array.isArray(raw)
|
|
83
|
+
? raw
|
|
84
|
+
: [];
|
|
85
|
+
return list.flatMap((c) => c.kind === 'unique' && Array.isArray(c.fields) ? [c.fields.map(String)] : []);
|
|
86
|
+
}
|
|
78
87
|
function recNameOf(registry, name, fields) {
|
|
79
88
|
const declared = registry.get(name).model.recName;
|
|
80
89
|
const key = typeof declared === 'string' ? declared : 'name';
|
|
@@ -186,6 +186,12 @@ function modelPaths(model) {
|
|
|
186
186
|
schema: { type: 'boolean' },
|
|
187
187
|
description: 'Also return the total count.',
|
|
188
188
|
},
|
|
189
|
+
{
|
|
190
|
+
name: 'archived',
|
|
191
|
+
in: 'query',
|
|
192
|
+
schema: { type: 'string', enum: ['include', 'only'] },
|
|
193
|
+
description: 'Archived records too (include), or alone (only); hidden otherwise.',
|
|
194
|
+
},
|
|
189
195
|
],
|
|
190
196
|
responses: responses({
|
|
191
197
|
type: 'object',
|
|
@@ -263,6 +269,15 @@ function modelPaths(model) {
|
|
|
263
269
|
},
|
|
264
270
|
},
|
|
265
271
|
};
|
|
272
|
+
paths[`${base}/{id}/duplicate`] = {
|
|
273
|
+
post: {
|
|
274
|
+
operationId: `duplicate${s}`,
|
|
275
|
+
tags: tag,
|
|
276
|
+
summary: 'A copy made as the caller, its lines with it (states and numbers start again)',
|
|
277
|
+
parameters: [ID_PARAM, IDEMPOTENCY],
|
|
278
|
+
responses: responses(ref, '201'),
|
|
279
|
+
},
|
|
280
|
+
};
|
|
266
281
|
for (const t of model.transitions)
|
|
267
282
|
paths[`${base}/{id}/transitions/${t}`] = {
|
|
268
283
|
post: {
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* - `POST /api/v1/models/:model/aggregate` grouped totals
|
|
12
12
|
* - `POST /api/v1/models/:model/onchange` form assistance
|
|
13
13
|
* - `POST /api/v1/models/:model/:id/transitions/:name` state transition (Idempotency-Key)
|
|
14
|
+
* - `POST /api/v1/models/:model/:id/duplicate` a copy made as the user, its lines with it (RFC 0023 R1)
|
|
14
15
|
* - `POST /api/v1/models/:model/export` export (needs `export`; audited)
|
|
15
16
|
* - `POST /api/v1/models/:model/import` import rows (needs `import` and `create`; audited)
|
|
16
17
|
* - `GET /api/v1/meta/:model` the caller's view of the model (needs `read`)
|
package/dist/server/api/rest.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { audit } from '../audit.js';
|
|
2
2
|
import { HttpProblem } from '../errors.js';
|
|
3
3
|
import { announceChange } from './changes.js';
|
|
4
|
+
import { duplicateValues } from './duplicate.js';
|
|
4
5
|
import { idempotent } from './idempotency.js';
|
|
5
6
|
import { defaultFields } from './metadata.js';
|
|
6
7
|
const ref = (env, model) => env.model(model);
|
|
@@ -128,16 +129,47 @@ async function send(reply, outcome) {
|
|
|
128
129
|
}
|
|
129
130
|
/**
|
|
130
131
|
* JSON has no dates: datetime fields arrive as ISO strings and become `Date`s (Shutro stores them in UTC); other
|
|
131
|
-
* kinds are left to Shutro's own validation. Applied to create, update and import values
|
|
132
|
+
* kinds are left to Shutro's own validation. Applied to create, update and import values, and through the lines
|
|
133
|
+
* of one2many and many2many commands (`create` rows and `update` values) to the lines' own datetimes.
|
|
132
134
|
*/
|
|
133
|
-
function coerceValues(model, values) {
|
|
135
|
+
function coerceValues(models, model, values, depth = 0) {
|
|
134
136
|
const out = { ...values };
|
|
135
137
|
for (const [key, value] of Object.entries(values)) {
|
|
136
|
-
|
|
138
|
+
const field = model.byKey.get(key);
|
|
139
|
+
if (field === undefined)
|
|
140
|
+
continue;
|
|
141
|
+
if (field.kind === 'datetime' && typeof value === 'string') {
|
|
142
|
+
const date = new Date(value);
|
|
143
|
+
if (!Number.isNaN(date.getTime()))
|
|
144
|
+
out[key] = date;
|
|
137
145
|
continue;
|
|
138
|
-
|
|
139
|
-
if (
|
|
140
|
-
|
|
146
|
+
}
|
|
147
|
+
if ((field.kind === 'one2many' || field.kind === 'many2many') &&
|
|
148
|
+
field.target !== null &&
|
|
149
|
+
depth < 4 &&
|
|
150
|
+
typeof value === 'object' &&
|
|
151
|
+
value !== null &&
|
|
152
|
+
!Array.isArray(value)) {
|
|
153
|
+
const target = models.get(field.target);
|
|
154
|
+
if (target === undefined)
|
|
155
|
+
continue;
|
|
156
|
+
const commands = value;
|
|
157
|
+
const next = { ...commands };
|
|
158
|
+
if (Array.isArray(commands.create))
|
|
159
|
+
next['create'] = commands.create.map((row) => typeof row === 'object' && row !== null
|
|
160
|
+
? coerceValues(models, target, row, depth + 1)
|
|
161
|
+
: row);
|
|
162
|
+
if (Array.isArray(commands.update))
|
|
163
|
+
next['update'] = commands.update.map((u) => typeof u === 'object' &&
|
|
164
|
+
u !== null &&
|
|
165
|
+
typeof u.values === 'object'
|
|
166
|
+
? {
|
|
167
|
+
...u,
|
|
168
|
+
values: coerceValues(models, target, u.values, depth + 1),
|
|
169
|
+
}
|
|
170
|
+
: u);
|
|
171
|
+
out[key] = next;
|
|
172
|
+
}
|
|
141
173
|
}
|
|
142
174
|
return out;
|
|
143
175
|
}
|
|
@@ -173,9 +205,16 @@ export function registerRestApi(app, options) {
|
|
|
173
205
|
throw problem(400, 'invalid-limit', 'Invalid limit', 'limit must be 1–1000.');
|
|
174
206
|
if (!Number.isInteger(offset) || offset < 0)
|
|
175
207
|
throw problem(400, 'invalid-offset', 'Invalid offset', 'offset must be 0 or more.');
|
|
176
|
-
const
|
|
208
|
+
const filter = parseFilter(q['filter']);
|
|
177
209
|
const order = parseOrder(model, q['order']);
|
|
178
|
-
|
|
210
|
+
// Archived records are hidden unless asked for: `archived=include` lists them with the rest, `archived=only`
|
|
211
|
+
// lists them alone (RFC 0023: archive, never delete).
|
|
212
|
+
const archived = q['archived'];
|
|
213
|
+
if (archived !== undefined && archived !== 'include' && archived !== 'only')
|
|
214
|
+
throw problem(400, 'invalid-archived', 'Invalid archived', "archived is 'include' or 'only'.");
|
|
215
|
+
const where = archived === 'only' ? { ...(filter ?? {}), active: false } : filter;
|
|
216
|
+
return request.env(async (requestEnv) => {
|
|
217
|
+
const env = archived === undefined ? requestEnv : requestEnv.withFlags({ activeTest: false });
|
|
179
218
|
const fields = fieldsOf(model, env, q['fields']);
|
|
180
219
|
const found = await ref(env, model.name).search({
|
|
181
220
|
...(where === undefined ? {} : { where }),
|
|
@@ -197,7 +236,7 @@ export function registerRestApi(app, options) {
|
|
|
197
236
|
const model = modelOf(request.params);
|
|
198
237
|
const values = body(request);
|
|
199
238
|
const outcome = await request.env((env) => idempotent(request, env, driver, async () => {
|
|
200
|
-
const created = await ref(env, model.name).create([coerceValues(model, values)]);
|
|
239
|
+
const created = await ref(env, model.name).create([coerceValues(models, model, values)]);
|
|
201
240
|
await env.flush(); // computes and constraints run now: the response shows the stored record
|
|
202
241
|
announceChange(env, driver, {
|
|
203
242
|
model: model.name,
|
|
@@ -232,7 +271,7 @@ export function registerRestApi(app, options) {
|
|
|
232
271
|
const outcome = await request.env((env) => idempotent(request, env, driver, async () => {
|
|
233
272
|
await ref(env, model.name)
|
|
234
273
|
.browse([id])
|
|
235
|
-
.write(coerceValues(model, values), expected === undefined ? {} : { expect: { updatedAt: expected } });
|
|
274
|
+
.write(coerceValues(models, model, values), expected === undefined ? {} : { expect: { updatedAt: expected } });
|
|
236
275
|
await env.flush();
|
|
237
276
|
announceChange(env, driver, { model: model.name, ids: [id], op: 'update' });
|
|
238
277
|
return record(env, model, id);
|
|
@@ -267,7 +306,7 @@ export function registerRestApi(app, options) {
|
|
|
267
306
|
changed.some((c) => typeof c !== 'string'))
|
|
268
307
|
throw problem(400, 'invalid-body', 'Invalid onchange request', 'The body is { values: { ...the record }, changed: ["field", ...] }.');
|
|
269
308
|
// Datetimes arrive as ISO strings, as for create and update.
|
|
270
|
-
const values = coerceValues(model, input.values);
|
|
309
|
+
const values = coerceValues(models, model, input.values);
|
|
271
310
|
return request.env((env) => ref(env, model.name).onchange({ ...input, values }));
|
|
272
311
|
});
|
|
273
312
|
// RFC 0022 §3.4: what comes next for a record — the transitions from its state the user may run, and for
|
|
@@ -322,6 +361,31 @@ export function registerRestApi(app, options) {
|
|
|
322
361
|
await send(reply, outcome);
|
|
323
362
|
});
|
|
324
363
|
// Name search (many2one pickers, display names of ids): the model's record name, within ACL and rules.
|
|
364
|
+
app.post('/api/v1/models/:model/:id/duplicate', async (request, reply) => {
|
|
365
|
+
const model = modelOf(request.params);
|
|
366
|
+
const id = idOf(request.params);
|
|
367
|
+
const outcome = await request.env((env) => idempotent(request, env, driver, async () => {
|
|
368
|
+
await ref(env, model.name).browse([id]).load(['id']); // the original, as the user may read it
|
|
369
|
+
const values = await duplicateValues(env, models, model, id);
|
|
370
|
+
const created = await ref(env, model.name).create([coerceValues(models, model, values)]);
|
|
371
|
+
await env.flush();
|
|
372
|
+
announceChange(env, driver, {
|
|
373
|
+
model: model.name,
|
|
374
|
+
ids: [created.ids[0]],
|
|
375
|
+
op: 'create',
|
|
376
|
+
});
|
|
377
|
+
const result = await record(env, model, created.ids[0]);
|
|
378
|
+
return {
|
|
379
|
+
...result,
|
|
380
|
+
status: 201,
|
|
381
|
+
headers: {
|
|
382
|
+
...result.headers,
|
|
383
|
+
location: `/api/v1/models/${model.name}/${created.ids[0]}`,
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}));
|
|
387
|
+
await send(reply, outcome);
|
|
388
|
+
});
|
|
325
389
|
app.get('/api/v1/models/:model/names', async (request) => {
|
|
326
390
|
const model = modelOf(request.params);
|
|
327
391
|
const q = query(request);
|
|
@@ -450,7 +514,7 @@ export function registerRestApi(app, options) {
|
|
|
450
514
|
throw problem(400, 'invalid-rows', 'Invalid rows', 'Each row is an object of field values.');
|
|
451
515
|
const outcome = await request.env((env) => idempotent(request, env, driver, async () => {
|
|
452
516
|
env.access.check(model.name, 'import'); // and `create`, checked by Shutro
|
|
453
|
-
const created = await ref(env, model.name).create(input.map((row) => coerceValues(model, row)));
|
|
517
|
+
const created = await ref(env, model.name).create(input.map((row) => coerceValues(models, model, row)));
|
|
454
518
|
await env.flush(); // all-or-nothing: constraints run before the response
|
|
455
519
|
announceChange(env, driver, { model: model.name, ids: [...created.ids], op: 'create' });
|
|
456
520
|
await audit(env, {
|
package/dist/server/api/views.js
CHANGED
|
@@ -174,7 +174,7 @@ export class ViewRegistry {
|
|
|
174
174
|
case 'form': {
|
|
175
175
|
if (view.header.statusbar !== undefined)
|
|
176
176
|
meta(model, view.header.statusbar);
|
|
177
|
-
const buttons = view.header.buttons.filter((b) => allowed.has(b.transition));
|
|
177
|
+
const buttons = view.header.buttons.filter((b) => b.open !== undefined || (b.transition !== undefined && allowed.has(b.transition)));
|
|
178
178
|
served = {
|
|
179
179
|
...view,
|
|
180
180
|
header: { ...view.header, buttons },
|
package/dist/server/apps.d.ts
CHANGED
|
@@ -10,6 +10,10 @@ export declare const APPS_LOG_TABLE = "kosh_app_log";
|
|
|
10
10
|
* installed set (`createAppHost`).
|
|
11
11
|
*/
|
|
12
12
|
export declare const APPS_CHANNEL = "kosh_apps";
|
|
13
|
+
/** What an uninstall dropped, kept for {@link SNAPSHOT_DAYS} days (RFC 0008 §5.4). */
|
|
14
|
+
export declare const APPS_SNAPSHOT_TABLE = "kosh_app_snapshot";
|
|
15
|
+
/** How long an uninstall's snapshot is kept. */
|
|
16
|
+
export declare const SNAPSHOT_DAYS = 30;
|
|
13
17
|
/** Tables the framework owns (not a model's): migrations never alter or drop them. */
|
|
14
18
|
export declare const FRAMEWORK_TABLES: readonly string[];
|
|
15
19
|
/** Creates the App manager's tables (idempotent). */
|
|
@@ -50,8 +54,16 @@ export interface PreparedApps {
|
|
|
50
54
|
readonly migration: MigrationPlan;
|
|
51
55
|
/** True when the plan changes nothing. */
|
|
52
56
|
readonly empty: boolean;
|
|
53
|
-
/** An upgrade may drop columns or tables its new code no longer has (`removeData`). */
|
|
57
|
+
/** An upgrade may drop columns or tables its new code no longer has (`removeData`); an uninstall drops its own. */
|
|
54
58
|
readonly allowDestructive: boolean;
|
|
59
|
+
/** The installed and disabled modules before the change (an uninstall's hooks run on it). */
|
|
60
|
+
readonly before: KoshRegistry;
|
|
61
|
+
/** What an uninstall drops: each table, or column, with the rows that hold data. */
|
|
62
|
+
readonly drops: readonly {
|
|
63
|
+
readonly table: string;
|
|
64
|
+
readonly column: string | null;
|
|
65
|
+
readonly rows: number;
|
|
66
|
+
}[];
|
|
55
67
|
}
|
|
56
68
|
/** Options of {@link prepareApps}. */
|
|
57
69
|
export interface PrepareAppsOptions {
|
|
@@ -111,6 +123,9 @@ export interface AppsResult {
|
|
|
111
123
|
readonly enabled: readonly string[];
|
|
112
124
|
readonly disabled: readonly string[];
|
|
113
125
|
readonly upgraded: readonly string[];
|
|
126
|
+
readonly uninstalled: readonly string[];
|
|
127
|
+
/** The snapshot of what the uninstall dropped (`kosh_app_snapshot.id`), or null. */
|
|
128
|
+
readonly snapshot: number | null;
|
|
114
129
|
}
|
|
115
130
|
/**
|
|
116
131
|
* Runs every served module's `hooks.defaults` (idempotent: approval policies, document templates, activity types,
|
package/dist/server/apps.js
CHANGED
|
@@ -33,8 +33,17 @@ export const APPS_LOG_TABLE = 'kosh_app_log';
|
|
|
33
33
|
* installed set (`createAppHost`).
|
|
34
34
|
*/
|
|
35
35
|
export const APPS_CHANNEL = 'kosh_apps';
|
|
36
|
+
/** What an uninstall dropped, kept for {@link SNAPSHOT_DAYS} days (RFC 0008 §5.4). */
|
|
37
|
+
export const APPS_SNAPSHOT_TABLE = 'kosh_app_snapshot';
|
|
38
|
+
/** How long an uninstall's snapshot is kept. */
|
|
39
|
+
export const SNAPSHOT_DAYS = 30;
|
|
36
40
|
/** Tables the framework owns (not a model's): migrations never alter or drop them. */
|
|
37
|
-
export const FRAMEWORK_TABLES = [
|
|
41
|
+
export const FRAMEWORK_TABLES = [
|
|
42
|
+
IDEMPOTENCY_TABLE,
|
|
43
|
+
APPS_TABLE,
|
|
44
|
+
APPS_LOG_TABLE,
|
|
45
|
+
APPS_SNAPSHOT_TABLE,
|
|
46
|
+
];
|
|
38
47
|
/** Creates the App manager's tables (idempotent). */
|
|
39
48
|
export function appTableStatements() {
|
|
40
49
|
return [
|
|
@@ -62,6 +71,16 @@ export function appTableStatements() {
|
|
|
62
71
|
digest text NOT NULL,
|
|
63
72
|
migration_file text,
|
|
64
73
|
migration_content text
|
|
74
|
+
)`,
|
|
75
|
+
params: [],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
text: `CREATE TABLE IF NOT EXISTS ${APPS_SNAPSHOT_TABLE} (
|
|
79
|
+
id bigserial PRIMARY KEY,
|
|
80
|
+
at timestamptz NOT NULL DEFAULT now(),
|
|
81
|
+
actor text NOT NULL,
|
|
82
|
+
modules text[] NOT NULL,
|
|
83
|
+
tables jsonb NOT NULL
|
|
65
84
|
)`,
|
|
66
85
|
params: [],
|
|
67
86
|
},
|
|
@@ -209,7 +228,7 @@ export async function prepareApps(options) {
|
|
|
209
228
|
registry: schema.registry,
|
|
210
229
|
ignore: FRAMEWORK_TABLES,
|
|
211
230
|
});
|
|
212
|
-
const removeData = options.removeData === true && plan.upgrade.length > 0;
|
|
231
|
+
const removeData = (options.removeData === true && plan.upgrade.length > 0) || plan.uninstall.length > 0;
|
|
213
232
|
if (migration.destructive.length > 0 && !removeData)
|
|
214
233
|
throw new AppPlanError(`The change would drop or alter data: ${migration.destructive.map((op) => migrate.renderOperation(op)).join('; ')}.`, plan.upgrade.length > 0
|
|
215
234
|
? 'Upgrade with "remove data" (shutro-erp apps upgrade --remove-data) to drop them, or keep the fields in the code.'
|
|
@@ -234,6 +253,21 @@ WHERE table_schema = 'public' AND table_name = $1 AND column_name = $2`,
|
|
|
234
253
|
}
|
|
235
254
|
if (orphaned.length > 0)
|
|
236
255
|
throw new AppPlanError(`Disabling would leave required fields that no enabled module fills: ${orphaned.join(', ')}.`, 'Disable the modules that extend these models too, or make the fields optional.');
|
|
256
|
+
// What an uninstall drops, with the rows that hold data: the plan shows it before anyone confirms.
|
|
257
|
+
const drops = [];
|
|
258
|
+
if (plan.uninstall.length > 0)
|
|
259
|
+
for (const op of migration.destructive) {
|
|
260
|
+
if (op.kind !== 'dropTable' && op.kind !== 'dropColumn')
|
|
261
|
+
continue;
|
|
262
|
+
const column = op.kind === 'dropColumn' ? op.column : null;
|
|
263
|
+
const [row] = await driver.query({
|
|
264
|
+
text: column === null
|
|
265
|
+
? `SELECT count(*)::text AS n FROM "${op.table}"`
|
|
266
|
+
: `SELECT count(*)::text AS n FROM "${op.table}" WHERE "${column}" IS NOT NULL`,
|
|
267
|
+
params: [],
|
|
268
|
+
});
|
|
269
|
+
drops.push({ table: op.table, column, rows: Number(row?.n ?? 0) });
|
|
270
|
+
}
|
|
237
271
|
return {
|
|
238
272
|
plan,
|
|
239
273
|
current,
|
|
@@ -244,8 +278,11 @@ WHERE table_schema = 'public' AND table_name = $1 AND column_name = $2`,
|
|
|
244
278
|
plan.enable.length === 0 &&
|
|
245
279
|
plan.disable.length === 0 &&
|
|
246
280
|
plan.upgrade.length === 0 &&
|
|
281
|
+
plan.uninstall.length === 0 &&
|
|
247
282
|
migration.statements.length === 0,
|
|
248
283
|
allowDestructive: removeData,
|
|
284
|
+
before,
|
|
285
|
+
drops,
|
|
249
286
|
};
|
|
250
287
|
}
|
|
251
288
|
/**
|
|
@@ -311,6 +348,7 @@ const defaultLoad = async (context, modules) => {
|
|
|
311
348
|
export async function applyApps(prepared, options) {
|
|
312
349
|
const { plan } = prepared;
|
|
313
350
|
const step = options.onStep ?? (() => { });
|
|
351
|
+
let snapshotId = null;
|
|
314
352
|
const file = prepared.migration.statements.length > 0
|
|
315
353
|
? await migrate.writeMigrationFile({
|
|
316
354
|
dir: options.dir,
|
|
@@ -328,6 +366,45 @@ export async function applyApps(prepared, options) {
|
|
|
328
366
|
// Before the first change the table was just created empty: compare with what was planned from.
|
|
329
367
|
if (now !== null && now.length > 0 && digestOf(now) !== digestOf(prepared.current))
|
|
330
368
|
throw new AppPlanError('The installed apps changed since this change was planned.', 'Plan it again.');
|
|
369
|
+
const removed = plan.uninstall.map((u) => u.name);
|
|
370
|
+
let snapshot = null;
|
|
371
|
+
if (removed.length > 0) {
|
|
372
|
+
// Each module may refuse (Accounting while posted entries exist), on the modules as they are now.
|
|
373
|
+
const before = orm.createShutro({ driver: tx, registry: prepared.before.registry });
|
|
374
|
+
for (const name of removed) {
|
|
375
|
+
const hook = prepared.before.loaded.byName.get(name)?.hooks?.preUninstall;
|
|
376
|
+
if (hook === undefined)
|
|
377
|
+
continue;
|
|
378
|
+
step('hooks', name);
|
|
379
|
+
try {
|
|
380
|
+
await before.bootstrap({ reason: `uninstall ${name}` }, (env) => hook(env));
|
|
381
|
+
}
|
|
382
|
+
catch (error) {
|
|
383
|
+
throw new AppPlanError(`${name} cannot be uninstalled: ${error instanceof Error ? error.message : String(error)}`, 'Disable it instead: the app goes away and its data stays.');
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
// The snapshot: every dropped table's rows and every dropped column's values, kept for 30 days.
|
|
387
|
+
step('state', 'snapshot');
|
|
388
|
+
const tables = [];
|
|
389
|
+
for (const d of prepared.drops) {
|
|
390
|
+
const [row] = await tx.query({
|
|
391
|
+
text: d.column === null
|
|
392
|
+
? `SELECT coalesce(json_agg(t), '[]'::json) AS rows FROM "${d.table}" t`
|
|
393
|
+
: `SELECT coalesce(json_agg(json_build_object('id', id, 'value', "${d.column}")), '[]'::json) AS rows FROM "${d.table}" WHERE "${d.column}" IS NOT NULL`,
|
|
394
|
+
params: [],
|
|
395
|
+
});
|
|
396
|
+
tables.push({ table: d.table, column: d.column, rows: row?.rows ?? [] });
|
|
397
|
+
}
|
|
398
|
+
await tx.query({
|
|
399
|
+
text: `DELETE FROM ${APPS_SNAPSHOT_TABLE} WHERE at < now() - make_interval(days => $1)`,
|
|
400
|
+
params: [SNAPSHOT_DAYS],
|
|
401
|
+
});
|
|
402
|
+
const [made] = await tx.query({
|
|
403
|
+
text: `INSERT INTO ${APPS_SNAPSHOT_TABLE} (actor, modules, tables) VALUES ($1, $2, $3::jsonb) RETURNING id::text AS id`,
|
|
404
|
+
params: [options.actor, removed, tables],
|
|
405
|
+
});
|
|
406
|
+
snapshot = made === undefined ? null : Number(made.id);
|
|
407
|
+
}
|
|
331
408
|
// Upgrades run the modules' data migrations even without schema statements (Shutro tracks the versions).
|
|
332
409
|
if (file !== null || plan.upgrade.length > 0) {
|
|
333
410
|
step('schema', `${prepared.migration.statements.length} statement(s)`);
|
|
@@ -373,6 +450,14 @@ export async function applyApps(prepared, options) {
|
|
|
373
450
|
step('data', upgraded.join(', '));
|
|
374
451
|
await (options.load ?? defaultLoad)({ shutro, driver: tx, registry: prepared.runtime, company: companyId }, upgraded);
|
|
375
452
|
}
|
|
453
|
+
if (removed.length > 0) {
|
|
454
|
+
step('data', `remove ${removed.join(', ')}`);
|
|
455
|
+
await removeModuleData(tx, shutro, prepared, removed);
|
|
456
|
+
await tx.query({
|
|
457
|
+
text: `DELETE FROM ${APPS_TABLE} WHERE name = ANY($1)`,
|
|
458
|
+
params: [removed],
|
|
459
|
+
});
|
|
460
|
+
}
|
|
376
461
|
step('state', `${plan.after.length} module(s)`);
|
|
377
462
|
for (const row of plan.after)
|
|
378
463
|
await tx.query({
|
|
@@ -396,6 +481,8 @@ VALUES ($1, $2::jsonb, $3, $4, $5, $6)`,
|
|
|
396
481
|
enable: plan.enable,
|
|
397
482
|
disable: plan.disable,
|
|
398
483
|
upgrade: plan.upgrade,
|
|
484
|
+
uninstall: plan.uninstall,
|
|
485
|
+
snapshot,
|
|
399
486
|
migration: file === null ? null : `${file.version}_${file.name}.sql`,
|
|
400
487
|
},
|
|
401
488
|
sql,
|
|
@@ -410,6 +497,7 @@ VALUES ($1, $2::jsonb, $3, $4, $5, $6)`,
|
|
|
410
497
|
...plan.enable.map((n) => `enabled ${n}`),
|
|
411
498
|
...plan.disable.map((m) => `disabled ${m.name}`),
|
|
412
499
|
...plan.upgrade.map((u) => `upgraded ${u.name} ${u.from} → ${u.to}`),
|
|
500
|
+
...plan.uninstall.map((u) => `uninstalled ${u.name} (data removed)`),
|
|
413
501
|
];
|
|
414
502
|
await shutro.bootstrap({ reason: 'apps change audit' }, (env) => audit(env, {
|
|
415
503
|
kind: 'apps.change',
|
|
@@ -419,6 +507,7 @@ VALUES ($1, $2::jsonb, $3, $4, $5, $6)`,
|
|
|
419
507
|
details: {
|
|
420
508
|
by: options.actor,
|
|
421
509
|
migration: file === null ? null : `${file.version}_${file.name}.sql`,
|
|
510
|
+
...(removed.length === 0 ? {} : { snapshot, drops: prepared.drops }),
|
|
422
511
|
},
|
|
423
512
|
...(options.audit?.ip === undefined ? {} : { ip: options.audit.ip }),
|
|
424
513
|
}));
|
|
@@ -428,6 +517,7 @@ VALUES ($1, $2::jsonb, $3, $4, $5, $6)`,
|
|
|
428
517
|
text: 'SELECT pg_notify($1, $2)',
|
|
429
518
|
params: [APPS_CHANNEL, digestOf(plan.after)],
|
|
430
519
|
});
|
|
520
|
+
snapshotId = snapshot;
|
|
431
521
|
});
|
|
432
522
|
}
|
|
433
523
|
catch (error) {
|
|
@@ -441,5 +531,77 @@ VALUES ($1, $2::jsonb, $3, $4, $5, $6)`,
|
|
|
441
531
|
enabled: plan.enable,
|
|
442
532
|
disabled: plan.disable.map((m) => m.name),
|
|
443
533
|
upgraded: plan.upgrade.map((u) => u.name),
|
|
534
|
+
uninstalled: plan.uninstall.map((u) => u.name),
|
|
535
|
+
snapshot: snapshotId,
|
|
444
536
|
};
|
|
445
537
|
}
|
|
538
|
+
/** Whether a table exists (a failed query would abort the transaction). */
|
|
539
|
+
async function hasTable(tx, table) {
|
|
540
|
+
const [row] = await tx.query({
|
|
541
|
+
text: 'SELECT to_regclass($1)::text AS found',
|
|
542
|
+
params: [`public.${table}`],
|
|
543
|
+
});
|
|
544
|
+
return row?.found !== null && row?.found !== undefined;
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* After an uninstall's schema change (RFC 0008 §5.4): the modules' records in the models that stay (by external
|
|
548
|
+
* id; one that others still use is kept and logged), their external ids, their groups and role grants, the runtime
|
|
549
|
+
* roles' rights on models that went, and Shutro's record of the modules.
|
|
550
|
+
*/
|
|
551
|
+
async function removeModuleData(tx, shutro, prepared, removed) {
|
|
552
|
+
const models = new Set([...prepared.runtime.registry.models.keys()].map(String));
|
|
553
|
+
const ids = await tx.query({
|
|
554
|
+
text: 'SELECT model, res_id::text AS res_id, name FROM shutro_external_id WHERE module = ANY($1)',
|
|
555
|
+
params: [removed],
|
|
556
|
+
});
|
|
557
|
+
for (const row of ids) {
|
|
558
|
+
if (!models.has(row.model))
|
|
559
|
+
continue;
|
|
560
|
+
try {
|
|
561
|
+
await shutro.bootstrap({ reason: `uninstall: ${row.name}` }, async (env) => {
|
|
562
|
+
const records = env.model(row.model);
|
|
563
|
+
await records.browse([Number(row.res_id)]).unlink();
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
catch {
|
|
567
|
+
// Still used by records that stay (a sequence, a category): kept, without its module.
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
await tx.query({
|
|
571
|
+
text: 'DELETE FROM shutro_external_id WHERE module = ANY($1)',
|
|
572
|
+
params: [removed],
|
|
573
|
+
});
|
|
574
|
+
const byName = prepared.before.loaded.byName;
|
|
575
|
+
const groups = removed.flatMap((n) => (byName.get(n)?.shutro.groups ?? []).map((g) => g.code));
|
|
576
|
+
const permissions = removed.flatMap((n) => (byName.get(n)?.shutro.permissions ?? []).map((p) => p.code));
|
|
577
|
+
if (groups.length > 0 && (await hasTable(tx, 'auth_user_group')))
|
|
578
|
+
await tx.query({
|
|
579
|
+
text: 'DELETE FROM auth_user_group WHERE "group" = ANY($1)',
|
|
580
|
+
params: [groups],
|
|
581
|
+
});
|
|
582
|
+
const kept = [...models];
|
|
583
|
+
if (await hasTable(tx, 'shutro_role')) {
|
|
584
|
+
await tx.query({
|
|
585
|
+
text: 'UPDATE shutro_role SET implies = array(SELECT g FROM unnest(implies) AS g WHERE NOT (g = ANY($1))) WHERE implies && $1',
|
|
586
|
+
params: [groups],
|
|
587
|
+
});
|
|
588
|
+
await tx.query({
|
|
589
|
+
text: 'DELETE FROM shutro_role_access WHERE role = ANY($1) OR NOT (model = ANY($2))',
|
|
590
|
+
params: [groups, kept],
|
|
591
|
+
});
|
|
592
|
+
await tx.query({
|
|
593
|
+
text: 'DELETE FROM shutro_role_rule WHERE role = ANY($1) OR NOT (model = ANY($2))',
|
|
594
|
+
params: [groups, kept],
|
|
595
|
+
});
|
|
596
|
+
await tx.query({
|
|
597
|
+
text: 'DELETE FROM shutro_role_permission WHERE role = ANY($1) OR permission = ANY($2)',
|
|
598
|
+
params: [groups, permissions],
|
|
599
|
+
});
|
|
600
|
+
await tx.query({
|
|
601
|
+
text: 'DELETE FROM shutro_role_field WHERE NOT (model = ANY($1))',
|
|
602
|
+
params: [kept],
|
|
603
|
+
});
|
|
604
|
+
}
|
|
605
|
+
if (await hasTable(tx, 'shutro_module'))
|
|
606
|
+
await tx.query({ text: 'DELETE FROM shutro_module WHERE name = ANY($1)', params: [removed] });
|
|
607
|
+
}
|
package/dist/server/auth/base.js
CHANGED
|
@@ -273,6 +273,8 @@ export const baseShutro = defineModule({
|
|
|
273
273
|
version: '0.1.0',
|
|
274
274
|
groups: [
|
|
275
275
|
defineGroup({ code: 'base.user', label: 'Internal user' }),
|
|
276
|
+
// Developer mode (RFC 0031 §3.1): the explain popovers of fields, buttons and models.
|
|
277
|
+
defineGroup({ code: 'base.developer', label: 'Developer', implies: ['base.user'] }),
|
|
276
278
|
defineGroup({
|
|
277
279
|
code: 'base.admin',
|
|
278
280
|
label: 'Administrator',
|
|
@@ -50,6 +50,8 @@ export interface UserInfo {
|
|
|
50
50
|
readonly administrator: boolean;
|
|
51
51
|
/** Administrator rights are withheld until this session has a second factor (enable or verify 2FA). */
|
|
52
52
|
readonly administratorNeedsSecondFactor: boolean;
|
|
53
|
+
/** May turn on Developer mode (RFC 0031 §3.1): administrators and the `base.developer` group. */
|
|
54
|
+
readonly developer: boolean;
|
|
53
55
|
/** The policy requires two-factor sign-in of everyone and it is not on: nothing but setting it up is allowed. */
|
|
54
56
|
readonly twoFactorSetupRequired: boolean;
|
|
55
57
|
}
|
|
@@ -174,6 +176,12 @@ export declare class AuthService {
|
|
|
174
176
|
/** Completes a pending sign-in with a TOTP or recovery code (a recovery code works once). */
|
|
175
177
|
verifyTwoFactor(session: SessionInfo, code: string): Promise<void>;
|
|
176
178
|
/** Turns 2FA off (password and a current code required). */
|
|
179
|
+
/**
|
|
180
|
+
* A step-up check (RFC 0008 §5.4): the user's current authenticator code, asked again for a dangerous action
|
|
181
|
+
* (removing an app with its data). Recovery codes do not count. @throws {HttpProblem} 409 when two-factor is off,
|
|
182
|
+
* 422 when the code does not match
|
|
183
|
+
*/
|
|
184
|
+
confirmSecondFactor(user: number, code: string): Promise<void>;
|
|
177
185
|
disableTwoFactor(session: SessionInfo, password: string, code: string): Promise<void>;
|
|
178
186
|
/** Changes the password; every other session of the user is revoked. */
|
|
179
187
|
changePassword(session: SessionInfo, current: string, next: string): Promise<void>;
|