shutro-erp 0.3.3 → 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 (412) hide show
  1. package/dist/account_portal/.kosh/bindings.js +1 -1
  2. package/dist/account_portal/.kosh/schema.d.ts +181 -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 +181 -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 +205 -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/integrations/index.js +12 -0
  71. package/dist/integrations/web/OAuthAppsPage.d.ts +9 -0
  72. package/dist/integrations/web/OAuthAppsPage.js +79 -0
  73. package/dist/integrations/web/index.d.ts +1 -0
  74. package/dist/integrations/web/index.js +3 -0
  75. package/dist/integrations/web/messages.js +58 -0
  76. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  77. package/dist/l10n_bd/.kosh/schema.d.ts +181 -3
  78. package/dist/l10n_bd/module.d.ts +359 -6
  79. package/dist/l10n_in/.kosh/bindings.js +1 -1
  80. package/dist/l10n_in/.kosh/schema.d.ts +181 -3
  81. package/dist/l10n_in/module.d.ts +380 -7
  82. package/dist/maintenance/.kosh/bindings.d.ts +22 -0
  83. package/dist/maintenance/.kosh/bindings.js +15 -0
  84. package/dist/maintenance/.kosh/schema.d.ts +911 -0
  85. package/dist/maintenance/demo.d.ts +6 -0
  86. package/dist/maintenance/demo.js +60 -0
  87. package/dist/maintenance/index.d.ts +3 -0
  88. package/dist/maintenance/index.js +422 -0
  89. package/dist/maintenance/module.d.ts +3545 -0
  90. package/dist/maintenance/module.js +446 -0
  91. package/dist/maintenance_stock/.kosh/bindings.d.ts +22 -0
  92. package/dist/maintenance_stock/.kosh/bindings.js +15 -0
  93. package/dist/maintenance_stock/.kosh/schema.d.ts +1371 -0
  94. package/dist/maintenance_stock/index.d.ts +9 -0
  95. package/dist/maintenance_stock/index.js +52 -0
  96. package/dist/maintenance_stock/module.d.ts +6953 -0
  97. package/dist/maintenance_stock/module.js +192 -0
  98. package/dist/messaging/web/index.js +1 -1
  99. package/dist/modules-sdk/index.d.ts +1 -1
  100. package/dist/modules-sdk/manifest.d.ts +70 -0
  101. package/dist/modules-sdk/manifest.js +35 -1
  102. package/dist/mrp/.kosh/bindings.d.ts +22 -0
  103. package/dist/mrp/.kosh/bindings.js +15 -0
  104. package/dist/mrp/.kosh/schema.d.ts +1378 -0
  105. package/dist/mrp/demo.d.ts +7 -0
  106. package/dist/mrp/demo.js +42 -0
  107. package/dist/mrp/index.d.ts +3 -0
  108. package/dist/mrp/index.js +392 -0
  109. package/dist/mrp/module.d.ts +8608 -0
  110. package/dist/mrp/module.js +695 -0
  111. package/dist/mrp_account/.kosh/bindings.d.ts +22 -0
  112. package/dist/mrp_account/.kosh/bindings.js +15 -0
  113. package/dist/mrp_account/.kosh/schema.d.ts +2106 -0
  114. package/dist/mrp_account/index.d.ts +3 -0
  115. package/dist/mrp_account/index.js +22 -0
  116. package/dist/mrp_account/module.d.ts +20840 -0
  117. package/dist/mrp_account/module.js +123 -0
  118. package/dist/notify/web/index.js +1 -1
  119. package/dist/payment/.kosh/bindings.js +1 -1
  120. package/dist/payment/.kosh/schema.d.ts +181 -3
  121. package/dist/payment/module.d.ts +359 -6
  122. package/dist/portal/.kosh/bindings.js +1 -1
  123. package/dist/portal/.kosh/schema.d.ts +205 -3
  124. package/dist/portal/module.d.ts +166 -2
  125. package/dist/pos/.kosh/bindings.d.ts +22 -0
  126. package/dist/pos/.kosh/bindings.js +15 -0
  127. package/dist/pos/.kosh/schema.d.ts +1690 -0
  128. package/dist/pos/accounting.d.ts +10 -0
  129. package/dist/pos/accounting.js +149 -0
  130. package/dist/pos/demo.d.ts +6 -0
  131. package/dist/pos/demo.js +90 -0
  132. package/dist/pos/index.d.ts +9 -0
  133. package/dist/pos/index.js +349 -0
  134. package/dist/pos/module.d.ts +9636 -0
  135. package/dist/pos/module.js +423 -0
  136. package/dist/pos/routes.d.ts +39 -0
  137. package/dist/pos/routes.js +368 -0
  138. package/dist/pos/web/Register.d.ts +9 -0
  139. package/dist/pos/web/Register.js +252 -0
  140. package/dist/pos/web/index.d.ts +11 -0
  141. package/dist/pos/web/index.js +16 -0
  142. package/dist/pos/web/messages.d.ts +2 -0
  143. package/dist/pos/web/messages.js +87 -0
  144. package/dist/pos_stock/.kosh/bindings.d.ts +22 -0
  145. package/dist/pos_stock/.kosh/bindings.js +15 -0
  146. package/dist/pos_stock/.kosh/schema.d.ts +2015 -0
  147. package/dist/pos_stock/index.d.ts +9 -0
  148. package/dist/pos_stock/index.js +35 -0
  149. package/dist/pos_stock/module.d.ts +11935 -0
  150. package/dist/pos_stock/module.js +115 -0
  151. package/dist/product/.kosh/bindings.js +1 -1
  152. package/dist/product/.kosh/schema.d.ts +178 -1
  153. package/dist/product/index.js +81 -4
  154. package/dist/product/module.d.ts +43 -1
  155. package/dist/product/module.js +9 -2
  156. package/dist/project/.kosh/bindings.js +1 -1
  157. package/dist/project/.kosh/schema.d.ts +181 -3
  158. package/dist/project/index.js +147 -7
  159. package/dist/project/module.d.ts +210 -2
  160. package/dist/project/module.js +52 -5
  161. package/dist/project_portal/.kosh/bindings.js +1 -1
  162. package/dist/project_portal/.kosh/schema.d.ts +181 -3
  163. package/dist/project_portal/module.d.ts +353 -4
  164. package/dist/purchase/.kosh/bindings.js +1 -1
  165. package/dist/purchase/.kosh/schema.d.ts +181 -3
  166. package/dist/purchase/index.js +163 -3
  167. package/dist/purchase/module.d.ts +215 -3
  168. package/dist/purchase/module.js +21 -3
  169. package/dist/purchase_account/.kosh/bindings.js +1 -1
  170. package/dist/purchase_account/.kosh/schema.d.ts +182 -3
  171. package/dist/purchase_account/index.js +26 -1
  172. package/dist/purchase_account/module.d.ts +393 -7
  173. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  174. package/dist/purchase_portal/.kosh/schema.d.ts +181 -3
  175. package/dist/purchase_portal/module.d.ts +366 -5
  176. package/dist/purchase_stock/.kosh/bindings.d.ts +22 -0
  177. package/dist/purchase_stock/.kosh/bindings.js +15 -0
  178. package/dist/purchase_stock/.kosh/schema.d.ts +1310 -0
  179. package/dist/purchase_stock/index.d.ts +7 -0
  180. package/dist/purchase_stock/index.js +91 -0
  181. package/dist/purchase_stock/module.d.ts +6763 -0
  182. package/dist/purchase_stock/module.js +144 -0
  183. package/dist/purchase_stock_account/.kosh/bindings.d.ts +22 -0
  184. package/dist/purchase_stock_account/.kosh/bindings.js +15 -0
  185. package/dist/purchase_stock_account/.kosh/schema.d.ts +1959 -0
  186. package/dist/purchase_stock_account/index.d.ts +3 -0
  187. package/dist/purchase_stock_account/index.js +24 -0
  188. package/dist/purchase_stock_account/module.d.ts +35366 -0
  189. package/dist/purchase_stock_account/module.js +162 -0
  190. package/dist/recruitment/.kosh/bindings.d.ts +22 -0
  191. package/dist/recruitment/.kosh/bindings.js +15 -0
  192. package/dist/recruitment/.kosh/schema.d.ts +990 -0
  193. package/dist/recruitment/demo.d.ts +6 -0
  194. package/dist/recruitment/demo.js +45 -0
  195. package/dist/recruitment/index.d.ts +3 -0
  196. package/dist/recruitment/index.js +385 -0
  197. package/dist/recruitment/module.d.ts +3247 -0
  198. package/dist/recruitment/module.js +193 -0
  199. package/dist/recruitment/web/HireButton.d.ts +9 -0
  200. package/dist/recruitment/web/HireButton.js +32 -0
  201. package/dist/recruitment/web/index.d.ts +11 -0
  202. package/dist/recruitment/web/index.js +16 -0
  203. package/dist/recruitment/web/messages.d.ts +2 -0
  204. package/dist/recruitment/web/messages.js +19 -0
  205. package/dist/reporting/documents.d.ts +5 -0
  206. package/dist/reporting/documents.js +28 -1
  207. package/dist/reporting/index.d.ts +1 -1
  208. package/dist/reporting/index.js +4 -2
  209. package/dist/reporting/models.js +9 -0
  210. package/dist/reporting/overviews.d.ts +16 -0
  211. package/dist/reporting/overviews.js +56 -0
  212. package/dist/reporting/routes-documents.js +1 -1
  213. package/dist/reporting/routes.js +138 -80
  214. package/dist/reporting/web/DashboardView.d.ts +11 -0
  215. package/dist/reporting/web/DashboardView.js +2 -2
  216. package/dist/reporting/web/DesignerView.js +5 -0
  217. package/dist/reporting/web/Overview.d.ts +9 -0
  218. package/dist/reporting/web/Overview.js +53 -0
  219. package/dist/reporting/web/index.d.ts +1 -0
  220. package/dist/reporting/web/index.js +4 -1
  221. package/dist/reporting/web/messages.js +9 -0
  222. package/dist/sale_account/.kosh/bindings.js +1 -1
  223. package/dist/sale_account/.kosh/schema.d.ts +186 -3
  224. package/dist/sale_account/index.js +22 -1
  225. package/dist/sale_account/module.d.ts +461 -7
  226. package/dist/sale_portal/.kosh/bindings.js +1 -1
  227. package/dist/sale_portal/.kosh/schema.d.ts +185 -3
  228. package/dist/sale_portal/module.d.ts +434 -5
  229. package/dist/sale_project/.kosh/bindings.js +1 -1
  230. package/dist/sale_project/.kosh/schema.d.ts +187 -3
  231. package/dist/sale_project/module.d.ts +476 -6
  232. package/dist/sale_stock/.kosh/bindings.d.ts +22 -0
  233. package/dist/sale_stock/.kosh/bindings.js +15 -0
  234. package/dist/sale_stock/.kosh/schema.d.ts +1228 -0
  235. package/dist/sale_stock/index.d.ts +7 -0
  236. package/dist/sale_stock/index.js +84 -0
  237. package/dist/sale_stock/module.d.ts +6791 -0
  238. package/dist/sale_stock/module.js +146 -0
  239. package/dist/sales/.kosh/bindings.js +1 -1
  240. package/dist/sales/.kosh/schema.d.ts +185 -3
  241. package/dist/sales/index.js +191 -4
  242. package/dist/sales/module.d.ts +355 -3
  243. package/dist/sales/module.js +78 -3
  244. package/dist/server/api/metadata.d.ts +5 -0
  245. package/dist/server/api/metadata.js +5 -0
  246. package/dist/server/api/rest.js +64 -1
  247. package/dist/server/api/views.js +1 -0
  248. package/dist/server/app-settings.d.ts +7 -0
  249. package/dist/server/app-settings.js +10 -0
  250. package/dist/server/auth/base.js +85 -0
  251. package/dist/server/auth/oauth.d.ts +21 -0
  252. package/dist/server/auth/oauth.js +579 -0
  253. package/dist/server/auth/service.d.ts +4 -0
  254. package/dist/server/auth/service.js +17 -0
  255. package/dist/server/identity.d.ts +2 -0
  256. package/dist/server/images.d.ts +12 -0
  257. package/dist/server/images.js +139 -0
  258. package/dist/server/index.d.ts +2 -0
  259. package/dist/server/index.js +2 -0
  260. package/dist/server/server.d.ts +4 -0
  261. package/dist/server/server.js +9 -1
  262. package/dist/settings/app-settings.d.ts +2 -0
  263. package/dist/settings/app-settings.js +518 -0
  264. package/dist/settings/company.js +3 -1
  265. package/dist/settings/index.d.ts +1 -0
  266. package/dist/settings/index.js +15 -0
  267. package/dist/settings/people.js +32 -6
  268. package/dist/settings/web/AppSettingsPage.d.ts +11 -0
  269. package/dist/settings/web/AppSettingsPage.js +210 -0
  270. package/dist/settings/web/CompaniesPage.js +2 -2
  271. package/dist/settings/web/CompanyPage.js +13 -2
  272. package/dist/settings/web/ConnectedAppsPage.d.ts +7 -0
  273. package/dist/settings/web/ConnectedAppsPage.js +33 -0
  274. package/dist/settings/web/OverviewPage.js +28 -4
  275. package/dist/settings/web/PeoplePage.js +23 -18
  276. package/dist/settings/web/ProfilePage.js +9 -7
  277. package/dist/settings/web/index.d.ts +2 -0
  278. package/dist/settings/web/index.js +8 -0
  279. package/dist/settings/web/messages.js +146 -2
  280. package/dist/stock/.kosh/bindings.d.ts +22 -0
  281. package/dist/stock/.kosh/bindings.js +15 -0
  282. package/dist/stock/.kosh/schema.d.ts +1147 -0
  283. package/dist/stock/demo.d.ts +7 -0
  284. package/dist/stock/demo.js +55 -0
  285. package/dist/stock/index.d.ts +3 -0
  286. package/dist/stock/index.js +674 -0
  287. package/dist/stock/module.d.ts +6330 -0
  288. package/dist/stock/module.js +1325 -0
  289. package/dist/stock/web/ScanBox.d.ts +8 -0
  290. package/dist/stock/web/ScanBox.js +90 -0
  291. package/dist/stock/web/index.d.ts +11 -0
  292. package/dist/stock/web/index.js +16 -0
  293. package/dist/stock/web/messages.d.ts +2 -0
  294. package/dist/stock/web/messages.js +25 -0
  295. package/dist/stock_account/.kosh/bindings.d.ts +22 -0
  296. package/dist/stock_account/.kosh/bindings.js +15 -0
  297. package/dist/stock_account/.kosh/schema.d.ts +1875 -0
  298. package/dist/stock_account/index.d.ts +3 -0
  299. package/dist/stock_account/index.js +130 -0
  300. package/dist/stock_account/module.d.ts +10655 -0
  301. package/dist/stock_account/module.js +339 -0
  302. package/dist/storage/attachments.d.ts +5 -0
  303. package/dist/storage/attachments.js +12 -1
  304. package/dist/storage/models.js +3 -0
  305. package/dist/storage/routes.js +13 -2
  306. package/dist/todo/.kosh/bindings.d.ts +22 -0
  307. package/dist/todo/.kosh/bindings.js +15 -0
  308. package/dist/todo/.kosh/schema.d.ts +732 -0
  309. package/dist/todo/demo.d.ts +6 -0
  310. package/dist/todo/demo.js +35 -0
  311. package/dist/todo/index.d.ts +3 -0
  312. package/dist/todo/index.js +187 -0
  313. package/dist/todo/module.d.ts +1180 -0
  314. package/dist/todo/module.js +130 -0
  315. package/dist/trade_finance/.kosh/bindings.d.ts +22 -0
  316. package/dist/trade_finance/.kosh/bindings.js +15 -0
  317. package/dist/trade_finance/.kosh/schema.d.ts +884 -0
  318. package/dist/trade_finance/demo.d.ts +6 -0
  319. package/dist/trade_finance/demo.js +63 -0
  320. package/dist/trade_finance/index.d.ts +3 -0
  321. package/dist/trade_finance/index.js +413 -0
  322. package/dist/trade_finance/module.d.ts +3779 -0
  323. package/dist/trade_finance/module.js +505 -0
  324. package/dist/trade_finance_account/.kosh/bindings.d.ts +22 -0
  325. package/dist/trade_finance_account/.kosh/bindings.js +15 -0
  326. package/dist/trade_finance_account/.kosh/schema.d.ts +1711 -0
  327. package/dist/trade_finance_account/index.d.ts +3 -0
  328. package/dist/trade_finance_account/index.js +92 -0
  329. package/dist/trade_finance_account/module.d.ts +7928 -0
  330. package/dist/trade_finance_account/module.js +244 -0
  331. package/dist/trade_finance_purchase/.kosh/bindings.d.ts +22 -0
  332. package/dist/trade_finance_purchase/.kosh/bindings.js +15 -0
  333. package/dist/trade_finance_purchase/.kosh/schema.d.ts +1153 -0
  334. package/dist/trade_finance_purchase/index.d.ts +8 -0
  335. package/dist/trade_finance_purchase/index.js +50 -0
  336. package/dist/trade_finance_purchase/module.d.ts +4252 -0
  337. package/dist/trade_finance_purchase/module.js +44 -0
  338. package/dist/trade_finance_sale/.kosh/bindings.d.ts +22 -0
  339. package/dist/trade_finance_sale/.kosh/bindings.js +15 -0
  340. package/dist/trade_finance_sale/.kosh/schema.d.ts +1071 -0
  341. package/dist/trade_finance_sale/index.d.ts +8 -0
  342. package/dist/trade_finance_sale/index.js +50 -0
  343. package/dist/trade_finance_sale/module.d.ts +4280 -0
  344. package/dist/trade_finance_sale/module.js +44 -0
  345. package/dist/ui/api.d.ts +6 -1
  346. package/dist/ui/api.js +2 -1
  347. package/dist/ui/app.js +51 -17
  348. package/dist/ui/components/AppIcon.js +33 -1
  349. package/dist/ui/components/Avatar.d.ts +21 -0
  350. package/dist/ui/components/Avatar.js +71 -0
  351. package/dist/ui/components/primitives.d.ts +2 -0
  352. package/dist/ui/components/primitives.js +7 -1
  353. package/dist/ui/i18n/messages.d.ts +53 -0
  354. package/dist/ui/i18n/messages.js +98 -0
  355. package/dist/ui/index.d.ts +2 -0
  356. package/dist/ui/index.js +2 -0
  357. package/dist/ui/offline/queue.d.ts +5 -1
  358. package/dist/ui/offline/queue.js +2 -0
  359. package/dist/ui/registry.d.ts +12 -1
  360. package/dist/ui/registry.js +10 -1
  361. package/dist/ui/search/ViewTabs.d.ts +14 -0
  362. package/dist/ui/search/ViewTabs.js +42 -0
  363. package/dist/ui/shell/Consent.d.ts +5 -0
  364. package/dist/ui/shell/Consent.js +35 -0
  365. package/dist/ui/shell/Home.js +136 -16
  366. package/dist/ui/shell/Sidebar.js +15 -22
  367. package/dist/ui/views/FormView.js +98 -17
  368. package/dist/ui/views/KanbanView.d.ts +2 -0
  369. package/dist/ui/views/KanbanView.js +32 -3
  370. package/dist/ui/views/ListView.js +155 -31
  371. package/dist/ui/views/StatsRow.d.ts +14 -0
  372. package/dist/ui/views/StatsRow.js +88 -0
  373. package/dist/ui/views/widgets.d.ts +2 -1
  374. package/dist/ui/views/widgets.js +194 -1
  375. package/dist/views/builders.d.ts +19 -2
  376. package/dist/views/builders.js +21 -1
  377. package/dist/views/condition.d.ts +6 -0
  378. package/dist/views/condition.js +20 -0
  379. package/dist/views/documents.d.ts +8 -0
  380. package/dist/views/extend.d.ts +2 -2
  381. package/dist/views/extend.js +11 -1
  382. package/dist/views/index.d.ts +2 -2
  383. package/dist/views/index.js +2 -2
  384. package/dist/views/types.d.ts +52 -5
  385. package/dist/views/validate.js +39 -1
  386. package/dist/website/.kosh/bindings.d.ts +22 -0
  387. package/dist/website/.kosh/bindings.js +15 -0
  388. package/dist/website/.kosh/schema.d.ts +881 -0
  389. package/dist/website/demo.d.ts +6 -0
  390. package/dist/website/demo.js +115 -0
  391. package/dist/website/index.d.ts +9 -0
  392. package/dist/website/index.js +250 -0
  393. package/dist/website/module.d.ts +2987 -0
  394. package/dist/website/module.js +281 -0
  395. package/dist/website/render.d.ts +45 -0
  396. package/dist/website/render.js +102 -0
  397. package/dist/website/routes.d.ts +12 -0
  398. package/dist/website/routes.js +246 -0
  399. package/dist/website/web/PagePreview.d.ts +7 -0
  400. package/dist/website/web/PagePreview.js +33 -0
  401. package/dist/website/web/index.d.ts +11 -0
  402. package/dist/website/web/index.js +16 -0
  403. package/dist/website/web/messages.d.ts +2 -0
  404. package/dist/website/web/messages.js +15 -0
  405. package/dist/website_crm/.kosh/bindings.d.ts +22 -0
  406. package/dist/website_crm/.kosh/bindings.js +15 -0
  407. package/dist/website_crm/.kosh/schema.d.ts +966 -0
  408. package/dist/website_crm/index.d.ts +3 -0
  409. package/dist/website_crm/index.js +39 -0
  410. package/dist/website_crm/module.d.ts +2955 -0
  411. package/dist/website_crm/module.js +44 -0
  412. package/package.json +118 -1
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Event Hub's demo data (RFC 0008 §5.8): a supplier day in a month — 3 seats, two ticket types and two sessions,
3
+ * open for registration — with two attendees confirmed, one checked in, and a third on the waiting list.
4
+ */
5
+ import type { DemoContext } from '../modules-sdk/index.js';
6
+ export declare function eventDemo(ctx: DemoContext): Promise<string>;
@@ -0,0 +1,65 @@
1
+ const DINA = { login: 'dina', name: 'Dina Rahman', groups: ['event.manager'] };
2
+ export async function eventDemo(ctx) {
3
+ await ctx.as(DINA, async (raw) => {
4
+ const env = raw;
5
+ const at = (days, hour) => {
6
+ const d = new Date(`${ctx.date(days)}T00:00:00`);
7
+ d.setHours(hour);
8
+ return d;
9
+ };
10
+ const [day] = (await env.model('event.event').create([
11
+ {
12
+ name: 'Supplier day 2026',
13
+ dateBegin: at(30, 9),
14
+ dateEnd: at(30, 17),
15
+ venue: 'Radisson Blu, Dhaka',
16
+ seatsMax: 3,
17
+ tickets: {
18
+ create: [
19
+ { name: 'Standard', price: '1500' },
20
+ { name: 'Workshop', price: '3500', seatsMax: 1 },
21
+ ],
22
+ },
23
+ sessions: {
24
+ create: [
25
+ {
26
+ name: 'Opening and market outlook',
27
+ dateBegin: at(30, 9),
28
+ dateEnd: at(30, 10),
29
+ room: 'Hall A',
30
+ },
31
+ {
32
+ name: 'Workshop: e-invoicing',
33
+ dateBegin: at(30, 14),
34
+ dateEnd: at(30, 16),
35
+ room: 'Room 2',
36
+ },
37
+ ],
38
+ },
39
+ },
40
+ ])).ids;
41
+ if (day === undefined)
42
+ return;
43
+ await env.model('event.event').browse([day]).publish();
44
+ const [standard] = (await env.model('event.ticket').search({ where: { event: day }, limit: 1 }))
45
+ .ids;
46
+ const people = [
47
+ { name: 'Nusrat Jahan', email: 'nusrat@example.test' },
48
+ { name: 'Arif Hossain', email: 'arif@example.test' },
49
+ { name: 'Tania Akter', email: 'tania@example.test' },
50
+ { name: 'Kamal Uddin', email: 'kamal@example.test' },
51
+ ];
52
+ const made = await env
53
+ .model('event.registration')
54
+ .create(people.map((p) => ({ ...p, event: day, ticket: standard ?? null })));
55
+ const [first, second, third, fourth] = made.ids;
56
+ for (const id of [first, second, third])
57
+ if (id !== undefined)
58
+ await env.model('event.registration').browse([id]).confirm();
59
+ if (fourth !== undefined)
60
+ await env.model('event.registration').browse([fourth]).wait();
61
+ if (first !== undefined)
62
+ await env.model('event.registration').browse([first]).attend();
63
+ });
64
+ return 'Event Hub: a supplier day open for registration, 3 attendees (one checked in) and one waiting.';
65
+ }
@@ -0,0 +1,14 @@
1
+ import { type Mailer } from '../mail/index.js';
2
+ import { defineModule } from '../modules-sdk/index.js';
3
+ export { CONFIRMED, ENDED, eventMailers } from './mails.js';
4
+ export { EventEvent, EventRegistration, EventSession, EventTicket, eventShutro, newCode, } from './module.js';
5
+ export interface EventOptions {
6
+ /** Who sends the emails; default: SMTP from the environment (`SMTP_URL`), none without it. */
7
+ readonly mailer?: Mailer | undefined;
8
+ /** Start the email workers (default: unless `KOSH_SCHEDULES=off`). */
9
+ readonly workers?: boolean | undefined;
10
+ }
11
+ /** Event Hub with explicit options (tests: a memory mailer, workers drained by hand). */
12
+ export declare function eventModule(options?: EventOptions): ReturnType<typeof defineModule>;
13
+ /** Event Hub: SMTP from the environment for its emails. */
14
+ export declare const event: ReturnType<typeof defineModule>;
@@ -0,0 +1,333 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * `shutro-erp/event` (RFC 0021): the **Event Hub** app. Events with sessions and ticket types, seats limited per event and
4
+ * per ticket, registrations confirmed while seats remain or put on a waiting list that moves up in order, and check-in
5
+ * by the code on the attendee's ticket.
6
+ *
7
+ * - `POST /api/v1/eventhub/checkin` `{ code }`: the attendee checked in, once (a phone scanning the QR sends it)
8
+ */
9
+ import { contacts } from '../contacts/index.js';
10
+ import { mailerFromEnv } from '../mail/index.js';
11
+ import { messaging } from '../messaging/index.js';
12
+ import { defineModule } from '../modules-sdk/index.js';
13
+ import { reporting } from '../reporting/index.js';
14
+ import { HttpProblem } from '../server/index.js';
15
+ import { action, calendar, form, kanban, list, search } from '../views/index.js';
16
+ import { eventDemo } from './demo.js';
17
+ import { CONFIRMED, ENDED, eventMailers } from './mails.js';
18
+ import { EventEvent, EventRegistration, EventSession, EventTicket, eventShutro } from './module.js';
19
+ export { CONFIRMED, ENDED, eventMailers } from './mails.js';
20
+ export { EventEvent, EventRegistration, EventSession, EventTicket, eventShutro, newCode, } from './module.js';
21
+ function registerEventRoutes({ app }) {
22
+ app.post('/api/v1/eventhub/checkin', async (request) => {
23
+ const code = String(request.body?.code ?? '')
24
+ .trim()
25
+ .toUpperCase();
26
+ if (code === '')
27
+ throw new HttpProblem(400, 'invalid-code', 'No code', 'Send the code on the ticket.');
28
+ return request.env(async (raw) => {
29
+ const env = raw;
30
+ const [r] = await (await env.model('event.registration').search({ where: { code }, limit: 1 })).load({ event: true, ticket: true });
31
+ if (r === undefined)
32
+ throw new HttpProblem(404, 'unknown-code', 'Unknown ticket', `No registration has the code ${code}.`);
33
+ if (r.state === 'attended')
34
+ throw new HttpProblem(409, 'already-checked-in', 'Already checked in', `${r.name} was checked in at ${r.checkedInAt?.toISOString() ?? 'an earlier time'}.`);
35
+ if (r.state !== 'confirmed')
36
+ throw new HttpProblem(409, 'not-confirmed', 'Not confirmed', `${r.name}'s registration is not confirmed.`);
37
+ await env.model('event.registration').browse([r.id]).attend();
38
+ return {
39
+ registration: r.id,
40
+ name: r.name,
41
+ event: r.event.name,
42
+ ticket: r.ticket?.name ?? null,
43
+ };
44
+ }, { write: true });
45
+ });
46
+ }
47
+ /** Event Hub with explicit options (tests: a memory mailer, workers drained by hand). */
48
+ export function eventModule(options = {}) {
49
+ const mailer = 'mailer' in options ? options.mailer : mailerFromEnv();
50
+ return defineModule({
51
+ shutro: eventShutro,
52
+ kosh: '^0.1.0',
53
+ hooks: { demo: eventDemo },
54
+ depends: [contacts, messaging, reporting],
55
+ // The ticket and badge: the attendee, the event, and the QR the door scans.
56
+ documents: [
57
+ {
58
+ key: 'ticket',
59
+ name: 'Ticket and badge',
60
+ model: 'event.registration',
61
+ layout: {
62
+ paper: 'A4',
63
+ header: '{{company.name}}',
64
+ blocks: [
65
+ { kind: 'heading', id: 'event_name', text: '{{event.name}}', level: 1 },
66
+ { kind: 'heading', id: 'attendee', text: '{{name}}', level: 2 },
67
+ {
68
+ kind: 'fields',
69
+ id: 'details',
70
+ items: [
71
+ { label: 'Ticket', bind: 'ticket.name' },
72
+ { label: 'Starts', bind: 'event.dateBegin' },
73
+ { label: 'Venue', bind: 'event.venue' },
74
+ { label: 'Code', bind: 'code' },
75
+ ],
76
+ },
77
+ { kind: 'qr', id: 'qr', bind: 'code', size: 45, label: 'Show this at the door' },
78
+ ],
79
+ },
80
+ },
81
+ ],
82
+ app: {
83
+ code: 'eventhub',
84
+ label: 'Event Hub',
85
+ icon: 'eventhub',
86
+ sequence: 41,
87
+ category: 'Marketing',
88
+ summary: 'Events, tickets and seats, waiting lists, check-in by QR',
89
+ },
90
+ accessLevels: {
91
+ user: { label: 'User (registrations, check-in)', groups: ['event.user'] },
92
+ manager: {
93
+ label: 'Manager (events, tickets, everything)',
94
+ groups: ['event.manager'],
95
+ implies: ['user'],
96
+ },
97
+ },
98
+ events: [
99
+ { topic: CONFIRMED, model: 'event.registration', label: 'Registration confirmed' },
100
+ { topic: ENDED, model: 'event.event', label: 'Event ended' },
101
+ ],
102
+ routes: (context) => {
103
+ registerEventRoutes(context);
104
+ if (!(options.workers ?? process.env['KOSH_SCHEDULES'] !== 'off'))
105
+ return;
106
+ const workers = eventMailers({
107
+ shutro: context.shutro,
108
+ log: (message, error) => context.app.log.error({ err: error }, message),
109
+ }, mailer);
110
+ for (const w of workers)
111
+ w.start();
112
+ context.app.addHook('onClose', async () => {
113
+ for (const w of workers)
114
+ await w.stop();
115
+ });
116
+ },
117
+ // The Overview (RFC 0022 step 2).
118
+ overview: {
119
+ cards: [
120
+ {
121
+ id: 'open_events',
122
+ title: 'Open for registration',
123
+ kind: 'kpi',
124
+ model: 'event.event',
125
+ measure: 'count',
126
+ filter: { state: 'published' },
127
+ w: 3,
128
+ },
129
+ {
130
+ id: 'confirmed',
131
+ title: 'Places confirmed',
132
+ kind: 'kpi',
133
+ model: 'event.registration',
134
+ measure: 'count',
135
+ filter: { state: { in: ['confirmed', 'attended'] } },
136
+ w: 3,
137
+ },
138
+ {
139
+ id: 'waiting',
140
+ title: 'On waiting lists',
141
+ kind: 'kpi',
142
+ model: 'event.registration',
143
+ measure: 'count',
144
+ filter: { state: 'waiting' },
145
+ w: 3,
146
+ },
147
+ {
148
+ id: 'revenue',
149
+ title: 'Ticket revenue',
150
+ kind: 'kpi',
151
+ model: 'event.registration',
152
+ measure: 'sum:price',
153
+ filter: { state: { in: ['confirmed', 'attended'] } },
154
+ w: 3,
155
+ },
156
+ {
157
+ id: 'by_event',
158
+ title: 'Places by event',
159
+ kind: 'pivot',
160
+ model: 'event.registration',
161
+ measure: 'count',
162
+ groupBy: 'event',
163
+ filter: { state: { in: ['confirmed', 'attended'] } },
164
+ w: 6,
165
+ },
166
+ {
167
+ id: 'by_ticket',
168
+ title: 'By ticket type',
169
+ kind: 'chart',
170
+ model: 'event.registration',
171
+ measure: 'count',
172
+ groupBy: 'ticket',
173
+ filter: { state: { in: ['confirmed', 'attended'] } },
174
+ chart: 'pie',
175
+ w: 6,
176
+ },
177
+ {
178
+ id: 'checked_in',
179
+ title: 'Checked in',
180
+ kind: 'kpi',
181
+ model: 'event.registration',
182
+ measure: 'count',
183
+ filter: { state: 'attended' },
184
+ w: 3,
185
+ },
186
+ {
187
+ id: 'biggest',
188
+ title: 'Events by registrations',
189
+ kind: 'list',
190
+ model: 'event.event',
191
+ measure: 'sum:seatsTaken',
192
+ w: 6,
193
+ },
194
+ ],
195
+ },
196
+ actions: {
197
+ 'eventhub.action_events': action(EventEvent, {
198
+ label: 'Events',
199
+ views: ['kanban', 'list', 'calendar', 'form'],
200
+ }),
201
+ 'eventhub.action_registrations': action(EventRegistration, {
202
+ label: 'Registrations',
203
+ views: ['list', 'form'],
204
+ }),
205
+ },
206
+ menus: [
207
+ { id: 'eventhub.root', label: 'Event Hub', app: 'eventhub' },
208
+ {
209
+ id: 'eventhub.events',
210
+ parent: 'eventhub.root',
211
+ label: 'Events',
212
+ action: 'eventhub.action_events',
213
+ sequence: 1,
214
+ },
215
+ {
216
+ id: 'eventhub.registrations',
217
+ parent: 'eventhub.root',
218
+ label: 'Registrations',
219
+ action: 'eventhub.action_registrations',
220
+ sequence: 2,
221
+ },
222
+ ],
223
+ views: {
224
+ 'event.event.kanban': kanban(EventEvent, {
225
+ groupBy: 'state',
226
+ card: { title: 'name', subtitle: 'venue', footer: ['dateBegin', 'seatsAvailable'] },
227
+ }),
228
+ 'event.event.list': list(EventEvent, {
229
+ columns: [
230
+ 'name',
231
+ 'dateBegin',
232
+ 'venue',
233
+ 'seatsMax',
234
+ 'seatsTaken',
235
+ 'attended',
236
+ { field: 'state', widget: 'badge' },
237
+ ],
238
+ }),
239
+ 'event.event.calendar': calendar(EventEvent, {
240
+ dateStart: 'dateBegin',
241
+ dateStop: 'dateEnd',
242
+ title: 'name',
243
+ mode: 'month',
244
+ }),
245
+ 'event.event.form': form(EventEvent, (k) => ({
246
+ header: {
247
+ statusbar: 'state',
248
+ buttons: [
249
+ k.button('publish', { primary: true, label: 'Open registrations' }),
250
+ k.button('close', { label: 'End' }),
251
+ k.button('cancel', { label: 'Cancel', confirm: 'Cancel this event?' }),
252
+ k.button('redraft', { label: 'Back to draft' }),
253
+ ],
254
+ },
255
+ sheet: [
256
+ k.group('main', ['name', 'dateBegin', 'dateEnd', 'venue', 'organizer']),
257
+ k.group('seats', [
258
+ 'seatsMax',
259
+ 'seatsTaken',
260
+ 'seatsAvailable',
261
+ 'attended',
262
+ 'waitingList',
263
+ 'currency',
264
+ ]),
265
+ k.component('checkin', 'event.CheckIn'),
266
+ k.lines('tickets', [
267
+ { field: 'name', id: 'ticket_name' },
268
+ { field: 'price', id: 'ticket_price' },
269
+ { field: 'seatsMax', id: 'ticket_seats' },
270
+ { field: 'saleEnd', id: 'ticket_sale_end' },
271
+ { field: 'seatsTaken', id: 'ticket_sold' },
272
+ ]),
273
+ k.lines('sessions', [
274
+ { field: 'name', id: 'session_name' },
275
+ { field: 'dateBegin', id: 'session_begin' },
276
+ { field: 'dateEnd', id: 'session_end' },
277
+ { field: 'room', id: 'session_room' },
278
+ { field: 'speaker', id: 'session_speaker' },
279
+ ]),
280
+ k.group('about', ['description'], { columns: 1 }),
281
+ ],
282
+ chatter: true,
283
+ })),
284
+ 'event.registration.list': list(EventRegistration, {
285
+ columns: ['name', 'event', 'ticket', 'email', 'code', { field: 'state', widget: 'badge' }],
286
+ }),
287
+ 'event.registration.form': form(EventRegistration, (k) => ({
288
+ header: {
289
+ statusbar: 'state',
290
+ buttons: [
291
+ k.button('confirm', { primary: true, label: 'Confirm' }),
292
+ k.button('wait', { label: 'Waiting list' }),
293
+ k.button('attend', { primary: true, label: 'Check in' }),
294
+ k.button('cancel', { label: 'Cancel' }),
295
+ ],
296
+ },
297
+ sheet: [
298
+ k.group('main', ['event', 'ticket', 'name', 'email', 'phone', 'partner', 'lang']),
299
+ k.group('ticket_info', ['code', 'price', 'checkedInAt']),
300
+ ],
301
+ chatter: true,
302
+ })),
303
+ 'event.ticket.list': list(EventTicket, {
304
+ columns: ['event', 'name', 'price', 'seatsMax', 'seatsTaken'],
305
+ }),
306
+ 'event.session.list': list(EventSession, {
307
+ columns: ['event', 'name', 'dateBegin', 'dateEnd', 'room', 'speaker'],
308
+ }),
309
+ },
310
+ search: {
311
+ 'event.event': search(EventEvent, {
312
+ fields: ['name', 'venue'],
313
+ filters: {
314
+ open: { label: 'Open for registration', domain: { state: 'published' } },
315
+ },
316
+ groupBy: ['state', 'organizer'],
317
+ }),
318
+ 'event.registration': search(EventRegistration, {
319
+ fields: ['name', 'email', 'code', 'event.name'],
320
+ filters: {
321
+ confirmed: { label: 'Confirmed', domain: { state: 'confirmed' }, group: 'state' },
322
+ waiting: { label: 'Waiting list', domain: { state: 'waiting' }, group: 'state' },
323
+ attended: { label: 'Checked in', domain: { state: 'attended' }, group: 'state' },
324
+ },
325
+ groupBy: ['event', 'ticket', 'state'],
326
+ }),
327
+ 'event.ticket': search(EventTicket, { fields: ['name', 'event.name'] }),
328
+ 'event.session': search(EventSession, { fields: ['name', 'speaker', 'event.name'] }),
329
+ },
330
+ });
331
+ }
332
+ /** Event Hub: SMTP from the environment for its emails. */
333
+ export const event = eventModule();
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Event Hub's emails (RFC 0021, Event Hub step 3): the confirmation to an attendee whose place is confirmed (also
3
+ * when the waiting list moves them up), with their ticket and check-in code, and the thanks to those who came, once
4
+ * the event ends. Through the outbox — each sent once (its time is kept on the registration), after the commit; a
5
+ * failed send is logged. In the attendee's language (English or Bangla, on the registration).
6
+ */
7
+ import { type Mailer } from '../mail/index.js';
8
+ import type { ModuleContext } from '../server/index.js';
9
+ import type { Worker } from '../server/orm.js';
10
+ export declare const CONFIRMED = "event.registration.confirmed";
11
+ export declare const ENDED = "event.event.ended";
12
+ /** The workers that send Event Hub's emails; `start()` them (the module does), tests `drain()` them. */
13
+ export declare function eventMailers(context: Pick<ModuleContext, 'shutro'> & {
14
+ readonly log?: (message: string, error: unknown) => void;
15
+ }, mailer: Mailer | undefined): Worker[];
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Event Hub's emails (RFC 0021, Event Hub step 3): the confirmation to an attendee whose place is confirmed (also
3
+ * when the waiting list moves them up), with their ticket and check-in code, and the thanks to those who came, once
4
+ * the event ends. Through the outbox — each sent once (its time is kept on the registration), after the commit; a
5
+ * failed send is logged. In the attendee's language (English or Bangla, on the registration).
6
+ */
7
+ import { layout } from '../mail/index.js';
8
+ export const CONFIRMED = 'event.registration.confirmed';
9
+ export const ENDED = 'event.event.ended';
10
+ const TEXTS = {
11
+ en: {
12
+ confirmedSubject: (event) => `Your place at ${event} is confirmed`,
13
+ confirmed: (name, event, when, venue) => `${name}, your registration for ${event} on ${when}${venue === '' ? '' : ` at ${venue}`} is confirmed.`,
14
+ ticket: (ticket) => `Ticket: ${ticket}.`,
15
+ code: (code) => `Your check-in code is ${code}: show it, or your printed ticket, at the door.`,
16
+ thanksSubject: (event) => `Thank you for coming to ${event}`,
17
+ thanks: (name, event) => `${name}, thank you for coming to ${event}. We hope to see you at the next one.`,
18
+ },
19
+ bn: {
20
+ confirmedSubject: (event) => `${event}-এ আপনার আসন নিশ্চিত হয়েছে`,
21
+ confirmed: (name, event, when, venue) => `${name}, ${when}${venue === '' ? '' : `, ${venue}`}-এ ${event}-এর জন্য আপনার নিবন্ধন নিশ্চিত হয়েছে।`,
22
+ ticket: (ticket) => `টিকিট: ${ticket}।`,
23
+ code: (code) => `আপনার চেক-ইন কোড ${code}: দরজায় এটি বা আপনার ছাপানো টিকিট দেখান।`,
24
+ thanksSubject: (event) => `${event}-এ আসার জন্য ধন্যবাদ`,
25
+ thanks: (name, event) => `${name}, ${event}-এ আসার জন্য ধন্যবাদ। পরের আয়োজনে আবার দেখা হবে আশা করি।`,
26
+ },
27
+ };
28
+ /** The workers that send Event Hub's emails; `start()` them (the module does), tests `drain()` them. */
29
+ export function eventMailers(context, mailer) {
30
+ const send = (env, mails) => {
31
+ if (mailer === undefined || mails.length === 0)
32
+ return;
33
+ env.afterCommit(async () => {
34
+ for (const mail of mails)
35
+ await mailer
36
+ .send(mail)
37
+ .catch((error) => context.log?.('event email not sent', error));
38
+ });
39
+ };
40
+ const textsOf = (lang) => (String(lang ?? '').startsWith('bn') ? TEXTS.bn : TEXTS.en);
41
+ return [
42
+ context.shutro.subscribe('event.confirmations', CONFIRMED, async (event, raw) => {
43
+ const env = raw;
44
+ const id = Number(event.payload?.id);
45
+ if (!Number.isInteger(id))
46
+ return;
47
+ await env.asSuperuser({ reason: 'event confirmation email' }, async (su) => {
48
+ const [r] = await su
49
+ .model('event.registration')
50
+ .browse([id])
51
+ .load({ event: true, ticket: true, partner: true });
52
+ if (r === undefined || r.state !== 'confirmed' || r.confirmationSentAt !== null)
53
+ return;
54
+ const to = r.email ?? r.partner?.email ?? null;
55
+ await su
56
+ .model('event.registration')
57
+ .browse([r.id])
58
+ .write({ confirmationSentAt: new Date() });
59
+ if (to === null || to === '')
60
+ return;
61
+ const t = textsOf(r.lang);
62
+ const when = r.event.dateBegin.toISOString().slice(0, 16).replace('T', ' ');
63
+ const subject = t.confirmedSubject(r.event.name);
64
+ send(su, [
65
+ {
66
+ to,
67
+ subject,
68
+ ...layout({
69
+ title: subject,
70
+ paragraphs: [
71
+ t.confirmed(r.name, r.event.name, when, r.event.venue ?? ''),
72
+ ...(r.ticket === null ? [] : [t.ticket(r.ticket.name)]),
73
+ t.code(r.code),
74
+ ],
75
+ lang: String(r.lang ?? 'en').slice(0, 2),
76
+ }),
77
+ },
78
+ ]);
79
+ });
80
+ }, { pollMs: 5000 }),
81
+ context.shutro.subscribe('event.thanks', ENDED, async (event, raw) => {
82
+ const env = raw;
83
+ const id = Number(event.payload?.id);
84
+ if (!Number.isInteger(id))
85
+ return;
86
+ await env.asSuperuser({ reason: 'event thanks email' }, async (su) => {
87
+ const came = await (await su.model('event.registration').search({
88
+ where: { event: id, state: 'attended', thanksSentAt: { isNull: true } },
89
+ })).load({ event: true, partner: true });
90
+ const mails = [];
91
+ for (const r of came) {
92
+ await su.model('event.registration').browse([r.id]).write({ thanksSentAt: new Date() });
93
+ const to = r.email ?? r.partner?.email ?? null;
94
+ if (to === null || to === '')
95
+ continue;
96
+ const t = textsOf(r.lang);
97
+ const subject = t.thanksSubject(r.event.name);
98
+ mails.push({
99
+ to,
100
+ subject,
101
+ ...layout({
102
+ title: subject,
103
+ paragraphs: [t.thanks(r.name, r.event.name)],
104
+ lang: String(r.lang ?? 'en').slice(0, 2),
105
+ }),
106
+ });
107
+ }
108
+ send(su, mails);
109
+ });
110
+ }, { pollMs: 5000 }),
111
+ ];
112
+ }