shutro-erp 0.3.4 → 0.3.5

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 (381) hide show
  1. package/dist/account_portal/.kosh/bindings.js +1 -1
  2. package/dist/account_portal/.kosh/schema.d.ts +54 -3
  3. package/dist/account_portal/module.d.ts +359 -6
  4. package/dist/accounting/.kosh/bindings.js +1 -1
  5. package/dist/accounting/.kosh/schema.d.ts +54 -3
  6. package/dist/accounting/index.js +214 -2
  7. package/dist/accounting/module.d.ts +217 -5
  8. package/dist/accounting/module.js +23 -2
  9. package/dist/calendar/.kosh/bindings.d.ts +22 -0
  10. package/dist/calendar/.kosh/bindings.js +15 -0
  11. package/dist/calendar/.kosh/schema.d.ts +795 -0
  12. package/dist/calendar/demo.d.ts +6 -0
  13. package/dist/calendar/demo.js +24 -0
  14. package/dist/calendar/index.d.ts +3 -0
  15. package/dist/calendar/index.js +291 -0
  16. package/dist/calendar/module.d.ts +1705 -0
  17. package/dist/calendar/module.js +180 -0
  18. package/dist/cli/init.js +48 -0
  19. package/dist/cli/templates.d.ts +1 -1
  20. package/dist/cli/templates.js +137 -0
  21. package/dist/contacts/.kosh/bindings.js +1 -1
  22. package/dist/contacts/.kosh/schema.d.ts +78 -3
  23. package/dist/contacts/demo.js +30 -1
  24. package/dist/contacts/index.d.ts +1 -1
  25. package/dist/contacts/index.js +155 -9
  26. package/dist/contacts/module.d.ts +338 -2
  27. package/dist/contacts/module.js +82 -5
  28. package/dist/crm/.kosh/bindings.d.ts +22 -0
  29. package/dist/crm/.kosh/bindings.js +15 -0
  30. package/dist/crm/.kosh/schema.d.ts +799 -0
  31. package/dist/crm/demo.d.ts +6 -0
  32. package/dist/crm/demo.js +45 -0
  33. package/dist/crm/index.d.ts +3 -0
  34. package/dist/crm/index.js +361 -0
  35. package/dist/crm/module.d.ts +1843 -0
  36. package/dist/crm/module.js +207 -0
  37. package/dist/event/.kosh/bindings.d.ts +22 -0
  38. package/dist/event/.kosh/bindings.js +15 -0
  39. package/dist/event/.kosh/schema.d.ts +843 -0
  40. package/dist/event/demo.d.ts +6 -0
  41. package/dist/event/demo.js +65 -0
  42. package/dist/event/index.d.ts +14 -0
  43. package/dist/event/index.js +333 -0
  44. package/dist/event/mails.d.ts +15 -0
  45. package/dist/event/mails.js +112 -0
  46. package/dist/event/module.d.ts +2800 -0
  47. package/dist/event/module.js +323 -0
  48. package/dist/event/web/CheckInBox.d.ts +9 -0
  49. package/dist/event/web/CheckInBox.js +87 -0
  50. package/dist/event/web/index.d.ts +11 -0
  51. package/dist/event/web/index.js +16 -0
  52. package/dist/event/web/messages.d.ts +2 -0
  53. package/dist/event/web/messages.js +23 -0
  54. package/dist/event_account/.kosh/bindings.d.ts +22 -0
  55. package/dist/event_account/.kosh/bindings.js +15 -0
  56. package/dist/event_account/.kosh/schema.d.ts +1639 -0
  57. package/dist/event_account/index.d.ts +3 -0
  58. package/dist/event_account/index.js +28 -0
  59. package/dist/event_account/module.d.ts +6968 -0
  60. package/dist/event_account/module.js +90 -0
  61. package/dist/hr/.kosh/bindings.d.ts +22 -0
  62. package/dist/hr/.kosh/bindings.js +15 -0
  63. package/dist/hr/.kosh/schema.d.ts +878 -0
  64. package/dist/hr/demo.d.ts +6 -0
  65. package/dist/hr/demo.js +60 -0
  66. package/dist/hr/index.d.ts +3 -0
  67. package/dist/hr/index.js +483 -0
  68. package/dist/hr/module.d.ts +2611 -0
  69. package/dist/hr/module.js +350 -0
  70. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  71. package/dist/l10n_bd/.kosh/schema.d.ts +54 -3
  72. package/dist/l10n_bd/module.d.ts +359 -6
  73. package/dist/l10n_in/.kosh/bindings.js +1 -1
  74. package/dist/l10n_in/.kosh/schema.d.ts +54 -3
  75. package/dist/l10n_in/module.d.ts +380 -7
  76. package/dist/maintenance/.kosh/bindings.d.ts +22 -0
  77. package/dist/maintenance/.kosh/bindings.js +15 -0
  78. package/dist/maintenance/.kosh/schema.d.ts +911 -0
  79. package/dist/maintenance/demo.d.ts +6 -0
  80. package/dist/maintenance/demo.js +60 -0
  81. package/dist/maintenance/index.d.ts +3 -0
  82. package/dist/maintenance/index.js +422 -0
  83. package/dist/maintenance/module.d.ts +3545 -0
  84. package/dist/maintenance/module.js +446 -0
  85. package/dist/maintenance_stock/.kosh/bindings.d.ts +22 -0
  86. package/dist/maintenance_stock/.kosh/bindings.js +15 -0
  87. package/dist/maintenance_stock/.kosh/schema.d.ts +1371 -0
  88. package/dist/maintenance_stock/index.d.ts +9 -0
  89. package/dist/maintenance_stock/index.js +52 -0
  90. package/dist/maintenance_stock/module.d.ts +6953 -0
  91. package/dist/maintenance_stock/module.js +192 -0
  92. package/dist/modules-sdk/index.d.ts +1 -1
  93. package/dist/modules-sdk/manifest.d.ts +70 -0
  94. package/dist/modules-sdk/manifest.js +35 -1
  95. package/dist/mrp/.kosh/bindings.d.ts +22 -0
  96. package/dist/mrp/.kosh/bindings.js +15 -0
  97. package/dist/mrp/.kosh/schema.d.ts +1378 -0
  98. package/dist/mrp/demo.d.ts +7 -0
  99. package/dist/mrp/demo.js +42 -0
  100. package/dist/mrp/index.d.ts +3 -0
  101. package/dist/mrp/index.js +392 -0
  102. package/dist/mrp/module.d.ts +8608 -0
  103. package/dist/mrp/module.js +695 -0
  104. package/dist/mrp_account/.kosh/bindings.d.ts +22 -0
  105. package/dist/mrp_account/.kosh/bindings.js +15 -0
  106. package/dist/mrp_account/.kosh/schema.d.ts +2106 -0
  107. package/dist/mrp_account/index.d.ts +3 -0
  108. package/dist/mrp_account/index.js +22 -0
  109. package/dist/mrp_account/module.d.ts +20840 -0
  110. package/dist/mrp_account/module.js +123 -0
  111. package/dist/payment/.kosh/bindings.js +1 -1
  112. package/dist/payment/.kosh/schema.d.ts +54 -3
  113. package/dist/payment/module.d.ts +359 -6
  114. package/dist/portal/.kosh/bindings.js +1 -1
  115. package/dist/portal/.kosh/schema.d.ts +78 -3
  116. package/dist/portal/module.d.ts +166 -2
  117. package/dist/pos/.kosh/bindings.d.ts +22 -0
  118. package/dist/pos/.kosh/bindings.js +15 -0
  119. package/dist/pos/.kosh/schema.d.ts +1690 -0
  120. package/dist/pos/accounting.d.ts +10 -0
  121. package/dist/pos/accounting.js +149 -0
  122. package/dist/pos/demo.d.ts +6 -0
  123. package/dist/pos/demo.js +90 -0
  124. package/dist/pos/index.d.ts +9 -0
  125. package/dist/pos/index.js +349 -0
  126. package/dist/pos/module.d.ts +9636 -0
  127. package/dist/pos/module.js +423 -0
  128. package/dist/pos/routes.d.ts +39 -0
  129. package/dist/pos/routes.js +368 -0
  130. package/dist/pos/web/Register.d.ts +9 -0
  131. package/dist/pos/web/Register.js +252 -0
  132. package/dist/pos/web/index.d.ts +11 -0
  133. package/dist/pos/web/index.js +16 -0
  134. package/dist/pos/web/messages.d.ts +2 -0
  135. package/dist/pos/web/messages.js +87 -0
  136. package/dist/pos_stock/.kosh/bindings.d.ts +22 -0
  137. package/dist/pos_stock/.kosh/bindings.js +15 -0
  138. package/dist/pos_stock/.kosh/schema.d.ts +2015 -0
  139. package/dist/pos_stock/index.d.ts +9 -0
  140. package/dist/pos_stock/index.js +35 -0
  141. package/dist/pos_stock/module.d.ts +11935 -0
  142. package/dist/pos_stock/module.js +115 -0
  143. package/dist/product/.kosh/bindings.js +1 -1
  144. package/dist/product/.kosh/schema.d.ts +51 -1
  145. package/dist/product/index.js +81 -4
  146. package/dist/product/module.d.ts +43 -1
  147. package/dist/product/module.js +9 -2
  148. package/dist/project/.kosh/bindings.js +1 -1
  149. package/dist/project/.kosh/schema.d.ts +54 -3
  150. package/dist/project/index.js +147 -7
  151. package/dist/project/module.d.ts +210 -2
  152. package/dist/project/module.js +52 -5
  153. package/dist/project_portal/.kosh/bindings.js +1 -1
  154. package/dist/project_portal/.kosh/schema.d.ts +54 -3
  155. package/dist/project_portal/module.d.ts +353 -4
  156. package/dist/purchase/.kosh/bindings.js +1 -1
  157. package/dist/purchase/.kosh/schema.d.ts +54 -3
  158. package/dist/purchase/index.js +163 -3
  159. package/dist/purchase/module.d.ts +215 -3
  160. package/dist/purchase/module.js +21 -3
  161. package/dist/purchase_account/.kosh/bindings.js +1 -1
  162. package/dist/purchase_account/.kosh/schema.d.ts +55 -3
  163. package/dist/purchase_account/index.js +26 -1
  164. package/dist/purchase_account/module.d.ts +393 -7
  165. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  166. package/dist/purchase_portal/.kosh/schema.d.ts +54 -3
  167. package/dist/purchase_portal/module.d.ts +366 -5
  168. package/dist/purchase_stock/.kosh/bindings.d.ts +22 -0
  169. package/dist/purchase_stock/.kosh/bindings.js +15 -0
  170. package/dist/purchase_stock/.kosh/schema.d.ts +1310 -0
  171. package/dist/purchase_stock/index.d.ts +7 -0
  172. package/dist/purchase_stock/index.js +91 -0
  173. package/dist/purchase_stock/module.d.ts +6763 -0
  174. package/dist/purchase_stock/module.js +144 -0
  175. package/dist/purchase_stock_account/.kosh/bindings.d.ts +22 -0
  176. package/dist/purchase_stock_account/.kosh/bindings.js +15 -0
  177. package/dist/purchase_stock_account/.kosh/schema.d.ts +1959 -0
  178. package/dist/purchase_stock_account/index.d.ts +3 -0
  179. package/dist/purchase_stock_account/index.js +24 -0
  180. package/dist/purchase_stock_account/module.d.ts +35366 -0
  181. package/dist/purchase_stock_account/module.js +162 -0
  182. package/dist/recruitment/.kosh/bindings.d.ts +22 -0
  183. package/dist/recruitment/.kosh/bindings.js +15 -0
  184. package/dist/recruitment/.kosh/schema.d.ts +990 -0
  185. package/dist/recruitment/demo.d.ts +6 -0
  186. package/dist/recruitment/demo.js +45 -0
  187. package/dist/recruitment/index.d.ts +3 -0
  188. package/dist/recruitment/index.js +385 -0
  189. package/dist/recruitment/module.d.ts +3247 -0
  190. package/dist/recruitment/module.js +193 -0
  191. package/dist/recruitment/web/HireButton.d.ts +9 -0
  192. package/dist/recruitment/web/HireButton.js +32 -0
  193. package/dist/recruitment/web/index.d.ts +11 -0
  194. package/dist/recruitment/web/index.js +16 -0
  195. package/dist/recruitment/web/messages.d.ts +2 -0
  196. package/dist/recruitment/web/messages.js +19 -0
  197. package/dist/reporting/documents.d.ts +5 -0
  198. package/dist/reporting/documents.js +28 -1
  199. package/dist/reporting/index.d.ts +1 -1
  200. package/dist/reporting/index.js +4 -2
  201. package/dist/reporting/models.js +9 -0
  202. package/dist/reporting/overviews.d.ts +16 -0
  203. package/dist/reporting/overviews.js +56 -0
  204. package/dist/reporting/routes-documents.js +1 -1
  205. package/dist/reporting/routes.js +138 -80
  206. package/dist/reporting/web/DashboardView.d.ts +11 -0
  207. package/dist/reporting/web/DashboardView.js +2 -2
  208. package/dist/reporting/web/DesignerView.js +5 -0
  209. package/dist/reporting/web/Overview.d.ts +9 -0
  210. package/dist/reporting/web/Overview.js +53 -0
  211. package/dist/reporting/web/index.d.ts +1 -0
  212. package/dist/reporting/web/index.js +4 -1
  213. package/dist/reporting/web/messages.js +9 -0
  214. package/dist/sale_account/.kosh/bindings.js +1 -1
  215. package/dist/sale_account/.kosh/schema.d.ts +59 -3
  216. package/dist/sale_account/index.js +22 -1
  217. package/dist/sale_account/module.d.ts +461 -7
  218. package/dist/sale_portal/.kosh/bindings.js +1 -1
  219. package/dist/sale_portal/.kosh/schema.d.ts +58 -3
  220. package/dist/sale_portal/module.d.ts +434 -5
  221. package/dist/sale_project/.kosh/bindings.js +1 -1
  222. package/dist/sale_project/.kosh/schema.d.ts +60 -3
  223. package/dist/sale_project/module.d.ts +476 -6
  224. package/dist/sale_stock/.kosh/bindings.d.ts +22 -0
  225. package/dist/sale_stock/.kosh/bindings.js +15 -0
  226. package/dist/sale_stock/.kosh/schema.d.ts +1228 -0
  227. package/dist/sale_stock/index.d.ts +7 -0
  228. package/dist/sale_stock/index.js +84 -0
  229. package/dist/sale_stock/module.d.ts +6791 -0
  230. package/dist/sale_stock/module.js +146 -0
  231. package/dist/sales/.kosh/bindings.js +1 -1
  232. package/dist/sales/.kosh/schema.d.ts +58 -3
  233. package/dist/sales/index.js +191 -4
  234. package/dist/sales/module.d.ts +355 -3
  235. package/dist/sales/module.js +78 -3
  236. package/dist/server/api/metadata.d.ts +5 -0
  237. package/dist/server/api/metadata.js +5 -0
  238. package/dist/server/api/rest.js +64 -1
  239. package/dist/server/api/views.js +1 -0
  240. package/dist/server/app-settings.d.ts +7 -0
  241. package/dist/server/app-settings.js +10 -0
  242. package/dist/server/auth/base.js +9 -0
  243. package/dist/server/index.d.ts +1 -0
  244. package/dist/server/index.js +1 -0
  245. package/dist/settings/app-settings.d.ts +2 -0
  246. package/dist/settings/app-settings.js +518 -0
  247. package/dist/settings/index.d.ts +1 -0
  248. package/dist/settings/index.js +15 -0
  249. package/dist/settings/people.js +29 -5
  250. package/dist/settings/web/AppSettingsPage.d.ts +11 -0
  251. package/dist/settings/web/AppSettingsPage.js +210 -0
  252. package/dist/settings/web/OverviewPage.js +16 -3
  253. package/dist/settings/web/PeoplePage.js +20 -6
  254. package/dist/settings/web/index.d.ts +1 -0
  255. package/dist/settings/web/index.js +3 -0
  256. package/dist/settings/web/messages.js +118 -2
  257. package/dist/stock/.kosh/bindings.d.ts +22 -0
  258. package/dist/stock/.kosh/bindings.js +15 -0
  259. package/dist/stock/.kosh/schema.d.ts +1147 -0
  260. package/dist/stock/demo.d.ts +7 -0
  261. package/dist/stock/demo.js +55 -0
  262. package/dist/stock/index.d.ts +3 -0
  263. package/dist/stock/index.js +674 -0
  264. package/dist/stock/module.d.ts +6330 -0
  265. package/dist/stock/module.js +1325 -0
  266. package/dist/stock/web/ScanBox.d.ts +8 -0
  267. package/dist/stock/web/ScanBox.js +90 -0
  268. package/dist/stock/web/index.d.ts +11 -0
  269. package/dist/stock/web/index.js +16 -0
  270. package/dist/stock/web/messages.d.ts +2 -0
  271. package/dist/stock/web/messages.js +25 -0
  272. package/dist/stock_account/.kosh/bindings.d.ts +22 -0
  273. package/dist/stock_account/.kosh/bindings.js +15 -0
  274. package/dist/stock_account/.kosh/schema.d.ts +1875 -0
  275. package/dist/stock_account/index.d.ts +3 -0
  276. package/dist/stock_account/index.js +130 -0
  277. package/dist/stock_account/module.d.ts +10655 -0
  278. package/dist/stock_account/module.js +339 -0
  279. package/dist/storage/attachments.d.ts +5 -0
  280. package/dist/storage/attachments.js +12 -1
  281. package/dist/storage/models.js +3 -0
  282. package/dist/storage/routes.js +13 -2
  283. package/dist/todo/.kosh/bindings.d.ts +22 -0
  284. package/dist/todo/.kosh/bindings.js +15 -0
  285. package/dist/todo/.kosh/schema.d.ts +732 -0
  286. package/dist/todo/demo.d.ts +6 -0
  287. package/dist/todo/demo.js +35 -0
  288. package/dist/todo/index.d.ts +3 -0
  289. package/dist/todo/index.js +187 -0
  290. package/dist/todo/module.d.ts +1180 -0
  291. package/dist/todo/module.js +130 -0
  292. package/dist/trade_finance/.kosh/bindings.d.ts +22 -0
  293. package/dist/trade_finance/.kosh/bindings.js +15 -0
  294. package/dist/trade_finance/.kosh/schema.d.ts +884 -0
  295. package/dist/trade_finance/demo.d.ts +6 -0
  296. package/dist/trade_finance/demo.js +63 -0
  297. package/dist/trade_finance/index.d.ts +3 -0
  298. package/dist/trade_finance/index.js +413 -0
  299. package/dist/trade_finance/module.d.ts +3779 -0
  300. package/dist/trade_finance/module.js +505 -0
  301. package/dist/trade_finance_account/.kosh/bindings.d.ts +22 -0
  302. package/dist/trade_finance_account/.kosh/bindings.js +15 -0
  303. package/dist/trade_finance_account/.kosh/schema.d.ts +1711 -0
  304. package/dist/trade_finance_account/index.d.ts +3 -0
  305. package/dist/trade_finance_account/index.js +92 -0
  306. package/dist/trade_finance_account/module.d.ts +7928 -0
  307. package/dist/trade_finance_account/module.js +244 -0
  308. package/dist/trade_finance_purchase/.kosh/bindings.d.ts +22 -0
  309. package/dist/trade_finance_purchase/.kosh/bindings.js +15 -0
  310. package/dist/trade_finance_purchase/.kosh/schema.d.ts +1153 -0
  311. package/dist/trade_finance_purchase/index.d.ts +8 -0
  312. package/dist/trade_finance_purchase/index.js +50 -0
  313. package/dist/trade_finance_purchase/module.d.ts +4252 -0
  314. package/dist/trade_finance_purchase/module.js +44 -0
  315. package/dist/trade_finance_sale/.kosh/bindings.d.ts +22 -0
  316. package/dist/trade_finance_sale/.kosh/bindings.js +15 -0
  317. package/dist/trade_finance_sale/.kosh/schema.d.ts +1071 -0
  318. package/dist/trade_finance_sale/index.d.ts +8 -0
  319. package/dist/trade_finance_sale/index.js +50 -0
  320. package/dist/trade_finance_sale/module.d.ts +4280 -0
  321. package/dist/trade_finance_sale/module.js +44 -0
  322. package/dist/ui/api.d.ts +2 -1
  323. package/dist/ui/api.js +2 -1
  324. package/dist/ui/app.js +2 -1
  325. package/dist/ui/components/AppIcon.js +23 -1
  326. package/dist/ui/components/primitives.d.ts +2 -0
  327. package/dist/ui/components/primitives.js +7 -1
  328. package/dist/ui/i18n/messages.d.ts +23 -0
  329. package/dist/ui/i18n/messages.js +38 -0
  330. package/dist/ui/index.d.ts +1 -0
  331. package/dist/ui/index.js +1 -0
  332. package/dist/ui/offline/queue.d.ts +5 -1
  333. package/dist/ui/offline/queue.js +2 -0
  334. package/dist/ui/registry.d.ts +5 -0
  335. package/dist/ui/shell/Home.js +55 -4
  336. package/dist/ui/views/FormView.js +98 -17
  337. package/dist/ui/views/KanbanView.d.ts +2 -0
  338. package/dist/ui/views/KanbanView.js +32 -3
  339. package/dist/ui/views/ListView.js +108 -11
  340. package/dist/ui/views/StatsRow.d.ts +14 -0
  341. package/dist/ui/views/StatsRow.js +88 -0
  342. package/dist/ui/views/widgets.d.ts +2 -1
  343. package/dist/ui/views/widgets.js +194 -1
  344. package/dist/views/builders.d.ts +19 -2
  345. package/dist/views/builders.js +21 -1
  346. package/dist/views/condition.d.ts +6 -0
  347. package/dist/views/condition.js +20 -0
  348. package/dist/views/documents.d.ts +8 -0
  349. package/dist/views/extend.d.ts +2 -2
  350. package/dist/views/extend.js +11 -1
  351. package/dist/views/index.d.ts +2 -2
  352. package/dist/views/index.js +2 -2
  353. package/dist/views/types.d.ts +52 -5
  354. package/dist/views/validate.js +39 -1
  355. package/dist/website/.kosh/bindings.d.ts +22 -0
  356. package/dist/website/.kosh/bindings.js +15 -0
  357. package/dist/website/.kosh/schema.d.ts +881 -0
  358. package/dist/website/demo.d.ts +6 -0
  359. package/dist/website/demo.js +115 -0
  360. package/dist/website/index.d.ts +9 -0
  361. package/dist/website/index.js +250 -0
  362. package/dist/website/module.d.ts +2987 -0
  363. package/dist/website/module.js +281 -0
  364. package/dist/website/render.d.ts +45 -0
  365. package/dist/website/render.js +102 -0
  366. package/dist/website/routes.d.ts +12 -0
  367. package/dist/website/routes.js +246 -0
  368. package/dist/website/web/PagePreview.d.ts +7 -0
  369. package/dist/website/web/PagePreview.js +33 -0
  370. package/dist/website/web/index.d.ts +11 -0
  371. package/dist/website/web/index.js +16 -0
  372. package/dist/website/web/messages.d.ts +2 -0
  373. package/dist/website/web/messages.js +15 -0
  374. package/dist/website_crm/.kosh/bindings.d.ts +22 -0
  375. package/dist/website_crm/.kosh/bindings.js +15 -0
  376. package/dist/website_crm/.kosh/schema.d.ts +966 -0
  377. package/dist/website_crm/index.d.ts +3 -0
  378. package/dist/website_crm/index.js +39 -0
  379. package/dist/website_crm/module.d.ts +2955 -0
  380. package/dist/website_crm/module.js +44 -0
  381. package/package.json +118 -1
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The data layer of the Sales ↔ Stockroom bridge (RFC 0021): a confirmed sales order gets its delivery — one
3
+ * Stockroom transfer with a line per goods line, in the line's unit, from the company's main warehouse to the
4
+ * customers — made once through the outbox (`sale.order.confirmed`) and confirmed, so its stock is reserved. The
5
+ * order shows its deliveries and what is delivered; each line, its delivered quantity.
6
+ */
7
+ import { productShutro } from '../product/index.js';
8
+ import { salesShutro } from '../sales/index.js';
9
+ import { orm } from '../server/orm.js';
10
+ import { defaultLocations, stockShutro } from '../stock/index.js';
11
+ import { compute, defineModule, extendModel, f } from './.kosh/bindings.js';
12
+ const zero = orm.toDecimal(0);
13
+ export const PickingSale = extendModel('stock.picking', {
14
+ fields: { saleOrder: f.many2one('sale.order', { ondelete: 'set null', label: 'Sales order' }) },
15
+ overrides: {
16
+ // A backorder stays with its order.
17
+ backorderValues: async function (next) {
18
+ const out = await next();
19
+ for (const p of await this.load({}))
20
+ out.set(p.id, { ...out.get(p.id), saleOrder: p.saleOrder?.id ?? null });
21
+ return out;
22
+ },
23
+ },
24
+ });
25
+ export const MoveSale = extendModel('stock.move', {
26
+ fields: {
27
+ saleLine: f.many2one('sale.order.line', { ondelete: 'set null', label: 'Order line' }),
28
+ },
29
+ overrides: {
30
+ // The rest of a line stays tied to its order line.
31
+ backorderValues: async function (next) {
32
+ const out = await next();
33
+ for (const m of await this.load({}))
34
+ out.set(m.id, { ...out.get(m.id), saleLine: m.saleLine?.id ?? null });
35
+ return out;
36
+ },
37
+ },
38
+ });
39
+ export const LineDelivered = extendModel('sale.order.line', {
40
+ fields: {
41
+ moves: f.one2many('stock.move', 'saleLine'),
42
+ /** Delivered by validated deliveries, in the line's unit. */
43
+ qtyDelivered: f.decimal({
44
+ scale: 3,
45
+ store: true,
46
+ label: 'Delivered',
47
+ ...compute('sale.order.line', 'qtyDelivered', ['moves.quantity', 'moves.quantityDone', 'moves.picking.state'], (lines) => {
48
+ for (const l of lines)
49
+ l.qtyDelivered = [...l.moves]
50
+ .filter((m) => m.picking.state === 'done')
51
+ .reduce((s, m) => s.plus(m.quantityDone ?? m.quantity), zero);
52
+ }),
53
+ }),
54
+ },
55
+ });
56
+ export const OrderDeliveries = extendModel('sale.order', {
57
+ fields: {
58
+ deliveries: f.one2many('stock.picking', 'saleOrder'),
59
+ deliveryState: f.selection({
60
+ nothing: 'Nothing to deliver',
61
+ to_deliver: 'To deliver',
62
+ partial: 'Partly delivered',
63
+ delivered: 'Delivered',
64
+ }, {
65
+ store: true,
66
+ label: 'Delivery',
67
+ ...compute('sale.order', 'deliveryState', ['state', 'lines.quantity', 'lines.qtyDelivered', 'lines.product.kind'], (orders) => {
68
+ for (const o of orders) {
69
+ const goods = [...o.lines].filter((l) => l.product.kind === 'goods');
70
+ if (goods.length === 0 || (o.state !== 'sale' && o.state !== 'done')) {
71
+ o.deliveryState = 'nothing';
72
+ continue;
73
+ }
74
+ const done = goods.every((l) => (l.qtyDelivered ?? zero).gte(l.quantity));
75
+ const some = goods.some((l) => (l.qtyDelivered ?? zero).gt(0));
76
+ o.deliveryState = done ? 'delivered' : some ? 'partial' : 'to_deliver';
77
+ }
78
+ }),
79
+ }),
80
+ },
81
+ });
82
+ /**
83
+ * Makes and confirms the delivery of a confirmed order's goods lines, once (an order with a delivery that is not
84
+ * cancelled gets no other). Runs in an audited superuser scope: the salesperson who confirmed needs no Stockroom
85
+ * rights for the warehouse to get its delivery. Returns the delivery, or null when there is nothing to deliver.
86
+ */
87
+ export async function createDeliveryFor(env, orderId) {
88
+ return env.asSuperuser({ reason: 'delivery for a confirmed sales order' }, async (su) => {
89
+ const [order] = await su
90
+ .model('sale.order')
91
+ .browse([orm.asId(orderId, 'sale.order')])
92
+ .load({
93
+ lines: { product: true, uom: true },
94
+ partner: true,
95
+ company: true,
96
+ deliveries: true,
97
+ });
98
+ if (order === undefined || (order.state !== 'sale' && order.state !== 'done'))
99
+ return null;
100
+ if ([...order.deliveries].some((d) => d.state !== 'cancel'))
101
+ return null;
102
+ const goods = order.lines.filter((l) => l.product.kind === 'goods');
103
+ if (goods.length === 0)
104
+ return null;
105
+ const places = await defaultLocations(su, 'delivery', null, order.company.id);
106
+ if (places.source === null || places.destination === null)
107
+ throw new orm.ValidationError('sale.order', ['state'], `The company of ${order.name} has no warehouse to deliver from.`, 'Add a warehouse in Stockroom › Configuration › Warehouses.');
108
+ const made = await su.model('stock.picking').create([
109
+ {
110
+ kind: 'delivery',
111
+ partner: order.partner.id,
112
+ company: order.company.id,
113
+ warehouse: places.warehouse,
114
+ source: places.source,
115
+ destination: places.destination,
116
+ origin: order.name,
117
+ saleOrder: order.id,
118
+ moves: {
119
+ create: goods.map((l) => ({
120
+ product: l.product.id,
121
+ name: l.name,
122
+ quantity: l.quantity,
123
+ uom: l.uom?.id ?? null,
124
+ saleLine: l.id,
125
+ })),
126
+ },
127
+ },
128
+ ]);
129
+ await made.confirm();
130
+ return made.ids[0] ?? null;
131
+ });
132
+ }
133
+ export const saleStockShutro = defineModule({
134
+ name: 'sale_stock',
135
+ version: '0.1.0',
136
+ depends: [salesShutro, stockShutro, productShutro],
137
+ groups: [
138
+ orm.defineGroup({
139
+ code: 'sale_stock.user',
140
+ label: 'Sales with deliveries',
141
+ implies: ['sales.user', 'stock.user'],
142
+ }),
143
+ ],
144
+ models: [],
145
+ extensions: [PickingSale, MoveSale, LineDelivered, OrderDeliveries],
146
+ });
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 680b65ef8ba5f90d
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 7a0589ddfebd3ce9
2
2
  // Bindings of module `sales`: import the definition API from here to get types scoped to what it can see.
