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,662 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Shop's logic (RFC 0024 §2.7), run as the system in the site's company once the public routes know the site:
|
|
3
|
+
* the catalogue (published products in the visitor's language), carts (a quotation per secret token, its lines priced
|
|
4
|
+
* by the server from the product — never by the visitor), delivery methods with their fee for the cart, and checkout —
|
|
5
|
+
* the customer found by email or made, cash on delivery confirmed (or left for someone to confirm above the site's
|
|
6
|
+
* limit), or an online payment started whose confirmation confirms, invoices and pays the order.
|
|
7
|
+
*/
|
|
8
|
+
import { startPayment } from '../payment/index.js';
|
|
9
|
+
import { HttpProblem } from '../server/index.js';
|
|
10
|
+
import { orm } from '../server/orm.js';
|
|
11
|
+
import { feeOf, hashToken, newCartToken } from './module.js';
|
|
12
|
+
const zero = orm.toDecimal(0);
|
|
13
|
+
const LANGS = { en: 'en_US', bn: 'bn_BD' };
|
|
14
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
15
|
+
/** At most this many of one product in a cart, and this many lines. */
|
|
16
|
+
const MAX_QUANTITY = orm.toDecimal(1000);
|
|
17
|
+
const MAX_LINES = 100;
|
|
18
|
+
const invalid = (field, detail) => new HttpProblem(422, 'invalid-input', `Invalid ${field}`, detail);
|
|
19
|
+
const money = (d) => (d ?? zero).toDecimalPlaces(2).toFixed(2);
|
|
20
|
+
/** The environment reading in the visitor's language. */
|
|
21
|
+
export const inLang = (env, lang) => env.withLang(LANGS[lang] ?? 'en_US');
|
|
22
|
+
/** Published products on sale, searched, filtered, sorted and paged. */
|
|
23
|
+
export async function catalogue(env, query, site) {
|
|
24
|
+
const size = Math.min(Math.max(query.size ?? 24, 1), 60);
|
|
25
|
+
const page = Math.max(query.page ?? 1, 1);
|
|
26
|
+
const where = { published: true, saleOk: true };
|
|
27
|
+
if (query.q !== undefined && query.q.trim() !== '') {
|
|
28
|
+
const q = query.q.trim().slice(0, 100);
|
|
29
|
+
where['or'] = [{ name: { ilike: `%${q}%` } }, { code: { ilike: `%${q}%` } }];
|
|
30
|
+
}
|
|
31
|
+
if (query.category !== undefined) {
|
|
32
|
+
// The category and those inside it.
|
|
33
|
+
const inside = new Set([query.category]);
|
|
34
|
+
let next = [query.category];
|
|
35
|
+
while (next.length > 0 && inside.size < 500) {
|
|
36
|
+
next = (await env.model('product.category').search({ where: { parent: { in: next } } })).ids.filter((c) => !inside.has(c));
|
|
37
|
+
for (const c of next)
|
|
38
|
+
inside.add(c);
|
|
39
|
+
}
|
|
40
|
+
where['category'] = { in: [...inside] };
|
|
41
|
+
}
|
|
42
|
+
if (query.min !== undefined || query.max !== undefined)
|
|
43
|
+
where['listPrice'] = {
|
|
44
|
+
...(query.min === undefined ? {} : { gte: query.min.toString() }),
|
|
45
|
+
...(query.max === undefined ? {} : { lte: query.max.toString() }),
|
|
46
|
+
};
|
|
47
|
+
const order = query.sort === 'price'
|
|
48
|
+
? ['listPrice asc', 'id asc']
|
|
49
|
+
: query.sort === '-price'
|
|
50
|
+
? ['listPrice desc', 'id asc']
|
|
51
|
+
: query.sort === 'name'
|
|
52
|
+
? ['name asc', 'id asc']
|
|
53
|
+
: query.sort === 'new'
|
|
54
|
+
? ['id desc']
|
|
55
|
+
: ['shopSequence asc', 'name asc', 'id asc'];
|
|
56
|
+
const all = await env.model('product.product').search({ where: where });
|
|
57
|
+
const found = await env.model('product.product').search({
|
|
58
|
+
where: where,
|
|
59
|
+
order: order,
|
|
60
|
+
limit: size,
|
|
61
|
+
offset: (page - 1) * size,
|
|
62
|
+
});
|
|
63
|
+
const products = await withAvailability(env, site, [...(await found.load({ category: true, uom: true }))].map(card));
|
|
64
|
+
return { products, total: all.length, page, pages: Math.max(Math.ceil(all.length / size), 1) };
|
|
65
|
+
}
|
|
66
|
+
// biome-ignore lint/suspicious/noExplicitAny: a loaded product of any load spec
|
|
67
|
+
function card(p) {
|
|
68
|
+
return {
|
|
69
|
+
id: p.id,
|
|
70
|
+
name: p.name,
|
|
71
|
+
code: p.code,
|
|
72
|
+
price: money(p.listPrice),
|
|
73
|
+
category: p.category === null ? null : { id: p.category.id, name: p.category.name },
|
|
74
|
+
unit: p.uom?.name ?? null,
|
|
75
|
+
hasImage: p.image !== null,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** A published product's page: its card, description, weight and related products (published ones). */
|
|
79
|
+
/**
|
|
80
|
+
* The cards with what is available, when stock is followed (`website_sale_stock`): in stock, few left (with how
|
|
81
|
+
* many) or out of stock, by the site's "few left" threshold; null when it is not.
|
|
82
|
+
*/
|
|
83
|
+
async function withAvailability(env, site, cards) {
|
|
84
|
+
if (cards.length === 0)
|
|
85
|
+
return cards;
|
|
86
|
+
const available = await env
|
|
87
|
+
.model('product.product')
|
|
88
|
+
.browse(cards.map((c) => c['id']))
|
|
89
|
+
.shopAvailable();
|
|
90
|
+
const [s] = site === undefined
|
|
91
|
+
? []
|
|
92
|
+
: await env
|
|
93
|
+
.model('website.site')
|
|
94
|
+
.browse([site.id])
|
|
95
|
+
.load({});
|
|
96
|
+
const few = orm.toDecimal(s?.fewLeft ?? 5);
|
|
97
|
+
return cards.map((c) => {
|
|
98
|
+
const raw = available.get(c['id']) ?? null;
|
|
99
|
+
if (raw === null)
|
|
100
|
+
return { ...c, availability: null };
|
|
101
|
+
const left = orm.toDecimal(raw);
|
|
102
|
+
return left.lte(0)
|
|
103
|
+
? { ...c, availability: 'out_of_stock' }
|
|
104
|
+
: left.lte(few)
|
|
105
|
+
? { ...c, availability: 'few_left', left: left.toString() }
|
|
106
|
+
: { ...c, availability: 'in_stock' };
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
export async function productPage(env, id, site) {
|
|
110
|
+
const [p] = await (await env.model('product.product').search({
|
|
111
|
+
where: { id: orm.asId(id, 'product.product'), published: true, saleOk: true },
|
|
112
|
+
limit: 1,
|
|
113
|
+
})).load({ category: true, uom: true, related: true });
|
|
114
|
+
if (p === undefined)
|
|
115
|
+
return null;
|
|
116
|
+
const related = await (await env.model('product.product').search({
|
|
117
|
+
where: {
|
|
118
|
+
id: { in: [...p.related.ids] },
|
|
119
|
+
published: true,
|
|
120
|
+
saleOk: true,
|
|
121
|
+
},
|
|
122
|
+
})).load({ category: true, uom: true });
|
|
123
|
+
const [self] = await withAvailability(env, site, [card(p)]);
|
|
124
|
+
return {
|
|
125
|
+
...self,
|
|
126
|
+
description: p.websiteDescription,
|
|
127
|
+
seo: {
|
|
128
|
+
title: p.seoTitle ?? p.name,
|
|
129
|
+
description: p.seoDescription ?? (p.websiteDescription ?? '').slice(0, 160),
|
|
130
|
+
},
|
|
131
|
+
weight: p.weight === null ? null : p.weight.toString(),
|
|
132
|
+
related: await withAvailability(env, site, [...related].map(card)),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/** The product categories that hold published products, with their parents. */
|
|
136
|
+
export async function categories(env) {
|
|
137
|
+
const products = await (await env.model('product.product').search({ where: { published: true, saleOk: true } })).load({ category: true });
|
|
138
|
+
const ids = new Set();
|
|
139
|
+
for (const p of products)
|
|
140
|
+
if (p.category !== null)
|
|
141
|
+
ids.add(p.category.id);
|
|
142
|
+
const out = new Map();
|
|
143
|
+
let next = [...ids];
|
|
144
|
+
while (next.length > 0) {
|
|
145
|
+
const found = await env
|
|
146
|
+
.model('product.category')
|
|
147
|
+
.browse(next)
|
|
148
|
+
.load({ parent: true });
|
|
149
|
+
next = [];
|
|
150
|
+
for (const c of found) {
|
|
151
|
+
out.set(c.id, { id: c.id, name: c.name, parent: c.parent?.id ?? null });
|
|
152
|
+
if (c.parent !== null && !out.has(c.parent.id))
|
|
153
|
+
next.push(c.parent.id);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return [...out.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
157
|
+
}
|
|
158
|
+
/** Who carts belong to before checkout: the site's visitor contact, made the first time. */
|
|
159
|
+
async function visitorOf(env, site) {
|
|
160
|
+
const [s] = await env
|
|
161
|
+
.model('website.site')
|
|
162
|
+
.browse([site.id])
|
|
163
|
+
.load({ visitor: true });
|
|
164
|
+
if (s?.visitor !== null && s?.visitor !== undefined)
|
|
165
|
+
return s.visitor.id;
|
|
166
|
+
const made = await env
|
|
167
|
+
.model('res.partner')
|
|
168
|
+
.create([{ name: `${s?.name ?? site.key} — shop visitor`, company: site.company }]);
|
|
169
|
+
const id = made.ids[0];
|
|
170
|
+
await env
|
|
171
|
+
.model('website.site')
|
|
172
|
+
.browse([site.id])
|
|
173
|
+
.write({ visitor: id });
|
|
174
|
+
return id;
|
|
175
|
+
}
|
|
176
|
+
/** Who the site's orders are assigned to: its salesperson, else whoever made the site. */
|
|
177
|
+
async function salespersonOf(env, site) {
|
|
178
|
+
const [s] = await env
|
|
179
|
+
.model('website.site')
|
|
180
|
+
.browse([site.id])
|
|
181
|
+
.load({ salesperson: true });
|
|
182
|
+
return s?.salesperson?.id ?? s?.createdBy ?? null;
|
|
183
|
+
}
|
|
184
|
+
/** A new cart: a quotation of the site's company for its visitor, with a new secret token. */
|
|
185
|
+
export async function newCart(env, site) {
|
|
186
|
+
const token = newCartToken();
|
|
187
|
+
const [company] = await env
|
|
188
|
+
.model('res.company')
|
|
189
|
+
.browse([site.company])
|
|
190
|
+
.load({ currency: true });
|
|
191
|
+
const visitor = await visitorOf(env, site);
|
|
192
|
+
const made = await env.model('sale.order').create([
|
|
193
|
+
{
|
|
194
|
+
partner: visitor,
|
|
195
|
+
// The customer's price list, when they have one (RFC 0028 §3.1).
|
|
196
|
+
pricelist: (await pricelistOf(env, visitor)),
|
|
197
|
+
currency: company?.currency.id,
|
|
198
|
+
company: site.company,
|
|
199
|
+
website: site.id,
|
|
200
|
+
cartToken: hashToken(token),
|
|
201
|
+
user: (await salespersonOf(env, site)),
|
|
202
|
+
},
|
|
203
|
+
]);
|
|
204
|
+
await env.flush();
|
|
205
|
+
return { token, id: made.ids[0] };
|
|
206
|
+
}
|
|
207
|
+
/** The order of `token` on the site: a cart (not checked out) unless `placed`. */
|
|
208
|
+
export async function orderOf(env, site, token, placed = false) {
|
|
209
|
+
if (token === '' || token.length > 100)
|
|
210
|
+
return null;
|
|
211
|
+
const [id] = (await env.model('sale.order').search({
|
|
212
|
+
where: {
|
|
213
|
+
website: site.id,
|
|
214
|
+
cartToken: hashToken(token),
|
|
215
|
+
...(placed
|
|
216
|
+
? { placedAt: { isNull: false } }
|
|
217
|
+
: { placedAt: { isNull: true }, state: 'draft' }),
|
|
218
|
+
},
|
|
219
|
+
limit: 1,
|
|
220
|
+
})).ids;
|
|
221
|
+
return id ?? null;
|
|
222
|
+
}
|
|
223
|
+
/** What the visitor sees of a cart or an order. */
|
|
224
|
+
export async function cartView(env, id) {
|
|
225
|
+
const [o] = await env
|
|
226
|
+
.model('sale.order')
|
|
227
|
+
.browse([id])
|
|
228
|
+
.load({ lines: { product: true }, currency: true, carrier: true, coupon: true });
|
|
229
|
+
if (o === undefined)
|
|
230
|
+
throw new HttpProblem(404, 'not-found', 'Not found', 'There is no cart.');
|
|
231
|
+
const goods = [...o.lines].filter((l) => !l.isDelivery && !l.isDiscount);
|
|
232
|
+
const fee = [...o.lines]
|
|
233
|
+
.filter((l) => l.isDelivery)
|
|
234
|
+
.reduce((s, l) => s.plus((l.priceUnit ?? zero).times(l.quantity)), zero);
|
|
235
|
+
const payments = await (await env.model('payment.transaction').search({
|
|
236
|
+
where: { saleOrder: id },
|
|
237
|
+
order: ['id desc'],
|
|
238
|
+
limit: 1,
|
|
239
|
+
})).load({});
|
|
240
|
+
return {
|
|
241
|
+
name: o.name,
|
|
242
|
+
state: o.state,
|
|
243
|
+
placed: o.placedAt !== null,
|
|
244
|
+
currency: o.currency.name,
|
|
245
|
+
lines: goods.map((l) => ({
|
|
246
|
+
product: l.product.id,
|
|
247
|
+
name: l.name,
|
|
248
|
+
quantity: l.quantity.toString(),
|
|
249
|
+
price: money(l.priceUnit ?? zero),
|
|
250
|
+
subtotal: money((l.priceUnit ?? zero).times(l.quantity)),
|
|
251
|
+
})),
|
|
252
|
+
carrier: o.carrier === null ? null : { id: o.carrier.id, name: o.carrier.name },
|
|
253
|
+
delivery: money(fee),
|
|
254
|
+
untaxed: money(o.amountUntaxed?.amount),
|
|
255
|
+
tax: money(o.amountTax?.amount),
|
|
256
|
+
total: money(o.amountTotal?.amount),
|
|
257
|
+
coupon: o.coupon === null ? null : o.coupon.code,
|
|
258
|
+
discount: money([...o.lines].reduce((s, l) => l.isDiscount
|
|
259
|
+
? s.minus((l.priceUnit ?? zero).times(l.quantity))
|
|
260
|
+
: s.plus((l.priceUnit ?? zero).times(l.quantity).times(l.discount).div(100)), zero)),
|
|
261
|
+
payment: o.shopPayment,
|
|
262
|
+
paymentState: [...payments][0]?.state ?? null,
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
/** A customer's price list (RFC 0028 §3.1), or null. */
|
|
266
|
+
async function pricelistOf(env, partner) {
|
|
267
|
+
if (typeof partner !== 'number')
|
|
268
|
+
return null;
|
|
269
|
+
const [p] = await env
|
|
270
|
+
.model('res.partner')
|
|
271
|
+
.browse([partner])
|
|
272
|
+
.load({ pricelist: true });
|
|
273
|
+
return p?.pricelist?.id ?? null;
|
|
274
|
+
}
|
|
275
|
+
const goodsOf = async (env, id) => {
|
|
276
|
+
const [o] = await env
|
|
277
|
+
.model('sale.order')
|
|
278
|
+
.browse([id])
|
|
279
|
+
.load({ lines: { product: true }, carrier: true, partner: true });
|
|
280
|
+
const goods = [...(o?.lines ?? [])].filter((l) => !l.isDelivery && !l.isDiscount);
|
|
281
|
+
return {
|
|
282
|
+
order: o,
|
|
283
|
+
amount: goods.reduce((s, l) => s.plus((l.priceUnit ?? zero).times(l.quantity)), zero),
|
|
284
|
+
weight: goods.reduce((s, l) => s.plus((l.product.weight ?? zero).times(l.quantity)), zero),
|
|
285
|
+
};
|
|
286
|
+
};
|
|
287
|
+
/** Sets how many of `product` the cart holds (0 removes it), at the product's price; the fee follows. */
|
|
288
|
+
export async function setLine(env, id, product, quantity) {
|
|
289
|
+
if (quantity.lt(0) || quantity.gt(MAX_QUANTITY))
|
|
290
|
+
throw invalid('quantity', `From 0 to ${MAX_QUANTITY.toString()}.`);
|
|
291
|
+
const [p] = await (await env.model('product.product').search({
|
|
292
|
+
where: { id: orm.asId(product, 'product.product'), published: true, saleOk: true },
|
|
293
|
+
limit: 1,
|
|
294
|
+
})).load({ taxes: true, uom: true });
|
|
295
|
+
if (p === undefined)
|
|
296
|
+
throw invalid('product', 'This product is not on the shop.');
|
|
297
|
+
// Beyond what is available, unless the site sells beyond stock.
|
|
298
|
+
const [cart] = await env.model('sale.order').browse([id]).load({ website: true });
|
|
299
|
+
if (cart?.website !== null && cart?.website !== undefined && !cart.website.allowBackorders) {
|
|
300
|
+
const raw = (await env.model('product.product').browse([p.id]).shopAvailable()).get(p.id);
|
|
301
|
+
if (raw !== null && raw !== undefined && quantity.gt(orm.toDecimal(raw)))
|
|
302
|
+
throw invalid('quantity', orm.toDecimal(raw).lte(0)
|
|
303
|
+
? `${p.name} is out of stock.`
|
|
304
|
+
: `Only ${orm.toDecimal(raw).toString()} of ${p.name} left.`);
|
|
305
|
+
}
|
|
306
|
+
const lines = await (await env
|
|
307
|
+
.model('sale.order.line')
|
|
308
|
+
.search({ where: { order: id, isDelivery: false, isDiscount: false } })).load({ product: true });
|
|
309
|
+
const line = [...lines].find((l) => l.product.id === p.id);
|
|
310
|
+
if (line !== undefined) {
|
|
311
|
+
if (quantity.isZero())
|
|
312
|
+
await env.model('sale.order.line').browse([line.id]).unlink();
|
|
313
|
+
else
|
|
314
|
+
await env.model('sale.order.line').browse([line.id]).write({ quantity });
|
|
315
|
+
}
|
|
316
|
+
else if (!quantity.isZero()) {
|
|
317
|
+
if (lines.length >= MAX_LINES)
|
|
318
|
+
throw invalid('cart', `At most ${MAX_LINES} products.`);
|
|
319
|
+
await env.model('sale.order.line').create([
|
|
320
|
+
{
|
|
321
|
+
order: id,
|
|
322
|
+
product: p.id,
|
|
323
|
+
name: p.name,
|
|
324
|
+
quantity,
|
|
325
|
+
uom: p.uom?.id ?? null,
|
|
326
|
+
// Priced by the cart's price list (the customer's), else at the list price (RFC 0028 §3.1).
|
|
327
|
+
taxes: { set: [...p.taxes.ids] },
|
|
328
|
+
},
|
|
329
|
+
]);
|
|
330
|
+
}
|
|
331
|
+
await env.flush();
|
|
332
|
+
await reprice(env, id);
|
|
333
|
+
}
|
|
334
|
+
/** The cart's fee and coupon again, after a change. */
|
|
335
|
+
async function reprice(env, id, city) {
|
|
336
|
+
const [o] = await env.model('sale.order').browse([id]).load({ carrier: true, partner: true });
|
|
337
|
+
if (o?.carrier !== null && o?.carrier !== undefined)
|
|
338
|
+
await setDelivery(env, id, o.carrier.id, city === undefined ? o.partner.city : city);
|
|
339
|
+
else
|
|
340
|
+
await refreshCoupon(env, id);
|
|
341
|
+
}
|
|
342
|
+
const day = (v) => v === null || v === undefined
|
|
343
|
+
? null
|
|
344
|
+
: v instanceof Date
|
|
345
|
+
? `${v.getFullYear()}-${String(v.getMonth() + 1).padStart(2, '0')}-${String(v.getDate()).padStart(2, '0')}`
|
|
346
|
+
: String(v).slice(0, 10);
|
|
347
|
+
/** Why `coupon` does not apply to goods worth `amount` today (null: it applies). */
|
|
348
|
+
function couponProblem(env, coupon, amount) {
|
|
349
|
+
const today = env.today();
|
|
350
|
+
const from = day(coupon.validFrom);
|
|
351
|
+
const to = day(coupon.validTo);
|
|
352
|
+
if (from !== null && today < from)
|
|
353
|
+
return `The coupon ${coupon.code} starts on ${from}.`;
|
|
354
|
+
if (to !== null && today > to)
|
|
355
|
+
return `The coupon ${coupon.code} ended on ${to}.`;
|
|
356
|
+
if (coupon.maxUses !== null && coupon.uses >= coupon.maxUses)
|
|
357
|
+
return `The coupon ${coupon.code} has been used up.`;
|
|
358
|
+
if (amount.lt(coupon.minimum))
|
|
359
|
+
return `The coupon ${coupon.code} is for orders of ${money(coupon.minimum)} or more.`;
|
|
360
|
+
return null;
|
|
361
|
+
}
|
|
362
|
+
/**
|
|
363
|
+
* Applies the cart's coupon: a percentage off each goods line, or an amount off as a line of its own (never more than
|
|
364
|
+
* the goods), or the delivery fee (`setDelivery`); a coupon that no longer applies (the cart fell below its minimum)
|
|
365
|
+
* is taken off.
|
|
366
|
+
*/
|
|
367
|
+
async function refreshCoupon(env, id) {
|
|
368
|
+
const [o] = await env.model('sale.order').browse([id]).load({ coupon: true, lines: true });
|
|
369
|
+
if (o === undefined)
|
|
370
|
+
return;
|
|
371
|
+
const goods = [...o.lines].filter((l) => !l.isDelivery && !l.isDiscount);
|
|
372
|
+
const amount = goods.reduce((s, l) => s.plus((l.priceUnit ?? zero).times(l.quantity)), zero);
|
|
373
|
+
let coupon = o.coupon;
|
|
374
|
+
if (coupon !== null && couponProblem(env, coupon, amount) !== null) {
|
|
375
|
+
await env.model('sale.order').browse([id]).write({ coupon: null });
|
|
376
|
+
coupon = null;
|
|
377
|
+
}
|
|
378
|
+
const percent = coupon?.kind === 'percent' ? coupon.value : zero;
|
|
379
|
+
for (const l of goods)
|
|
380
|
+
if (!l.discount.eq(percent))
|
|
381
|
+
await env.model('sale.order.line').browse([l.id]).write({ discount: percent });
|
|
382
|
+
const old = await env.model('sale.order.line').search({ where: { order: id, isDiscount: true } });
|
|
383
|
+
if (old.length > 0)
|
|
384
|
+
await old.unlink();
|
|
385
|
+
if (coupon?.kind === 'amount' && coupon.value.gt(0) && amount.gt(0)) {
|
|
386
|
+
const off = coupon.value.lt(amount) ? coupon.value : amount;
|
|
387
|
+
await env.model('sale.order.line').create([
|
|
388
|
+
{
|
|
389
|
+
order: id,
|
|
390
|
+
product: await discountProduct(env, coupon.id),
|
|
391
|
+
name: `Coupon ${coupon.code}`,
|
|
392
|
+
quantity: '1',
|
|
393
|
+
priceUnit: off.negated(),
|
|
394
|
+
isDiscount: true,
|
|
395
|
+
},
|
|
396
|
+
]);
|
|
397
|
+
}
|
|
398
|
+
await env.flush();
|
|
399
|
+
}
|
|
400
|
+
/** The service product an amount off is charged as, made the first time. */
|
|
401
|
+
async function discountProduct(env, coupon) {
|
|
402
|
+
const [c] = await env
|
|
403
|
+
.model('shop.coupon')
|
|
404
|
+
.browse([coupon])
|
|
405
|
+
.load({ product: true });
|
|
406
|
+
if (c?.product !== null && c?.product !== undefined)
|
|
407
|
+
return c.product.id;
|
|
408
|
+
const made = await env
|
|
409
|
+
.model('product.product')
|
|
410
|
+
.create([
|
|
411
|
+
{ name: `Coupon ${c?.code ?? ''}`, kind: 'service', saleOk: false, purchaseOk: false },
|
|
412
|
+
]);
|
|
413
|
+
const product = made.ids[0];
|
|
414
|
+
await env
|
|
415
|
+
.model('shop.coupon')
|
|
416
|
+
.browse([coupon])
|
|
417
|
+
.write({ product: product });
|
|
418
|
+
return product;
|
|
419
|
+
}
|
|
420
|
+
/** Puts a coupon on the cart (an empty code takes it off). @throws 422 with why it does not apply */
|
|
421
|
+
export async function setCoupon(env, site, id, code) {
|
|
422
|
+
const clean = code.trim().toUpperCase();
|
|
423
|
+
if (clean === '') {
|
|
424
|
+
await env.model('sale.order').browse([id]).write({ coupon: null });
|
|
425
|
+
await reprice(env, id);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
const [coupon] = await (await env.model('shop.coupon').search({
|
|
429
|
+
where: { code: clean.slice(0, 40), company: site.company },
|
|
430
|
+
limit: 1,
|
|
431
|
+
})).load({});
|
|
432
|
+
if (coupon === undefined)
|
|
433
|
+
throw invalid('coupon', `There is no coupon ${clean}.`);
|
|
434
|
+
const { amount } = await goodsOf(env, id);
|
|
435
|
+
const problem = couponProblem(env, coupon, amount);
|
|
436
|
+
if (problem !== null)
|
|
437
|
+
throw invalid('coupon', problem);
|
|
438
|
+
await env.model('sale.order').browse([id]).write({ coupon: coupon.id });
|
|
439
|
+
await reprice(env, id);
|
|
440
|
+
}
|
|
441
|
+
/** At checkout: the coupon still applies, and once per customer means once for this email. */
|
|
442
|
+
async function checkCouponAtCheckout(env, site, id, email) {
|
|
443
|
+
const [o] = await env.model('sale.order').browse([id]).load({ coupon: true });
|
|
444
|
+
const coupon = o?.coupon;
|
|
445
|
+
if (coupon === null || coupon === undefined)
|
|
446
|
+
return null;
|
|
447
|
+
const { amount } = await goodsOf(env, id);
|
|
448
|
+
const problem = couponProblem(env, coupon, amount);
|
|
449
|
+
if (problem !== null)
|
|
450
|
+
throw invalid('coupon', problem);
|
|
451
|
+
if (coupon.oncePerCustomer) {
|
|
452
|
+
const before = await env.model('sale.order').search({
|
|
453
|
+
where: {
|
|
454
|
+
coupon: coupon.id,
|
|
455
|
+
website: site.id,
|
|
456
|
+
placedAt: { isNull: false },
|
|
457
|
+
state: { ne: 'cancel' },
|
|
458
|
+
'partner.email': email,
|
|
459
|
+
},
|
|
460
|
+
limit: 1,
|
|
461
|
+
});
|
|
462
|
+
if (before.length > 0)
|
|
463
|
+
throw invalid('coupon', `The coupon ${coupon.code} is once per customer: it was used already.`);
|
|
464
|
+
}
|
|
465
|
+
return coupon.id;
|
|
466
|
+
}
|
|
467
|
+
/** The company's delivery methods with their fee for the cart, delivered to `city`. */
|
|
468
|
+
export async function carriersFor(env, id, city) {
|
|
469
|
+
const { amount, weight, order } = await goodsOf(env, id);
|
|
470
|
+
const found = await (await env.model('delivery.carrier').search({
|
|
471
|
+
where: { company: order?.company.id },
|
|
472
|
+
})).load({});
|
|
473
|
+
return [...found].map((c) => ({
|
|
474
|
+
id: c.id,
|
|
475
|
+
name: c.name,
|
|
476
|
+
fee: money(feeOf(c, amount, weight, city)),
|
|
477
|
+
cod: c.cod,
|
|
478
|
+
description: c.description,
|
|
479
|
+
}));
|
|
480
|
+
}
|
|
481
|
+
/** The fee's service product of a method, made the first time. */
|
|
482
|
+
async function feeProduct(env, carrier) {
|
|
483
|
+
const [c] = await env
|
|
484
|
+
.model('delivery.carrier')
|
|
485
|
+
.browse([carrier])
|
|
486
|
+
.load({ product: true });
|
|
487
|
+
if (c?.product !== null && c?.product !== undefined)
|
|
488
|
+
return c.product.id;
|
|
489
|
+
const made = await env
|
|
490
|
+
.model('product.product')
|
|
491
|
+
.create([
|
|
492
|
+
{ name: `Delivery: ${c?.name ?? ''}`, kind: 'service', saleOk: false, purchaseOk: false },
|
|
493
|
+
]);
|
|
494
|
+
const product = made.ids[0];
|
|
495
|
+
await env
|
|
496
|
+
.model('delivery.carrier')
|
|
497
|
+
.browse([carrier])
|
|
498
|
+
.write({ product: product });
|
|
499
|
+
return product;
|
|
500
|
+
}
|
|
501
|
+
/** Chooses the delivery method: its fee, for the cart and `city`, replaces the cart's fee line. */
|
|
502
|
+
export async function setDelivery(env, id, carrier, city) {
|
|
503
|
+
const { amount, weight, order } = await goodsOf(env, id);
|
|
504
|
+
const [c] = await (await env.model('delivery.carrier').search({
|
|
505
|
+
where: { id: carrier, company: order?.company.id },
|
|
506
|
+
limit: 1,
|
|
507
|
+
})).load({});
|
|
508
|
+
if (c === undefined)
|
|
509
|
+
throw invalid('delivery method', 'Choose one of the methods offered.');
|
|
510
|
+
const [withCoupon] = await env.model('sale.order').browse([id]).load({ coupon: true });
|
|
511
|
+
const free = withCoupon?.coupon?.kind === 'free_delivery' &&
|
|
512
|
+
couponProblem(env, withCoupon.coupon, amount) === null;
|
|
513
|
+
const fee = free ? zero : feeOf(c, amount, weight, city);
|
|
514
|
+
const old = await env.model('sale.order.line').search({ where: { order: id, isDelivery: true } });
|
|
515
|
+
if (old.length > 0)
|
|
516
|
+
await old.unlink();
|
|
517
|
+
await env.model('sale.order.line').create([
|
|
518
|
+
{
|
|
519
|
+
order: id,
|
|
520
|
+
product: await feeProduct(env, c.id),
|
|
521
|
+
name: c.name,
|
|
522
|
+
quantity: '1',
|
|
523
|
+
priceUnit: fee,
|
|
524
|
+
isDelivery: true,
|
|
525
|
+
},
|
|
526
|
+
]);
|
|
527
|
+
await env.model('sale.order').browse([id]).write({ carrier: c.id });
|
|
528
|
+
await env.flush();
|
|
529
|
+
await refreshCoupon(env, id);
|
|
530
|
+
}
|
|
531
|
+
const text = (v, field, max, required = true) => {
|
|
532
|
+
const s = typeof v === 'string' ? v.trim() : '';
|
|
533
|
+
if (required && s === '')
|
|
534
|
+
throw invalid(field, 'is required.');
|
|
535
|
+
if (s.length > max)
|
|
536
|
+
throw invalid(field, `is at most ${max} characters.`);
|
|
537
|
+
return s;
|
|
538
|
+
};
|
|
539
|
+
/** Reads and checks a checkout's input. */
|
|
540
|
+
export function checkoutInput(body) {
|
|
541
|
+
const b = (body ?? {});
|
|
542
|
+
const email = text(b['email'], 'email', 200);
|
|
543
|
+
if (!EMAIL.test(email))
|
|
544
|
+
throw invalid('email', 'is an email address.');
|
|
545
|
+
const returnUrl = text(b['returnUrl'], 'return address', 500, false);
|
|
546
|
+
if (returnUrl !== '' && !/^https?:\/\/[^\s]+$/i.test(returnUrl))
|
|
547
|
+
throw invalid('return address', 'is an http(s) address.');
|
|
548
|
+
const carrier = Number(b['carrier']);
|
|
549
|
+
if (!Number.isSafeInteger(carrier) || carrier <= 0)
|
|
550
|
+
throw invalid('delivery method', 'Choose one of the methods offered.');
|
|
551
|
+
return {
|
|
552
|
+
name: text(b['name'], 'name', 120),
|
|
553
|
+
email,
|
|
554
|
+
phone: text(b['phone'], 'phone', 30),
|
|
555
|
+
street: text(b['street'], 'address', 200),
|
|
556
|
+
city: text(b['city'], 'city', 80),
|
|
557
|
+
carrier,
|
|
558
|
+
payment: text(b['payment'], 'payment', 20),
|
|
559
|
+
note: text(b['note'], 'note', 1000, false),
|
|
560
|
+
...(returnUrl === '' ? {} : { returnUrl }),
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Places the cart: the customer (found by email in the company, else made), the delivery to their city, the note.
|
|
565
|
+
* Cash on delivery confirms it when the method takes cash and the total is within the site's limit (above it, it
|
|
566
|
+
* stays a quotation for the team to confirm); paying online starts a payment and returns where to send the visitor.
|
|
567
|
+
*/
|
|
568
|
+
export async function checkout(env, site, id, input) {
|
|
569
|
+
const lines = await env
|
|
570
|
+
.model('sale.order.line')
|
|
571
|
+
.search({ where: { order: id, isDelivery: false } });
|
|
572
|
+
if (lines.length === 0)
|
|
573
|
+
throw invalid('cart', 'The cart is empty.');
|
|
574
|
+
const [existing] = (await env.model('res.partner').search({
|
|
575
|
+
where: { email: input.email, company: site.company },
|
|
576
|
+
limit: 1,
|
|
577
|
+
})).ids;
|
|
578
|
+
const values = {
|
|
579
|
+
name: input.name,
|
|
580
|
+
email: input.email,
|
|
581
|
+
phone: input.phone,
|
|
582
|
+
street: input.street,
|
|
583
|
+
city: input.city,
|
|
584
|
+
};
|
|
585
|
+
let partner;
|
|
586
|
+
if (existing !== undefined) {
|
|
587
|
+
partner = existing;
|
|
588
|
+
// A known customer's address is not changed by a visitor: the order keeps it in its note.
|
|
589
|
+
}
|
|
590
|
+
else
|
|
591
|
+
partner = (await env.model('res.partner').create([{ ...values, company: site.company }])).ids[0];
|
|
592
|
+
await env
|
|
593
|
+
.model('sale.order')
|
|
594
|
+
.browse([id])
|
|
595
|
+
.write({ partner: partner });
|
|
596
|
+
await setDelivery(env, id, input.carrier, input.city);
|
|
597
|
+
const [c] = await env
|
|
598
|
+
.model('delivery.carrier')
|
|
599
|
+
.browse([input.carrier])
|
|
600
|
+
.load({});
|
|
601
|
+
const deliverTo = `Deliver to: ${input.name}, ${input.phone}, ${input.street}, ${input.city}`;
|
|
602
|
+
const note = [deliverTo, input.note ?? ''].filter((s) => s !== '').join('\n');
|
|
603
|
+
const [s] = await env
|
|
604
|
+
.model('website.site')
|
|
605
|
+
.browse([site.id])
|
|
606
|
+
.load({});
|
|
607
|
+
const order = env.model('sale.order').browse([id]);
|
|
608
|
+
const coupon = await checkCouponAtCheckout(env, site, id, input.email);
|
|
609
|
+
if (coupon !== null) {
|
|
610
|
+
const [c] = await env
|
|
611
|
+
.model('shop.coupon')
|
|
612
|
+
.browse([coupon])
|
|
613
|
+
.load({});
|
|
614
|
+
await env
|
|
615
|
+
.model('shop.coupon')
|
|
616
|
+
.browse([coupon])
|
|
617
|
+
.write({ uses: (c?.uses ?? 0) + 1 });
|
|
618
|
+
}
|
|
619
|
+
if (input.payment === 'cod') {
|
|
620
|
+
if (c?.cod !== true)
|
|
621
|
+
throw invalid('payment', 'This delivery method does not take cash on delivery.');
|
|
622
|
+
await order.write({ shopPayment: 'cod', shopNote: note, placedAt: new Date() });
|
|
623
|
+
await env.flush();
|
|
624
|
+
const [o] = await order.load({});
|
|
625
|
+
if (s?.codLimit === null ||
|
|
626
|
+
s?.codLimit === undefined ||
|
|
627
|
+
(o?.amountTotal?.amount ?? zero).lte(s.codLimit))
|
|
628
|
+
await order.confirm();
|
|
629
|
+
const [placed] = await order.load({});
|
|
630
|
+
return {
|
|
631
|
+
order: placed?.name ?? '',
|
|
632
|
+
state: placed?.state ?? 'draft',
|
|
633
|
+
total: money(placed?.amountTotal?.amount),
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
const provider = Number(input.payment);
|
|
637
|
+
if (!Number.isSafeInteger(provider) || provider <= 0)
|
|
638
|
+
throw invalid('payment', 'Cash on delivery, or one of the providers offered.');
|
|
639
|
+
await order.write({ shopPayment: 'online', shopNote: note, placedAt: new Date() });
|
|
640
|
+
await env.flush();
|
|
641
|
+
const [o] = await order.load({ partner: true, currency: true });
|
|
642
|
+
if (o === undefined)
|
|
643
|
+
throw new HttpProblem(404, 'not-found', 'Not found', 'There is no cart.');
|
|
644
|
+
const started = await startPayment(env, {
|
|
645
|
+
provider,
|
|
646
|
+
move: null,
|
|
647
|
+
partner: o.partner,
|
|
648
|
+
currency: o.currency,
|
|
649
|
+
amount: o.amountTotal?.amount ?? zero,
|
|
650
|
+
company: site.company,
|
|
651
|
+
description: `Order ${o.name}`,
|
|
652
|
+
...(input.returnUrl === undefined ? {} : { returnUrl: input.returnUrl }),
|
|
653
|
+
values: { saleOrder: o.id },
|
|
654
|
+
});
|
|
655
|
+
return {
|
|
656
|
+
order: o.name,
|
|
657
|
+
state: o.state,
|
|
658
|
+
total: money(o.amountTotal?.amount),
|
|
659
|
+
redirectUrl: started.redirectUrl,
|
|
660
|
+
reference: started.reference,
|
|
661
|
+
};
|
|
662
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export { defineModel, defineModule, extendModel, f, replace } from '../../server/orm.js';
|
|
2
|
+
import { type DefinitionHelpers } from '../../server/orm.js';
|
|
3
|
+
import type { DefaultLoaded, Env as CoreEnv, RecordSet as CoreRecordSet, Shutro as CoreShutro } from '../../server/orm.js';
|
|
4
|
+
import type { SchemaView } from './schema.js';
|
|
5
|
+
export type { SchemaView };
|
|
6
|
+
/** Every model visible in this view. */
|
|
7
|
+
export type ModelName = keyof SchemaView & string;
|
|
8
|
+
/** `Env` typed to this view. */
|
|
9
|
+
export type Env = CoreEnv<SchemaView>;
|
|
10
|
+
/** `Shutro` typed to this view. */
|
|
11
|
+
export type Shutro = CoreShutro<SchemaView>;
|
|
12
|
+
/** A recordset of `M`, with the default loaded shape unless `L` is given. */
|
|
13
|
+
export type Records<M extends ModelName, L = DefaultLoaded<SchemaView, M>> = CoreRecordSet<SchemaView, M, L>;
|
|
14
|
+
export declare const compute: DefinitionHelpers<SchemaView>['compute'];
|
|
15
|
+
export declare const check: DefinitionHelpers<SchemaView>['check'];
|
|
16
|
+
export declare const onchange: DefinitionHelpers<SchemaView>['onchange'];
|
|
17
|
+
export declare const records: DefinitionHelpers<SchemaView>['records'];
|
|
18
|
+
export declare const method: DefinitionHelpers<SchemaView>['method'];
|
|
19
|
+
/** Module data (RFC 0017): records, overrides of dependencies' records, and references, typed like creates. */
|
|
20
|
+
export declare const record: DefinitionHelpers<SchemaView>['record'];
|
|
21
|
+
export declare const override: DefinitionHelpers<SchemaView>['override'];
|
|
22
|
+
export declare const ref: DefinitionHelpers<SchemaView>['ref'];
|