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
@@ -6,9 +6,11 @@
6
6
  * - `POST /api/v1/models/:model/export/xlsx` a list as a formatted XLSX (needs `export`; audited)
7
7
  */
8
8
  import { audit, HttpProblem, publicApiModels, } from '../server/index.js';
9
+ import { renderPdf } from './documents.js';
9
10
  import { ReportError, runReport, } from './pivot.js';
10
11
  import { moneyFormat, workbook } from './xlsx.js';
11
12
  const LIST_XLSX_LIMIT = 100_000;
13
+ const LIST_PDF_LIMIT = 2000;
12
14
  const search = (env, model, query) => env.model(model).search(query);
13
15
  function bad(detail) {
14
16
  return new HttpProblem(400, 'invalid-report', 'Invalid report', detail);
@@ -220,91 +222,34 @@ export function registerReportRoutes(context) {
220
222
  const limit = raw['limit'] === undefined ? LIST_XLSX_LIMIT : Number(raw['limit']);
221
223
  if (!Number.isInteger(limit) || limit < 1 || limit > LIST_XLSX_LIMIT)
222
224
  throw bad(`limit must be 1–${LIST_XLSX_LIMIT}.`);
223
- const sheet = await request.env(async (env) => {
224
- env.access.check(model.name, 'export');
225
- const fields = (keys.length > 0
226
- ? keys
227
- : model.fields
228
- .filter((f) => f.stored &&
229
- f.kind !== 'binary' &&
230
- f.kind !== 'one2many' &&
231
- f.kind !== 'many2many')
232
- .map((f) => f.key))
233
- .map((k) => model.byKey.get(k))
234
- .filter((f) => f !== undefined && env.access.canField(model.name, f.key, 'read'));
235
- const found = await search(env, model.name, {
236
- ...(where === undefined ? {} : { where }),
237
- order: ['id asc'],
238
- limit,
239
- });
240
- const loaded = found.length === 0
241
- ? []
242
- : (await found.load(['id', ...fields.map((f) => f?.key)])).read(fields.map((f) => f?.key));
243
- // many2one columns show names, not ids.
244
- const names = new Map();
245
- for (const f of fields)
246
- if (f?.kind === 'many2one' && f.target !== null) {
247
- const ids = [
248
- ...new Set(loaded.map((r) => r[f.key]).filter((v) => typeof v === 'number')),
249
- ];
250
- names.set(f.key, ids.length === 0 ? new Map() : await displayNames(env, f.target, ids));
251
- }
252
- await audit(env, {
253
- kind: 'data.export',
254
- summary: `Exported ${loaded.length} ${model.name} record(s) as xlsx.`,
255
- targetModel: model.name,
256
- details: { fields: fields.map((f) => f?.key), count: loaded.length, filter: where ?? null },
257
- ip: request.ip,
258
- });
259
- const header = fields.map((f) => f?.label ?? '');
260
- const body = loaded.map((r) => fields.map((f) => {
261
- if (f === undefined)
262
- return null;
263
- const v = r[f.key];
264
- if (v === null || v === undefined)
265
- return null;
266
- switch (f.kind) {
267
- case 'many2one':
268
- return names.get(f.key)?.get(v) ?? `#${String(v)}`;
269
- case 'decimal':
270
- case 'float':
271
- return { decimal: String(v) };
272
- case 'monetary': {
273
- const m = (typeof v.toJSON === 'function'
274
- ? v.toJSON()
275
- : v);
276
- return {
277
- decimal: String(m.amount ?? v),
278
- format: moneyFormat(String(m.currency ?? '')),
279
- };
280
- }
281
- case 'date':
282
- return new Date(`${String(v).slice(0, 10)}T00:00:00Z`);
283
- case 'datetime':
284
- return v instanceof Date ? v : new Date(String(v));
285
- case 'selection':
286
- case 'state':
287
- return f.options?.find(([value]) => value === v)?.[1] ?? String(v);
288
- case 'boolean':
289
- return v === true;
290
- case 'integer':
291
- case 'bigint':
292
- return Number(v);
293
- default:
294
- return String(v);
295
- }
296
- }));
297
- return {
298
- name: model.label,
299
- rows: [header, ...body],
300
- widths: fields.map((f) => Math.min(48, Math.max(10, (f?.label.length ?? 10) + 4))),
301
- };
302
- });
225
+ const sheet = await request.env((env) => listSheet(env, model, keys, where, limit, request.ip, 'xlsx'));
303
226
  await reply
304
227
  .type('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
305
228
  .header('content-disposition', `attachment; filename="${model.name}.xlsx"`)
306
229
  .send(workbook([sheet]));
307
230
  });
231
+ // RFC 0022 §3.5: a list printed as PDF — the columns shown, the filter, up to LIST_PDF_LIMIT rows.
232
+ app.post('/api/v1/models/:model/export/pdf', async (request, reply) => {
233
+ const model = modelOf(request.params);
234
+ const raw = bodyOf(request);
235
+ const keys = strings(raw['fields'], 'fields');
236
+ const where = raw['filter'];
237
+ const limit = raw['limit'] === undefined ? LIST_PDF_LIMIT : Number(raw['limit']);
238
+ if (!Number.isInteger(limit) || limit < 1 || limit > LIST_PDF_LIMIT)
239
+ throw bad(`limit must be 1–${LIST_PDF_LIMIT}.`);
240
+ const sheet = await request.env((env) => listSheet(env, model, keys, where, limit, request.ip, 'pdf'));
241
+ const lang = String(request.headers['accept-language'] ?? 'en').slice(0, 2);
242
+ const html = listHtml(sheet, { lang, dir: lang === 'ar' || lang === 'he' ? 'rtl' : 'ltr' });
243
+ const pdf = await renderPdf(html, {
244
+ paper: 'A4',
245
+ orientation: (sheet.rows[0]?.length ?? 0) > 6 ? 'landscape' : 'portrait',
246
+ blocks: [],
247
+ });
248
+ await reply
249
+ .type('application/pdf')
250
+ .header('content-disposition', `attachment; filename="${model.name}.pdf"`)
251
+ .send(pdf);
252
+ });
308
253
  }