3
3
  export { defineModel, defineModule, extendModel, f, replace } from '../../server/orm.js';
4
4
  import { definitions } from '../../server/orm.js';
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 680b65ef8ba5f90d
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 7a0589ddfebd3ce9
2
2
  // Schema view of module `sales` (RFC 0002 §7.7): the models it owns or depends on, with the extensions it can see.
3
3
  import type { F } from '../../server/orm.js';
4
4
 
@@ -14,6 +14,7 @@ export interface SchemaView {
14
14
  readonly 'auth.user_group': AuthUserGroup;
15
15
  readonly 'kosh.image': KoshImage;
16
16
  readonly 'kosh.saved_view': KoshSavedView;
17
+ readonly 'kosh.setting': KoshSetting;
17
18
  readonly 'mail.activity': MailActivity;
18
19
  readonly 'mail.activity.type': MailActivityType;
19
20
  readonly 'mail.channel': MailChannel;
@@ -30,6 +31,7 @@ export interface SchemaView {
30
31
  readonly 'res.company': ResCompany;
31
32
  readonly 'res.currency': ResCurrency;
32
33
  readonly 'res.partner': ResPartner;
34
+ readonly 'res.partner.tag': ResPartnerTag;
33
35
  readonly 'res.users': ResUsers;
34
36
  readonly 'sale.order': SaleOrder;
35
37
  readonly 'sale.order.line': SaleOrderLine;
@@ -281,6 +283,26 @@ export interface KoshSavedView {
281
283
  export interface KoshSavedViewMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'kosh.saved_view'> {
282
284
  }
283
285
 
286
+ /** `kosh.setting` (owner: base). */
287
+ export interface KoshSetting {
288
+ readonly name: 'kosh.setting';
289
+ readonly fields: {
290
+ readonly id: F.Id<'kosh.setting'>;
291
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
292
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
293
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
294
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
295
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
296
+ readonly key: F.Char<{ readonly required: true }>;
297
+ readonly value: F.Json;
298
+ };
299
+ readonly methods: KoshSettingMethods;
300
+ }
301
+
302
+ /** Methods of `kosh.setting`: declared ones and transitions. */
303
+ export interface KoshSettingMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'kosh.setting'> {
304
+ }
305
+
284
306
  /** `mail.activity` (owner: messaging). */
285
307
  export interface MailActivity {
286
308
  readonly name: 'mail.activity';
@@ -595,6 +617,7 @@ export interface ProductProduct {
595
617
  readonly category: F.Many2one<'product.category'>;
596
618
  readonly uom: F.Many2one<'uom.uom'>;
597
619
  readonly barcode: F.Char;
620
+ readonly image: F.Many2one<'storage.attachment'>;
598
621
  readonly listPrice: F.Decimal<{ readonly required: true; readonly default: true }>;
599
622
  readonly standardPrice: F.Decimal<{ readonly required: true; readonly default: true }>;
600
623
  readonly saleOk: F.Bool<{ readonly required: true; readonly default: true }>;
@@ -612,7 +635,7 @@ export interface ProductProduct {
612
635
  export interface ProductProductMethods extends F.Methods<typeof import('../../product/index.js').productShutro, 'product.product'> {
613
636
  }
614
637
 
615
- /** `res.company` (owner: base). */
638
+ /** `res.company` (owner: base; extended by: contacts, sales). */
616
639
  export interface ResCompany {
617
640
  readonly name: 'res.company';
618
641
  readonly fields: {
@@ -632,12 +655,16 @@ export interface ResCompany {
632
655
  readonly email: F.Char;
633
656
  readonly phone: F.Char;
634
657
  readonly website: F.Char;
658
+ readonly contactTaxIdRequired: F.Bool<{ readonly required: true; readonly default: true }>; // contacts
659
+ readonly contactUniqueEmail: F.Bool<{ readonly required: true; readonly default: true }>; // contacts
660
+ readonly saleValidityDays: F.Integer<{ readonly default: true }>; // sales
661
+ readonly saleTerms: F.Text; // sales
635
662
  };
636
663
  readonly methods: ResCompanyMethods;
637
664
  }
638
665
 
639
666
  /** Methods of `res.company`: declared ones and transitions. */
640
- export interface ResCompanyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'res.company'> {
667
+ export interface ResCompanyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'res.company'>, F.Methods<typeof import('../../contacts/index.js').contactsShutro, 'res.company'>, F.Methods<typeof import('../module.js').salesShutro, 'res.company'> {
641
668
  }
642
669
 
643
670
  /** `res.currency` (owner: base). */
@@ -674,6 +701,10 @@ export interface ResPartner {
674
701
  readonly active: F.Bool<{ readonly required: true; readonly default: true }>;
675
702
  readonly company: F.Many2one<'res.company', { readonly default: true }>;
676
703
  readonly name: F.Char<{ readonly required: true }>;
704
+ readonly image: F.Many2one<'storage.attachment'>;
705
+ readonly tags: F.Many2many<'res.partner.tag', { readonly required: true }>;
706
+ readonly latitude: F.Decimal;
707
+ readonly longitude: F.Decimal;
677
708
  readonly isCompany: F.Bool<{ readonly required: true; readonly default: true }>;
678
709
  readonly parent: F.Many2one<'res.partner'>;
679
710
  readonly contacts: F.One2many<'res.partner', { readonly required: true }>;
@@ -699,6 +730,25 @@ export interface ResPartner {
699
730
  export interface ResPartnerMethods extends F.Methods<typeof import('../../contacts/index.js').contactsShutro, 'res.partner'> {
700
731
  }
701
732
 
733
+ /** `res.partner.tag` (owner: contacts). */
734
+ export interface ResPartnerTag {
735
+ readonly name: 'res.partner.tag';
736
+ readonly fields: {
737
+ readonly id: F.Id<'res.partner.tag'>;
738
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
739
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
740
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
741
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
742
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
743
+ readonly name: F.Char<{ readonly required: true }>;
744
+ };
745
+ readonly methods: ResPartnerTagMethods;
746
+ }
747
+
748
+ /** Methods of `res.partner.tag`: declared ones and transitions. */
749
+ export interface ResPartnerTagMethods extends F.Methods<typeof import('../../contacts/index.js').contactsShutro, 'res.partner.tag'> {
750
+ }
751
+
702
752
  /** `res.users` (owner: base). */
703
753
  export interface ResUsers {
704
754
  readonly name: 'res.users';
@@ -750,6 +800,8 @@ export interface SaleOrder {
750
800
  readonly amountUntaxed: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
751
801
  readonly amountTax: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
752
802
  readonly amountTotal: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
803
+ readonly margin: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
804
+ readonly marginPercent: F.Decimal<{ readonly readonly: true; readonly compute: true; readonly store: true }>;
753
805
  readonly state: F.State<'draft' | 'sent' | 'sale' | 'done' | 'cancel', { readonly required: true; readonly readonly: true; readonly default: true }>;
754
806
  };
755
807
  readonly methods: SaleOrderMethods;
@@ -786,6 +838,7 @@ export interface SaleOrderLine {
786
838
  readonly tax: F.Many2one<'account.tax', { readonly readonly: true }>;
787
839
  readonly currency: F.Many2one<'res.currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
788
840
  readonly subtotal: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
841
+ readonly margin: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
789
842
  readonly taxAmount: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
790
843
  };
791
844
  readonly methods: SaleOrderLineMethods;
@@ -812,6 +865,8 @@ export interface StorageAttachment {
812
865
  readonly key: F.Char<{ readonly required: true }>;
813
866
  readonly resModel: F.Char<{ readonly required: true }>;
814
867
  readonly resId: F.Integer<{ readonly required: true }>;
868
+ readonly variantOf: F.Many2one<'storage.attachment'>;
869
+ readonly width: F.Integer;
815
870
  };
816
871
  readonly methods: StorageAttachmentMethods;
817
872
  }
@@ -8,7 +8,8 @@ import { contacts } from '../contacts/index.js';
8
8
  import { messaging } from '../messaging/index.js';
9
9
  import { defineModule } from '../modules-sdk/index.js';
10
10
  import { product } from '../product/index.js';
11
- import { action, form, kanban, list, search } from '../views/index.js';
11
+ import { reporting } from '../reporting/index.js';
12
+ import { action, calendar, form, kanban, list, search, stat } from '../views/index.js';
12
13
  import { salesDemo } from './demo.js';
13
14
  import { SaleOrder, SaleOrderLine, salesShutro } from './module.js';
14
15
  export { SaleOrder, SaleOrderLine, salesShutro } from './module.js';
@@ -16,7 +17,7 @@ export const sales = defineModule({
16
17
  shutro: salesShutro,
17
18
  kosh: '^0.1.0',
18
19
  hooks: { demo: salesDemo },
19
- depends: [contacts, product, messaging],
20
+ depends: [contacts, product, messaging, reporting],
20
21
  // What webhooks may subscribe to besides record changes (RFC 0017 §5.3).
21
22
  events: [{ topic: 'sale.order.confirmed', model: 'sale.order', label: 'Sales order confirmed' }],
22
23
  app: {
@@ -31,6 +32,31 @@ export const sales = defineModule({
31
32
  user: { label: 'Salesperson (own orders)', groups: ['sales.user'] },
32
33
  manager: { label: 'Manager (all orders)', groups: ['sales.manager'], implies: ['user'] },
33
34
  },
35
+ // Settings › App settings › Sales (RFC 0020): per company, changed by sales managers.
36
+ settings: {
37
+ sections: [
38
+ {
39
+ key: 'quotations',
40
+ label: 'Quotations',
41
+ description: 'How long new quotations stay valid, and the terms they start with.',
42
+ keywords: 'quote validity expiry terms conditions',
43
+ scope: 'company',
44
+ editors: ['sales.manager'],
45
+ fields: [
46
+ {
47
+ key: 'saleValidityDays',
48
+ min: 0,
49
+ max: 365,
50
+ help: 'A new quotation is valid until its date plus these days; 0 leaves it open.',
51
+ },
52
+ {
53
+ key: 'saleTerms',
54
+ help: 'Filled in on new quotations; each can still be changed.',
55
+ },
56
+ ],
57
+ },
58
+ ],
59
+ },
34
60
  // The printed quotation (RFC 0005), installed as a template designers can change; the portal prints it too.
35
61
  documents: [
36
62
  {
@@ -97,10 +123,157 @@ export const sales = defineModule({
97
123
  },
98
124
  },
99
125
  ],
126
+ // The Overview page (RFC 0022 §3.2): this month against the last, the year's curve, who buys, what to chase.
127
+ overview: {
128
+ cards: [
129
+ {
130
+ id: 'sales_month',
131
+ title: 'Sales this month',
132
+ kind: 'kpi',
133
+ model: 'sale.order',
134
+ measure: 'sum:amountTotal',
135
+ filter: { state: { in: ['sale', 'done'] } },
136
+ comparison: { field: 'dateOrder', mode: 'previous' },
137
+ w: 3,
138
+ },
139
+ {
140
+ id: 'orders_month',
141
+ title: 'Orders this month',
142
+ kind: 'kpi',
143
+ model: 'sale.order',
144
+ measure: 'count',
145
+ filter: { state: { in: ['sale', 'done'] } },
146
+ comparison: { field: 'dateOrder', mode: 'previous' },
147
+ w: 3,
148
+ },
149
+ {
150
+ id: 'open_quotations',
151
+ title: 'Open quotations',
152
+ kind: 'kpi',
153
+ model: 'sale.order',
154
+ measure: 'count',
155
+ filter: { state: { in: ['draft', 'sent'] } },
156
+ w: 3,
157
+ },
158
+ {
159
+ id: 'average_order',
160
+ title: 'Average order',
161
+ kind: 'kpi',
162
+ model: 'sale.order',
163
+ measure: 'avg:amountTotal',
164
+ filter: { state: { in: ['sale', 'done'] } },
165
+ w: 3,
166
+ },
167
+ {
168
+ id: 'margin_month',
169
+ title: 'Margin this month',
170
+ kind: 'kpi',
171
+ model: 'sale.order',
172
+ measure: 'sum:margin',
173
+ filter: { state: { in: ['sale', 'done'] } },
174
+ comparison: { field: 'dateOrder', mode: 'previous' },
175
+ w: 3,
176
+ },
177
+ {
178
+ id: 'sales_by_month',
179
+ title: 'Sales by month',
180
+ kind: 'chart',
181
+ model: 'sale.order',
182
+ measure: 'sum:amountTotal',
183
+ groupBy: 'dateOrder:month',
184
+ filter: { state: { in: ['sale', 'done'] } },
185
+ chart: 'bar',
186
+ w: 6,
187
+ },
188
+ {
189
+ id: 'top_customers',
190
+ title: 'Top customers',
191
+ kind: 'pivot',
192
+ model: 'sale.order',
193
+ measure: 'sum:amountTotal',
194
+ groupBy: 'partner',
195
+ filter: { state: { in: ['sale', 'done'] } },
196
+ w: 6,
197
+ },
198
+ {
199
+ id: 'by_salesperson',
200
+ title: 'By salesperson',
201
+ kind: 'chart',
202
+ model: 'sale.order',
203
+ measure: 'sum:amountTotal',
204
+ groupBy: 'user',
205
+ filter: { state: { in: ['sale', 'done'] } },
206
+ chart: 'pie',
207
+ w: 6,
208
+ },
209
+ {
210
+ id: 'quotations_to_chase',
211
+ title: 'Biggest open quotations',
212
+ kind: 'list',
213
+ model: 'sale.order',
214
+ measure: 'sum:amountTotal',
215
+ filter: { state: { in: ['draft', 'sent'] } },
216
+ w: 6,
217
+ },
218
+ ],
219
+ },
220
+ viewExtensions: [
221
+ {
222
+ view: 'product.product.form',
223
+ operations: [
224
+ {
225
+ append: 'related',
226
+ nodes: [
227
+ stat({
228
+ id: 'product_sold',
229
+ label: 'Sold',
230
+ model: SaleOrderLine,
231
+ where: { product: { $record: 'id' }, 'order.state': { in: ['sale', 'done'] } },
232
+ measure: { sum: 'quantity' },
233
+ }),
234
+ stat({
235
+ id: 'product_revenue',
236
+ label: 'Sales',
237
+ model: SaleOrderLine,
238
+ where: { product: { $record: 'id' }, 'order.state': { in: ['sale', 'done'] } },
239
+ measure: { sum: 'subtotal' },
240
+ tone: 'success',
241
+ }),
242
+ ],
243
+ },
244
+ ],
245
+ },
246
+ {
247
+ view: 'res.partner.form',
248
+ operations: [
249
+ {
250
+ append: 'related',
251
+ nodes: [
252
+ stat({
253
+ id: 'sale_orders',
254
+ label: 'Sales orders',
255
+ model: SaleOrder,
256
+ where: { partner: { $record: 'id' }, state: { in: ['sale', 'done'] } },
257
+ action: 'sales.action_orders',
258
+ }),
259
+ stat({
260
+ id: 'sale_revenue',
261
+ label: 'Sales',
262
+ model: SaleOrder,
263
+ where: { partner: { $record: 'id' }, state: { in: ['sale', 'done'] } },
264
+ measure: { sum: 'amountTotal' },
265
+ action: 'sales.action_orders',
266
+ tone: 'success',
267
+ }),
268
+ ],
269
+ },
270
+ ],
271
+ },
272
+ ],
100
273
  actions: {
101
274
  'sales.action_quotations': action(SaleOrder, {
102
275
  label: 'Quotations',
103
- views: ['list', 'kanban', 'form'],
276
+ views: ['list', 'kanban', 'calendar', 'form'],
104
277
  defaultFilters: ['quotations'],
105
278
  }),
106
279
  'sales.action_orders': action(SaleOrder, {
@@ -147,9 +320,12 @@ export const sales = defineModule({
147
320
  'partner',
148
321
  'user',
149
322
  { field: 'amountTotal', sum: true },
323
+ { field: 'margin', sum: true },
324
+ { field: 'marginPercent', widget: 'percentage' },
150
325
  { field: 'state', widget: 'badge' },
151
326
  ],
152
327
  defaultOrder: ['dateOrder desc'],
328
+ decorations: { success: { state: 'done' }, neutral: { state: 'cancel' } },
153
329
  }),
154
330
  'sale.order.kanban': kanban(SaleOrder, {
155
331
  groupBy: 'state',
@@ -168,6 +344,8 @@ export const sales = defineModule({
168
344
  ],
169
345
  },
170
346
  sheet: [
347
+ // Invoices, deliveries, tasks: the bridges append their tiles (RFC 0022 §3.1).
348
+ k.stats('related', []),
171
349
  k.group('customer', ['partner', 'clientReference', 'currency']),
172
350
  k.group('dates', ['name', 'dateOrder', 'validityDate', 'user']),
173
351
  k.lines('lines', [
@@ -180,12 +358,21 @@ export const sales = defineModule({
180
358
  'taxes',
181
359
  'subtotal',
182
360
  ], { readonlyWhen: { state: { in: ['done', 'cancel'] } } }),
183
- k.group('totals', ['amountUntaxed', 'amountTax', 'amountTotal'], { label: 'Totals' }),
361
+ k.group('totals', ['amountUntaxed', 'amountTax', 'amountTotal', 'margin', 'marginPercent'], {
362
+ label: 'Totals',
363
+ }),
184
364
  k.group('terms', ['note'], { columns: 1 }),
185
365
  ],
186
366
  readonlyWhen: { state: { in: ['done', 'cancel'] } },
187
367
  chatter: true,
188
368
  })),
369
+ // Quotations by the day they expire: what to chase this week.
370
+ 'sale.order.calendar': calendar(SaleOrder, {
371
+ dateStart: 'validityDate',
372
+ title: 'name',
373
+ color: 'state',
374
+ mode: 'month',
375
+ }),
189
376
  'sale.order.line.list': list(SaleOrderLine, {
190
377
  columns: ['order', 'product', 'quantity', 'priceUnit', 'subtotal'],
191
378
  }),