shutro-erp 0.3.6 → 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.
Files changed (623) hide show
  1. package/dist/account_asset/.kosh/bindings.js +1 -1
  2. package/dist/account_asset/.kosh/schema.d.ts +530 -8
  3. package/dist/account_asset/i18n/bn.d.ts +2 -0
  4. package/dist/account_asset/i18n/bn.js +97 -0
  5. package/dist/account_asset/index.js +2 -0
  6. package/dist/account_asset/module.d.ts +4899 -1445
  7. package/dist/account_budget/.kosh/bindings.js +1 -1
  8. package/dist/account_budget/.kosh/schema.d.ts +1130 -56
  9. package/dist/account_budget/i18n/bn.d.ts +2 -0
  10. package/dist/account_budget/i18n/bn.js +63 -0
  11. package/dist/account_budget/index.js +14 -0
  12. package/dist/account_budget/module.d.ts +17958 -4532
  13. package/dist/account_budget/module.js +15 -0
  14. package/dist/account_portal/.kosh/bindings.js +1 -1
  15. package/dist/account_portal/.kosh/schema.d.ts +530 -8
  16. package/dist/account_portal/i18n/bn.d.ts +2 -0
  17. package/dist/account_portal/i18n/bn.js +6 -0
  18. package/dist/account_portal/index.js +2 -0
  19. package/dist/account_portal/module.d.ts +5167 -1693
  20. package/dist/accounting/.kosh/bindings.js +1 -1
  21. package/dist/accounting/.kosh/schema.d.ts +530 -8
  22. package/dist/accounting/closing.js +1 -0
  23. package/dist/accounting/fiscal.d.ts +578 -0
  24. package/dist/accounting/fiscal.js +401 -0
  25. package/dist/accounting/followups.d.ts +618 -0
  26. package/dist/accounting/followups.js +420 -0
  27. package/dist/accounting/i18n/bn.d.ts +2 -0
  28. package/dist/accounting/i18n/bn.js +425 -0
  29. package/dist/accounting/index.d.ts +2 -0
  30. package/dist/accounting/index.js +649 -2
  31. package/dist/accounting/module.d.ts +4998 -1451
  32. package/dist/accounting/module.js +124 -18
  33. package/dist/accounting/statements.d.ts +878 -0
  34. package/dist/accounting/statements.js +728 -0
  35. package/dist/accounting/web/BankReconcilePage.d.ts +9 -0
  36. package/dist/accounting/web/BankReconcilePage.js +138 -0
  37. package/dist/accounting/web/FollowupsPage.d.ts +8 -0
  38. package/dist/accounting/web/FollowupsPage.js +54 -0
  39. package/dist/accounting/web/PostingPreview.d.ts +8 -0
  40. package/dist/accounting/web/PostingPreview.js +26 -0
  41. package/dist/accounting/web/index.d.ts +3 -0
  42. package/dist/accounting/web/index.js +9 -0
  43. package/dist/accounting/web/messages.js +140 -0
  44. package/dist/approvals/.kosh/bindings.js +1 -1
  45. package/dist/approvals/.kosh/schema.d.ts +272 -4
  46. package/dist/approvals/demo.d.ts +7 -0
  47. package/dist/approvals/demo.js +41 -0
  48. package/dist/approvals/i18n/bn.d.ts +2 -0
  49. package/dist/approvals/i18n/bn.js +133 -0
  50. package/dist/approvals/index.d.ts +19 -1
  51. package/dist/approvals/index.js +477 -7
  52. package/dist/approvals/mails.d.ts +7 -2
  53. package/dist/approvals/mails.js +8 -3
  54. package/dist/approvals/module.d.ts +2930 -1
  55. package/dist/approvals/module.js +468 -7
  56. package/dist/approvals/web/Config.d.ts +12 -0
  57. package/dist/approvals/web/Config.js +60 -0
  58. package/dist/approvals/web/Matrix.d.ts +9 -0
  59. package/dist/approvals/web/Matrix.js +26 -0
  60. package/dist/approvals/web/index.d.ts +3 -1
  61. package/dist/approvals/web/index.js +8 -1
  62. package/dist/approvals/web/messages.js +92 -0
  63. package/dist/approvals_purchase/.kosh/bindings.js +15 -0
  64. package/dist/approvals_purchase/.kosh/schema.d.ts +1997 -0
  65. package/dist/approvals_purchase/i18n/bn.d.ts +2 -0
  66. package/dist/approvals_purchase/i18n/bn.js +5 -0
  67. package/dist/approvals_purchase/index.d.ts +3 -0
  68. package/dist/approvals_purchase/index.js +47 -0
  69. package/dist/approvals_purchase/module.d.ts +12670 -0
  70. package/dist/approvals_purchase/module.js +84 -0
  71. package/dist/attendance/.kosh/bindings.js +1 -1
  72. package/dist/attendance/.kosh/schema.d.ts +59 -1
  73. package/dist/attendance/i18n/bn.d.ts +2 -0
  74. package/dist/attendance/i18n/bn.js +147 -0
  75. package/dist/attendance/index.js +3 -1
  76. package/dist/attendance/module.d.ts +452 -8
  77. package/dist/automation/engine.js +1 -1
  78. package/dist/calendar/.kosh/bindings.js +1 -1
  79. package/dist/calendar/.kosh/schema.d.ts +2 -1
  80. package/dist/calendar/i18n/bn.d.ts +2 -0
  81. package/dist/calendar/i18n/bn.js +80 -0
  82. package/dist/calendar/index.js +77 -0
  83. package/dist/calendar/module.d.ts +26 -0
  84. package/dist/calendar/module.js +14 -4
  85. package/dist/cli/apps.js +25 -5
  86. package/dist/cli/init.js +14 -4
  87. package/dist/cli/templates.d.ts +1 -1
  88. package/dist/cli/templates.js +34 -5
  89. package/dist/contacts/.kosh/bindings.js +1 -1
  90. package/dist/contacts/.kosh/schema.d.ts +2 -1
  91. package/dist/contacts/i18n/bn.d.ts +2 -0
  92. package/dist/contacts/i18n/bn.js +67 -0
  93. package/dist/contacts/index.js +3 -0
  94. package/dist/contacts/module.d.ts +42 -0
  95. package/dist/contacts/module.js +6 -1
  96. package/dist/crm/.kosh/bindings.js +1 -1
  97. package/dist/crm/.kosh/schema.d.ts +2 -1
  98. package/dist/crm/i18n/bn.d.ts +2 -0
  99. package/dist/crm/i18n/bn.js +80 -0
  100. package/dist/crm/index.js +22 -0
  101. package/dist/crm/module.d.ts +20 -0
  102. package/dist/crm/module.js +4 -2
  103. package/dist/documents/.kosh/bindings.js +1 -1
  104. package/dist/documents/.kosh/schema.d.ts +2 -1
  105. package/dist/documents/i18n/bn.d.ts +2 -0
  106. package/dist/documents/i18n/bn.js +83 -0
  107. package/dist/documents/index.js +128 -1
  108. package/dist/documents/module.d.ts +20 -0
  109. package/dist/documents/module.js +1 -0
  110. package/dist/event/.kosh/bindings.js +1 -1
  111. package/dist/event/.kosh/schema.d.ts +2 -1
  112. package/dist/event/i18n/bn.d.ts +2 -0
  113. package/dist/event/i18n/bn.js +101 -0
  114. package/dist/event/index.js +66 -0
  115. package/dist/event/module.d.ts +20 -0
  116. package/dist/event/module.js +8 -1
  117. package/dist/event_account/.kosh/bindings.js +1 -1
  118. package/dist/event_account/.kosh/schema.d.ts +530 -8
  119. package/dist/event_account/i18n/bn.d.ts +2 -0
  120. package/dist/event_account/i18n/bn.js +5 -0
  121. package/dist/event_account/index.js +2 -0
  122. package/dist/event_account/module.d.ts +4905 -1431
  123. package/dist/expense/.kosh/bindings.js +1 -1
  124. package/dist/expense/.kosh/schema.d.ts +323 -2
  125. package/dist/expense/i18n/bn.d.ts +2 -0
  126. package/dist/expense/i18n/bn.js +155 -0
  127. package/dist/expense/index.js +8 -1
  128. package/dist/expense/module.d.ts +2715 -694
  129. package/dist/expense/module.js +3 -1
  130. package/dist/expense_account/.kosh/bindings.js +1 -1
  131. package/dist/expense_account/.kosh/schema.d.ts +587 -8
  132. package/dist/expense_account/i18n/bn.d.ts +2 -0
  133. package/dist/expense_account/i18n/bn.js +15 -0
  134. package/dist/expense_account/index.js +2 -0
  135. package/dist/expense_account/module.d.ts +8094 -2619
  136. package/dist/helpdesk/.kosh/bindings.js +1 -1
  137. package/dist/helpdesk/.kosh/schema.d.ts +2 -1
  138. package/dist/helpdesk/i18n/bn.d.ts +2 -0
  139. package/dist/helpdesk/i18n/bn.js +127 -0
  140. package/dist/helpdesk/index.js +2 -0
  141. package/dist/helpdesk/module.d.ts +20 -0
  142. package/dist/hr/.kosh/bindings.js +1 -1
  143. package/dist/hr/.kosh/schema.d.ts +59 -1
  144. package/dist/hr/demo.js +11 -1
  145. package/dist/hr/i18n/bn.d.ts +2 -0
  146. package/dist/hr/i18n/bn.js +126 -0
  147. package/dist/hr/index.d.ts +1 -1
  148. package/dist/hr/index.js +158 -12
  149. package/dist/hr/module.d.ts +932 -59
  150. package/dist/hr/module.js +114 -17
  151. package/dist/{mrp → indent}/.kosh/bindings.js +2 -2
  152. package/dist/indent/.kosh/schema.d.ts +2084 -0
  153. package/dist/indent/demo.d.ts +7 -0
  154. package/dist/indent/demo.js +48 -0
  155. package/dist/indent/i18n/bn.d.ts +2 -0
  156. package/dist/indent/i18n/bn.js +143 -0
  157. package/dist/indent/index.d.ts +3 -0
  158. package/dist/indent/index.js +450 -0
  159. package/dist/{mrp → indent}/module.d.ts +7164 -3075
  160. package/dist/indent/module.js +792 -0
  161. package/dist/indent_purchase/.kosh/bindings.d.ts +22 -0
  162. package/dist/indent_purchase/.kosh/bindings.js +15 -0
  163. package/dist/indent_purchase/.kosh/schema.d.ts +2710 -0
  164. package/dist/indent_purchase/i18n/bn.d.ts +2 -0
  165. package/dist/indent_purchase/i18n/bn.js +13 -0
  166. package/dist/indent_purchase/index.d.ts +3 -0
  167. package/dist/indent_purchase/index.js +61 -0
  168. package/dist/indent_purchase/module.d.ts +27950 -0
  169. package/dist/indent_purchase/module.js +163 -0
  170. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  171. package/dist/l10n_bd/.kosh/schema.d.ts +530 -8
  172. package/dist/l10n_bd/i18n/bn.d.ts +2 -0
  173. package/dist/l10n_bd/i18n/bn.js +69 -0
  174. package/dist/l10n_bd/index.js +2 -0
  175. package/dist/l10n_bd/module.d.ts +5167 -1693
  176. package/dist/l10n_bd_payroll/.kosh/bindings.js +1 -1
  177. package/dist/l10n_bd_payroll/.kosh/schema.d.ts +587 -8
  178. package/dist/l10n_bd_payroll/i18n/bn.d.ts +2 -0
  179. package/dist/l10n_bd_payroll/i18n/bn.js +4 -0
  180. package/dist/l10n_bd_payroll/index.js +2 -0
  181. package/dist/l10n_bd_payroll/module.d.ts +5910 -1548
  182. package/dist/l10n_bd_pos/.kosh/bindings.d.ts +22 -0
  183. package/dist/{mrp_account → l10n_bd_pos}/.kosh/bindings.js +2 -2
  184. package/dist/{mrp_account → l10n_bd_pos}/.kosh/schema.d.ts +693 -651
  185. package/dist/l10n_bd_pos/i18n/bn.d.ts +2 -0
  186. package/dist/l10n_bd_pos/i18n/bn.js +32 -0
  187. package/dist/l10n_bd_pos/index.d.ts +3 -0
  188. package/dist/l10n_bd_pos/index.js +111 -0
  189. package/dist/{mrp_account → l10n_bd_pos}/module.d.ts +7402 -7647
  190. package/dist/l10n_bd_pos/module.js +43 -0
  191. package/dist/l10n_in/.kosh/bindings.js +1 -1
  192. package/dist/l10n_in/.kosh/schema.d.ts +530 -8
  193. package/dist/l10n_in/i18n/bn.d.ts +2 -0
  194. package/dist/l10n_in/i18n/bn.js +95 -0
  195. package/dist/l10n_in/index.js +2 -0
  196. package/dist/l10n_in/module.d.ts +6703 -1652
  197. package/dist/l10n_in/module.js +25 -5
  198. package/dist/l10n_in/web/GstSettings.js +2 -2
  199. package/dist/mail/inbound/handle.js +2 -4
  200. package/dist/mail/inbound/models.js +5 -3
  201. package/dist/mail/index.d.ts +12 -1
  202. package/dist/mail/index.js +139 -2
  203. package/dist/mail/send.d.ts +56 -0
  204. package/dist/mail/send.js +394 -0
  205. package/dist/mail/web/SendButton.d.ts +9 -0
  206. package/dist/mail/web/SendButton.js +71 -0
  207. package/dist/mail/web/SendSelected.d.ts +8 -0
  208. package/dist/mail/web/SendSelected.js +33 -0
  209. package/dist/mail/web/index.d.ts +2 -0
  210. package/dist/mail/web/index.js +6 -0
  211. package/dist/mail/web/messages.js +36 -0
  212. package/dist/maintenance/.kosh/bindings.js +1 -1
  213. package/dist/maintenance/.kosh/schema.d.ts +2 -1
  214. package/dist/maintenance/i18n/bn.d.ts +2 -0
  215. package/dist/maintenance/i18n/bn.js +132 -0
  216. package/dist/maintenance/index.js +85 -0
  217. package/dist/maintenance/module.d.ts +25 -0
  218. package/dist/maintenance/module.js +27 -9
  219. package/dist/maintenance_stock/.kosh/bindings.js +1 -1
  220. package/dist/maintenance_stock/.kosh/schema.d.ts +298 -2
  221. package/dist/maintenance_stock/i18n/bn.d.ts +2 -0
  222. package/dist/maintenance_stock/i18n/bn.js +21 -0
  223. package/dist/maintenance_stock/index.js +2 -0
  224. package/dist/maintenance_stock/module.d.ts +5302 -1256
  225. package/dist/maintenance_stock/module.js +1 -0
  226. package/dist/modules-sdk/apps.d.ts +10 -0
  227. package/dist/modules-sdk/apps.js +32 -0
  228. package/dist/modules-sdk/index.d.ts +1 -1
  229. package/dist/modules-sdk/manifest.d.ts +68 -2
  230. package/dist/payment/.kosh/bindings.js +1 -1
  231. package/dist/payment/.kosh/schema.d.ts +530 -8
  232. package/dist/payment/i18n/bn.d.ts +2 -0
  233. package/dist/payment/i18n/bn.js +59 -0
  234. package/dist/payment/index.js +85 -0
  235. package/dist/payment/module.d.ts +5134 -1660
  236. package/dist/payment/module.js +17 -3
  237. package/dist/payment/settle.js +1 -0
  238. package/dist/payroll/.kosh/bindings.js +1 -1
  239. package/dist/payroll/.kosh/schema.d.ts +59 -1
  240. package/dist/payroll/i18n/bn.d.ts +2 -0
  241. package/dist/payroll/i18n/bn.js +206 -0
  242. package/dist/payroll/index.js +2 -0
  243. package/dist/payroll/module.d.ts +936 -48
  244. package/dist/payroll_account/.kosh/bindings.js +1 -1
  245. package/dist/payroll_account/.kosh/schema.d.ts +587 -8
  246. package/dist/payroll_account/i18n/bn.d.ts +2 -0
  247. package/dist/payroll_account/i18n/bn.js +10 -0
  248. package/dist/payroll_account/index.js +2 -0
  249. package/dist/payroll_account/module.d.ts +7534 -3192
  250. package/dist/permissions/approvals.d.ts +97 -4
  251. package/dist/permissions/approvals.js +780 -31
  252. package/dist/permissions/index.d.ts +1 -1
  253. package/dist/permissions/index.js +2 -2
  254. package/dist/permissions/models.js +177 -5
  255. package/dist/permissions/routes.d.ts +3 -0
  256. package/dist/permissions/routes.js +36 -2
  257. package/dist/permissions/web/ApprovalPanel.js +43 -5
  258. package/dist/permissions/web/messages.js +52 -0
  259. package/dist/portal/.kosh/bindings.js +1 -1
  260. package/dist/portal/.kosh/schema.d.ts +2 -1
  261. package/dist/portal/documents.d.ts +3 -0
  262. package/dist/portal/documents.js +21 -1
  263. package/dist/portal/i18n/bn.d.ts +2 -0
  264. package/dist/portal/i18n/bn.js +33 -0
  265. package/dist/portal/index.js +2 -0
  266. package/dist/portal/module.d.ts +20 -0
  267. package/dist/portal/routes-documents.js +76 -0
  268. package/dist/pos/.kosh/bindings.js +1 -1
  269. package/dist/pos/.kosh/schema.d.ts +531 -8
  270. package/dist/pos/i18n/bn.d.ts +2 -0
  271. package/dist/pos/i18n/bn.js +133 -0
  272. package/dist/pos/index.js +83 -1
  273. package/dist/pos/module.d.ts +6888 -1777
  274. package/dist/pos/module.js +49 -5
  275. package/dist/pos/routes.js +80 -7
  276. package/dist/pos/web/Register.js +2 -5
  277. package/dist/pos_stock/.kosh/bindings.js +1 -1
  278. package/dist/pos_stock/.kosh/schema.d.ts +626 -9
  279. package/dist/pos_stock/i18n/bn.d.ts +2 -0
  280. package/dist/pos_stock/i18n/bn.js +6 -0
  281. package/dist/pos_stock/index.js +2 -0
  282. package/dist/pos_stock/module.d.ts +10798 -3276
  283. package/dist/product/.kosh/bindings.js +1 -1
  284. package/dist/product/.kosh/schema.d.ts +202 -1
  285. package/dist/product/i18n/bn.d.ts +2 -0
  286. package/dist/product/i18n/bn.js +175 -0
  287. package/dist/product/index.d.ts +1 -0
  288. package/dist/product/index.js +288 -1
  289. package/dist/product/module.d.ts +1725 -0
  290. package/dist/product/module.js +66 -6
  291. package/dist/product/pricelist.d.ts +575 -0
  292. package/dist/product/pricelist.js +257 -0
  293. package/dist/product/variants.d.ts +908 -0
  294. package/dist/product/variants.js +251 -0
  295. package/dist/production/.kosh/bindings.d.ts +22 -0
  296. package/dist/production/.kosh/bindings.js +15 -0
  297. package/dist/production/.kosh/schema.d.ts +2345 -0
  298. package/dist/{mrp → production}/demo.d.ts +1 -1
  299. package/dist/{mrp → production}/demo.js +5 -5
  300. package/dist/production/depth.d.ts +790 -0
  301. package/dist/production/depth.js +262 -0
  302. package/dist/production/i18n/bn.d.ts +2 -0
  303. package/dist/production/i18n/bn.js +350 -0
  304. package/dist/production/index.d.ts +6 -0
  305. package/dist/production/index.js +1256 -0
  306. package/dist/production/module.d.ts +17327 -0
  307. package/dist/production/module.js +1333 -0
  308. package/dist/production/plan.d.ts +921 -0
  309. package/dist/production/plan.js +511 -0
  310. package/dist/production/schedule.d.ts +656 -0
  311. package/dist/production/schedule.js +309 -0
  312. package/dist/production_account/.kosh/bindings.d.ts +22 -0
  313. package/dist/production_account/.kosh/bindings.js +15 -0
  314. package/dist/production_account/.kosh/schema.d.ts +3496 -0
  315. package/dist/production_account/i18n/bn.d.ts +2 -0
  316. package/dist/production_account/i18n/bn.js +93 -0
  317. package/dist/production_account/index.d.ts +4 -0
  318. package/dist/production_account/index.js +246 -0
  319. package/dist/production_account/module.d.ts +1192 -0
  320. package/dist/{mrp_account → production_account}/module.js +72 -18
  321. package/dist/production_account/overhead.d.ts +1195 -0
  322. package/dist/production_account/overhead.js +554 -0
  323. package/dist/project/.kosh/bindings.js +1 -1
  324. package/dist/project/.kosh/schema.d.ts +65 -2
  325. package/dist/project/i18n/bn.d.ts +2 -0
  326. package/dist/project/i18n/bn.js +383 -0
  327. package/dist/project/index.js +123 -0
  328. package/dist/project/module.d.ts +20 -0
  329. package/dist/project/module.js +73 -14
  330. package/dist/project_account/.kosh/bindings.js +1 -1
  331. package/dist/project_account/.kosh/schema.d.ts +530 -8
  332. package/dist/project_account/i18n/bn.d.ts +2 -0
  333. package/dist/project_account/i18n/bn.js +6 -0
  334. package/dist/project_account/index.js +2 -0
  335. package/dist/project_account/module.d.ts +4926 -1452
  336. package/dist/project_portal/.kosh/bindings.js +1 -1
  337. package/dist/project_portal/.kosh/schema.d.ts +65 -2
  338. package/dist/project_portal/i18n/bn.d.ts +2 -0
  339. package/dist/project_portal/i18n/bn.js +6 -0
  340. package/dist/project_portal/index.js +2 -0
  341. package/dist/project_portal/module.d.ts +40 -0
  342. package/dist/project_purchase/.kosh/bindings.js +1 -1
  343. package/dist/project_purchase/.kosh/schema.d.ts +1126 -53
  344. package/dist/project_purchase/i18n/bn.d.ts +2 -0
  345. package/dist/project_purchase/i18n/bn.js +4 -0
  346. package/dist/project_purchase/index.js +2 -0
  347. package/dist/project_purchase/module.d.ts +17912 -4446
  348. package/dist/purchase/.kosh/bindings.js +1 -1
  349. package/dist/purchase/.kosh/schema.d.ts +813 -3
  350. package/dist/purchase/comparison.d.ts +993 -0
  351. package/dist/purchase/comparison.js +415 -0
  352. package/dist/purchase/extras.d.ts +1509 -0
  353. package/dist/purchase/extras.js +445 -0
  354. package/dist/purchase/i18n/bn.d.ts +2 -0
  355. package/dist/purchase/i18n/bn.js +413 -0
  356. package/dist/purchase/index.d.ts +5 -0
  357. package/dist/purchase/index.js +1280 -4
  358. package/dist/purchase/module.d.ts +7139 -398
  359. package/dist/purchase/module.js +295 -11
  360. package/dist/purchase/request.d.ts +696 -0
  361. package/dist/purchase/request.js +446 -0
  362. package/dist/purchase/tender.d.ts +1208 -0
  363. package/dist/purchase/tender.js +507 -0
  364. package/dist/purchase/vendorprice.d.ts +340 -0
  365. package/dist/purchase/vendorprice.js +153 -0
  366. package/dist/purchase_account/.kosh/bindings.js +1 -1
  367. package/dist/purchase_account/.kosh/schema.d.ts +1130 -57
  368. package/dist/purchase_account/i18n/bn.d.ts +2 -0
  369. package/dist/purchase_account/i18n/bn.js +22 -0
  370. package/dist/purchase_account/index.js +19 -0
  371. package/dist/purchase_account/module.d.ts +12276 -2247
  372. package/dist/purchase_account/module.js +241 -9
  373. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  374. package/dist/purchase_portal/.kosh/schema.d.ts +813 -3
  375. package/dist/purchase_portal/i18n/bn.d.ts +2 -0
  376. package/dist/purchase_portal/i18n/bn.js +5 -0
  377. package/dist/purchase_portal/index.js +2 -0
  378. package/dist/purchase_portal/module.d.ts +6849 -353
  379. package/dist/purchase_stock/.kosh/bindings.js +1 -1
  380. package/dist/purchase_stock/.kosh/schema.d.ts +910 -4
  381. package/dist/purchase_stock/i18n/bn.d.ts +2 -0
  382. package/dist/purchase_stock/i18n/bn.js +19 -0
  383. package/dist/purchase_stock/index.d.ts +1 -1
  384. package/dist/purchase_stock/index.js +29 -0
  385. package/dist/purchase_stock/module.d.ts +12410 -1864
  386. package/dist/purchase_stock/module.js +81 -9
  387. package/dist/purchase_stock/scorecard.d.ts +22 -0
  388. package/dist/purchase_stock/scorecard.js +77 -0
  389. package/dist/purchase_stock/web/Scorecard.d.ts +7 -0
  390. package/dist/purchase_stock/web/Scorecard.js +22 -0
  391. package/dist/purchase_stock/web/index.d.ts +11 -0
  392. package/dist/purchase_stock/web/index.js +16 -0
  393. package/dist/purchase_stock/web/messages.d.ts +2 -0
  394. package/dist/purchase_stock/web/messages.js +27 -0
  395. package/dist/purchase_stock_account/.kosh/bindings.js +1 -1
  396. package/dist/purchase_stock_account/.kosh/schema.d.ts +1286 -58
  397. package/dist/purchase_stock_account/i18n/bn.d.ts +2 -0
  398. package/dist/purchase_stock_account/i18n/bn.js +8 -0
  399. package/dist/purchase_stock_account/index.js +18 -0
  400. package/dist/purchase_stock_account/module.d.ts +7 -37995
  401. package/dist/purchase_stock_account/module.js +24 -3
  402. package/dist/recruitment/.kosh/bindings.js +1 -1
  403. package/dist/recruitment/.kosh/schema.d.ts +59 -1
  404. package/dist/recruitment/i18n/bn.d.ts +2 -0
  405. package/dist/recruitment/i18n/bn.js +93 -0
  406. package/dist/recruitment/index.js +64 -0
  407. package/dist/recruitment/module.d.ts +458 -8
  408. package/dist/recruitment/module.js +11 -3
  409. package/dist/reporting/documents.d.ts +5 -0
  410. package/dist/reporting/documents.js +11 -1
  411. package/dist/reporting/index.d.ts +1 -1
  412. package/dist/reporting/index.js +1 -1
  413. package/dist/reporting/web/CalendarView.d.ts +18 -5
  414. package/dist/reporting/web/CalendarView.js +163 -29
  415. package/dist/reporting/web/messages.js +18 -0
  416. package/dist/sale_account/.kosh/bindings.js +1 -1
  417. package/dist/sale_account/.kosh/schema.d.ts +642 -16
  418. package/dist/sale_account/i18n/bn.d.ts +2 -0
  419. package/dist/sale_account/i18n/bn.js +48 -0
  420. package/dist/sale_account/index.d.ts +1 -1
  421. package/dist/sale_account/index.js +102 -4
  422. package/dist/sale_account/module.d.ts +8254 -2212
  423. package/dist/sale_account/module.js +535 -46
  424. package/dist/sale_crm/.kosh/bindings.d.ts +22 -0
  425. package/dist/sale_crm/.kosh/bindings.js +15 -0
  426. package/dist/{mrp → sale_crm}/.kosh/schema.d.ts +389 -567
  427. package/dist/sale_crm/i18n/bn.d.ts +2 -0
  428. package/dist/sale_crm/i18n/bn.js +9 -0
  429. package/dist/sale_crm/index.d.ts +7 -0
  430. package/dist/sale_crm/index.js +104 -0
  431. package/dist/sale_crm/module.d.ts +5609 -0
  432. package/dist/sale_crm/module.js +73 -0
  433. package/dist/sale_portal/.kosh/bindings.js +1 -1
  434. package/dist/sale_portal/.kosh/schema.d.ts +291 -4
  435. package/dist/sale_portal/i18n/bn.d.ts +2 -0
  436. package/dist/sale_portal/i18n/bn.js +7 -0
  437. package/dist/sale_portal/index.js +10 -0
  438. package/dist/sale_portal/module.d.ts +2564 -294
  439. package/dist/sale_project/.kosh/bindings.js +1 -1
  440. package/dist/sale_project/.kosh/schema.d.ts +354 -5
  441. package/dist/sale_project/i18n/bn.d.ts +2 -0
  442. package/dist/sale_project/i18n/bn.js +12 -0
  443. package/dist/sale_project/index.js +2 -0
  444. package/dist/sale_project/module.d.ts +6249 -2402
  445. package/dist/sale_stock/.kosh/bindings.js +1 -1
  446. package/dist/sale_stock/.kosh/schema.d.ts +387 -5
  447. package/dist/sale_stock/i18n/bn.d.ts +2 -0
  448. package/dist/sale_stock/i18n/bn.js +15 -0
  449. package/dist/sale_stock/index.js +2 -0
  450. package/dist/sale_stock/module.d.ts +8057 -1759
  451. package/dist/sale_stock/module.js +20 -3
  452. package/dist/sales/.kosh/bindings.js +1 -1
  453. package/dist/sales/.kosh/schema.d.ts +291 -4
  454. package/dist/sales/i18n/bn.d.ts +2 -0
  455. package/dist/sales/i18n/bn.js +128 -0
  456. package/dist/sales/index.js +120 -5
  457. package/dist/sales/module.d.ts +3567 -633
  458. package/dist/sales/module.js +306 -11
  459. package/dist/server/api/duplicate.d.ts +14 -0
  460. package/dist/server/api/duplicate.js +75 -0
  461. package/dist/server/api/explain.d.ts +70 -0
  462. package/dist/server/api/explain.js +111 -0
  463. package/dist/server/api/metadata.d.ts +2 -0
  464. package/dist/server/api/metadata.js +9 -0
  465. package/dist/server/api/openapi.js +15 -0
  466. package/dist/server/api/rest.d.ts +1 -0
  467. package/dist/server/api/rest.js +76 -12
  468. package/dist/server/api/views.js +1 -1
  469. package/dist/server/apps.d.ts +16 -1
  470. package/dist/server/apps.js +164 -2
  471. package/dist/server/auth/base.js +2 -0
  472. package/dist/server/auth/service.d.ts +8 -0
  473. package/dist/server/auth/service.js +19 -0
  474. package/dist/server/events.d.ts +13 -0
  475. package/dist/server/events.js +22 -0
  476. package/dist/server/i18n.d.ts +27 -0
  477. package/dist/server/i18n.js +143 -0
  478. package/dist/server/index.d.ts +2 -0
  479. package/dist/server/index.js +2 -0
  480. package/dist/server/server.js +6 -0
  481. package/dist/settings/company.d.ts +3 -1
  482. package/dist/settings/company.js +26 -5
  483. package/dist/settings/routes.d.ts +10 -0
  484. package/dist/settings/routes.js +19 -3
  485. package/dist/settings/web/AppsPage.d.ts +3 -2
  486. package/dist/settings/web/AppsPage.js +26 -8
  487. package/dist/settings/web/CompaniesPage.js +91 -6
  488. package/dist/settings/web/messages.js +64 -0
  489. package/dist/sign/.kosh/bindings.js +1 -1
  490. package/dist/sign/.kosh/schema.d.ts +2 -1
  491. package/dist/sign/i18n/bn.d.ts +2 -0
  492. package/dist/sign/i18n/bn.js +97 -0
  493. package/dist/sign/index.d.ts +15 -0
  494. package/dist/sign/index.js +129 -1
  495. package/dist/sign/module.d.ts +40 -0
  496. package/dist/sign/module.js +11 -2
  497. package/dist/stock/.kosh/bindings.js +1 -1
  498. package/dist/stock/.kosh/schema.d.ts +298 -2
  499. package/dist/stock/i18n/bn.d.ts +2 -0
  500. package/dist/stock/i18n/bn.js +311 -0
  501. package/dist/stock/index.d.ts +3 -1
  502. package/dist/stock/index.js +442 -3
  503. package/dist/stock/ledger.d.ts +78 -0
  504. package/dist/stock/ledger.js +582 -0
  505. package/dist/stock/module.d.ts +5280 -2044
  506. package/dist/stock/module.js +222 -23
  507. package/dist/stock/replenish.js +16 -1
  508. package/dist/stock/returns.d.ts +157 -0
  509. package/dist/stock/returns.js +163 -0
  510. package/dist/stock/web/Ledger.d.ts +9 -0
  511. package/dist/stock/web/Ledger.js +108 -0
  512. package/dist/stock/web/Operations.d.ts +8 -0
  513. package/dist/stock/web/Operations.js +33 -0
  514. package/dist/stock/web/ReturnButton.d.ts +7 -0
  515. package/dist/stock/web/ReturnButton.js +62 -0
  516. package/dist/stock/web/index.d.ts +3 -0
  517. package/dist/stock/web/index.js +9 -0
  518. package/dist/stock/web/messages.js +154 -0
  519. package/dist/stock_account/.kosh/bindings.js +1 -1
  520. package/dist/stock_account/.kosh/schema.d.ts +682 -9
  521. package/dist/stock_account/i18n/bn.d.ts +2 -0
  522. package/dist/stock_account/i18n/bn.js +73 -0
  523. package/dist/stock_account/index.d.ts +1 -0
  524. package/dist/stock_account/index.js +131 -3
  525. package/dist/stock_account/landed.d.ts +429 -0
  526. package/dist/stock_account/landed.js +335 -0
  527. package/dist/stock_account/module.d.ts +11247 -3363
  528. package/dist/stock_account/module.js +135 -6
  529. package/dist/stock_account/web/LandedPreview.d.ts +7 -0
  530. package/dist/stock_account/web/LandedPreview.js +23 -0
  531. package/dist/stock_account/web/index.d.ts +11 -0
  532. package/dist/stock_account/web/index.js +16 -0
  533. package/dist/stock_account/web/messages.d.ts +2 -0
  534. package/dist/stock_account/web/messages.js +27 -0
  535. package/dist/todo/i18n/bn.d.ts +2 -0
  536. package/dist/todo/i18n/bn.js +66 -0
  537. package/dist/todo/index.js +59 -0
  538. package/dist/todo/module.d.ts +5 -0
  539. package/dist/todo/module.js +5 -3
  540. package/dist/trade_finance/.kosh/bindings.js +1 -1
  541. package/dist/trade_finance/.kosh/schema.d.ts +2 -1
  542. package/dist/trade_finance/i18n/bn.d.ts +2 -0
  543. package/dist/trade_finance/i18n/bn.js +162 -0
  544. package/dist/trade_finance/index.js +93 -0
  545. package/dist/trade_finance/module.d.ts +26 -0
  546. package/dist/trade_finance/module.js +27 -8
  547. package/dist/trade_finance_account/.kosh/bindings.js +1 -1
  548. package/dist/trade_finance_account/.kosh/schema.d.ts +530 -8
  549. package/dist/trade_finance_account/i18n/bn.d.ts +2 -0
  550. package/dist/trade_finance_account/i18n/bn.js +27 -0
  551. package/dist/trade_finance_account/index.js +2 -0
  552. package/dist/trade_finance_account/module.d.ts +4906 -1432
  553. package/dist/trade_finance_account/module.js +1 -0
  554. package/dist/trade_finance_purchase/.kosh/bindings.js +1 -1
  555. package/dist/trade_finance_purchase/.kosh/schema.d.ts +813 -3
  556. package/dist/trade_finance_purchase/i18n/bn.d.ts +2 -0
  557. package/dist/trade_finance_purchase/i18n/bn.js +6 -0
  558. package/dist/trade_finance_purchase/index.js +2 -0
  559. package/dist/trade_finance_purchase/module.d.ts +6909 -413
  560. package/dist/trade_finance_sale/.kosh/bindings.js +1 -1
  561. package/dist/trade_finance_sale/.kosh/schema.d.ts +291 -4
  562. package/dist/trade_finance_sale/i18n/bn.d.ts +2 -0
  563. package/dist/trade_finance_sale/i18n/bn.js +6 -0
  564. package/dist/trade_finance_sale/index.js +2 -0
  565. package/dist/trade_finance_sale/module.d.ts +2601 -331
  566. package/dist/ui/api.d.ts +2 -0
  567. package/dist/ui/app.d.ts +9 -0
  568. package/dist/ui/app.js +24 -4
  569. package/dist/ui/components/AppIcon.js +2 -2
  570. package/dist/ui/components/Explain.d.ts +69 -0
  571. package/dist/ui/components/Explain.js +62 -0
  572. package/dist/ui/context.d.ts +8 -1
  573. package/dist/ui/context.js +34 -1
  574. package/dist/ui/i18n/messages.d.ts +36 -0
  575. package/dist/ui/i18n/messages.js +72 -0
  576. package/dist/ui/index.d.ts +2 -1
  577. package/dist/ui/index.js +1 -0
  578. package/dist/ui/registry.d.ts +14 -0
  579. package/dist/ui/registry.js +10 -0
  580. package/dist/ui/views/FormView.d.ts +6 -0
  581. package/dist/ui/views/FormView.js +47 -10
  582. package/dist/ui/views/ListView.d.ts +8 -0
  583. package/dist/ui/views/ListView.js +10 -3
  584. package/dist/ui/views/StatsRow.js +2 -1
  585. package/dist/views/extend.js +12 -0
  586. package/dist/views/types.d.ts +13 -2
  587. package/dist/views/validate.js +8 -1
  588. package/dist/website/.kosh/bindings.js +1 -1
  589. package/dist/website/.kosh/schema.d.ts +2 -1
  590. package/dist/website/i18n/bn.d.ts +2 -0
  591. package/dist/website/i18n/bn.js +113 -0
  592. package/dist/website/index.js +36 -0
  593. package/dist/website/module.d.ts +20 -0
  594. package/dist/website/module.js +29 -5
  595. package/dist/website_crm/.kosh/bindings.js +1 -1
  596. package/dist/website_crm/.kosh/schema.d.ts +2 -1
  597. package/dist/website_crm/i18n/bn.d.ts +2 -0
  598. package/dist/website_crm/i18n/bn.js +5 -0
  599. package/dist/website_crm/index.js +2 -0
  600. package/dist/website_crm/module.d.ts +40 -0
  601. package/dist/website_sale/.kosh/bindings.js +1 -1
  602. package/dist/website_sale/.kosh/schema.d.ts +642 -17
  603. package/dist/website_sale/i18n/bn.d.ts +2 -0
  604. package/dist/website_sale/i18n/bn.js +132 -0
  605. package/dist/website_sale/index.js +83 -1
  606. package/dist/website_sale/module.d.ts +18705 -5550
  607. package/dist/website_sale/module.js +32 -10
  608. package/dist/website_sale/shop.js +22 -9
  609. package/dist/website_sale_stock/.kosh/bindings.js +1 -1
  610. package/dist/website_sale_stock/.kosh/schema.d.ts +737 -18
  611. package/dist/website_sale_stock/i18n/bn.d.ts +2 -0
  612. package/dist/website_sale_stock/i18n/bn.js +4 -0
  613. package/dist/website_sale_stock/index.js +2 -0
  614. package/dist/website_sale_stock/module.d.ts +10 -24997
  615. package/dist/website_sale_stock/module.js +7 -3
  616. package/package.json +36 -8
  617. package/dist/mrp/index.d.ts +0 -3
  618. package/dist/mrp/index.js +0 -393
  619. package/dist/mrp/module.js +0 -757
  620. package/dist/mrp_account/index.d.ts +0 -3
  621. package/dist/mrp_account/index.js +0 -22
  622. /package/dist/{mrp → approvals_purchase}/.kosh/bindings.d.ts +0 -0
  623. /package/dist/{mrp_account → indent}/.kosh/bindings.d.ts +0 -0
