shutro-erp 0.3.4 → 0.3.6
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.d.ts +22 -0
- package/dist/account_asset/.kosh/bindings.js +15 -0
- package/dist/account_asset/.kosh/schema.d.ts +1755 -0
- 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/index.d.ts +9 -0
- package/dist/account_asset/index.js +335 -0
- package/dist/account_asset/module.d.ts +7622 -0
- package/dist/account_asset/module.js +230 -0
- package/dist/account_budget/.kosh/bindings.d.ts +22 -0
- package/dist/account_budget/.kosh/bindings.js +15 -0
- package/dist/account_budget/.kosh/schema.d.ts +1767 -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/index.d.ts +4 -0
- package/dist/account_budget/index.js +223 -0
- package/dist/account_budget/module.d.ts +13863 -0
- package/dist/account_budget/module.js +167 -0
- package/dist/account_portal/.kosh/bindings.js +1 -1
- package/dist/account_portal/.kosh/schema.d.ts +176 -6
- package/dist/account_portal/module.d.ts +505 -6
- package/dist/accounting/.kosh/bindings.js +1 -1
- package/dist/accounting/.kosh/schema.d.ts +176 -6
- package/dist/accounting/index.d.ts +1 -1
- package/dist/accounting/index.js +344 -4
- package/dist/accounting/module.d.ts +513 -5
- package/dist/accounting/module.js +55 -3
- package/dist/approvals/.kosh/bindings.d.ts +22 -0
- package/dist/approvals/.kosh/bindings.js +15 -0
- package/dist/approvals/.kosh/schema.d.ts +611 -0
- package/dist/approvals/index.d.ts +18 -0
- package/dist/approvals/index.js +302 -0
- package/dist/approvals/mails.d.ts +25 -0
- package/dist/approvals/mails.js +82 -0
- package/dist/approvals/module.d.ts +1 -0
- package/dist/approvals/module.js +12 -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/Mine.d.ts +7 -0
- package/dist/approvals/web/Mine.js +27 -0
- package/dist/approvals/web/index.d.ts +13 -0
- package/dist/approvals/web/index.js +22 -0
- package/dist/approvals/web/messages.d.ts +2 -0
- package/dist/approvals/web/messages.js +119 -0
- package/dist/approvals/web/shared.d.ts +59 -0
- package/dist/approvals/web/shared.js +46 -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 +1048 -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/index.d.ts +4 -0
- package/dist/attendance/index.js +513 -0
- package/dist/attendance/module.d.ts +3969 -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/calendar/.kosh/bindings.d.ts +22 -0
- package/dist/calendar/.kosh/bindings.js +15 -0
- package/dist/calendar/.kosh/schema.d.ts +795 -0
- package/dist/calendar/demo.d.ts +6 -0
- package/dist/calendar/demo.js +24 -0
- package/dist/calendar/index.d.ts +3 -0
- package/dist/calendar/index.js +291 -0
- package/dist/calendar/module.d.ts +1705 -0
- package/dist/calendar/module.js +180 -0
- package/dist/cli/init.js +80 -0
- package/dist/cli/templates.d.ts +1 -1
- package/dist/cli/templates.js +226 -0
- package/dist/contacts/.kosh/bindings.js +1 -1
- package/dist/contacts/.kosh/schema.d.ts +78 -3
- package/dist/contacts/demo.js +30 -1
- package/dist/contacts/index.d.ts +1 -1
- package/dist/contacts/index.js +155 -9
- package/dist/contacts/module.d.ts +338 -2
- package/dist/contacts/module.js +93 -5
- package/dist/crm/.kosh/bindings.d.ts +22 -0
- package/dist/crm/.kosh/bindings.js +15 -0
- package/dist/crm/.kosh/schema.d.ts +800 -0
- package/dist/crm/demo.d.ts +6 -0
- package/dist/crm/demo.js +45 -0
- package/dist/crm/index.d.ts +3 -0
- package/dist/crm/index.js +361 -0
- package/dist/crm/module.d.ts +1845 -0
- package/dist/crm/module.js +208 -0
- package/dist/documents/.kosh/bindings.d.ts +22 -0
- package/dist/documents/.kosh/bindings.js +15 -0
- package/dist/documents/.kosh/schema.d.ts +862 -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/index.d.ts +30 -0
- package/dist/documents/index.js +442 -0
- package/dist/documents/module.d.ts +2781 -0
- package/dist/documents/module.js +214 -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.d.ts +22 -0
- package/dist/event/.kosh/bindings.js +15 -0
- package/dist/event/.kosh/schema.d.ts +843 -0
- package/dist/event/demo.d.ts +6 -0
- package/dist/event/demo.js +65 -0
- package/dist/event/index.d.ts +14 -0
- package/dist/event/index.js +333 -0
- package/dist/event/mails.d.ts +15 -0
- package/dist/event/mails.js +112 -0
- package/dist/event/module.d.ts +2800 -0
- package/dist/event/module.js +323 -0
- package/dist/event/web/CheckInBox.d.ts +9 -0
- package/dist/event/web/CheckInBox.js +87 -0
- package/dist/event/web/index.d.ts +11 -0
- package/dist/event/web/index.js +16 -0
- package/dist/event/web/messages.d.ts +2 -0
- package/dist/event/web/messages.js +23 -0
- package/dist/event_account/.kosh/bindings.d.ts +22 -0
- package/dist/event_account/.kosh/bindings.js +15 -0
- package/dist/event_account/.kosh/schema.d.ts +1758 -0
- package/dist/event_account/index.d.ts +3 -0
- package/dist/event_account/index.js +28 -0
- package/dist/event_account/module.d.ts +7114 -0
- package/dist/event_account/module.js +90 -0
- package/dist/expense/.kosh/bindings.d.ts +22 -0
- package/dist/expense/.kosh/bindings.js +15 -0
- package/dist/expense/.kosh/schema.d.ts +1299 -0
- package/dist/expense/demo.d.ts +7 -0
- package/dist/expense/demo.js +69 -0
- package/dist/expense/index.d.ts +3 -0
- package/dist/expense/index.js +495 -0
- package/dist/expense/module.d.ts +4747 -0
- package/dist/expense/module.js +647 -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 +1936 -0
- package/dist/expense_account/index.d.ts +3 -0
- package/dist/expense_account/index.js +74 -0
- package/dist/expense_account/module.d.ts +9153 -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 +885 -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/index.d.ts +10 -0
- package/dist/helpdesk/index.js +461 -0
- package/dist/helpdesk/module.d.ts +2940 -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.d.ts +22 -0
- package/dist/hr/.kosh/bindings.js +15 -0
- package/dist/hr/.kosh/schema.d.ts +878 -0
- package/dist/hr/demo.d.ts +6 -0
- package/dist/hr/demo.js +60 -0
- package/dist/hr/index.d.ts +3 -0
- package/dist/hr/index.js +483 -0
- package/dist/hr/module.d.ts +2611 -0
- package/dist/hr/module.js +350 -0
- package/dist/l10n_bd/.kosh/bindings.js +1 -1
- package/dist/l10n_bd/.kosh/schema.d.ts +176 -6
- package/dist/l10n_bd/module.d.ts +505 -6
- 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 +2312 -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/index.d.ts +4 -0
- package/dist/l10n_bd_payroll/index.js +24 -0
- package/dist/l10n_bd_payroll/module.d.ts +12932 -0
- package/dist/l10n_bd_payroll/module.js +86 -0
- package/dist/l10n_in/.kosh/bindings.js +1 -1
- package/dist/l10n_in/.kosh/schema.d.ts +176 -6
- package/dist/l10n_in/module.d.ts +527 -7
- package/dist/mail/inbound/handle.js +6 -1
- package/dist/maintenance/.kosh/bindings.d.ts +22 -0
- package/dist/maintenance/.kosh/bindings.js +15 -0
- package/dist/maintenance/.kosh/schema.d.ts +911 -0
- package/dist/maintenance/demo.d.ts +6 -0
- package/dist/maintenance/demo.js +60 -0
- package/dist/maintenance/index.d.ts +3 -0
- package/dist/maintenance/index.js +422 -0
- package/dist/maintenance/module.d.ts +3545 -0
- package/dist/maintenance/module.js +446 -0
- package/dist/maintenance_stock/.kosh/bindings.d.ts +22 -0
- package/dist/maintenance_stock/.kosh/bindings.js +15 -0
- package/dist/maintenance_stock/.kosh/schema.d.ts +1446 -0
- package/dist/maintenance_stock/index.d.ts +9 -0
- package/dist/maintenance_stock/index.js +52 -0
- package/dist/maintenance_stock/module.d.ts +7675 -0
- package/dist/maintenance_stock/module.js +192 -0
- package/dist/modules-sdk/index.d.ts +1 -1
- package/dist/modules-sdk/manifest.d.ts +109 -2
- package/dist/modules-sdk/manifest.js +35 -1
- package/dist/mrp/.kosh/bindings.d.ts +22 -0
- package/dist/mrp/.kosh/bindings.js +15 -0
- package/dist/mrp/.kosh/schema.d.ts +1453 -0
- package/dist/mrp/demo.d.ts +7 -0
- package/dist/mrp/demo.js +42 -0
- package/dist/mrp/index.d.ts +3 -0
- package/dist/mrp/index.js +393 -0
- package/dist/mrp/module.d.ts +9335 -0
- package/dist/mrp/module.js +757 -0
- package/dist/mrp_account/.kosh/bindings.d.ts +22 -0
- package/dist/mrp_account/.kosh/bindings.js +15 -0
- package/dist/mrp_account/.kosh/schema.d.ts +2299 -0
- package/dist/mrp_account/index.d.ts +3 -0
- package/dist/mrp_account/index.js +22 -0
- package/dist/mrp_account/module.d.ts +23151 -0
- package/dist/mrp_account/module.js +123 -0
- package/dist/payment/.kosh/bindings.js +1 -1
- package/dist/payment/.kosh/schema.d.ts +178 -7
- package/dist/payment/index.d.ts +41 -0
- package/dist/payment/index.js +131 -74
- package/dist/payment/module.d.ts +569 -12
- package/dist/payment/module.js +19 -3
- package/dist/payment/settle.js +10 -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 +1390 -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/index.d.ts +230 -0
- package/dist/payroll/index.js +713 -0
- package/dist/payroll/module.d.ts +9390 -0
- 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 +2308 -0
- package/dist/payroll_account/index.d.ts +3 -0
- package/dist/payroll_account/index.js +40 -0
- package/dist/payroll_account/module.d.ts +12396 -0
- package/dist/payroll_account/module.js +256 -0
- package/dist/permissions/approvals.d.ts +131 -9
- package/dist/permissions/approvals.js +662 -57
- package/dist/permissions/index.d.ts +1 -1
- package/dist/permissions/index.js +2 -2
- package/dist/permissions/models.js +191 -12
- package/dist/permissions/routes.d.ts +6 -1
- package/dist/permissions/routes.js +37 -4
- package/dist/permissions/web/ApprovalPanel.d.ts +5 -4
- package/dist/permissions/web/ApprovalPanel.js +39 -13
- 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 +43 -0
- package/dist/portal/.kosh/bindings.js +1 -1
- package/dist/portal/.kosh/schema.d.ts +78 -3
- package/dist/portal/index.js +1 -1
- package/dist/portal/module.d.ts +166 -2
- package/dist/pos/.kosh/bindings.d.ts +22 -0
- package/dist/pos/.kosh/bindings.js +15 -0
- package/dist/pos/.kosh/schema.d.ts +1809 -0
- package/dist/pos/accounting.d.ts +10 -0
- package/dist/pos/accounting.js +149 -0
- package/dist/pos/demo.d.ts +6 -0
- package/dist/pos/demo.js +90 -0
- package/dist/pos/index.d.ts +9 -0
- package/dist/pos/index.js +353 -0
- package/dist/pos/module.d.ts +9783 -0
- package/dist/pos/module.js +423 -0
- package/dist/pos/routes.d.ts +39 -0
- package/dist/pos/routes.js +368 -0
- package/dist/pos/web/Register.d.ts +9 -0
- package/dist/pos/web/Register.js +252 -0
- package/dist/pos/web/index.d.ts +11 -0
- package/dist/pos/web/index.js +16 -0
- package/dist/pos/web/messages.d.ts +2 -0
- package/dist/pos/web/messages.js +87 -0
- package/dist/pos_stock/.kosh/bindings.d.ts +22 -0
- package/dist/pos_stock/.kosh/bindings.js +15 -0
- package/dist/pos_stock/.kosh/schema.d.ts +2208 -0
- package/dist/pos_stock/index.d.ts +9 -0
- package/dist/pos_stock/index.js +35 -0
- package/dist/pos_stock/module.d.ts +12803 -0
- package/dist/pos_stock/module.js +115 -0
- package/dist/product/.kosh/bindings.js +1 -1
- package/dist/product/.kosh/schema.d.ts +52 -1
- package/dist/product/index.js +94 -4
- package/dist/product/module.d.ts +45 -1
- package/dist/product/module.js +11 -3
- package/dist/project/.kosh/bindings.js +1 -1
- package/dist/project/.kosh/schema.d.ts +725 -3
- package/dist/project/index.d.ts +3 -1
- package/dist/project/index.js +1045 -18
- package/dist/project/module.d.ts +10168 -458
- package/dist/project/module.js +1578 -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 +2241 -0
- package/dist/project_account/index.d.ts +3 -0
- package/dist/project_account/index.js +27 -0
- package/dist/project_account/module.d.ts +11539 -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 +725 -3
- package/dist/project_portal/module.d.ts +5137 -119
- 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 +2320 -0
- package/dist/project_purchase/index.d.ts +9 -0
- package/dist/project_purchase/index.js +22 -0
- package/dist/project_purchase/module.d.ts +25147 -0
- package/dist/project_purchase/module.js +56 -0
- package/dist/purchase/.kosh/bindings.js +1 -1
- package/dist/purchase/.kosh/schema.d.ts +150 -6
- package/dist/purchase/index.js +163 -3
- package/dist/purchase/module.d.ts +228 -5
- package/dist/purchase/module.js +30 -3
- package/dist/purchase_account/.kosh/bindings.js +1 -1
- package/dist/purchase_account/.kosh/schema.d.ts +180 -8
- package/dist/purchase_account/index.js +27 -1
- package/dist/purchase_account/module.d.ts +591 -30
- package/dist/purchase_account/module.js +13 -2
- package/dist/purchase_portal/.kosh/bindings.js +1 -1
- package/dist/purchase_portal/.kosh/schema.d.ts +150 -6
- package/dist/purchase_portal/module.d.ts +372 -6
- package/dist/purchase_stock/.kosh/bindings.d.ts +22 -0
- package/dist/purchase_stock/.kosh/bindings.js +15 -0
- package/dist/purchase_stock/.kosh/schema.d.ts +1479 -0
- package/dist/purchase_stock/index.d.ts +7 -0
- package/dist/purchase_stock/index.js +98 -0
- package/dist/purchase_stock/module.d.ts +7535 -0
- package/dist/purchase_stock/module.js +380 -0
- package/dist/purchase_stock_account/.kosh/bindings.d.ts +22 -0
- package/dist/purchase_stock_account/.kosh/bindings.js +15 -0
- package/dist/purchase_stock_account/.kosh/schema.d.ts +2155 -0
- package/dist/purchase_stock_account/index.d.ts +3 -0
- package/dist/purchase_stock_account/index.js +24 -0
- package/dist/purchase_stock_account/module.d.ts +38015 -0
- package/dist/purchase_stock_account/module.js +168 -0
- package/dist/recruitment/.kosh/bindings.d.ts +22 -0
- package/dist/recruitment/.kosh/bindings.js +15 -0
- package/dist/recruitment/.kosh/schema.d.ts +991 -0
- package/dist/recruitment/demo.d.ts +6 -0
- package/dist/recruitment/demo.js +45 -0
- package/dist/recruitment/index.d.ts +3 -0
- package/dist/recruitment/index.js +385 -0
- package/dist/recruitment/module.d.ts +3249 -0
- package/dist/recruitment/module.js +194 -0
- package/dist/recruitment/web/HireButton.d.ts +9 -0
- package/dist/recruitment/web/HireButton.js +32 -0
- package/dist/recruitment/web/index.d.ts +11 -0
- package/dist/recruitment/web/index.js +16 -0
- package/dist/recruitment/web/messages.d.ts +2 -0
- package/dist/recruitment/web/messages.js +19 -0
- package/dist/reporting/documents.d.ts +5 -0
- package/dist/reporting/documents.js +28 -1
- package/dist/reporting/index.d.ts +1 -1
- package/dist/reporting/index.js +4 -2
- package/dist/reporting/models.js +9 -0
- package/dist/reporting/overviews.d.ts +16 -0
- package/dist/reporting/overviews.js +56 -0
- package/dist/reporting/routes-documents.js +1 -1
- package/dist/reporting/routes.js +138 -80
- package/dist/reporting/web/DashboardView.d.ts +11 -0
- package/dist/reporting/web/DashboardView.js +2 -2
- package/dist/reporting/web/DesignerView.js +5 -0
- package/dist/reporting/web/Overview.d.ts +9 -0
- package/dist/reporting/web/Overview.js +53 -0
- package/dist/reporting/web/index.d.ts +1 -0
- package/dist/reporting/web/index.js +4 -1
- package/dist/reporting/web/messages.js +9 -0
- package/dist/sale_account/.kosh/bindings.js +1 -1
- package/dist/sale_account/.kosh/schema.d.ts +181 -6
- package/dist/sale_account/index.js +22 -1
- package/dist/sale_account/module.d.ts +639 -38
- package/dist/sale_portal/.kosh/bindings.js +1 -1
- package/dist/sale_portal/.kosh/schema.d.ts +59 -3
- package/dist/sale_portal/module.d.ts +435 -5
- package/dist/sale_project/.kosh/bindings.js +1 -1
- package/dist/sale_project/.kosh/schema.d.ts +732 -3
- package/dist/sale_project/module.d.ts +5349 -208
- package/dist/sale_project/module.js +17 -1
- package/dist/sale_stock/.kosh/bindings.d.ts +22 -0
- package/dist/sale_stock/.kosh/bindings.js +15 -0
- package/dist/sale_stock/.kosh/schema.d.ts +1303 -0
- package/dist/sale_stock/index.d.ts +7 -0
- package/dist/sale_stock/index.js +84 -0
- package/dist/sale_stock/module.d.ts +7514 -0
- package/dist/sale_stock/module.js +146 -0
- package/dist/sales/.kosh/bindings.js +1 -1
- package/dist/sales/.kosh/schema.d.ts +59 -3
- package/dist/sales/index.js +195 -4
- package/dist/sales/module.d.ts +356 -3
- package/dist/sales/module.js +78 -3
- package/dist/server/api/metadata.d.ts +5 -0
- package/dist/server/api/metadata.js +5 -0
- package/dist/server/api/rest.js +64 -1
- package/dist/server/api/views.js +1 -0
- package/dist/server/app-settings.d.ts +7 -0
- package/dist/server/app-settings.js +10 -0
- package/dist/server/auth/base.js +9 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +1 -0
- package/dist/settings/app-settings.d.ts +2 -0
- package/dist/settings/app-settings.js +518 -0
- package/dist/settings/index.d.ts +1 -0
- package/dist/settings/index.js +15 -0
- package/dist/settings/people.js +29 -5
- package/dist/settings/web/AppSettingsPage.d.ts +11 -0
- package/dist/settings/web/AppSettingsPage.js +210 -0
- package/dist/settings/web/OverviewPage.js +16 -3
- package/dist/settings/web/PeoplePage.js +20 -6
- package/dist/settings/web/index.d.ts +1 -0
- package/dist/settings/web/index.js +3 -0
- package/dist/settings/web/messages.js +118 -2
- package/dist/sign/.kosh/bindings.d.ts +22 -0
- package/dist/sign/.kosh/bindings.js +15 -0
- package/dist/sign/.kosh/schema.d.ts +990 -0
- package/dist/sign/index.d.ts +14 -0
- package/dist/sign/index.js +350 -0
- package/dist/sign/module.d.ts +4478 -0
- package/dist/sign/module.js +158 -0
- package/dist/sign/sign.d.ts +82 -0
- package/dist/sign/sign.js +474 -0
- package/dist/stock/.kosh/bindings.d.ts +22 -0
- package/dist/stock/.kosh/bindings.js +15 -0
- package/dist/stock/.kosh/schema.d.ts +1222 -0
- package/dist/stock/demo.d.ts +8 -0
- package/dist/stock/demo.js +82 -0
- package/dist/stock/index.d.ts +4 -0
- package/dist/stock/index.js +903 -0
- package/dist/stock/module.d.ts +7856 -0
- package/dist/stock/module.js +1880 -0
- package/dist/stock/replenish.d.ts +38 -0
- package/dist/stock/replenish.js +176 -0
- package/dist/stock/web/Replenishment.d.ts +8 -0
- package/dist/stock/web/Replenishment.js +84 -0
- package/dist/stock/web/ScanBox.d.ts +8 -0
- package/dist/stock/web/ScanBox.js +90 -0
- package/dist/stock/web/index.d.ts +13 -0
- package/dist/stock/web/index.js +20 -0
- package/dist/stock/web/messages.d.ts +2 -0
- package/dist/stock/web/messages.js +77 -0
- package/dist/stock_account/.kosh/bindings.d.ts +22 -0
- package/dist/stock_account/.kosh/bindings.js +15 -0
- package/dist/stock_account/.kosh/schema.d.ts +2068 -0
- package/dist/stock_account/index.d.ts +3 -0
- package/dist/stock_account/index.js +140 -0
- package/dist/stock_account/module.d.ts +11523 -0
- package/dist/stock_account/module.js +339 -0
- package/dist/storage/attachments.d.ts +5 -0
- package/dist/storage/attachments.js +12 -1
- package/dist/storage/models.js +3 -0
- package/dist/storage/routes.js +13 -2
- package/dist/todo/.kosh/bindings.d.ts +22 -0
- package/dist/todo/.kosh/bindings.js +15 -0
- package/dist/todo/.kosh/schema.d.ts +732 -0
- package/dist/todo/demo.d.ts +6 -0
- package/dist/todo/demo.js +35 -0
- package/dist/todo/index.d.ts +3 -0
- package/dist/todo/index.js +187 -0
- package/dist/todo/module.d.ts +1180 -0
- package/dist/todo/module.js +130 -0
- package/dist/trade_finance/.kosh/bindings.d.ts +22 -0
- package/dist/trade_finance/.kosh/bindings.js +15 -0
- package/dist/trade_finance/.kosh/schema.d.ts +884 -0
- package/dist/trade_finance/demo.d.ts +6 -0
- package/dist/trade_finance/demo.js +63 -0
- package/dist/trade_finance/index.d.ts +3 -0
- package/dist/trade_finance/index.js +415 -0
- package/dist/trade_finance/module.d.ts +3779 -0
- package/dist/trade_finance/module.js +507 -0
- package/dist/trade_finance_account/.kosh/bindings.d.ts +22 -0
- package/dist/trade_finance_account/.kosh/bindings.js +15 -0
- package/dist/trade_finance_account/.kosh/schema.d.ts +1830 -0
- package/dist/trade_finance_account/index.d.ts +3 -0
- package/dist/trade_finance_account/index.js +93 -0
- package/dist/trade_finance_account/module.d.ts +8074 -0
- package/dist/trade_finance_account/module.js +245 -0
- package/dist/trade_finance_purchase/.kosh/bindings.d.ts +22 -0
- package/dist/trade_finance_purchase/.kosh/bindings.js +15 -0
- package/dist/trade_finance_purchase/.kosh/schema.d.ts +1246 -0
- package/dist/trade_finance_purchase/index.d.ts +8 -0
- package/dist/trade_finance_purchase/index.js +50 -0
- package/dist/trade_finance_purchase/module.d.ts +4257 -0
- package/dist/trade_finance_purchase/module.js +44 -0
- package/dist/trade_finance_sale/.kosh/bindings.d.ts +22 -0
- package/dist/trade_finance_sale/.kosh/bindings.js +15 -0
- package/dist/trade_finance_sale/.kosh/schema.d.ts +1072 -0
- package/dist/trade_finance_sale/index.d.ts +8 -0
- package/dist/trade_finance_sale/index.js +50 -0
- package/dist/trade_finance_sale/module.d.ts +4281 -0
- package/dist/trade_finance_sale/module.js +44 -0
- package/dist/ui/api.d.ts +2 -1
- package/dist/ui/api.js +2 -1
- package/dist/ui/app.js +26 -13
- package/dist/ui/components/AppIcon.js +30 -1
- package/dist/ui/components/primitives.d.ts +2 -0
- package/dist/ui/components/primitives.js +7 -1
- package/dist/ui/i18n/messages.d.ts +27 -0
- package/dist/ui/i18n/messages.js +46 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +1 -0
- package/dist/ui/offline/queue.d.ts +5 -1
- package/dist/ui/offline/queue.js +2 -0
- package/dist/ui/registry.d.ts +5 -0
- package/dist/ui/shell/Home.js +55 -4
- package/dist/ui/views/FormView.js +102 -19
- package/dist/ui/views/KanbanView.d.ts +2 -0
- package/dist/ui/views/KanbanView.js +40 -3
- package/dist/ui/views/ListView.js +110 -11
- package/dist/ui/views/StatsRow.d.ts +14 -0
- package/dist/ui/views/StatsRow.js +88 -0
- package/dist/ui/views/widgets.d.ts +2 -1
- package/dist/ui/views/widgets.js +200 -1
- package/dist/views/builders.d.ts +19 -2
- package/dist/views/builders.js +21 -1
- package/dist/views/condition.d.ts +6 -0
- package/dist/views/condition.js +20 -0
- package/dist/views/documents.d.ts +8 -0
- package/dist/views/extend.d.ts +2 -2
- package/dist/views/extend.js +11 -1
- package/dist/views/index.d.ts +2 -2
- package/dist/views/index.js +2 -2
- package/dist/views/types.d.ts +52 -5
- package/dist/views/validate.js +39 -1
- package/dist/website/.kosh/bindings.d.ts +22 -0
- package/dist/website/.kosh/bindings.js +15 -0
- package/dist/website/.kosh/schema.d.ts +881 -0
- package/dist/website/demo.d.ts +6 -0
- package/dist/website/demo.js +115 -0
- package/dist/website/index.d.ts +9 -0
- package/dist/website/index.js +251 -0
- package/dist/website/module.d.ts +2987 -0
- package/dist/website/module.js +282 -0
- package/dist/website/render.d.ts +45 -0
- package/dist/website/render.js +102 -0
- package/dist/website/routes.d.ts +12 -0
- package/dist/website/routes.js +246 -0
- package/dist/website/web/PagePreview.d.ts +7 -0
- package/dist/website/web/PagePreview.js +33 -0
- package/dist/website/web/index.d.ts +11 -0
- package/dist/website/web/index.js +16 -0
- package/dist/website/web/messages.d.ts +2 -0
- package/dist/website/web/messages.js +15 -0
- package/dist/website_crm/.kosh/bindings.d.ts +22 -0
- package/dist/website_crm/.kosh/bindings.js +15 -0
- package/dist/website_crm/.kosh/schema.d.ts +967 -0
- package/dist/website_crm/index.d.ts +3 -0
- package/dist/website_crm/index.js +39 -0
- package/dist/website_crm/module.d.ts +2956 -0
- package/dist/website_crm/module.js +44 -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 +2284 -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/index.d.ts +49 -0
- package/dist/website_sale/index.js +538 -0
- package/dist/website_sale/module.d.ts +22027 -0
- package/dist/website_sale/module.js +397 -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 +649 -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 +2681 -0
- package/dist/website_sale_stock/index.d.ts +9 -0
- package/dist/website_sale_stock/index.js +20 -0
- package/dist/website_sale_stock/module.d.ts +24998 -0
- package/dist/website_sale_stock/module.js +28 -0
- package/package.json +200 -2
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The data layer of Production (RFC 0021): bills of materials with versions and effective dates, work centres with
|
|
3
|
+
* their hours and cost, and production orders that say what is required, available and short before they start and
|
|
4
|
+
* what they will cost. Orders move stock only through Stockroom: Confirm reserves the components in a consumption
|
|
5
|
+
* transfer (stock → Production), Produce validates it and the output transfer (Production → stock).
|
|
6
|
+
*/
|
|
7
|
+
import { contactsShutro } from '../contacts/index.js';
|
|
8
|
+
import { messagingShutro } from '../messaging/index.js';
|
|
9
|
+
import { productShutro } from '../product/index.js';
|
|
10
|
+
import { orm } from '../server/orm.js';
|
|
11
|
+
import { defaultLocations, inProductUnit, stockShutro } from '../stock/index.js';
|
|
12
|
+
import { check, compute, defineModel, defineModule, extendModel, f, onchange, records, } from './.kosh/bindings.js';
|
|
13
|
+
const READ = ['read'];
|
|
14
|
+
const ALL = ['read', 'write', 'create', 'unlink'];
|
|
15
|
+
const zero = orm.toDecimal(0);
|
|
16
|
+
const money = (d) => d.toDecimalPlaces(2);
|
|
17
|
+
/** A date as `YYYY-MM-DD` (date-only values from PostgreSQL are local midnights: their local parts). */
|
|
18
|
+
const iso = (v) => {
|
|
19
|
+
if (!(v instanceof Date))
|
|
20
|
+
return String(v ?? '');
|
|
21
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
22
|
+
return `${v.getFullYear()}-${pad(v.getMonth() + 1)}-${pad(v.getDate())}`;
|
|
23
|
+
};
|
|
24
|
+
/** The bill of `product` valid on `date`: the latest that starts on or before it and has not ended. */
|
|
25
|
+
export async function validBom(env, product, date) {
|
|
26
|
+
const bills = await (await env.model('mrp.bom').search({ where: { product } })).load([
|
|
27
|
+
'validFrom',
|
|
28
|
+
'validTo',
|
|
29
|
+
]);
|
|
30
|
+
const valid = [...bills]
|
|
31
|
+
.filter((b) => (b.validFrom === null || iso(b.validFrom) <= date) &&
|
|
32
|
+
(b.validTo === null || iso(b.validTo) >= date))
|
|
33
|
+
.sort((a, b) => iso(b.validFrom).localeCompare(iso(a.validFrom)) || b.id - a.id);
|
|
34
|
+
return valid[0]?.id ?? null;
|
|
35
|
+
}
|
|
36
|
+
export const MrpWorkcenter = defineModel('mrp.workcenter', {
|
|
37
|
+
label: 'Work centre',
|
|
38
|
+
recName: 'name',
|
|
39
|
+
order: ['name asc'],
|
|
40
|
+
archivable: true,
|
|
41
|
+
companyScoped: { required: true },
|
|
42
|
+
access: { 'mrp.user': READ, 'mrp.manager': [...ALL, 'export', 'import'] },
|
|
43
|
+
fields: {
|
|
44
|
+
name: f.char({ required: true }),
|
|
45
|
+
code: f.char(),
|
|
46
|
+
/** Hours it works a day (its capacity). */
|
|
47
|
+
hoursPerDay: f.decimal({ scale: 2, required: true, default: '8', label: 'Hours a day' }),
|
|
48
|
+
/** Labour and overhead per hour of work. */
|
|
49
|
+
costPerHour: f.decimal({ scale: 2, required: true, default: '0', label: 'Cost per hour' }),
|
|
50
|
+
note: f.text(),
|
|
51
|
+
},
|
|
52
|
+
sqlConstraints: {
|
|
53
|
+
positive: {
|
|
54
|
+
check: orm.sql `hours_per_day > 0 AND cost_per_hour >= 0`,
|
|
55
|
+
message: 'A work centre works some hours a day, at a cost that is not negative.',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
export const MrpBom = defineModel('mrp.bom', {
|
|
60
|
+
label: 'Bill of materials',
|
|
61
|
+
recName: 'label',
|
|
62
|
+
order: ['product asc', 'validFrom desc', 'id desc'],
|
|
63
|
+
archivable: true,
|
|
64
|
+
companyScoped: { required: true },
|
|
65
|
+
tracking: ['quantity', 'validFrom', 'validTo'],
|
|
66
|
+
access: { 'mrp.user': READ, 'mrp.manager': [...ALL, 'export', 'import'] },
|
|
67
|
+
fields: {
|
|
68
|
+
product: f.many2one('product.product', { required: true, ondelete: 'restrict' }),
|
|
69
|
+
/** How many of the product one bill makes, in the product's unit. */
|
|
70
|
+
quantity: f.decimal({ scale: 4, required: true, default: '1', label: 'Makes' }),
|
|
71
|
+
version: f.char({ required: true, default: 'v1' }),
|
|
72
|
+
/** From when this version is used (empty: always). */
|
|
73
|
+
validFrom: f.date({ label: 'Valid from' }),
|
|
74
|
+
validTo: f.date({ label: 'Valid until' }),
|
|
75
|
+
lines: f.one2many('mrp.bom.line', 'bom', { label: 'Components' }),
|
|
76
|
+
operations: f.one2many('mrp.bom.operation', 'bom'),
|
|
77
|
+
byproducts: f.one2many('mrp.bom.byproduct', 'bom', { label: 'By-products' }),
|
|
78
|
+
note: f.text(),
|
|
79
|
+
label: f.char({
|
|
80
|
+
store: true,
|
|
81
|
+
label: 'Bill of materials',
|
|
82
|
+
...compute('mrp.bom', 'label', ['product.name', 'version'], (bills) => {
|
|
83
|
+
for (const b of bills)
|
|
84
|
+
b.label = `${b.product.name} ${b.version}`;
|
|
85
|
+
}),
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
sqlConstraints: {
|
|
89
|
+
positive: { check: orm.sql `quantity > 0`, message: 'A bill makes a positive quantity.' },
|
|
90
|
+
period: {
|
|
91
|
+
check: orm.sql `valid_from IS NULL OR valid_to IS NULL OR valid_to >= valid_from`,
|
|
92
|
+
message: 'A bill ends on or after it starts.',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
export const MrpBomLine = defineModel('mrp.bom.line', {
|
|
97
|
+
label: 'Component',
|
|
98
|
+
order: ['id asc'],
|
|
99
|
+
access: { 'mrp.user': READ, 'mrp.manager': ALL },
|
|
100
|
+
fields: {
|
|
101
|
+
bom: f.many2one('mrp.bom', { required: true, ondelete: 'cascade' }),
|
|
102
|
+
product: f.many2one('product.product', { required: true, ondelete: 'restrict' }),
|
|
103
|
+
quantity: f.decimal({ scale: 4, required: true, default: '1' }),
|
|
104
|
+
uom: f.many2one('uom.uom', { label: 'Unit' }),
|
|
105
|
+
},
|
|
106
|
+
onchange: {
|
|
107
|
+
product: onchange('mrp.bom.line', async (d) => {
|
|
108
|
+
const p = await d.record('product');
|
|
109
|
+
if (p?.uom)
|
|
110
|
+
d.set('uom', p.uom.id);
|
|
111
|
+
}),
|
|
112
|
+
},
|
|
113
|
+
sqlConstraints: {
|
|
114
|
+
positive: {
|
|
115
|
+
check: orm.sql `quantity > 0`,
|
|
116
|
+
message: 'A component is needed in a positive quantity.',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
constraints: {
|
|
120
|
+
notItself: {
|
|
121
|
+
...check('mrp.bom.line', ['product', 'bom'], async (lines) => (await lines.load({ bom: true }))
|
|
122
|
+
.filter((l) => l.bom.product.id === l.product.id)
|
|
123
|
+
.map((l) => l.id)),
|
|
124
|
+
message: 'A product is not a component of itself.',
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
/** What else one bill makes (sawdust, offcuts), and the share of the order's cost it carries. */
|
|
129
|
+
export const MrpBomByproduct = defineModel('mrp.bom.byproduct', {
|
|
130
|
+
label: 'By-product',
|
|
131
|
+
order: ['id asc'],
|
|
132
|
+
access: { 'mrp.user': READ, 'mrp.manager': ALL },
|
|
133
|
+
fields: {
|
|
134
|
+
bom: f.many2one('mrp.bom', { required: true, ondelete: 'cascade' }),
|
|
135
|
+
product: f.many2one('product.product', { required: true, ondelete: 'restrict' }),
|
|
136
|
+
quantity: f.decimal({ scale: 4, required: true, default: '1' }),
|
|
137
|
+
uom: f.many2one('uom.uom', { label: 'Unit' }),
|
|
138
|
+
/** Percent of the order's cost (the main product carries the rest). */
|
|
139
|
+
costShare: f.decimal({
|
|
140
|
+
scale: 2,
|
|
141
|
+
required: true,
|
|
142
|
+
default: '0',
|
|
143
|
+
label: 'Share of the cost (%)',
|
|
144
|
+
}),
|
|
145
|
+
},
|
|
146
|
+
sqlConstraints: {
|
|
147
|
+
positive: {
|
|
148
|
+
check: orm.sql `quantity > 0 AND cost_share >= 0 AND cost_share < 100`,
|
|
149
|
+
message: 'A by-product is made in a positive quantity, with a share of the cost under 100 %.',
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
constraints: {
|
|
153
|
+
sharesUnderAll: {
|
|
154
|
+
...check('mrp.bom.byproduct', ['costShare', 'bom', 'product'], async (lines) => {
|
|
155
|
+
const failing = [];
|
|
156
|
+
for (const l of await lines.load({ bom: { byproducts: true } })) {
|
|
157
|
+
const total = [...l.bom.byproducts].reduce((s, b) => s.plus(b.costShare), zero);
|
|
158
|
+
if (total.gte(100) || l.product.id === l.bom.product.id)
|
|
159
|
+
failing.push(l.id);
|
|
160
|
+
}
|
|
161
|
+
return failing;
|
|
162
|
+
}),
|
|
163
|
+
message: 'By-products are other products than the one made, and their shares of the cost stay under 100 %.',
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
export const MrpBomOperation = defineModel('mrp.bom.operation', {
|
|
168
|
+
label: 'Operation',
|
|
169
|
+
order: ['id asc'],
|
|
170
|
+
access: { 'mrp.user': READ, 'mrp.manager': ALL },
|
|
171
|
+
fields: {
|
|
172
|
+
bom: f.many2one('mrp.bom', { required: true, ondelete: 'cascade' }),
|
|
173
|
+
name: f.char({ required: true }),
|
|
174
|
+
workcenter: f.many2one('mrp.workcenter', { required: true, ondelete: 'restrict' }),
|
|
175
|
+
/** Minutes for what one bill makes. */
|
|
176
|
+
minutes: f.decimal({ scale: 2, required: true, default: '0' }),
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
export const MrpProductionComponent = defineModel('mrp.production.component', {
|
|
180
|
+
label: 'Component need',
|
|
181
|
+
order: ['id asc'],
|
|
182
|
+
access: { 'mrp.user': READ, 'mrp.manager': READ },
|
|
183
|
+
fields: {
|
|
184
|
+
production: f.many2one('mrp.production', { required: true, ondelete: 'cascade' }),
|
|
185
|
+
product: f.many2one('product.product', { required: true, ondelete: 'restrict' }),
|
|
186
|
+
/** In the product's unit. */
|
|
187
|
+
required: f.decimal({ scale: 4, required: true, default: '0' }),
|
|
188
|
+
/** Available in the warehouse when the order was last checked. */
|
|
189
|
+
available: f.decimal({ scale: 4, required: true, default: '0' }),
|
|
190
|
+
short: f.decimal({
|
|
191
|
+
scale: 4,
|
|
192
|
+
store: true,
|
|
193
|
+
label: 'Short',
|
|
194
|
+
...compute('mrp.production.component', 'short', ['required', 'available'], (lines) => {
|
|
195
|
+
for (const l of lines) {
|
|
196
|
+
const missing = l.required.minus(l.available);
|
|
197
|
+
l.short = missing.gt(0) ? missing : zero;
|
|
198
|
+
}
|
|
199
|
+
}),
|
|
200
|
+
}),
|
|
201
|
+
cost: f.decimal({
|
|
202
|
+
scale: 2,
|
|
203
|
+
store: true,
|
|
204
|
+
label: 'Cost',
|
|
205
|
+
...compute('mrp.production.component', 'cost', ['required', 'product.standardPrice'], (lines) => {
|
|
206
|
+
for (const l of lines)
|
|
207
|
+
l.cost = money(l.required.times(l.product.standardPrice));
|
|
208
|
+
}),
|
|
209
|
+
}),
|
|
210
|
+
},
|
|
211
|
+
});
|
|
212
|
+
/**
|
|
213
|
+
* Lists the orders' component needs from their bills (in the product's unit, for the quantity ordered) with what is
|
|
214
|
+
* available in their warehouse now; as the system (component lines are not edited by hand).
|
|
215
|
+
*/
|
|
216
|
+
async function explode(ids, env) {
|
|
217
|
+
await env.asSuperuser({ reason: 'production components' }, async (su) => {
|
|
218
|
+
const orders = await su
|
|
219
|
+
.model('mrp.production')
|
|
220
|
+
.browse(ids)
|
|
221
|
+
.load({ bom: { lines: { product: { uom: true }, uom: true } }, components: true });
|
|
222
|
+
for (const o of orders) {
|
|
223
|
+
if (o.components.length > 0)
|
|
224
|
+
await o.components.unlink();
|
|
225
|
+
if (o.bom === null)
|
|
226
|
+
continue;
|
|
227
|
+
const factor = o.quantity.div(o.bom.quantity);
|
|
228
|
+
const needs = new Map();
|
|
229
|
+
for (const l of o.bom.lines) {
|
|
230
|
+
const inUnit = inProductUnit(l.quantity.times(factor), l.uom ?? null, l.product.uom ?? null);
|
|
231
|
+
needs.set(l.product.id, (needs.get(l.product.id) ?? zero).plus(inUnit));
|
|
232
|
+
}
|
|
233
|
+
const places = await defaultLocations(su, 'internal', o.warehouse?.id ?? null, o.company.id);
|
|
234
|
+
for (const [product, required] of needs) {
|
|
235
|
+
let available = zero;
|
|
236
|
+
if (places.source !== null) {
|
|
237
|
+
const quants = await (await su.model('stock.quant').search({
|
|
238
|
+
where: {
|
|
239
|
+
product: orm.asId(product, 'product.product'),
|
|
240
|
+
location: { childOf: places.source },
|
|
241
|
+
},
|
|
242
|
+
})).load(['quantity', 'reserved']);
|
|
243
|
+
available = [...quants].reduce((s, q) => s.plus(q.quantity).minus(q.reserved ?? zero), zero);
|
|
244
|
+
}
|
|
245
|
+
await su.model('mrp.production.component').create([
|
|
246
|
+
{
|
|
247
|
+
production: o.id,
|
|
248
|
+
product: orm.asId(product, 'product.product'),
|
|
249
|
+
required: required.toDecimalPlaces(4),
|
|
250
|
+
available: available.gt(0) ? available : zero,
|
|
251
|
+
},
|
|
252
|
+
]);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
/** When a work centre's day starts (local time). */
|
|
258
|
+
export const DAY_STARTS_AT = 8;
|
|
259
|
+
/**
|
|
260
|
+
* Forward schedule of `minutes` of work at a centre working `hoursPerDay` from `DAY_STARTS_AT`, from `from`: what does
|
|
261
|
+
* not fit in a day continues the next day. Returns the start and the end.
|
|
262
|
+
*/
|
|
263
|
+
export function schedule(from, minutes, hoursPerDay) {
|
|
264
|
+
const dayStart = (d) => new Date(d.getFullYear(), d.getMonth(), d.getDate(), DAY_STARTS_AT);
|
|
265
|
+
const dayEnd = (d) => new Date(dayStart(d).getTime() + hoursPerDay * 3_600_000);
|
|
266
|
+
let at = new Date(Math.max(from.getTime(), dayStart(from).getTime()));
|
|
267
|
+
if (at >= dayEnd(at))
|
|
268
|
+
at = dayStart(new Date(at.getTime() + 86_400_000));
|
|
269
|
+
const start = at;
|
|
270
|
+
let left = minutes * 60_000;
|
|
271
|
+
for (;;) {
|
|
272
|
+
const room = dayEnd(at).getTime() - at.getTime();
|
|
273
|
+
if (left <= room)
|
|
274
|
+
return { start, stop: new Date(at.getTime() + left) };
|
|
275
|
+
left -= room;
|
|
276
|
+
at = dayStart(new Date(dayStart(at).getTime() + 86_400_000));
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* A step of a production order at a work centre: planned from the bill's operation (its minutes for the quantity
|
|
281
|
+
* ordered), scheduled after what the centre already has, then started and finished with the minutes it took.
|
|
282
|
+
*/
|
|
283
|
+
export const MrpWorkorder = defineModel('mrp.workorder', {
|
|
284
|
+
label: 'Work order',
|
|
285
|
+
tracking: ['state', 'plannedStart', 'workcenter'],
|
|
286
|
+
recName: 'name',
|
|
287
|
+
order: ['plannedStart asc', 'id asc'],
|
|
288
|
+
companyScoped: { required: true },
|
|
289
|
+
access: {
|
|
290
|
+
'mrp.user': ['read', 'write'],
|
|
291
|
+
'mrp.manager': [...ALL, 'export'],
|
|
292
|
+
},
|
|
293
|
+
fields: {
|
|
294
|
+
production: f.many2one('mrp.production', { required: true, ondelete: 'cascade' }),
|
|
295
|
+
name: f.char({ required: true, label: 'Operation' }),
|
|
296
|
+
workcenter: f.many2one('mrp.workcenter', { required: true, ondelete: 'restrict' }),
|
|
297
|
+
plannedMinutes: f.decimal({ scale: 2, required: true, default: '0', label: 'Planned minutes' }),
|
|
298
|
+
plannedStart: f.datetime({ label: 'Planned start' }),
|
|
299
|
+
plannedStop: f.datetime({ label: 'Planned end' }),
|
|
300
|
+
startedAt: f.datetime({ label: 'Started' }),
|
|
301
|
+
finishedAt: f.datetime({ label: 'Finished' }),
|
|
302
|
+
/** The minutes it took (from start to finish). */
|
|
303
|
+
actualMinutes: f.decimal({ scale: 2, label: 'Minutes taken' }),
|
|
304
|
+
state: f.state({
|
|
305
|
+
states: { ready: 'Ready', progress: 'In progress', done: 'Done', cancel: 'Cancelled' },
|
|
306
|
+
initial: 'ready',
|
|
307
|
+
transitions: {
|
|
308
|
+
start: {
|
|
309
|
+
from: ['ready'],
|
|
310
|
+
to: 'progress',
|
|
311
|
+
after: records('mrp.workorder', async (orders) => {
|
|
312
|
+
await orders.write({ startedAt: new Date() });
|
|
313
|
+
}),
|
|
314
|
+
},
|
|
315
|
+
finish: {
|
|
316
|
+
from: ['progress'],
|
|
317
|
+
to: 'done',
|
|
318
|
+
after: records('mrp.workorder', async (orders) => {
|
|
319
|
+
const now = new Date();
|
|
320
|
+
for (const w of await orders.load({})) {
|
|
321
|
+
const began = w.startedAt instanceof Date ? w.startedAt : now;
|
|
322
|
+
await w.write({
|
|
323
|
+
finishedAt: now,
|
|
324
|
+
// Whole milliseconds, then minutes (Shutro takes no fractional JS numbers).
|
|
325
|
+
actualMinutes: orm
|
|
326
|
+
.toDecimal(now.getTime() - began.getTime())
|
|
327
|
+
.div(60_000)
|
|
328
|
+
.toDecimalPlaces(2),
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}),
|
|
332
|
+
},
|
|
333
|
+
/** Done with its order (Produce), however far it got. */
|
|
334
|
+
close: { from: ['ready', 'progress'], to: 'done' },
|
|
335
|
+
cancel: { from: ['ready', 'progress'], to: 'cancel' },
|
|
336
|
+
},
|
|
337
|
+
}),
|
|
338
|
+
},
|
|
339
|
+
});
|
|
340
|
+
/**
|
|
341
|
+
* Makes the orders' work orders from their bills' operations and schedules them forward, step after step, each at
|
|
342
|
+
* its work centre after what the centre already has planned (from the order's date).
|
|
343
|
+
*/
|
|
344
|
+
async function planWork(ids, env) {
|
|
345
|
+
await env.asSuperuser({ reason: 'production schedule' }, async (su) => {
|
|
346
|
+
const orders = await su
|
|
347
|
+
.model('mrp.production')
|
|
348
|
+
.browse(ids)
|
|
349
|
+
.load({ bom: { operations: { workcenter: true } } });
|
|
350
|
+
for (const o of orders) {
|
|
351
|
+
if (o.bom === null)
|
|
352
|
+
continue;
|
|
353
|
+
const factor = o.quantity.div(o.bom.quantity);
|
|
354
|
+
// The order's day, local (a date is read as text or as a local midnight).
|
|
355
|
+
const day = new Date(`${iso(o.date)}T00:00:00`);
|
|
356
|
+
let after = new Date(day.getFullYear(), day.getMonth(), day.getDate(), DAY_STARTS_AT);
|
|
357
|
+
for (const op of o.bom.operations) {
|
|
358
|
+
const minutes = op.minutes.times(factor).toDecimalPlaces(2);
|
|
359
|
+
// The centre is free after the last step it has that is not done or cancelled.
|
|
360
|
+
const busy = await (await su.model('mrp.workorder').search({
|
|
361
|
+
where: { workcenter: op.workcenter.id, state: { in: ['ready', 'progress'] } },
|
|
362
|
+
order: ['plannedStop desc'],
|
|
363
|
+
limit: 1,
|
|
364
|
+
})).load(['plannedStop']);
|
|
365
|
+
const free = [...busy][0]?.plannedStop;
|
|
366
|
+
const from = free instanceof Date && free > after ? free : after;
|
|
367
|
+
const { start, stop } = schedule(from, Number(minutes), Number(op.workcenter.hoursPerDay));
|
|
368
|
+
await su.model('mrp.workorder').create([
|
|
369
|
+
{
|
|
370
|
+
production: o.id,
|
|
371
|
+
company: o.company.id,
|
|
372
|
+
name: op.name,
|
|
373
|
+
workcenter: op.workcenter.id,
|
|
374
|
+
plannedMinutes: minutes,
|
|
375
|
+
plannedStart: start,
|
|
376
|
+
plannedStop: stop,
|
|
377
|
+
},
|
|
378
|
+
]);
|
|
379
|
+
after = stop;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
/** The places of an order: its warehouse's stock, and the Production location. */
|
|
385
|
+
async function placesOf(env, warehouse, company) {
|
|
386
|
+
const stock = (await defaultLocations(env, 'internal', warehouse, company)).source;
|
|
387
|
+
const production = (await env.model('stock.location').search({ where: { usage: 'production' }, limit: 1 })).ids[0];
|
|
388
|
+
if (stock === null || production === undefined)
|
|
389
|
+
throw new orm.ValidationError('mrp.production', ['warehouse'], 'There is no stock location or Production location to work from.', 'Check the warehouse in Stockroom › Configuration.');
|
|
390
|
+
return { stock, production };
|
|
391
|
+
}
|
|
392
|
+
export const MrpProduction = defineModel('mrp.production', {
|
|
393
|
+
label: 'Production order',
|
|
394
|
+
recName: 'name',
|
|
395
|
+
order: ['date desc', 'id desc'],
|
|
396
|
+
companyScoped: { required: true },
|
|
397
|
+
tracking: ['state', 'quantity', 'bom'],
|
|
398
|
+
access: {
|
|
399
|
+
'mrp.user': ['read', 'write', 'create'],
|
|
400
|
+
'mrp.manager': [...ALL, 'export', 'import'],
|
|
401
|
+
},
|
|
402
|
+
fields: {
|
|
403
|
+
name: f.char({
|
|
404
|
+
required: true,
|
|
405
|
+
readonly: true,
|
|
406
|
+
sequence: { code: 'mrp.production', dateField: 'date' },
|
|
407
|
+
label: 'Reference',
|
|
408
|
+
}),
|
|
409
|
+
product: f.many2one('product.product', { required: true, ondelete: 'restrict' }),
|
|
410
|
+
quantity: f.decimal({ scale: 4, required: true, default: '1' }),
|
|
411
|
+
bom: f.many2one('mrp.bom', { ondelete: 'restrict', label: 'Bill of materials' }),
|
|
412
|
+
date: f.date({ required: true, default: (env) => env.today(), label: 'Planned for' }),
|
|
413
|
+
warehouse: f.many2one('stock.warehouse', { ondelete: 'restrict' }),
|
|
414
|
+
components: f.one2many('mrp.production.component', 'production'),
|
|
415
|
+
/** Scrap recorded on the order, with its reasons (Stockroom's scrap). */
|
|
416
|
+
scraps: f.one2many('stock.scrap', 'production'),
|
|
417
|
+
workorders: f.one2many('mrp.workorder', 'production', { label: 'Work orders' }),
|
|
418
|
+
consumption: f.many2one('stock.picking', {
|
|
419
|
+
ondelete: 'set null',
|
|
420
|
+
label: 'Components transfer',
|
|
421
|
+
}),
|
|
422
|
+
output: f.many2one('stock.picking', { ondelete: 'set null', label: 'Output transfer' }),
|
|
423
|
+
/** The components at their cost. */
|
|
424
|
+
materialCost: f.decimal({
|
|
425
|
+
scale: 2,
|
|
426
|
+
store: true,
|
|
427
|
+
label: 'Materials',
|
|
428
|
+
...compute('mrp.production', 'materialCost', ['components.cost'], (orders) => {
|
|
429
|
+
for (const o of orders)
|
|
430
|
+
o.materialCost = [...o.components].reduce((s, c) => s.plus(c.cost ?? zero), zero);
|
|
431
|
+
}),
|
|
432
|
+
}),
|
|
433
|
+
/** The operations at their work centres' cost per hour. */
|
|
434
|
+
operationCost: f.decimal({
|
|
435
|
+
scale: 2,
|
|
436
|
+
store: true,
|
|
437
|
+
label: 'Operations',
|
|
438
|
+
...compute('mrp.production', 'operationCost', [
|
|
439
|
+
'quantity',
|
|
440
|
+
'bom.quantity',
|
|
441
|
+
'bom.operations.minutes',
|
|
442
|
+
'bom.operations.workcenter.costPerHour',
|
|
443
|
+
], (orders) => {
|
|
444
|
+
for (const o of orders) {
|
|
445
|
+
if (o.bom === null) {
|
|
446
|
+
o.operationCost = zero;
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const factor = o.quantity.div(o.bom.quantity);
|
|
450
|
+
o.operationCost = money([...o.bom.operations].reduce((s, op) => s.plus(op.minutes.times(factor).div(60).times(op.workcenter.costPerHour)), zero));
|
|
451
|
+
}
|
|
452
|
+
}),
|
|
453
|
+
}),
|
|
454
|
+
totalCost: f.decimal({
|
|
455
|
+
scale: 2,
|
|
456
|
+
store: true,
|
|
457
|
+
label: 'Total cost',
|
|
458
|
+
...compute('mrp.production', 'totalCost', ['materialCost', 'operationCost'], (orders) => {
|
|
459
|
+
for (const o of orders)
|
|
460
|
+
o.totalCost = (o.materialCost ?? zero).plus(o.operationCost ?? zero);
|
|
461
|
+
}),
|
|
462
|
+
}),
|
|
463
|
+
unitCost: f.decimal({
|
|
464
|
+
scale: 4,
|
|
465
|
+
store: true,
|
|
466
|
+
label: 'Unit cost',
|
|
467
|
+
...compute('mrp.production', 'unitCost', ['totalCost', 'quantity'], (orders) => {
|
|
468
|
+
for (const o of orders)
|
|
469
|
+
o.unitCost = o.quantity.gt(0)
|
|
470
|
+
? (o.totalCost ?? zero).div(o.quantity).toDecimalPlaces(4)
|
|
471
|
+
: zero;
|
|
472
|
+
}),
|
|
473
|
+
}),
|
|
474
|
+
/** The one chip: Draft, Waiting (components short), Ready, Done, Cancelled. */
|
|
475
|
+
status: f.selection({ draft: 'Draft', waiting: 'Waiting', ready: 'Ready', done: 'Done', cancelled: 'Cancelled' }, {
|
|
476
|
+
store: true,
|
|
477
|
+
label: 'Status',
|
|
478
|
+
...compute('mrp.production', 'status', ['state', 'consumption.availability'], (orders) => {
|
|
479
|
+
for (const o of orders)
|
|
480
|
+
o.status =
|
|
481
|
+
o.state === 'confirmed'
|
|
482
|
+
? o.consumption?.availability === 'full'
|
|
483
|
+
? 'ready'
|
|
484
|
+
: 'waiting'
|
|
485
|
+
: o.state === 'cancel'
|
|
486
|
+
? 'cancelled'
|
|
487
|
+
: o.state;
|
|
488
|
+
}),
|
|
489
|
+
}),
|
|
490
|
+
note: f.text(),
|
|
491
|
+
state: f.state({
|
|
492
|
+
states: { draft: 'Draft', confirmed: 'Confirmed', done: 'Done', cancel: 'Cancelled' },
|
|
493
|
+
initial: 'draft',
|
|
494
|
+
transitions: {
|
|
495
|
+
/** Lists the components with what is available and short, and the cost, before anything starts. */
|
|
496
|
+
check: {
|
|
497
|
+
from: ['draft', 'confirmed'],
|
|
498
|
+
to: 'draft',
|
|
499
|
+
guard: records('mrp.production', async (orders) => (await orders.load({})).filter((o) => o.state !== 'draft')),
|
|
500
|
+
message: 'A confirmed order is checked with Check availability.',
|
|
501
|
+
after: records('mrp.production', async (orders) => {
|
|
502
|
+
await explode(orders.ids, orders.env);
|
|
503
|
+
}),
|
|
504
|
+
},
|
|
505
|
+
confirm: {
|
|
506
|
+
from: ['draft'],
|
|
507
|
+
to: 'confirmed',
|
|
508
|
+
guard: records('mrp.production', async (orders) => (await orders.load({})).filter((o) => o.bom === null)),
|
|
509
|
+
message: 'Choose the bill of materials first.',
|
|
510
|
+
// The components, their transfer to Production (reserved), and the work orders, scheduled.
|
|
511
|
+
after: records('mrp.production', async (orders) => {
|
|
512
|
+
await explode(orders.ids, orders.env);
|
|
513
|
+
await planWork(orders.ids, orders.env);
|
|
514
|
+
const env = orders.env;
|
|
515
|
+
for (const o of await env
|
|
516
|
+
.model('mrp.production')
|
|
517
|
+
.browse(orders.ids)
|
|
518
|
+
.load({ components: true })) {
|
|
519
|
+
// Nothing to consume (labour only): no components transfer.
|
|
520
|
+
if (o.components.length === 0)
|
|
521
|
+
continue;
|
|
522
|
+
const { stock, production } = await placesOf(env, o.warehouse?.id ?? null, o.company.id);
|
|
523
|
+
const made = await env.model('stock.picking').create([
|
|
524
|
+
{
|
|
525
|
+
kind: 'production',
|
|
526
|
+
company: o.company.id,
|
|
527
|
+
warehouse: o.warehouse?.id ?? null,
|
|
528
|
+
source: stock,
|
|
529
|
+
destination: production,
|
|
530
|
+
origin: o.name,
|
|
531
|
+
scheduledDate: o.date,
|
|
532
|
+
moves: {
|
|
533
|
+
create: [...o.components].map((c) => ({
|
|
534
|
+
product: c.product.id,
|
|
535
|
+
quantity: c.required,
|
|
536
|
+
})),
|
|
537
|
+
},
|
|
538
|
+
},
|
|
539
|
+
]);
|
|
540
|
+
await made.confirm();
|
|
541
|
+
await env
|
|
542
|
+
.model('mrp.production')
|
|
543
|
+
.browse([o.id])
|
|
544
|
+
.write({ consumption: made.ids[0] ?? null });
|
|
545
|
+
}
|
|
546
|
+
}),
|
|
547
|
+
},
|
|
548
|
+
/** "Check availability": reserve the components that came in since. */
|
|
549
|
+
reserve: {
|
|
550
|
+
from: ['confirmed'],
|
|
551
|
+
to: 'confirmed',
|
|
552
|
+
after: records('mrp.production', async (orders) => {
|
|
553
|
+
const env = orders.env;
|
|
554
|
+
for (const o of await env.model('mrp.production').browse(orders.ids).load({}))
|
|
555
|
+
if (o.consumption !== null)
|
|
556
|
+
await env.model('stock.picking').browse([o.consumption.id]).reserve();
|
|
557
|
+
}),
|
|
558
|
+
},
|
|
559
|
+
produce: {
|
|
560
|
+
from: ['confirmed'],
|
|
561
|
+
to: 'done',
|
|
562
|
+
guard: records('mrp.production', async (orders) => (await orders.load({ consumption: true })).filter((o) => o.consumption !== null && o.consumption.availability !== 'full')),
|
|
563
|
+
message: 'Components are short: check availability, or wait for them to come in.',
|
|
564
|
+
before: records('mrp.production', async (orders) => {
|
|
565
|
+
const env = orders.env;
|
|
566
|
+
// What is left of the work is done with the order.
|
|
567
|
+
const open = await env.model('mrp.workorder').search({
|
|
568
|
+
where: { production: { in: [...orders.ids] }, state: { in: ['ready', 'progress'] } },
|
|
569
|
+
});
|
|
570
|
+
if (open.length > 0)
|
|
571
|
+
await open.close();
|
|
572
|
+
for (const o of await env.model('mrp.production').browse(orders.ids).load({})) {
|
|
573
|
+
if (o.consumption !== null)
|
|
574
|
+
await env.model('stock.picking').browse([o.consumption.id]).validate();
|
|
575
|
+
const { stock, production } = await placesOf(env, o.warehouse?.id ?? null, o.company.id);
|
|
576
|
+
const [bill] = o.bom === null
|
|
577
|
+
? []
|
|
578
|
+
: await env.model('mrp.bom').browse([o.bom.id]).load({ byproducts: true });
|
|
579
|
+
const factor = bill === undefined ? zero : o.quantity.div(bill.quantity);
|
|
580
|
+
const made = await env.model('stock.picking').create([
|
|
581
|
+
{
|
|
582
|
+
kind: 'production',
|
|
583
|
+
company: o.company.id,
|
|
584
|
+
warehouse: o.warehouse?.id ?? null,
|
|
585
|
+
source: production,
|
|
586
|
+
destination: stock,
|
|
587
|
+
origin: o.name,
|
|
588
|
+
moves: {
|
|
589
|
+
create: [
|
|
590
|
+
{ product: o.product.id, quantity: o.quantity },
|
|
591
|
+
...[...(bill?.byproducts ?? [])].map((b) => ({
|
|
592
|
+
product: b.product.id,
|
|
593
|
+
quantity: b.quantity.times(factor).toDecimalPlaces(4),
|
|
594
|
+
uom: b.uom?.id ?? null,
|
|
595
|
+
})),
|
|
596
|
+
],
|
|
597
|
+
},
|
|
598
|
+
},
|
|
599
|
+
]);
|
|
600
|
+
// Linked before it is validated: what values it (mrp_account) finds its order.
|
|
601
|
+
await env
|
|
602
|
+
.model('mrp.production')
|
|
603
|
+
.browse([o.id])
|
|
604
|
+
.write({ output: made.ids[0] ?? null });
|
|
605
|
+
await made.confirm();
|
|
606
|
+
await made.validate();
|
|
607
|
+
}
|
|
608
|
+
}),
|
|
609
|
+
},
|
|
610
|
+
cancel: {
|
|
611
|
+
from: ['draft', 'confirmed'],
|
|
612
|
+
to: 'cancel',
|
|
613
|
+
after: records('mrp.production', async (orders) => {
|
|
614
|
+
const env = orders.env;
|
|
615
|
+
const open = await env.model('mrp.workorder').search({
|
|
616
|
+
where: { production: { in: [...orders.ids] }, state: { in: ['ready', 'progress'] } },
|
|
617
|
+
});
|
|
618
|
+
for (const w of await open.load({}))
|
|
619
|
+
await w.cancel();
|
|
620
|
+
for (const o of await env
|
|
621
|
+
.model('mrp.production')
|
|
622
|
+
.browse(orders.ids)
|
|
623
|
+
.load({ consumption: true }))
|
|
624
|
+
if (o.consumption !== null &&
|
|
625
|
+
o.consumption.state !== 'done' &&
|
|
626
|
+
o.consumption.state !== 'cancel')
|
|
627
|
+
await env.model('stock.picking').browse([o.consumption.id]).cancel();
|
|
628
|
+
}),
|
|
629
|
+
},
|
|
630
|
+
redraft: { from: ['cancel'], to: 'draft' },
|
|
631
|
+
},
|
|
632
|
+
}),
|
|
633
|
+
},
|
|
634
|
+
onchange: {
|
|
635
|
+
// The bill valid on the order's date, and the company's first warehouse.
|
|
636
|
+
product: onchange('mrp.production', async (d, env) => {
|
|
637
|
+
const product = d.get('product');
|
|
638
|
+
if (product === null || product === undefined)
|
|
639
|
+
return;
|
|
640
|
+
d.set('bom', await validBom(env, product, String(d.get('date') ?? env.today())));
|
|
641
|
+
if ((d.get('warehouse') ?? null) === null)
|
|
642
|
+
d.set('warehouse', (await defaultLocations(env, 'internal')).warehouse);
|
|
643
|
+
}),
|
|
644
|
+
date: onchange('mrp.production', async (d, env) => {
|
|
645
|
+
const product = d.get('product');
|
|
646
|
+
if (product === null || product === undefined)
|
|
647
|
+
return;
|
|
648
|
+
d.set('bom', await validBom(env, product, String(d.get('date') ?? env.today())));
|
|
649
|
+
}),
|
|
650
|
+
},
|
|
651
|
+
sqlConstraints: {
|
|
652
|
+
positive: { check: orm.sql `quantity > 0`, message: 'An order makes a positive quantity.' },
|
|
653
|
+
},
|
|
654
|
+
constraints: {
|
|
655
|
+
billOfTheProduct: {
|
|
656
|
+
...check('mrp.production', ['bom', 'product'], async (orders) => (await orders.load({ bom: true }))
|
|
657
|
+
.filter((o) => o.bom !== null && o.bom.product.id !== o.product.id)
|
|
658
|
+
.map((o) => o.id)),
|
|
659
|
+
message: 'The bill of materials is for another product.',
|
|
660
|
+
},
|
|
661
|
+
},
|
|
662
|
+
});
|
|
663
|
+
/** Scrap recorded on a production order (a broken component, a faulty unit made), with its reason. */
|
|
664
|
+
export const ScrapProduction = extendModel('stock.scrap', {
|
|
665
|
+
fields: {
|
|
666
|
+
production: f.many2one('mrp.production', { ondelete: 'set null', label: 'Production order' }),
|
|
667
|
+
},
|
|
668
|
+
});
|
|
669
|
+
/**
|
|
670
|
+
* Reorder rules that make (RFC 0024 §2.6): production orders not produced yet are on order; ordering makes one
|
|
671
|
+
* production order per rule with the bill valid on its date, confirmed (its components reserved, its work planned).
|
|
672
|
+
*/
|
|
673
|
+
export const ReorderMake = extendModel('stock.reorder', {
|
|
674
|
+
overrides: {
|
|
675
|
+
pending: async function (next) {
|
|
676
|
+
const out = await next();
|
|
677
|
+
const rules = this;
|
|
678
|
+
const env = rules.env;
|
|
679
|
+
for (const r of await rules.load({})) {
|
|
680
|
+
const main = (await defaultLocations(env, 'internal', null, r.company.id))
|
|
681
|
+
.warehouse;
|
|
682
|
+
const orders = await (await env.model('mrp.production').search({
|
|
683
|
+
where: {
|
|
684
|
+
product: r.product.id,
|
|
685
|
+
company: r.company.id,
|
|
686
|
+
state: { in: ['draft', 'confirmed'] },
|
|
687
|
+
},
|
|
688
|
+
})).load({});
|
|
689
|
+
const found = out.get(r.id) ?? [];
|
|
690
|
+
for (const o of orders)
|
|
691
|
+
if ((o.warehouse?.id ?? main) === r.warehouse.id)
|
|
692
|
+
found.push({ quantity: o.quantity.toString(), document: o.name });
|
|
693
|
+
out.set(r.id, found);
|
|
694
|
+
}
|
|
695
|
+
return out;
|
|
696
|
+
},
|
|
697
|
+
procure: async function (next, quantities) {
|
|
698
|
+
const out = await next(quantities);
|
|
699
|
+
const rules = this;
|
|
700
|
+
const env = rules.env;
|
|
701
|
+
for (const r of await rules.load({ product: true })) {
|
|
702
|
+
const qty = quantities.get(r.id);
|
|
703
|
+
if (r.method !== 'make' || qty === undefined)
|
|
704
|
+
continue;
|
|
705
|
+
const date = new Date(`${env.today()}T00:00:00Z`);
|
|
706
|
+
date.setUTCDate(date.getUTCDate() + r.leadDays);
|
|
707
|
+
const when = date.toISOString().slice(0, 10);
|
|
708
|
+
const bom = await validBom(env, r.product.id, when);
|
|
709
|
+
if (bom === null)
|
|
710
|
+
throw new orm.ValidationError('stock.reorder', ['method'], `${r.product.name} has no bill of materials valid on ${when}.`, 'Add one in Production › Bills of materials, or replenish it another way.');
|
|
711
|
+
const made = await env.model('mrp.production').create([
|
|
712
|
+
{
|
|
713
|
+
product: r.product.id,
|
|
714
|
+
quantity: qty,
|
|
715
|
+
bom,
|
|
716
|
+
date: when,
|
|
717
|
+
warehouse: r.warehouse.id,
|
|
718
|
+
company: r.company.id,
|
|
719
|
+
note: 'Replenishment',
|
|
720
|
+
},
|
|
721
|
+
]);
|
|
722
|
+
await made.confirm();
|
|
723
|
+
const [order] = await made.load({});
|
|
724
|
+
out.set(r.id, order?.name ?? '');
|
|
725
|
+
}
|
|
726
|
+
return out;
|
|
727
|
+
},
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
export const mrpShutro = defineModule({
|
|
731
|
+
name: 'mrp',
|
|
732
|
+
version: '0.2.0',
|
|
733
|
+
depends: [productShutro, stockShutro, contactsShutro, messagingShutro],
|
|
734
|
+
groups: [
|
|
735
|
+
orm.defineGroup({ code: 'mrp.user', label: 'Production user', implies: ['stock.user'] }),
|
|
736
|
+
orm.defineGroup({ code: 'mrp.manager', label: 'Production manager', implies: ['mrp.user'] }),
|
|
737
|
+
],
|
|
738
|
+
sequences: [
|
|
739
|
+
orm.defineSequence({
|
|
740
|
+
code: 'mrp.production',
|
|
741
|
+
prefix: 'MO/{year}/',
|
|
742
|
+
padding: 5,
|
|
743
|
+
reset: 'yearly',
|
|
744
|
+
}),
|
|
745
|
+
],
|
|
746
|
+
models: [
|
|
747
|
+
MrpWorkcenter,
|
|
748
|
+
MrpBom,
|
|
749
|
+
MrpBomLine,
|
|
750
|
+
MrpBomByproduct,
|
|
751
|
+
MrpBomOperation,
|
|
752
|
+
MrpProduction,
|
|
753
|
+
MrpProductionComponent,
|
|
754
|
+
MrpWorkorder,
|
|
755
|
+
],
|
|
756
|
+
extensions: [ScrapProduction, ReorderMake],
|
|
757
|
+
});
|