309
254
  function csvCell(value) {
310
255
  let t = value;
@@ -312,3 +257,116 @@ function csvCell(value) {
312
257
  t = `'${t}`;
313
258
  return /[",\r\n]/.test(t) ? `"${t.replaceAll('"', '""')}"` : t;
314
259
  }
260
+ /** The rows of a list for an export: the columns the user may read, many2one names, typed cells; audited. */
261
+ async function listSheet(env, model, keys, where, limit, ip, format) {
262
+ env.access.check(model.name, 'export');
263
+ const fields = (keys.length > 0
264
+ ? keys
265
+ : model.fields
266
+ .filter((f) => f.stored && f.kind !== 'binary' && f.kind !== 'one2many' && f.kind !== 'many2many')
267
+ .map((f) => f.key))
268
+ .map((k) => model.byKey.get(k))
269
+ .filter((f) => f !== undefined && env.access.canField(model.name, f.key, 'read'));
270
+ const found = await search(env, model.name, {
271
+ ...(where === undefined ? {} : { where }),
272
+ order: ['id asc'],
273
+ limit,
274
+ });
275
+ const loaded = found.length === 0
276
+ ? []
277
+ : (await found.load(['id', ...fields.map((f) => f?.key)])).read(fields.map((f) => f?.key));
278
+ // many2one columns show names, not ids.
279
+ const names = new Map();
280
+ for (const f of fields)
281
+ if (f?.kind === 'many2one' && f.target !== null) {
282
+ const ids = [
283
+ ...new Set(loaded.map((r) => r[f.key]).filter((v) => typeof v === 'number')),
284
+ ];
285
+ names.set(f.key, ids.length === 0 ? new Map() : await displayNames(env, f.target, ids));
286
+ }
287
+ await audit(env, {
288
+ kind: 'data.export',
289
+ summary: `Exported ${loaded.length} ${model.name} record(s) as ${format}.`,
290
+ targetModel: model.name,
291
+ details: { fields: fields.map((f) => f?.key), count: loaded.length, filter: where ?? null },
292
+ ip,
293
+ });
294
+ const header = fields.map((f) => f?.label ?? '');
295
+ const body = loaded.map((r) => fields.map((f) => {
296
+ if (f === undefined)
297
+ return null;
298
+ const v = r[f.key];
299
+ if (v === null || v === undefined)
300
+ return null;
301
+ switch (f.kind) {
302
+ case 'many2one':
303
+ return names.get(f.key)?.get(v) ?? `#${String(v)}`;
304
+ case 'decimal':
305
+ case 'float':
306
+ return { decimal: String(v) };
307
+ case 'monetary': {
308
+ const m = (typeof v.toJSON === 'function'
309
+ ? v.toJSON()
310
+ : v);
311
+ return {
312
+ decimal: String(m.amount ?? v),
313
+ format: moneyFormat(String(m.currency ?? '')),
314
+ };
315
+ }
316
+ case 'date':
317
+ return new Date(`${String(v).slice(0, 10)}T00:00:00Z`);
318
+ case 'datetime':
319
+ return v instanceof Date ? v : new Date(String(v));
320
+ case 'selection':
321
+ case 'state':
322
+ return f.options?.find(([value]) => value === v)?.[1] ?? String(v);
323
+ case 'boolean':
324
+ return v === true;
325
+ case 'integer':
326
+ case 'bigint':
327
+ return Number(v);
328
+ default:
329
+ return String(v);
330
+ }
331
+ }));
332
+ return {
333
+ name: model.label,
334
+ rows: [header, ...body],
335
+ widths: fields.map((f) => Math.min(48, Math.max(10, (f?.label.length ?? 10) + 4))),
336
+ };
337
+ }
338
+ const escapeHtml = (text) => text.replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[c] ?? c);
339
+ /** A cell as text on paper. */
340
+ function cellText(cell) {
341
+ if (cell === null)
342
+ return '';
343
+ if (cell instanceof Date)
344
+ return cell.toISOString().slice(0, 10);
345
+ if (typeof cell === 'boolean')
346
+ return cell ? '✓' : '';
347
+ if (typeof cell === 'object')
348
+ return 'decimal' in cell ? cell.decimal : cell.text;
349
+ return String(cell);
350
+ }
351
+ /** A list as a printable page: the title, the count, a table with the header on every page. */
352
+ function listHtml(sheet, options) {
353
+ const [header = [], ...body] = sheet.rows;
354
+ const numeric = header.map((_, i) => body.every((row) => {
355
+ const c = row[i];
356
+ return (c === null ||
357
+ c === undefined ||
358
+ typeof c === 'number' ||
359
+ (typeof c === 'object' && 'decimal' in c));
360
+ }));
361
+ const th = header.map((h) => `<th>${escapeHtml(cellText(h))}</th>`).join('');
362
+ const tr = body
363
+ .map((row) => `<tr>${row.map((c, i) => `<td${numeric[i] ? ' class="n"' : ''}>${escapeHtml(cellText(c))}</td>`).join('')}</tr>`)
364
+ .join('\n');
365
+ return `<!doctype html><html lang="${escapeHtml(options.lang)}" dir="${options.dir}"><head><meta charset="utf-8"><title>${escapeHtml(sheet.name)}</title><style>
366
+ body{font:10pt/1.4 "Inter","Noto Sans Bengali",system-ui,sans-serif;color:#111;margin:0}
367
+ h1{font-size:14pt;margin:0 0 2mm}p{margin:0 0 4mm;color:#555}
368
+ table{width:100%;border-collapse:collapse}thead{display:table-header-group}
369
+ th,td{padding:1.2mm 2mm;border-bottom:0.2mm solid #ccc;text-align:start;vertical-align:top}
370
+ th{font-weight:600;background:#f3f4f6}td.n,th.n{text-align:end;font-variant-numeric:tabular-nums}
371
+ tr{break-inside:avoid}</style></head><body><h1>${escapeHtml(sheet.name)}</h1><p>${body.length}</p><table><thead><tr>${th}</tr></thead><tbody>${tr}</tbody></table></body></html>`;
372
+ }
@@ -20,4 +20,15 @@ export interface Card {
20
20
  readonly w: 3 | 4 | 6 | 12;
21
21
  }
22
22
  export declare function measureOf(text: string): MeasureSpec;
23
+ export declare function Records({ model, domain, onClose, }: {
24
+ readonly model: string;
25
+ readonly domain: Condition;
26
+ readonly onClose: () => void;
27
+ }): ReactElement;
28
+ export declare function CardFrame({ card, editing, onRemove, onDrill, }: {
29
+ readonly card: Card;
30
+ readonly editing: boolean;
31
+ readonly onRemove: () => void;
32
+ readonly onDrill: (domain: Condition) => void;
33
+ }): ReactElement;
23
34
  export declare function DashboardView(_props: ViewProps): ReactElement;
@@ -25,7 +25,7 @@ function thisMonth() {
25
25
  const to = new Date(Date.UTC(now.getFullYear(), now.getMonth() + 1, 1));
26
26
  return { from: from.toISOString().slice(0, 10), to: to.toISOString().slice(0, 10) };
27
27
  }
28
- function Records({ model, domain, onClose, }) {
28
+ export function Records({ model, domain, onClose, }) {
29
29
  const { api } = useKosh();
30
30
  const { t } = useTranslation('reporting');
31
31
  const rows = useQuery({
@@ -90,7 +90,7 @@ const SPAN = {
90
90
  6: 'lg:col-span-6',
91
91
  12: 'lg:col-span-12',
92
92
  };
93
- function CardFrame({ card, editing, onRemove, onDrill, }) {
93
+ export function CardFrame({ card, editing, onRemove, onDrill, }) {
94
94
  const { t } = useTranslation('reporting');
95
95
  const drag = useDraggable({ id: card.id, disabled: !editing });
96
96
  const drop = useDroppable({ id: card.id });
@@ -21,6 +21,7 @@ const KINDS = [
21
21
  'divider',
22
22
  'pageBreak',
23
23
  'signature',
24
+ 'qr',
24
25
  ];
25
26
  function newBlock(kind, n) {
26
27
  const id = `${kind}_${n}`;
@@ -39,6 +40,8 @@ function newBlock(kind, n) {
39
40
  return { kind, id, left: [], right: [] };
40
41
  case 'signature':
41
42
  return { kind, id, labels: ['Prepared by', 'Approved by'] };
43
+ case 'qr':
44
+ return { kind, id, bind: 'name' };
42
45
  default:
43
46
  return { kind, id };
44
47
  }
@@ -52,6 +55,8 @@ function BlockEditor({ block, onChange, }) {
52
55
  return (_jsxs("div", { className: "grid gap-2", children: [text(t('designer.text'), block.text, (v) => onChange({ ...block, text: v })), _jsx(Select, { label: "Level", value: String(block.level ?? 1), options: ['1', '2', '3'].map((l) => ({ value: l, label: `H${l}` })), onChange: (v) => onChange({ ...block, level: Number(v) }) })] }));
53
56
  case 'text':
54
57
  return text(t('designer.text'), block.text, (v) => onChange({ ...block, text: v }), true);
58
+ case 'qr':
59
+ return (_jsxs("div", { className: "grid gap-2", children: [text(t('designer.bind'), block.bind, (v) => onChange({ ...block, bind: v })), text(t('designer.label'), block.label ?? '', (v) => onChange({ ...block, label: v === '' ? undefined : v }))] }));
55
60
  case 'field':
56
61
  return (_jsxs("div", { className: "grid gap-2", children: [text(t('designer.bind'), block.bind, (v) => onChange({ ...block, bind: v })), text(t('designer.label'), block.label ?? '', (v) => onChange({ ...block, label: v === '' ? undefined : v }))] }));
57
62
  case 'fields':
@@ -0,0 +1,9 @@
1
+ /**
2
+ * An app's overview page (RFC 0022 §3.2): the cards its manifest declared — KPIs with their change against the
3
+ * previous period, charts, small pivots, top records — opened by the app's first menu. The cards come from the
4
+ * overview route; each one queries the reports as the user (their rights and record rules apply), and a chart point
5
+ * or a pivot row opens the records behind it. Read-only: the layout belongs to the app (personal layouts: step 3).
6
+ */
7
+ import { type PageProps } from '../../ui/index.js';
8
+ import { type ReactElement } from 'react';
9
+ export declare function Overview({ app }: PageProps): ReactElement;
@@ -0,0 +1,53 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * An app's overview page (RFC 0022 §3.2): the cards its manifest declared — KPIs with their change against the
4
+ * previous period, charts, small pivots, top records — opened by the app's first menu. The cards come from the
5
+ * overview route; each one queries the reports as the user (their rights and record rules apply), and a chart point
6
+ * or a pivot row opens the records behind it. Read-only: the layout belongs to the app (personal layouts: step 3).
7
+ */
8
+ import { Alert, Button, Spinner, useKosh } from '../../ui/index.js';
9
+ import { useQuery } from '@tanstack/react-query';
10
+ import { useState } from 'react';
11
+ import { useTranslation } from 'react-i18next';
12
+ import { CardFrame, Records } from './DashboardView.js';
13
+ export function Overview({ app }) {
14
+ const { api, uid } = useKosh();
15
+ const { t } = useTranslation('reporting');
16
+ const [drill, setDrill] = useState(null);
17
+ // RFC 0022 step 3: the cards a person hid, kept in their browser (like a list's columns).
18
+ const hiddenKey = `kosh.overview.${uid}.${app ?? ''}`;
19
+ const [hidden, setHidden] = useState(() => {
20
+ try {
21
+ const raw = localStorage.getItem(hiddenKey);
22
+ return raw === null ? [] : JSON.parse(raw);
23
+ }
24
+ catch {
25
+ return [];
26
+ }
27
+ });
28
+ const [editing, setEditing] = useState(false);
29
+ const hide = (ids) => {
30
+ setHidden(ids);
31
+ try {
32
+ localStorage.setItem(hiddenKey, JSON.stringify(ids));
33
+ }
34
+ catch {
35
+ // storage unavailable: the choice lasts for the page
36
+ }
37
+ };
38
+ const overview = useQuery({
39
+ queryKey: ['overview', app],
40
+ queryFn: () => api.call('GET', `/api/v1/reports/overview?app=${encodeURIComponent(app ?? '')}`),
41
+ enabled: app !== undefined,
42
+ });
43
+ if (overview.isError)
44
+ return _jsx(Alert, { children: overview.error.message });
45
+ if (overview.data === undefined)
46
+ return _jsx(Spinner, { label: t('overview') });
47
+ return (_jsxs("div", { className: "grid gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("h2", { className: "text-base font-semibold", children: `${overview.data.label} · ${t('overview')}` }), _jsxs("div", { className: "ms-auto flex items-center gap-2", children: [hidden.length === 0 ? null : (_jsx(Button, { size: "sm", variant: "ghost", onClick: () => hide([]), children: t('showAllCards', { count: hidden.length }) })), _jsx(Button, { size: "sm", onClick: () => setEditing((e) => !e), children: editing ? t('done') : t('customise') })] })] }), _jsx("div", { className: "grid grid-cols-12 gap-3", children: overview.data.cards
48
+ .filter((card) => editing || !hidden.includes(card.id))
49
+ .map((card) => (_jsx(CardFrame, { card: card, editing: editing && !hidden.includes(card.id), onRemove: () => hide([...hidden, card.id]), onDrill: (domain) => setDrill({
50
+ model: card.model,
51
+ domain: card.filter === undefined ? domain : { and: [card.filter, domain] },
52
+ }) }, card.id))) }), drill === null ? null : (_jsx(Records, { model: drill.model, domain: drill.domain, onClose: () => setDrill(null) }))] }));
53
+ }
@@ -17,6 +17,7 @@ export { GanttView, windowOf } from './GanttView.js';
17
17
  export { chartData, GraphView, ReportChart, type ReportChartProps } from './GraphView.js';
18
18
  export { HierarchyView } from './HierarchyView.js';
19
19
  export { DEFAULT_MAP_STYLE, MapView } from './MapView.js';
20
+ export { Overview } from './Overview.js';
20
21
  export { PivotView } from './PivotView.js';
21
22
  export { SpreadsheetView } from './SpreadsheetView.js';
22
23
  /** Registers the report views (and their messages in `i18n`, when given). */
@@ -15,6 +15,7 @@ import { GraphView } from './GraphView.js';
15
15
  import { HierarchyView } from './HierarchyView.js';
16
16
  import { MapView } from './MapView.js';
17
17
  import { messages } from './messages.js';
18
+ import { Overview } from './Overview.js';
18
19
  import { PivotView } from './PivotView.js';
19
20
  import { SpreadsheetView } from './SpreadsheetView.js';
20
21
  export { CalendarView, gridDays } from './CalendarView.js';
@@ -27,6 +28,7 @@ export { GanttView, windowOf } from './GanttView.js';
27
28
  export { chartData, GraphView, ReportChart } from './GraphView.js';
28
29
  export { HierarchyView } from './HierarchyView.js';
29
30
  export { DEFAULT_MAP_STYLE, MapView } from './MapView.js';
31
+ export { Overview } from './Overview.js';
30
32
  export { PivotView } from './PivotView.js';
31
33
  export { SpreadsheetView } from './SpreadsheetView.js';
32
34
  /** Registers the report views (and their messages in `i18n`, when given). */
@@ -43,7 +45,8 @@ export function registerReportingWeb(registry, i18n) {
43
45
  .registerView('dashboard', DashboardView)
44
46
  .registerView('designer', DesignerView)
45
47
  .registerComponent('reporting.Documents', Documents)
46
- .registerComponent('reporting.ScheduleOutputs', ScheduleOutputs);
48
+ .registerComponent('reporting.ScheduleOutputs', ScheduleOutputs)
49
+ .registerPage('reporting.Overview', Overview);
47
50
  if (i18n !== undefined)
48
51
  addMessages(i18n, 'reporting', messages);
49
52
  return registry;
@@ -33,6 +33,10 @@ export const messages = {
33
33
  churn: 'Closed',
34
34
  newSheet: 'New spreadsheet',
35
35
  newDashboard: 'New dashboard',
36
+ overview: 'Overview',
37
+ customise: 'Customise',
38
+ showAllCards_one: 'Show the {{count}} hidden card',
39
+ showAllCards_other: 'Show the {{count}} hidden cards',
36
40
  name: 'Name',
37
41
  save: 'Save',
38
42
  saved: 'Saved',
@@ -87,6 +91,7 @@ export const messages = {
87
91
  divider: 'Divider',
88
92
  pageBreak: 'Page break',
89
93
  signature: 'Signatures',
94
+ qr: 'QR code',
90
95
  },
91
96
  },
92
97
  },
@@ -112,6 +117,10 @@ export const messages = {
112
117
  next: 'পরের',
113
118
  newSheet: 'নতুন স্প্রেডশিট',
114
119
  newDashboard: 'নতুন ড্যাশবোর্ড',
120
+ overview: 'সারসংক্ষেপ',
121
+ customise: 'সাজান',
122
+ showAllCards_one: 'লুকানো {{count}}টি কার্ড দেখান',
123
+ showAllCards_other: 'লুকানো {{count}}টি কার্ড দেখান',
115
124
  name: 'নাম',
116
125
  save: 'সংরক্ষণ',
117
126
  addCard: 'কার্ড যোগ করুন',
@@ -1,4 +1,4 @@
1
- // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: f361b5260d093aaf
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 6e014c4aaba6871d
2
2
  // Bindings of module `sale_account`: 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: f361b5260d093aaf
1
+ // GENERATED by `pnpm generate:models` (Shutro’s generator). Do not edit. definitions-hash: 6e014c4aaba6871d
2
2
  // Schema view of module `sale_account` (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
 
@@ -30,6 +30,7 @@ export interface SchemaView {
30
30
  readonly 'auth.user_group': AuthUserGroup;
31
31
  readonly 'kosh.image': KoshImage;
32
32
  readonly 'kosh.saved_view': KoshSavedView;
33
+ readonly 'kosh.setting': KoshSetting;
33
34
  readonly 'mail.activity': MailActivity;
34
35
  readonly 'mail.activity.type': MailActivityType;
35
36
  readonly 'mail.channel': MailChannel;
@@ -53,6 +54,7 @@ export interface SchemaView {
53
54
  readonly 'res.currency': ResCurrency;
54
55
  readonly 'res.currency.rate': ResCurrencyRate;
55
56
  readonly 'res.partner': ResPartner;
57
+ readonly 'res.partner.tag': ResPartnerTag;
56
58
  readonly 'res.users': ResUsers;
57
59
  readonly 'sale.order': SaleOrder;
58
60
  readonly 'sale.order.line': SaleOrderLine;
@@ -779,6 +781,26 @@ export interface KoshSavedView {
779
781
  export interface KoshSavedViewMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'kosh.saved_view'> {
780
782
  }
781
783
 
784
+ /** `kosh.setting` (owner: base). */
785
+ export interface KoshSetting {
786
+ readonly name: 'kosh.setting';
787
+ readonly fields: {
788
+ readonly id: F.Id<'kosh.setting'>;
789
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
790
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
791
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
792
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
793
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
794
+ readonly key: F.Char<{ readonly required: true }>;
795
+ readonly value: F.Json;
796
+ };
797
+ readonly methods: KoshSettingMethods;
798
+ }
799
+
800
+ /** Methods of `kosh.setting`: declared ones and transitions. */
801
+ export interface KoshSettingMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'kosh.setting'> {
802
+ }
803
+
782
804
  /** `mail.activity` (owner: messaging). */
783
805
  export interface MailActivity {
784
806
  readonly name: 'mail.activity';
@@ -1093,6 +1115,7 @@ export interface ProductProduct {
1093
1115
  readonly category: F.Many2one<'product.category'>;
1094
1116
  readonly uom: F.Many2one<'uom.uom'>;
1095
1117
  readonly barcode: F.Char;
1118
+ readonly image: F.Many2one<'storage.attachment'>;
1096
1119
  readonly listPrice: F.Decimal<{ readonly required: true; readonly default: true }>;
1097
1120
  readonly standardPrice: F.Decimal<{ readonly required: true; readonly default: true }>;
1098
1121
  readonly saleOk: F.Bool<{ readonly required: true; readonly default: true }>;
@@ -1123,6 +1146,7 @@ export interface ReportDashboard {
1123
1146
  readonly name: F.Char<{ readonly required: true }>;
1124
1147
  readonly items: F.Json<unknown, { readonly required: true; readonly default: true }>;
1125
1148
  readonly shared: F.Bool<{ readonly required: true; readonly default: true }>;
1149
+ readonly key: F.Char;
1126
1150
  };
1127
1151
  readonly methods: ReportDashboardMethods;
1128
1152
  }
@@ -1263,7 +1287,7 @@ export interface ResBankAccount {
1263
1287
  export interface ResBankAccountMethods extends F.Methods<typeof import('../../accounting/index.js').accountingShutro, 'res.bank.account'> {
1264
1288
  }
1265
1289
 
1266
- /** `res.company` (owner: base; extended by: accounting). */
1290
+ /** `res.company` (owner: base; extended by: contacts, accounting, sales). */
1267
1291
  export interface ResCompany {
1268
1292
  readonly name: 'res.company';
1269
1293
  readonly fields: {
@@ -1283,6 +1307,8 @@ export interface ResCompany {
1283
1307
  readonly email: F.Char;
1284
1308
  readonly phone: F.Char;
1285
1309
  readonly website: F.Char;
1310
+ readonly contactTaxIdRequired: F.Bool<{ readonly required: true; readonly default: true }>; // contacts
1311
+ readonly contactUniqueEmail: F.Bool<{ readonly required: true; readonly default: true }>; // contacts
1286
1312
  readonly fiscalYearStart: F.Integer<{ readonly required: true; readonly default: true }>; // accounting
1287
1313
  readonly chartTemplate: F.Char; // accounting
1288
1314
  readonly country: F.Char; // accounting
@@ -1294,12 +1320,14 @@ export interface ResCompany {
1294
1320
  readonly accountExchangeGain: F.Many2one<'account.account'>; // accounting
1295
1321
  readonly accountExchangeLoss: F.Many2one<'account.account'>; // accounting
1296
1322
  readonly accountRetainedEarnings: F.Many2one<'account.account'>; // accounting
1323
+ readonly saleValidityDays: F.Integer<{ readonly default: true }>; // sales
1324
+ readonly saleTerms: F.Text; // sales
1297
1325
  };
1298
1326
  readonly methods: ResCompanyMethods;
1299
1327
  }
1300
1328
 
1301
1329
  /** Methods of `res.company`: declared ones and transitions. */
1302
- export interface ResCompanyMethods extends F.Methods<typeof import('../../server/index.js').baseShutro, 'res.company'>, F.Methods<typeof import('../../accounting/index.js').accountingShutro, 'res.company'> {
1330
+ 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('../../accounting/index.js').accountingShutro, 'res.company'>, F.Methods<typeof import('../../sales/index.js').salesShutro, 'res.company'> {
1303
1331
  }
1304
1332
 
1305
1333
  /** `res.currency` (owner: base). */
@@ -1358,6 +1386,10 @@ export interface ResPartner {
1358
1386
  readonly active: F.Bool<{ readonly required: true; readonly default: true }>;
1359
1387
  readonly company: F.Many2one<'res.company', { readonly default: true }>;
1360
1388
  readonly name: F.Char<{ readonly required: true }>;
1389
+ readonly image: F.Many2one<'storage.attachment'>;
1390
+ readonly tags: F.Many2many<'res.partner.tag', { readonly required: true }>;
1391
+ readonly latitude: F.Decimal;
1392
+ readonly longitude: F.Decimal;
1361
1393
  readonly isCompany: F.Bool<{ readonly required: true; readonly default: true }>;
1362
1394
  readonly parent: F.Many2one<'res.partner'>;
1363
1395
  readonly contacts: F.One2many<'res.partner', { readonly required: true }>;
@@ -1384,6 +1416,25 @@ export interface ResPartner {
1384
1416
  export interface ResPartnerMethods extends F.Methods<typeof import('../../contacts/index.js').contactsShutro, 'res.partner'>, F.Methods<typeof import('../../accounting/index.js').accountingShutro, 'res.partner'> {
1385
1417
  }
1386
1418
 
1419
+ /** `res.partner.tag` (owner: contacts). */
1420
+ export interface ResPartnerTag {
1421
+ readonly name: 'res.partner.tag';
1422
+ readonly fields: {
1423
+ readonly id: F.Id<'res.partner.tag'>;
1424
+ readonly createdAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
1425
+ readonly updatedAt: F.Datetime<{ readonly required: true; readonly readonly: true }>;
1426
+ readonly createdBy: F.Many2one<'res.users', { readonly readonly: true }>;
1427
+ readonly updatedBy: F.Many2one<'res.users', { readonly readonly: true }>;
1428
+ readonly displayName: F.Char<{ readonly required: true; readonly readonly: true; readonly compute: true }>;
1429
+ readonly name: F.Char<{ readonly required: true }>;
1430
+ };
1431
+ readonly methods: ResPartnerTagMethods;
1432
+ }
1433
+
1434
+ /** Methods of `res.partner.tag`: declared ones and transitions. */
1435
+ export interface ResPartnerTagMethods extends F.Methods<typeof import('../../contacts/index.js').contactsShutro, 'res.partner.tag'> {
1436
+ }
1437
+
1387
1438
  /** `res.users` (owner: base). */
1388
1439
  export interface ResUsers {
1389
1440
  readonly name: 'res.users';
@@ -1435,6 +1486,8 @@ export interface SaleOrder {
1435
1486
  readonly amountUntaxed: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1436
1487
  readonly amountTax: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1437
1488
  readonly amountTotal: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1489
+ readonly margin: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1490
+ readonly marginPercent: F.Decimal<{ readonly readonly: true; readonly compute: true; readonly store: true }>;
1438
1491
  readonly state: F.State<'draft' | 'sent' | 'sale' | 'done' | 'cancel', { readonly required: true; readonly readonly: true; readonly default: true }>;
1439
1492
  readonly invoices: F.One2many<'account.move', { readonly required: true }>; // sale_account
1440
1493
  readonly invoiceState: F.Selection<'no' | 'to_invoice' | 'invoiced', { readonly readonly: true; readonly compute: true; readonly store: true }>; // sale_account
@@ -1474,6 +1527,7 @@ export interface SaleOrderLine {
1474
1527
  readonly tax: F.Many2one<'account.tax', { readonly readonly: true }>;
1475
1528
  readonly currency: F.Many2one<'res.currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1476
1529
  readonly subtotal: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1530
+ readonly margin: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1477
1531
  readonly taxAmount: F.Monetary<'currency', { readonly readonly: true; readonly compute: true; readonly store: true }>;
1478
1532
  };
1479
1533
  readonly methods: SaleOrderLineMethods;
@@ -1500,6 +1554,8 @@ export interface StorageAttachment {
1500
1554
  readonly key: F.Char<{ readonly required: true }>;
1501
1555
  readonly resModel: F.Char<{ readonly required: true }>;
1502
1556
  readonly resId: F.Integer<{ readonly required: true }>;
1557
+ readonly variantOf: F.Many2one<'storage.attachment'>;
1558
+ readonly width: F.Integer;
1503
1559
  };
1504
1560
  readonly methods: StorageAttachmentMethods;
1505
1561
  }
@@ -7,7 +7,7 @@
7
7
  import { accounting } from '../accounting/index.js';
8
8
  import { defineModule } from '../modules-sdk/index.js';
9
9
  import { sales } from '../sales/index.js';
10
- import { field } from '../views/index.js';
10
+ import { field, stat } from '../views/index.js';
11
11
  import { saleAccountDemo } from './demo.js';
12
12
  import { saleAccountShutro } from './module.js';
13
13
  export { MoveSaleOrder, SaleOrderInvoicing, saleAccountShutro } from './module.js';
@@ -37,6 +37,27 @@ export const saleAccount = defineModule({
37
37
  ],
38
38
  },
39
39
  { append: 'dates', nodes: [field({ field: 'invoiceState', id: 'invoice_state' })] },
40
+ {
41
+ append: 'related',
42
+ nodes: [
43
+ stat({
44
+ id: 'order_invoices',
45
+ label: 'Invoices',
46
+ model: 'account.move',
47
+ where: { saleOrder: { $record: 'id' } },
48
+ action: 'accounting.action_customer_invoices',
49
+ }),
50
+ stat({
51
+ id: 'order_invoiced',
52
+ label: 'Invoiced total',
53
+ model: 'account.move',
54
+ where: { saleOrder: { $record: 'id' }, state: 'posted' },
55
+ measure: { sum: 'amountTotal' },
56
+ action: 'accounting.action_customer_invoices',
57
+ tone: 'success',
58
+ }),
59
+ ],
60
+ },
40
61
  ],
41
62
  },
42
63
  {