@@ -0,0 +1,507 @@
1
+ /**
2
+ * RFQs to many vendors (RFC 0032 §3.4, after Paragon's procurement chain): a buyer asks several vendors to quote the
3
+ * same lines by a deadline. Each vendor gets a **response** of their own and a **quote link** — single use until
4
+ * submitted, valid a day past the deadline, only its digest kept — to a page where they type their prices, delivery
5
+ * date and terms without an account; buyers may type a response for them. A submitted response moves the RFQ to
6
+ * *Responses received*; the comparison (step 8) chooses the vendors. An approved purchase request becomes such an
7
+ * RFQ in one step, its lines followed by the line tracker.
8
+ */
9
+ import { randomBytes } from 'node:crypto';
10
+ import { layout, mailerFromEnv } from '../mail/index.js';
11
+ import { audit, digest, HttpProblem, randomToken } from '../server/index.js';
12
+ import { orm } from '../server/orm.js';
13
+ import { compute, defineModel, f, onchange, records } from './.kosh/bindings.js';
14
+ const ALL = ['read', 'write', 'create', 'unlink'];
15
+ const zero = orm.toDecimal(0);
16
+ let config = {
17
+ mailer: mailerFromEnv(),
18
+ apiUrl: (process.env['KOSH_API_URL'] ?? 'http://localhost:3000').replace(/\/$/, ''),
19
+ log: () => undefined,
20
+ };
21
+ /** The mailer and public address the quote links use (tests: a memory mailer). */
22
+ export function configurePurchaseMails(next) {
23
+ config = { ...config, ...next };
24
+ }
25
+ /** A date-only value as `YYYY-MM-DD`. */
26
+ function isoDay(v) {
27
+ if (!(v instanceof Date))
28
+ return String(v ?? '').slice(0, 10);
29
+ const pad = (n) => String(n).padStart(2, '0');
30
+ return `${v.getFullYear()}-${pad(v.getMonth() + 1)}-${pad(v.getDate())}`;
31
+ }
32
+ const TENDER_STATES = {
33
+ draft: 'Draft',
34
+ sent: 'Sent',
35
+ received: 'Responses received',
36
+ comparing: 'In comparison',
37
+ selected: 'Vendor selected',
38
+ ordered: 'Orders made',
39
+ cancel: 'Cancelled',
40
+ };
41
+ export const PurchaseTender = defineModel('purchase.tender', {
42
+ label: 'RFQ to vendors',
43
+ recName: 'name',
44
+ order: ['id desc'],
45
+ companyScoped: { required: true },
46
+ tracking: ['state', 'deadline', 'dateDelivery'],
47
+ access: { 'purchase.user': ALL, 'purchase.manager': [...ALL, 'export', 'import'] },
48
+ rules: { all: { groups: ['purchase.user'], perms: [...ALL], domain: {} } },
49
+ fields: {
50
+ name: f.char({ required: true, sequence: 'purchase.tender', label: 'Reference' }),
51
+ buyer: f.many2one('res.users', {
52
+ required: true,
53
+ ondelete: 'restrict',
54
+ default: (env) => env.userId,
55
+ }),
56
+ date: f.date({ required: true, default: (env) => env.today(), label: 'RFQ date' }),
57
+ deadline: f.date({
58
+ required: true,
59
+ label: 'Response deadline',
60
+ help: 'Vendors quote by this day; their links work a day longer.',
61
+ }),
62
+ dateDelivery: f.date({ label: 'Delivery date' }),
63
+ sourcing: f.selection({ local: 'Local', import: 'Import' }, {
64
+ required: true,
65
+ default: 'local',
66
+ help: 'Bought in the country, or imported (a letter of credit may follow).',
67
+ }),
68
+ topic: f.char({ label: 'RFQ topic' }),
69
+ vendors: f.many2many('res.partner', { relation: 'purchase_tender_vendor', label: 'Vendors' }),
70
+ currency: f.many2one('res.currency', { ondelete: 'restrict' }),
71
+ paymentTerms: f.char({ label: 'Payment terms' }),
72
+ technical: f.boolean({
73
+ default: false,
74
+ label: 'Technical comparison first',
75
+ help: 'An engineer scores the vendors on the specifications before prices are compared.',
76
+ }),
77
+ note: f.text({ label: 'Notes and special instructions' }),
78
+ request: f.many2one('purchase.request', { ondelete: 'set null', label: 'Purchase request' }),
79
+ lines: f.one2many('purchase.tender.line', 'tender', { label: 'Products' }),
80
+ responses: f.one2many('purchase.tender.response', 'tender', { label: 'Vendor responses' }),
81
+ responseCount: f.integer({
82
+ store: true,
83
+ label: 'Responses',
84
+ help: 'Vendors who submitted a quotation.',
85
+ ...compute('purchase.tender', 'responseCount', ['responses.state'], (tenders) => {
86
+ for (const t of tenders)
87
+ t.responseCount = [...t.responses].filter((r) => r.state === 'submitted').length;
88
+ }),
89
+ }),
90
+ state: f.state({
91
+ states: TENDER_STATES,
92
+ initial: 'draft',
93
+ help: 'Drafted, sent to the vendors with their quote links, received once they answer, compared, a vendor selected, orders made.',
94
+ transitions: {
95
+ send: {
96
+ from: ['draft'],
97
+ to: 'sent',
98
+ guard: records('purchase.tender', async (tenders) => [...(await tenders.load({ lines: true, vendors: true }))]
99
+ .filter((t) => t.lines.length === 0 || t.vendors.length === 0)
100
+ .map((t) => t.id)),
101
+ message: 'Add the products and the vendors before sending the RFQ.',
102
+ after: records('purchase.tender', async (tenders) => {
103
+ const env = tenders.env;
104
+ await env.flush();
105
+ for (const id of tenders.ids)
106
+ await inviteVendors(env, id);
107
+ }),
108
+ },
109
+ /** After the deadline, the buyer moves on with the answers in hand. */
110
+ close: {
111
+ from: ['sent'],
112
+ to: 'received',
113
+ guard: records('purchase.tender', async (tenders) => [...(await tenders.load({}))]
114
+ .filter((t) => (t.responseCount ?? 0) === 0)
115
+ .map((t) => t.id)),
116
+ message: 'No vendor has answered yet.',
117
+ },
118
+ /** The comparative statement is being drawn up (step 8). */
119
+ compare: { from: ['received'], to: 'comparing' },
120
+ /** The statement is approved: the vendors are chosen. */
121
+ select: { from: ['comparing', 'received'], to: 'selected' },
122
+ /** The orders are made from the confirmed statement. */
123
+ order: { from: ['selected', 'comparing', 'received'], to: 'ordered' },
124
+ cancel: { from: ['draft', 'sent', 'received', 'comparing'], to: 'cancel' },
125
+ redraft: { from: ['cancel'], to: 'draft' },
126
+ },
127
+ extensible: true,
128
+ }),
129
+ },
130
+ onchange: {
131
+ topic: onchange('purchase.tender', async (d, env) => {
132
+ if ((d.get('currency') ?? null) !== null)
133
+ return;
134
+ const [company] = await env
135
+ .model('res.company')
136
+ .browse([env.companyId])
137
+ .load({ currency: true });
138
+ if (company?.currency)
139
+ d.set('currency', company.currency.id);
140
+ }),
141
+ },
142
+ });
143
+ export const PurchaseTenderLine = defineModel('purchase.tender.line', {
144
+ label: 'RFQ line',
145
+ order: ['tender desc', 'sequence asc', 'id asc'],
146
+ access: { 'purchase.user': ALL, 'purchase.manager': [...ALL, 'export', 'import'] },
147
+ rules: { all: { groups: ['purchase.user'], perms: [...ALL], domain: {} } },
148
+ fields: {
149
+ tender: f.many2one('purchase.tender', { required: true, ondelete: 'cascade', label: 'RFQ' }),
150
+ sequence: f.integer({ default: 10 }),
151
+ product: f.many2one('product.product', { required: true, ondelete: 'restrict' }),
152
+ name: f.char({ label: 'Description' }),
153
+ uom: f.many2one('uom.uom', { ondelete: 'restrict', label: 'Unit' }),
154
+ quantity: f.decimal({ scale: 3, required: true, default: '1' }),
155
+ requestLine: f.many2one('purchase.request.line', {
156
+ ondelete: 'set null',
157
+ label: 'Request line',
158
+ }),
159
+ },
160
+ sqlConstraints: {
161
+ positive: { check: orm.sql `quantity > 0`, message: 'Quantities are positive.' },
162
+ },
163
+ onchange: {
164
+ product: onchange('purchase.tender.line', async (d) => {
165
+ const product = (await d.record('product'));
166
+ if (product === null)
167
+ return;
168
+ if (product.uom !== null)
169
+ d.set('uom', product.uom.id);
170
+ if ((d.get('name') ?? '') === '')
171
+ d.set('name', product.name);
172
+ }),
173
+ },
174
+ });
175
+ export const PurchaseTenderResponse = defineModel('purchase.tender.response', {
176
+ label: 'Vendor response',
177
+ recName: 'name',
178
+ order: ['id desc'],
179
+ companyScoped: { required: true },
180
+ tracking: ['state', 'amountTotal', 'dateDelivery'],
181
+ access: { 'purchase.user': ALL, 'purchase.manager': [...ALL, 'export', 'import'] },
182
+ rules: { all: { groups: ['purchase.user'], perms: [...ALL], domain: {} } },
183
+ fields: {
184
+ name: f.char({ required: true, sequence: 'purchase.tender.response', label: 'Reference' }),
185
+ tender: f.many2one('purchase.tender', { required: true, ondelete: 'cascade', label: 'RFQ' }),
186
+ vendor: f.many2one('res.partner', { required: true, ondelete: 'restrict' }),
187
+ responseDate: f.datetime({ label: 'Responded on' }),
188
+ dateDelivery: f.date({ label: 'Delivery date' }),
189
+ paymentTerms: f.char({ label: 'Payment terms' }),
190
+ advancePercent: f.decimal({ scale: 2, default: '0', label: 'Advance (%)' }),
191
+ technicalRank: f.integer({
192
+ default: 0,
193
+ label: 'Technical rank',
194
+ help: 'Given by the technical comparison when the RFQ asks for one; 0 until then.',
195
+ }),
196
+ remark: f.text(),
197
+ lines: f.one2many('purchase.tender.response.line', 'response', { label: 'Quoted lines' }),
198
+ amountTotal: f.decimal({
199
+ scale: 2,
200
+ store: true,
201
+ label: 'Total',
202
+ help: 'The quoted lines together, before tax.',
203
+ ...compute('purchase.tender.response', 'amountTotal', ['lines.subtotal'], (responses) => {
204
+ for (const r of responses)
205
+ r.amountTotal = [...r.lines].reduce((s, l) => s.plus(l.subtotal ?? zero), zero);
206
+ }),
207
+ }),
208
+ /** The quote link's digest; the link itself is shown once to whoever makes it. */
209
+ tokenHash: f.char({ index: 'unique', label: 'Quote link' }),
210
+ tokenExpiresAt: f.datetime({ label: 'Quote link valid until' }),
211
+ state: f.state({
212
+ states: {
213
+ invited: 'Invited',
214
+ quoted: 'Quoted, not sent',
215
+ submitted: 'Submitted',
216
+ not_responded: 'Not responded',
217
+ rejected: 'Rejected',
218
+ },
219
+ initial: 'invited',
220
+ help: 'Invited with a quote link; quoted while the vendor fills it in; submitted once sent; not responded after the deadline; rejected by the buyer.',
221
+ transitions: {
222
+ submit: {
223
+ from: ['invited', 'quoted'],
224
+ to: 'submitted',
225
+ guard: records('purchase.tender.response', async (responses) => [...(await responses.load({ lines: true }))]
226
+ .filter((r) => r.lines.length === 0 || [...r.lines].some((l) => l.priceUnit.lte(0)))
227
+ .map((r) => r.id)),
228
+ message: 'Every line needs a price.',
229
+ after: records('purchase.tender.response', async (responses) => {
230
+ const env = responses.env;
231
+ await responses.write({ responseDate: new Date() });
232
+ for (const r of await responses.load({ tender: true })) {
233
+ if (r.tender.state === 'sent')
234
+ await env.model('purchase.tender').browse([r.tender.id]).close();
235
+ await env.emit('purchase.tender.response_submitted', { id: r.id });
236
+ }
237
+ }),
238
+ },
239
+ /** The vendor saved prices through their link, without submitting yet. */
240
+ quote: { from: ['invited', 'quoted'], to: 'quoted' },
241
+ unanswered: { from: ['invited', 'quoted'], to: 'not_responded' },
242
+ reject: { from: ['submitted'], to: 'rejected' },
243
+ reopen: { from: ['not_responded', 'rejected'], to: 'invited' },
244
+ },
245
+ extensible: true,
246
+ }),
247
+ },
248
+ });
249
+ export const PurchaseTenderResponseLine = defineModel('purchase.tender.response.line', {
250
+ label: 'Quoted line',
251
+ order: ['response desc', 'id asc'],
252
+ access: { 'purchase.user': ALL, 'purchase.manager': [...ALL, 'export', 'import'] },
253
+ rules: { all: { groups: ['purchase.user'], perms: [...ALL], domain: {} } },
254
+ fields: {
255
+ response: f.many2one('purchase.tender.response', { required: true, ondelete: 'cascade' }),
256
+ tenderLine: f.many2one('purchase.tender.line', {
257
+ required: true,
258
+ ondelete: 'restrict',
259
+ label: 'RFQ line',
260
+ }),
261
+ product: f.many2one('product.product', { required: true, ondelete: 'restrict' }),
262
+ quantity: f.decimal({ scale: 3, required: true, default: '1' }),
263
+ priceUnit: f.decimal({ scale: 2, required: true, default: '0', label: 'Quoted price' }),
264
+ subtotal: f.decimal({
265
+ scale: 2,
266
+ store: true,
267
+ help: 'The quantity at the quoted price.',
268
+ ...compute('purchase.tender.response.line', 'subtotal', ['quantity', 'priceUnit'], (lines) => {
269
+ for (const l of lines)
270
+ l.subtotal = l.quantity.times(l.priceUnit).toDecimalPlaces(2);
271
+ }),
272
+ }),
273
+ note: f.char(),
274
+ },
275
+ });
276
+ /** A fresh quote link for a response: the token is stored as its digest; the URL is returned once. */
277
+ async function issueQuoteLink(env, responseId) {
278
+ const [r] = await env
279
+ .model('purchase.tender.response')
280
+ .browse([responseId])
281
+ .load({ tender: true });
282
+ if (r === undefined)
283
+ throw new HttpProblem(404, 'not-found', 'Not found', 'No such response.');
284
+ const token = randomToken(24);
285
+ const expires = new Date(`${isoDay(r.tender.deadline)}T00:00:00`);
286
+ expires.setDate(expires.getDate() + 2);
287
+ await env
288
+ .model('purchase.tender.response')
289
+ .browse([responseId])
290
+ .write({ tokenHash: digest(token), tokenExpiresAt: expires });
291
+ return `${config.apiUrl}/api/v1/public/purchase/quotes/${token}`;
292
+ }
293
+ /**
294
+ * On sending: one response per vendor with the RFQ's lines, each with its quote link, emailed to the vendor when a
295
+ * mailer is configured and the vendor has an email (a failed send is logged; the buyer may make a new link).
296
+ */
297
+ async function inviteVendors(env, tenderId) {
298
+ const [t] = await env
299
+ .model('purchase.tender')
300
+ .browse([tenderId])
301
+ .load({ vendors: true, lines: { product: true }, company: true, buyer: true });
302
+ if (t === undefined)
303
+ return;
304
+ for (const vendor of t.vendors) {
305
+ const made = await env.model('purchase.tender.response').create([
306
+ {
307
+ tender: t.id,
308
+ vendor: vendor.id,
309
+ company: t.company.id,
310
+ paymentTerms: t.paymentTerms,
311
+ dateDelivery: t.dateDelivery,
312
+ lines: {
313
+ create: [...t.lines].map((l) => ({
314
+ tenderLine: l.id,
315
+ product: l.product.id,
316
+ quantity: l.quantity,
317
+ })),
318
+ },
319
+ },
320
+ ]);
321
+ const id = made.ids[0];
322
+ const url = await issueQuoteLink(env, id);
323
+ const email = vendor.email ?? null;
324
+ if (config.mailer === undefined || email === null || email === '')
325
+ continue;
326
+ try {
327
+ const body = layout({
328
+ title: `Request for quotation ${t.name}`,
329
+ paragraphs: [
330
+ `${t.company.name} asks ${vendor.name} to quote ${t.lines.length} product(s) by ${isoDay(t.deadline)}.`,
331
+ ...(t.note ? [t.note] : []),
332
+ 'The link is yours alone and works until the deadline; you may save and come back before you submit.',
333
+ ],
334
+ action: { label: 'Send your quotation', url },
335
+ });
336
+ await config.mailer.send({
337
+ to: email,
338
+ subject: `Request for quotation ${t.name}${t.topic ? ` — ${t.topic}` : ''}`,
339
+ html: body.html,
340
+ text: body.text,
341
+ });
342
+ }
343
+ catch (error) {
344
+ config.log(`The quote link of ${t.name} could not be emailed to ${vendor.name}.`, error);
345
+ }
346
+ }
347
+ await env.emit('purchase.tender.sent', { id: t.id });
348
+ }
349
+ const esc = (s) => s.replace(/[&<>"]/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' })[c]);
350
+ function quotePage(q, nonce) {
351
+ const closed = q.state === 'submitted' || q.state === 'rejected' || q.state === 'not_responded';
352
+ const rows = q.lines
353
+ .map((l) => `<tr><td>${esc(l.product)}</td><td class="n">${esc(l.quantity)} ${esc(l.uom)}</td><td><input type="number" min="0" step="0.01" data-line="${l.id}" value="${esc(l.priceUnit)}" ${closed ? 'disabled' : ''} aria-label="Price of ${esc(l.product)}"></td></tr>`)
354
+ .join('');
355
+ return `<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
356
+ <title>${esc(q.tender.name)} — quotation</title><style>body{font:16px system-ui,sans-serif;max-width:44rem;margin:2rem auto;padding:0 1rem;color:#111;background:#fff}
357
+ @media (prefers-color-scheme:dark){body{color:#eee;background:#111}input,textarea{background:#222;color:#eee}}
358
+ table{width:100%;border-collapse:collapse}td,th{border-bottom:1px solid #8884;padding:.4rem;text-align:start}.n{text-align:end;white-space:nowrap}
359
+ input,textarea{font:inherit;width:100%;box-sizing:border-box;padding:.3rem}label{display:block;margin-top:.8rem;font-weight:600}
360
+ button{font:inherit;padding:.6rem 1rem;border-radius:.5rem;border:1px solid #888;cursor:pointer;margin:.8rem .5rem 0 0}.go{background:#16a34a;color:#fff;border-color:#16a34a}</style></head>
361
+ <body><h1>Request for quotation ${esc(q.tender.name)}</h1>
362
+ <p>${esc(q.tender.company)} asks ${esc(q.vendor)} to quote${q.tender.topic ? ` — ${esc(q.tender.topic)}` : ''}. Deadline: ${esc(q.tender.deadline)}. Prices in ${esc(q.tender.currency)}.</p>
363
+ ${q.tender.note ? `<p>${esc(q.tender.note)}</p>` : ''}
364
+ ${closed ? `<p role="status"><strong>${q.state === 'submitted' ? 'Your quotation was received — thank you.' : 'This request is closed.'}</strong></p>` : ''}
365
+ <table><thead><tr><th>Product</th><th class="n">Quantity</th><th>Unit price</th></tr></thead><tbody>${rows}</tbody></table>
366
+ <label for="d">Delivery date</label><input id="d" type="date" value="${esc(q.dateDelivery ?? '')}" ${closed ? 'disabled' : ''}>
367
+ <label for="p">Payment terms</label><input id="p" maxlength="200" value="${esc(q.paymentTerms ?? '')}" ${closed ? 'disabled' : ''}>
368
+ <label for="r">Remarks</label><textarea id="r" maxlength="2000" ${closed ? 'disabled' : ''}>${esc(q.remark ?? '')}</textarea>
369
+ ${closed ? '' : '<div><button data-s="0">Save for later</button><button class="go" data-s="1">Submit the quotation</button></div>'}
370
+ <p id="out" role="status"></p>
371
+ <script nonce="${nonce}">for(const b of document.querySelectorAll('button[data-s]')){b.addEventListener('click',async()=>{const out=document.getElementById('out');out.textContent='Sending…';
372
+ const lines=[...document.querySelectorAll('input[data-line]')].map(i=>({id:Number(i.dataset.line),priceUnit:i.value}));
373
+ const r=await fetch(location.pathname,{method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({lines,dateDelivery:document.getElementById('d').value,paymentTerms:document.getElementById('p').value,remark:document.getElementById('r').value,submit:b.dataset.s==='1'})});
374
+ let j={};try{j=await r.json()}catch{}out.textContent=r.ok?(j.state==='submitted'?'Submitted — thank you.':'Saved.'):(j.detail||'It could not be saved.');if(r.ok&&j.state==='submitted')for(const x of document.querySelectorAll('button,input,textarea'))x.disabled=true;})}</script></body></html>`;
375
+ }
376
+ /** The quote link's response, by the token's digest, while the link is valid. */
377
+ async function responseOfToken(su, token) {
378
+ const found = await su
379
+ .withFlags({ checkCompany: false })
380
+ .model('purchase.tender.response')
381
+ .search({ where: { tokenHash: digest(token) }, limit: 1 });
382
+ const [r] = await found.load({
383
+ vendor: true,
384
+ tender: { currency: true, company: true },
385
+ lines: { tenderLine: { product: true, uom: true } },
386
+ });
387
+ if (r === undefined)
388
+ return null;
389
+ if (r.tokenExpiresAt !== null && new Date(r.tokenExpiresAt).getTime() < Date.now())
390
+ return null;
391
+ return {
392
+ id: r.id,
393
+ name: r.name,
394
+ state: r.state,
395
+ vendor: r.vendor.name,
396
+ tender: {
397
+ name: r.tender.name,
398
+ topic: r.tender.topic ?? null,
399
+ deadline: isoDay(r.tender.deadline),
400
+ note: r.tender.note ?? null,
401
+ currency: r.tender.currency?.name ?? '',
402
+ company: r.tender.company.name,
403
+ },
404
+ dateDelivery: r.dateDelivery === null ? null : isoDay(r.dateDelivery),
405
+ paymentTerms: r.paymentTerms ?? null,
406
+ remark: r.remark ?? null,
407
+ lines: [...r.lines].map((l) => ({
408
+ id: l.id,
409
+ product: l.tenderLine.product.name,
410
+ quantity: l.quantity.toString(),
411
+ uom: l.tenderLine.uom?.name ?? '',
412
+ priceUnit: l.priceUnit.toString(),
413
+ })),
414
+ };
415
+ }
416
+ export function registerTenderRoutes(context) {
417
+ const { app, shutro } = context;
418
+ configurePurchaseMails({ log: (message, error) => app.log.warn({ err: error }, message) });
419
+ const tokenOf = (params) => String(params['token'] ?? '');
420
+ const asSystem = (reason, run) => shutro.bootstrap({ reason }, (raw) => raw.asSuperuser({ reason }, (su) => run(su)));
421
+ // A new quote link for a response (the old one stops working), for buyers who email or paste it themselves.
422
+ app.post('/api/v1/purchase/responses/:id/link', async (request) => {
423
+ const id = Number(request.params['id']);
424
+ return request.env(async (raw) => {
425
+ const env = raw;
426
+ env.access.check('purchase.tender.response', 'write');
427
+ await env.access.checkRecords('purchase.tender.response', [id], 'write');
428
+ return { url: await issueQuoteLink(env, id) };
429
+ }, { write: true });
430
+ });
431
+ // The vendor's page: the lines to price, the delivery date and terms; its script (allowed by a nonce) posts.
432
+ app.get('/api/v1/public/purchase/quotes/:token', { config: { auth: false } }, async (request, reply) => {
433
+ const q = await asSystem('quote link', (su) => responseOfToken(su, tokenOf(request.params)));
434
+ if (q === null)
435
+ throw new HttpProblem(404, 'not-found', 'Not found', 'This link has expired or does not exist.');
436
+ const nonce = randomBytes(16).toString('base64');
437
+ return reply
438
+ .type('text/html; charset=utf-8')
439
+ .header('content-security-policy', `default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${nonce}'; connect-src 'self'`)
440
+ .header('cache-control', 'no-store')
441
+ .send(quotePage(q, nonce));
442
+ });
443
+ // The vendor saves, or submits once; audited with the address.
444
+ app.post('/api/v1/public/purchase/quotes/:token', { config: { auth: false }, bodyLimit: 64 * 1024 }, async (request) => {
445
+ const token = tokenOf(request.params);
446
+ const body = (request.body ?? {});
447
+ const submit = body['submit'] === true;
448
+ const given = Array.isArray(body['lines']) ? body['lines'] : [];
449
+ const text = (key, max) => typeof body[key] === 'string' ? body[key].slice(0, max) : null;
450
+ return asSystem('quote link', async (su) => {
451
+ const q = await responseOfToken(su, token);
452
+ if (q === null)
453
+ throw new HttpProblem(404, 'not-found', 'Not found', 'This link has expired or does not exist.');
454
+ if (q.state === 'submitted' || q.state === 'rejected' || q.state === 'not_responded')
455
+ throw new HttpProblem(409, 'closed', 'Closed', 'This quotation was submitted already.');
456
+ const scoped = su.withFlags({ checkCompany: false });
457
+ for (const l of given) {
458
+ const id = Number(l['id']);
459
+ const price = String(l['priceUnit'] ?? '');
460
+ if (!q.lines.some((x) => x.id === id) || !/^\d+(\.\d{1,2})?$/.test(price))
461
+ continue;
462
+ await scoped
463
+ .model('purchase.tender.response.line')
464
+ .browse([id])
465
+ .write({ priceUnit: price });
466
+ }
467
+ // Only what the page sent changes: a submit after a save keeps the saved terms.
468
+ const values = {};
469
+ const delivery = text('dateDelivery', 10);
470
+ if (delivery !== null)
471
+ values['dateDelivery'] = /^\d{4}-\d{2}-\d{2}$/.test(delivery) ? delivery : null;
472
+ const terms = text('paymentTerms', 200);
473
+ if (terms !== null)
474
+ values['paymentTerms'] = terms;
475
+ const remark = text('remark', 2000);
476
+ if (remark !== null)
477
+ values['remark'] = remark;
478
+ if (Object.keys(values).length > 0)
479
+ await scoped
480
+ .model('purchase.tender.response')
481
+ .browse([q.id])
482
+ .write(values);
483
+ await scoped
484
+ .model('purchase.tender.response')
485
+ .browse([q.id])
486
+ .quote();
487
+ await su.flush();
488
+ let state = 'quoted';
489
+ if (submit) {
490
+ await scoped
491
+ .model('purchase.tender.response')
492
+ .browse([q.id])
493
+ .submit();
494
+ state = 'submitted';
495
+ }
496
+ await audit(su, {
497
+ kind: submit ? 'purchase.quote_submitted' : 'purchase.quote_saved',
498
+ summary: `${q.vendor} ${submit ? 'submitted' : 'saved'} their quotation ${q.name} through their link.`,
499
+ targetModel: 'purchase.tender.response',
500
+ targetId: q.id,
501
+ ip: request.ip,
502
+ });
503
+ await su.flush();
504
+ return { ok: true, state };
505
+ });
506
+ });
507
+ }