shutro-erp 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (618) hide show
  1. package/dist/account_asset/.kosh/bindings.d.ts +22 -0
  2. package/dist/account_asset/.kosh/bindings.js +15 -0
  3. package/dist/account_asset/.kosh/schema.d.ts +1755 -0
  4. package/dist/account_asset/board.d.ts +61 -0
  5. package/dist/account_asset/board.js +86 -0
  6. package/dist/account_asset/books.d.ts +42 -0
  7. package/dist/account_asset/books.js +398 -0
  8. package/dist/account_asset/demo.d.ts +7 -0
  9. package/dist/account_asset/demo.js +72 -0
  10. package/dist/account_asset/index.d.ts +9 -0
  11. package/dist/account_asset/index.js +335 -0
  12. package/dist/account_asset/module.d.ts +7622 -0
  13. package/dist/account_asset/module.js +230 -0
  14. package/dist/account_budget/.kosh/bindings.d.ts +22 -0
  15. package/dist/account_budget/.kosh/bindings.js +15 -0
  16. package/dist/account_budget/.kosh/schema.d.ts +1767 -0
  17. package/dist/account_budget/budget.d.ts +5 -0
  18. package/dist/account_budget/budget.js +142 -0
  19. package/dist/account_budget/demo.d.ts +6 -0
  20. package/dist/account_budget/demo.js +30 -0
  21. package/dist/account_budget/index.d.ts +4 -0
  22. package/dist/account_budget/index.js +223 -0
  23. package/dist/account_budget/module.d.ts +13863 -0
  24. package/dist/account_budget/module.js +167 -0
  25. package/dist/account_portal/.kosh/bindings.js +1 -1
  26. package/dist/account_portal/.kosh/schema.d.ts +176 -6
  27. package/dist/account_portal/module.d.ts +505 -6
  28. package/dist/accounting/.kosh/bindings.js +1 -1
  29. package/dist/accounting/.kosh/schema.d.ts +176 -6
  30. package/dist/accounting/index.d.ts +1 -1
  31. package/dist/accounting/index.js +344 -4
  32. package/dist/accounting/module.d.ts +513 -5
  33. package/dist/accounting/module.js +55 -3
  34. package/dist/approvals/.kosh/bindings.d.ts +22 -0
  35. package/dist/approvals/.kosh/bindings.js +15 -0
  36. package/dist/approvals/.kosh/schema.d.ts +611 -0
  37. package/dist/approvals/index.d.ts +18 -0
  38. package/dist/approvals/index.js +302 -0
  39. package/dist/approvals/mails.d.ts +25 -0
  40. package/dist/approvals/mails.js +82 -0
  41. package/dist/approvals/module.d.ts +1 -0
  42. package/dist/approvals/module.js +12 -0
  43. package/dist/approvals/web/History.d.ts +4 -0
  44. package/dist/approvals/web/History.js +13 -0
  45. package/dist/approvals/web/Inbox.d.ts +9 -0
  46. package/dist/approvals/web/Inbox.js +114 -0
  47. package/dist/approvals/web/Mine.d.ts +7 -0
  48. package/dist/approvals/web/Mine.js +27 -0
  49. package/dist/approvals/web/index.d.ts +13 -0
  50. package/dist/approvals/web/index.js +22 -0
  51. package/dist/approvals/web/messages.d.ts +2 -0
  52. package/dist/approvals/web/messages.js +119 -0
  53. package/dist/approvals/web/shared.d.ts +59 -0
  54. package/dist/approvals/web/shared.js +46 -0
  55. package/dist/attendance/.kosh/bindings.d.ts +22 -0
  56. package/dist/attendance/.kosh/bindings.js +15 -0
  57. package/dist/attendance/.kosh/schema.d.ts +1048 -0
  58. package/dist/attendance/days.d.ts +96 -0
  59. package/dist/attendance/days.js +430 -0
  60. package/dist/attendance/demo.d.ts +7 -0
  61. package/dist/attendance/demo.js +58 -0
  62. package/dist/attendance/index.d.ts +4 -0
  63. package/dist/attendance/index.js +513 -0
  64. package/dist/attendance/module.d.ts +3969 -0
  65. package/dist/attendance/module.js +412 -0
  66. package/dist/attendance/routes.d.ts +16 -0
  67. package/dist/attendance/routes.js +266 -0
  68. package/dist/attendance/web/Check.d.ts +8 -0
  69. package/dist/attendance/web/Check.js +40 -0
  70. package/dist/attendance/web/Kiosk.d.ts +8 -0
  71. package/dist/attendance/web/Kiosk.js +62 -0
  72. package/dist/attendance/web/PinButton.d.ts +7 -0
  73. package/dist/attendance/web/PinButton.js +29 -0
  74. package/dist/attendance/web/index.d.ts +13 -0
  75. package/dist/attendance/web/index.js +22 -0
  76. package/dist/attendance/web/messages.d.ts +2 -0
  77. package/dist/attendance/web/messages.js +61 -0
  78. package/dist/calendar/.kosh/bindings.d.ts +22 -0
  79. package/dist/calendar/.kosh/bindings.js +15 -0
  80. package/dist/calendar/.kosh/schema.d.ts +795 -0
  81. package/dist/calendar/demo.d.ts +6 -0
  82. package/dist/calendar/demo.js +24 -0
  83. package/dist/calendar/index.d.ts +3 -0
  84. package/dist/calendar/index.js +291 -0
  85. package/dist/calendar/module.d.ts +1705 -0
  86. package/dist/calendar/module.js +180 -0
  87. package/dist/cli/init.js +80 -0
  88. package/dist/cli/templates.d.ts +1 -1
  89. package/dist/cli/templates.js +226 -0
  90. package/dist/contacts/.kosh/bindings.js +1 -1
  91. package/dist/contacts/.kosh/schema.d.ts +78 -3
  92. package/dist/contacts/demo.js +30 -1
  93. package/dist/contacts/index.d.ts +1 -1
  94. package/dist/contacts/index.js +155 -9
  95. package/dist/contacts/module.d.ts +338 -2
  96. package/dist/contacts/module.js +93 -5
  97. package/dist/crm/.kosh/bindings.d.ts +22 -0
  98. package/dist/crm/.kosh/bindings.js +15 -0
  99. package/dist/crm/.kosh/schema.d.ts +800 -0
  100. package/dist/crm/demo.d.ts +6 -0
  101. package/dist/crm/demo.js +45 -0
  102. package/dist/crm/index.d.ts +3 -0
  103. package/dist/crm/index.js +361 -0
  104. package/dist/crm/module.d.ts +1845 -0
  105. package/dist/crm/module.js +208 -0
  106. package/dist/documents/.kosh/bindings.d.ts +22 -0
  107. package/dist/documents/.kosh/bindings.js +15 -0
  108. package/dist/documents/.kosh/schema.d.ts +862 -0
  109. package/dist/documents/demo.d.ts +6 -0
  110. package/dist/documents/demo.js +22 -0
  111. package/dist/documents/documents.d.ts +98 -0
  112. package/dist/documents/documents.js +318 -0
  113. package/dist/documents/index.d.ts +30 -0
  114. package/dist/documents/index.js +442 -0
  115. package/dist/documents/module.d.ts +2781 -0
  116. package/dist/documents/module.js +214 -0
  117. package/dist/documents/web/Versions.d.ts +7 -0
  118. package/dist/documents/web/Versions.js +45 -0
  119. package/dist/documents/web/index.d.ts +11 -0
  120. package/dist/documents/web/index.js +16 -0
  121. package/dist/documents/web/messages.d.ts +2 -0
  122. package/dist/documents/web/messages.js +21 -0
  123. package/dist/event/.kosh/bindings.d.ts +22 -0
  124. package/dist/event/.kosh/bindings.js +15 -0
  125. package/dist/event/.kosh/schema.d.ts +843 -0
  126. package/dist/event/demo.d.ts +6 -0
  127. package/dist/event/demo.js +65 -0
  128. package/dist/event/index.d.ts +14 -0
  129. package/dist/event/index.js +333 -0
  130. package/dist/event/mails.d.ts +15 -0
  131. package/dist/event/mails.js +112 -0
  132. package/dist/event/module.d.ts +2800 -0
  133. package/dist/event/module.js +323 -0
  134. package/dist/event/web/CheckInBox.d.ts +9 -0
  135. package/dist/event/web/CheckInBox.js +87 -0
  136. package/dist/event/web/index.d.ts +11 -0
  137. package/dist/event/web/index.js +16 -0
  138. package/dist/event/web/messages.d.ts +2 -0
  139. package/dist/event/web/messages.js +23 -0
  140. package/dist/event_account/.kosh/bindings.d.ts +22 -0
  141. package/dist/event_account/.kosh/bindings.js +15 -0
  142. package/dist/event_account/.kosh/schema.d.ts +1758 -0
  143. package/dist/event_account/index.d.ts +3 -0
  144. package/dist/event_account/index.js +28 -0
  145. package/dist/event_account/module.d.ts +7114 -0
  146. package/dist/event_account/module.js +90 -0
  147. package/dist/expense/.kosh/bindings.d.ts +22 -0
  148. package/dist/expense/.kosh/bindings.js +15 -0
  149. package/dist/expense/.kosh/schema.d.ts +1299 -0
  150. package/dist/expense/demo.d.ts +7 -0
  151. package/dist/expense/demo.js +69 -0
  152. package/dist/expense/index.d.ts +3 -0
  153. package/dist/expense/index.js +495 -0
  154. package/dist/expense/module.d.ts +4747 -0
  155. package/dist/expense/module.js +647 -0
  156. package/dist/expense_account/.kosh/bindings.d.ts +22 -0
  157. package/dist/expense_account/.kosh/bindings.js +15 -0
  158. package/dist/expense_account/.kosh/schema.d.ts +1936 -0
  159. package/dist/expense_account/index.d.ts +3 -0
  160. package/dist/expense_account/index.js +74 -0
  161. package/dist/expense_account/module.d.ts +9153 -0
  162. package/dist/expense_account/module.js +318 -0
  163. package/dist/helpdesk/.kosh/bindings.d.ts +22 -0
  164. package/dist/helpdesk/.kosh/bindings.js +15 -0
  165. package/dist/helpdesk/.kosh/schema.d.ts +885 -0
  166. package/dist/helpdesk/demo.d.ts +7 -0
  167. package/dist/helpdesk/demo.js +65 -0
  168. package/dist/helpdesk/hours.d.ts +17 -0
  169. package/dist/helpdesk/hours.js +94 -0
  170. package/dist/helpdesk/index.d.ts +10 -0
  171. package/dist/helpdesk/index.js +461 -0
  172. package/dist/helpdesk/module.d.ts +2940 -0
  173. package/dist/helpdesk/module.js +326 -0
  174. package/dist/helpdesk/routes.d.ts +12 -0
  175. package/dist/helpdesk/routes.js +132 -0
  176. package/dist/helpdesk/sla.d.ts +37 -0
  177. package/dist/helpdesk/sla.js +226 -0
  178. package/dist/hr/.kosh/bindings.d.ts +22 -0
  179. package/dist/hr/.kosh/bindings.js +15 -0
  180. package/dist/hr/.kosh/schema.d.ts +878 -0
  181. package/dist/hr/demo.d.ts +6 -0
  182. package/dist/hr/demo.js +60 -0
  183. package/dist/hr/index.d.ts +3 -0
  184. package/dist/hr/index.js +483 -0
  185. package/dist/hr/module.d.ts +2611 -0
  186. package/dist/hr/module.js +350 -0
  187. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  188. package/dist/l10n_bd/.kosh/schema.d.ts +176 -6
  189. package/dist/l10n_bd/module.d.ts +505 -6
  190. package/dist/l10n_bd_payroll/.kosh/bindings.d.ts +22 -0
  191. package/dist/l10n_bd_payroll/.kosh/bindings.js +15 -0
  192. package/dist/l10n_bd_payroll/.kosh/schema.d.ts +2312 -0
  193. package/dist/l10n_bd_payroll/data.d.ts +47 -0
  194. package/dist/l10n_bd_payroll/data.js +347 -0
  195. package/dist/l10n_bd_payroll/index.d.ts +4 -0
  196. package/dist/l10n_bd_payroll/index.js +24 -0
  197. package/dist/l10n_bd_payroll/module.d.ts +12932 -0
  198. package/dist/l10n_bd_payroll/module.js +86 -0
  199. package/dist/l10n_in/.kosh/bindings.js +1 -1
  200. package/dist/l10n_in/.kosh/schema.d.ts +176 -6
  201. package/dist/l10n_in/module.d.ts +527 -7
  202. package/dist/mail/inbound/handle.js +6 -1
  203. package/dist/maintenance/.kosh/bindings.d.ts +22 -0
  204. package/dist/maintenance/.kosh/bindings.js +15 -0
  205. package/dist/maintenance/.kosh/schema.d.ts +911 -0
  206. package/dist/maintenance/demo.d.ts +6 -0
  207. package/dist/maintenance/demo.js +60 -0
  208. package/dist/maintenance/index.d.ts +3 -0
  209. package/dist/maintenance/index.js +422 -0
  210. package/dist/maintenance/module.d.ts +3545 -0
  211. package/dist/maintenance/module.js +446 -0
  212. package/dist/maintenance_stock/.kosh/bindings.d.ts +22 -0
  213. package/dist/maintenance_stock/.kosh/bindings.js +15 -0
  214. package/dist/maintenance_stock/.kosh/schema.d.ts +1446 -0
  215. package/dist/maintenance_stock/index.d.ts +9 -0
  216. package/dist/maintenance_stock/index.js +52 -0
  217. package/dist/maintenance_stock/module.d.ts +7675 -0
  218. package/dist/maintenance_stock/module.js +192 -0
  219. package/dist/modules-sdk/index.d.ts +1 -1
  220. package/dist/modules-sdk/manifest.d.ts +109 -2
  221. package/dist/modules-sdk/manifest.js +35 -1
  222. package/dist/mrp/.kosh/bindings.d.ts +22 -0
  223. package/dist/mrp/.kosh/bindings.js +15 -0
  224. package/dist/mrp/.kosh/schema.d.ts +1453 -0
  225. package/dist/mrp/demo.d.ts +7 -0
  226. package/dist/mrp/demo.js +42 -0
  227. package/dist/mrp/index.d.ts +3 -0
  228. package/dist/mrp/index.js +393 -0
  229. package/dist/mrp/module.d.ts +9335 -0
  230. package/dist/mrp/module.js +757 -0
  231. package/dist/mrp_account/.kosh/bindings.d.ts +22 -0
  232. package/dist/mrp_account/.kosh/bindings.js +15 -0
  233. package/dist/mrp_account/.kosh/schema.d.ts +2299 -0
  234. package/dist/mrp_account/index.d.ts +3 -0
  235. package/dist/mrp_account/index.js +22 -0
  236. package/dist/mrp_account/module.d.ts +23151 -0
  237. package/dist/mrp_account/module.js +123 -0
  238. package/dist/payment/.kosh/bindings.js +1 -1
  239. package/dist/payment/.kosh/schema.d.ts +178 -7
  240. package/dist/payment/index.d.ts +41 -0
  241. package/dist/payment/index.js +131 -74
  242. package/dist/payment/module.d.ts +569 -12
  243. package/dist/payment/module.js +19 -3
  244. package/dist/payment/settle.js +10 -1
  245. package/dist/payroll/.kosh/bindings.d.ts +22 -0
  246. package/dist/payroll/.kosh/bindings.js +15 -0
  247. package/dist/payroll/.kosh/schema.d.ts +1390 -0
  248. package/dist/payroll/compute.d.ts +35 -0
  249. package/dist/payroll/compute.js +398 -0
  250. package/dist/payroll/demo.d.ts +55 -0
  251. package/dist/payroll/demo.js +121 -0
  252. package/dist/payroll/formula.d.ts +62 -0
  253. package/dist/payroll/formula.js +320 -0
  254. package/dist/payroll/index.d.ts +230 -0
  255. package/dist/payroll/index.js +713 -0
  256. package/dist/payroll/module.d.ts +9390 -0
  257. package/dist/payroll/module.js +606 -0
  258. package/dist/payroll/routes.d.ts +10 -0
  259. package/dist/payroll/routes.js +112 -0
  260. package/dist/payroll_account/.kosh/bindings.d.ts +22 -0
  261. package/dist/payroll_account/.kosh/bindings.js +15 -0
  262. package/dist/payroll_account/.kosh/schema.d.ts +2308 -0
  263. package/dist/payroll_account/index.d.ts +3 -0
  264. package/dist/payroll_account/index.js +40 -0
  265. package/dist/payroll_account/module.d.ts +12396 -0
  266. package/dist/payroll_account/module.js +256 -0
  267. package/dist/permissions/approvals.d.ts +131 -9
  268. package/dist/permissions/approvals.js +662 -57
  269. package/dist/permissions/index.d.ts +1 -1
  270. package/dist/permissions/index.js +2 -2
  271. package/dist/permissions/models.js +191 -12
  272. package/dist/permissions/routes.d.ts +6 -1
  273. package/dist/permissions/routes.js +37 -4
  274. package/dist/permissions/web/ApprovalPanel.d.ts +5 -4
  275. package/dist/permissions/web/ApprovalPanel.js +39 -13
  276. package/dist/permissions/web/index.d.ts +6 -4
  277. package/dist/permissions/web/index.js +13 -4
  278. package/dist/permissions/web/messages.d.ts +2 -0
  279. package/dist/permissions/web/messages.js +43 -0
  280. package/dist/portal/.kosh/bindings.js +1 -1
  281. package/dist/portal/.kosh/schema.d.ts +78 -3
  282. package/dist/portal/index.js +1 -1
  283. package/dist/portal/module.d.ts +166 -2
  284. package/dist/pos/.kosh/bindings.d.ts +22 -0
  285. package/dist/pos/.kosh/bindings.js +15 -0
  286. package/dist/pos/.kosh/schema.d.ts +1809 -0
  287. package/dist/pos/accounting.d.ts +10 -0
  288. package/dist/pos/accounting.js +149 -0
  289. package/dist/pos/demo.d.ts +6 -0
  290. package/dist/pos/demo.js +90 -0
  291. package/dist/pos/index.d.ts +9 -0
  292. package/dist/pos/index.js +353 -0
  293. package/dist/pos/module.d.ts +9783 -0
  294. package/dist/pos/module.js +423 -0
  295. package/dist/pos/routes.d.ts +39 -0
  296. package/dist/pos/routes.js +368 -0
  297. package/dist/pos/web/Register.d.ts +9 -0
  298. package/dist/pos/web/Register.js +252 -0
  299. package/dist/pos/web/index.d.ts +11 -0
  300. package/dist/pos/web/index.js +16 -0
  301. package/dist/pos/web/messages.d.ts +2 -0
  302. package/dist/pos/web/messages.js +87 -0
  303. package/dist/pos_stock/.kosh/bindings.d.ts +22 -0
  304. package/dist/pos_stock/.kosh/bindings.js +15 -0
  305. package/dist/pos_stock/.kosh/schema.d.ts +2208 -0
  306. package/dist/pos_stock/index.d.ts +9 -0
  307. package/dist/pos_stock/index.js +35 -0
  308. package/dist/pos_stock/module.d.ts +12803 -0
  309. package/dist/pos_stock/module.js +115 -0
  310. package/dist/product/.kosh/bindings.js +1 -1
  311. package/dist/product/.kosh/schema.d.ts +52 -1
  312. package/dist/product/index.js +94 -4
  313. package/dist/product/module.d.ts +45 -1
  314. package/dist/product/module.js +11 -3
  315. package/dist/project/.kosh/bindings.js +1 -1
  316. package/dist/project/.kosh/schema.d.ts +725 -3
  317. package/dist/project/index.d.ts +3 -1
  318. package/dist/project/index.js +1045 -18
  319. package/dist/project/module.d.ts +10168 -458
  320. package/dist/project/module.js +1578 -14
  321. package/dist/project/routes.d.ts +44 -0
  322. package/dist/project/routes.js +314 -0
  323. package/dist/project/schedule.d.ts +72 -0
  324. package/dist/project/schedule.js +192 -0
  325. package/dist/project/web/Refresh.d.ts +8 -0
  326. package/dist/project/web/Refresh.js +29 -0
  327. package/dist/project/web/Schedule.d.ts +9 -0
  328. package/dist/project/web/Schedule.js +93 -0
  329. package/dist/project/web/Structure.d.ts +8 -0
  330. package/dist/project/web/Structure.js +86 -0
  331. package/dist/project/web/Workload.d.ts +8 -0
  332. package/dist/project/web/Workload.js +43 -0
  333. package/dist/project/web/index.d.ts +14 -0
  334. package/dist/project/web/index.js +25 -0
  335. package/dist/project/web/messages.d.ts +2 -0
  336. package/dist/project/web/messages.js +85 -0
  337. package/dist/project_account/.kosh/bindings.d.ts +22 -0
  338. package/dist/project_account/.kosh/bindings.js +15 -0
  339. package/dist/project_account/.kosh/schema.d.ts +2241 -0
  340. package/dist/project_account/index.d.ts +3 -0
  341. package/dist/project_account/index.js +27 -0
  342. package/dist/project_account/module.d.ts +11539 -0
  343. package/dist/project_account/module.js +76 -0
  344. package/dist/project_portal/.kosh/bindings.js +1 -1
  345. package/dist/project_portal/.kosh/schema.d.ts +725 -3
  346. package/dist/project_portal/module.d.ts +5137 -119
  347. package/dist/project_purchase/.kosh/bindings.d.ts +22 -0
  348. package/dist/project_purchase/.kosh/bindings.js +15 -0
  349. package/dist/project_purchase/.kosh/schema.d.ts +2320 -0
  350. package/dist/project_purchase/index.d.ts +9 -0
  351. package/dist/project_purchase/index.js +22 -0
  352. package/dist/project_purchase/module.d.ts +25147 -0
  353. package/dist/project_purchase/module.js +56 -0
  354. package/dist/purchase/.kosh/bindings.js +1 -1
  355. package/dist/purchase/.kosh/schema.d.ts +150 -6
  356. package/dist/purchase/index.js +163 -3
  357. package/dist/purchase/module.d.ts +228 -5
  358. package/dist/purchase/module.js +30 -3
  359. package/dist/purchase_account/.kosh/bindings.js +1 -1
  360. package/dist/purchase_account/.kosh/schema.d.ts +180 -8
  361. package/dist/purchase_account/index.js +27 -1
  362. package/dist/purchase_account/module.d.ts +591 -30
  363. package/dist/purchase_account/module.js +13 -2
  364. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  365. package/dist/purchase_portal/.kosh/schema.d.ts +150 -6
  366. package/dist/purchase_portal/module.d.ts +372 -6
  367. package/dist/purchase_stock/.kosh/bindings.d.ts +22 -0
  368. package/dist/purchase_stock/.kosh/bindings.js +15 -0
  369. package/dist/purchase_stock/.kosh/schema.d.ts +1479 -0
  370. package/dist/purchase_stock/index.d.ts +7 -0
  371. package/dist/purchase_stock/index.js +98 -0
  372. package/dist/purchase_stock/module.d.ts +7535 -0
  373. package/dist/purchase_stock/module.js +380 -0
  374. package/dist/purchase_stock_account/.kosh/bindings.d.ts +22 -0
  375. package/dist/purchase_stock_account/.kosh/bindings.js +15 -0
  376. package/dist/purchase_stock_account/.kosh/schema.d.ts +2155 -0
  377. package/dist/purchase_stock_account/index.d.ts +3 -0
  378. package/dist/purchase_stock_account/index.js +24 -0
  379. package/dist/purchase_stock_account/module.d.ts +38015 -0
  380. package/dist/purchase_stock_account/module.js +168 -0
  381. package/dist/recruitment/.kosh/bindings.d.ts +22 -0
  382. package/dist/recruitment/.kosh/bindings.js +15 -0
  383. package/dist/recruitment/.kosh/schema.d.ts +991 -0
  384. package/dist/recruitment/demo.d.ts +6 -0
  385. package/dist/recruitment/demo.js +45 -0
  386. package/dist/recruitment/index.d.ts +3 -0
  387. package/dist/recruitment/index.js +385 -0
  388. package/dist/recruitment/module.d.ts +3249 -0
  389. package/dist/recruitment/module.js +194 -0
  390. package/dist/recruitment/web/HireButton.d.ts +9 -0
  391. package/dist/recruitment/web/HireButton.js +32 -0
  392. package/dist/recruitment/web/index.d.ts +11 -0
  393. package/dist/recruitment/web/index.js +16 -0
  394. package/dist/recruitment/web/messages.d.ts +2 -0
  395. package/dist/recruitment/web/messages.js +19 -0
  396. package/dist/reporting/documents.d.ts +5 -0
  397. package/dist/reporting/documents.js +28 -1
  398. package/dist/reporting/index.d.ts +1 -1
  399. package/dist/reporting/index.js +4 -2
  400. package/dist/reporting/models.js +9 -0
  401. package/dist/reporting/overviews.d.ts +16 -0
  402. package/dist/reporting/overviews.js +56 -0
  403. package/dist/reporting/routes-documents.js +1 -1
  404. package/dist/reporting/routes.js +138 -80
  405. package/dist/reporting/web/DashboardView.d.ts +11 -0
  406. package/dist/reporting/web/DashboardView.js +2 -2
  407. package/dist/reporting/web/DesignerView.js +5 -0
  408. package/dist/reporting/web/Overview.d.ts +9 -0
  409. package/dist/reporting/web/Overview.js +53 -0
  410. package/dist/reporting/web/index.d.ts +1 -0
  411. package/dist/reporting/web/index.js +4 -1
  412. package/dist/reporting/web/messages.js +9 -0
  413. package/dist/sale_account/.kosh/bindings.js +1 -1
  414. package/dist/sale_account/.kosh/schema.d.ts +181 -6
  415. package/dist/sale_account/index.js +22 -1
  416. package/dist/sale_account/module.d.ts +639 -38
  417. package/dist/sale_portal/.kosh/bindings.js +1 -1
  418. package/dist/sale_portal/.kosh/schema.d.ts +59 -3
  419. package/dist/sale_portal/module.d.ts +435 -5
  420. package/dist/sale_project/.kosh/bindings.js +1 -1
  421. package/dist/sale_project/.kosh/schema.d.ts +732 -3
  422. package/dist/sale_project/module.d.ts +5349 -208
  423. package/dist/sale_project/module.js +17 -1
  424. package/dist/sale_stock/.kosh/bindings.d.ts +22 -0
  425. package/dist/sale_stock/.kosh/bindings.js +15 -0
  426. package/dist/sale_stock/.kosh/schema.d.ts +1303 -0
  427. package/dist/sale_stock/index.d.ts +7 -0
  428. package/dist/sale_stock/index.js +84 -0
  429. package/dist/sale_stock/module.d.ts +7514 -0
  430. package/dist/sale_stock/module.js +146 -0
  431. package/dist/sales/.kosh/bindings.js +1 -1
  432. package/dist/sales/.kosh/schema.d.ts +59 -3
  433. package/dist/sales/index.js +195 -4
  434. package/dist/sales/module.d.ts +356 -3
  435. package/dist/sales/module.js +78 -3
  436. package/dist/server/api/metadata.d.ts +5 -0
  437. package/dist/server/api/metadata.js +5 -0
  438. package/dist/server/api/rest.js +64 -1
  439. package/dist/server/api/views.js +1 -0
  440. package/dist/server/app-settings.d.ts +7 -0
  441. package/dist/server/app-settings.js +10 -0
  442. package/dist/server/auth/base.js +9 -0
  443. package/dist/server/index.d.ts +1 -0
  444. package/dist/server/index.js +1 -0
  445. package/dist/settings/app-settings.d.ts +2 -0
  446. package/dist/settings/app-settings.js +518 -0
  447. package/dist/settings/index.d.ts +1 -0
  448. package/dist/settings/index.js +15 -0
  449. package/dist/settings/people.js +29 -5
  450. package/dist/settings/web/AppSettingsPage.d.ts +11 -0
  451. package/dist/settings/web/AppSettingsPage.js +210 -0
  452. package/dist/settings/web/OverviewPage.js +16 -3
  453. package/dist/settings/web/PeoplePage.js +20 -6
  454. package/dist/settings/web/index.d.ts +1 -0
  455. package/dist/settings/web/index.js +3 -0
  456. package/dist/settings/web/messages.js +118 -2
  457. package/dist/sign/.kosh/bindings.d.ts +22 -0
  458. package/dist/sign/.kosh/bindings.js +15 -0
  459. package/dist/sign/.kosh/schema.d.ts +990 -0
  460. package/dist/sign/index.d.ts +14 -0
  461. package/dist/sign/index.js +350 -0
  462. package/dist/sign/module.d.ts +4478 -0
  463. package/dist/sign/module.js +158 -0
  464. package/dist/sign/sign.d.ts +82 -0
  465. package/dist/sign/sign.js +474 -0
  466. package/dist/stock/.kosh/bindings.d.ts +22 -0
  467. package/dist/stock/.kosh/bindings.js +15 -0
  468. package/dist/stock/.kosh/schema.d.ts +1222 -0
  469. package/dist/stock/demo.d.ts +8 -0
  470. package/dist/stock/demo.js +82 -0
  471. package/dist/stock/index.d.ts +4 -0
  472. package/dist/stock/index.js +903 -0
  473. package/dist/stock/module.d.ts +7856 -0
  474. package/dist/stock/module.js +1880 -0
  475. package/dist/stock/replenish.d.ts +38 -0
  476. package/dist/stock/replenish.js +176 -0
  477. package/dist/stock/web/Replenishment.d.ts +8 -0
  478. package/dist/stock/web/Replenishment.js +84 -0
  479. package/dist/stock/web/ScanBox.d.ts +8 -0
  480. package/dist/stock/web/ScanBox.js +90 -0
  481. package/dist/stock/web/index.d.ts +13 -0
  482. package/dist/stock/web/index.js +20 -0
  483. package/dist/stock/web/messages.d.ts +2 -0
  484. package/dist/stock/web/messages.js +77 -0
  485. package/dist/stock_account/.kosh/bindings.d.ts +22 -0
  486. package/dist/stock_account/.kosh/bindings.js +15 -0
  487. package/dist/stock_account/.kosh/schema.d.ts +2068 -0
  488. package/dist/stock_account/index.d.ts +3 -0
  489. package/dist/stock_account/index.js +140 -0
  490. package/dist/stock_account/module.d.ts +11523 -0
  491. package/dist/stock_account/module.js +339 -0
  492. package/dist/storage/attachments.d.ts +5 -0
  493. package/dist/storage/attachments.js +12 -1
  494. package/dist/storage/models.js +3 -0
  495. package/dist/storage/routes.js +13 -2
  496. package/dist/todo/.kosh/bindings.d.ts +22 -0
  497. package/dist/todo/.kosh/bindings.js +15 -0
  498. package/dist/todo/.kosh/schema.d.ts +732 -0
  499. package/dist/todo/demo.d.ts +6 -0
  500. package/dist/todo/demo.js +35 -0
  501. package/dist/todo/index.d.ts +3 -0
  502. package/dist/todo/index.js +187 -0
  503. package/dist/todo/module.d.ts +1180 -0
  504. package/dist/todo/module.js +130 -0
  505. package/dist/trade_finance/.kosh/bindings.d.ts +22 -0
  506. package/dist/trade_finance/.kosh/bindings.js +15 -0
  507. package/dist/trade_finance/.kosh/schema.d.ts +884 -0
  508. package/dist/trade_finance/demo.d.ts +6 -0
  509. package/dist/trade_finance/demo.js +63 -0
  510. package/dist/trade_finance/index.d.ts +3 -0
  511. package/dist/trade_finance/index.js +415 -0
  512. package/dist/trade_finance/module.d.ts +3779 -0
  513. package/dist/trade_finance/module.js +507 -0
  514. package/dist/trade_finance_account/.kosh/bindings.d.ts +22 -0
  515. package/dist/trade_finance_account/.kosh/bindings.js +15 -0
  516. package/dist/trade_finance_account/.kosh/schema.d.ts +1830 -0
  517. package/dist/trade_finance_account/index.d.ts +3 -0
  518. package/dist/trade_finance_account/index.js +93 -0
  519. package/dist/trade_finance_account/module.d.ts +8074 -0
  520. package/dist/trade_finance_account/module.js +245 -0
  521. package/dist/trade_finance_purchase/.kosh/bindings.d.ts +22 -0
  522. package/dist/trade_finance_purchase/.kosh/bindings.js +15 -0
  523. package/dist/trade_finance_purchase/.kosh/schema.d.ts +1246 -0
  524. package/dist/trade_finance_purchase/index.d.ts +8 -0
  525. package/dist/trade_finance_purchase/index.js +50 -0
  526. package/dist/trade_finance_purchase/module.d.ts +4257 -0
  527. package/dist/trade_finance_purchase/module.js +44 -0
  528. package/dist/trade_finance_sale/.kosh/bindings.d.ts +22 -0
  529. package/dist/trade_finance_sale/.kosh/bindings.js +15 -0
  530. package/dist/trade_finance_sale/.kosh/schema.d.ts +1072 -0
  531. package/dist/trade_finance_sale/index.d.ts +8 -0
  532. package/dist/trade_finance_sale/index.js +50 -0
  533. package/dist/trade_finance_sale/module.d.ts +4281 -0
  534. package/dist/trade_finance_sale/module.js +44 -0
  535. package/dist/ui/api.d.ts +2 -1
  536. package/dist/ui/api.js +2 -1
  537. package/dist/ui/app.js +26 -13
  538. package/dist/ui/components/AppIcon.js +30 -1
  539. package/dist/ui/components/primitives.d.ts +2 -0
  540. package/dist/ui/components/primitives.js +7 -1
  541. package/dist/ui/i18n/messages.d.ts +27 -0
  542. package/dist/ui/i18n/messages.js +46 -0
  543. package/dist/ui/index.d.ts +1 -0
  544. package/dist/ui/index.js +1 -0
  545. package/dist/ui/offline/queue.d.ts +5 -1
  546. package/dist/ui/offline/queue.js +2 -0
  547. package/dist/ui/registry.d.ts +5 -0
  548. package/dist/ui/shell/Home.js +55 -4
  549. package/dist/ui/views/FormView.js +102 -19
  550. package/dist/ui/views/KanbanView.d.ts +2 -0
  551. package/dist/ui/views/KanbanView.js +40 -3
  552. package/dist/ui/views/ListView.js +110 -11
  553. package/dist/ui/views/StatsRow.d.ts +14 -0
  554. package/dist/ui/views/StatsRow.js +88 -0
  555. package/dist/ui/views/widgets.d.ts +2 -1
  556. package/dist/ui/views/widgets.js +200 -1
  557. package/dist/views/builders.d.ts +19 -2
  558. package/dist/views/builders.js +21 -1
  559. package/dist/views/condition.d.ts +6 -0
  560. package/dist/views/condition.js +20 -0
  561. package/dist/views/documents.d.ts +8 -0
  562. package/dist/views/extend.d.ts +2 -2
  563. package/dist/views/extend.js +11 -1
  564. package/dist/views/index.d.ts +2 -2
  565. package/dist/views/index.js +2 -2
  566. package/dist/views/types.d.ts +52 -5
  567. package/dist/views/validate.js +39 -1
  568. package/dist/website/.kosh/bindings.d.ts +22 -0
  569. package/dist/website/.kosh/bindings.js +15 -0
  570. package/dist/website/.kosh/schema.d.ts +881 -0
  571. package/dist/website/demo.d.ts +6 -0
  572. package/dist/website/demo.js +115 -0
  573. package/dist/website/index.d.ts +9 -0
  574. package/dist/website/index.js +251 -0
  575. package/dist/website/module.d.ts +2987 -0
  576. package/dist/website/module.js +282 -0
  577. package/dist/website/render.d.ts +45 -0
  578. package/dist/website/render.js +102 -0
  579. package/dist/website/routes.d.ts +12 -0
  580. package/dist/website/routes.js +246 -0
  581. package/dist/website/web/PagePreview.d.ts +7 -0
  582. package/dist/website/web/PagePreview.js +33 -0
  583. package/dist/website/web/index.d.ts +11 -0
  584. package/dist/website/web/index.js +16 -0
  585. package/dist/website/web/messages.d.ts +2 -0
  586. package/dist/website/web/messages.js +15 -0
  587. package/dist/website_crm/.kosh/bindings.d.ts +22 -0
  588. package/dist/website_crm/.kosh/bindings.js +15 -0
  589. package/dist/website_crm/.kosh/schema.d.ts +967 -0
  590. package/dist/website_crm/index.d.ts +3 -0
  591. package/dist/website_crm/index.js +39 -0
  592. package/dist/website_crm/module.d.ts +2956 -0
  593. package/dist/website_crm/module.js +44 -0
  594. package/dist/website_sale/.kosh/bindings.d.ts +22 -0
  595. package/dist/website_sale/.kosh/bindings.js +15 -0
  596. package/dist/website_sale/.kosh/schema.d.ts +2284 -0
  597. package/dist/website_sale/couriers.d.ts +50 -0
  598. package/dist/website_sale/couriers.js +59 -0
  599. package/dist/website_sale/demo.d.ts +7 -0
  600. package/dist/website_sale/demo.js +90 -0
  601. package/dist/website_sale/growth.d.ts +31 -0
  602. package/dist/website_sale/growth.js +163 -0
  603. package/dist/website_sale/index.d.ts +49 -0
  604. package/dist/website_sale/index.js +538 -0
  605. package/dist/website_sale/module.d.ts +22027 -0
  606. package/dist/website_sale/module.js +397 -0
  607. package/dist/website_sale/shipments.d.ts +16 -0
  608. package/dist/website_sale/shipments.js +113 -0
  609. package/dist/website_sale/shop.d.ts +81 -0
  610. package/dist/website_sale/shop.js +649 -0
  611. package/dist/website_sale_stock/.kosh/bindings.d.ts +22 -0
  612. package/dist/website_sale_stock/.kosh/bindings.js +15 -0
  613. package/dist/website_sale_stock/.kosh/schema.d.ts +2681 -0
  614. package/dist/website_sale_stock/index.d.ts +9 -0
  615. package/dist/website_sale_stock/index.js +20 -0
  616. package/dist/website_sale_stock/module.d.ts +24998 -0
  617. package/dist/website_sale_stock/module.js +28 -0
  618. package/package.json +200 -2
@@ -0,0 +1,368 @@
1
+ /**
2
+ * Counter's routes (RFC 0021): what the register needs in few calls — the shop's catalogue, the cashier's open
3
+ * session, a sale posted whole (lines and payments, accepted once by its client id, paid at once), a session
4
+ * closed with its count.
5
+ *
6
+ * - `GET /api/v1/counter/shops` the shops the cashier may sell in, with their payment methods
7
+ * - `GET /api/v1/counter/catalogue?shop=` products for the register: price, taxes, barcode, picture
8
+ * - `GET /api/v1/counter/session?shop=` the cashier's open session at the shop, or null
9
+ * - `POST /api/v1/counter/sessions` `{ shop, openingCash }`: a session opened
10
+ * - `POST /api/v1/counter/orders` a sale: `{ session, clientId, partner?, lines, payments, note? }`
11
+ * - `POST /api/v1/counter/sessions/:id/close` `{ closingCash, differenceReason? }`: the count, then closed
12
+ */
13
+ import { mailerFromEnv } from '../mail/index.js';
14
+ import { messengerFromEnv } from '../notify/index.js';
15
+ import { documentFields, renderDocument } from '../reporting/index.js';
16
+ import { HttpProblem, publicApiModels } from '../server/index.js';
17
+ import { orm } from '../server/orm.js';
18
+ const idOf = (value, what) => {
19
+ const id = Number(value);
20
+ if (!Number.isSafeInteger(id) || id <= 0)
21
+ throw new HttpProblem(400, 'invalid-id', 'Invalid id', `${what} is a positive integer.`);
22
+ return id;
23
+ };
24
+ const text = (value) => (typeof value === 'string' ? value.trim() : '');
25
+ export function registerCounterRoutes(context, options = {}) {
26
+ const { app } = context;
27
+ const mailer = 'mailer' in options ? options.mailer : mailerFromEnv();
28
+ const messenger = 'messenger' in options ? options.messenger : messengerFromEnv();
29
+ const models = publicApiModels(context.registry);
30
+ const fields = documentFields(models);
31
+ // `POST /api/v1/counter/orders/:id/receipt` `{ email?, phone? }`: the receipt sent — the document by email,
32
+ // a summary by text; what was sent is said back.
33
+ app.post('/api/v1/counter/orders/:id/receipt', async (request) => {
34
+ const id = idOf(request.params.id, 'id');
35
+ const body = (request.body ?? {});
36
+ const email = text(body.email);
37
+ const phone = text(body.phone);
38
+ if (email === '' && phone === '')
39
+ throw new HttpProblem(422, 'no-address', 'Nowhere to send', 'Give an email address or a phone number.');
40
+ const model = models.get('pos.order');
41
+ if (model === undefined)
42
+ throw new HttpProblem(404, 'unknown-model', 'Unknown model', 'pos.order');
43
+ return request.env(async (raw) => {
44
+ const env = raw;
45
+ const order = await orderOut(env, id);
46
+ const sent = [];
47
+ if (email !== '') {
48
+ if (mailer === undefined)
49
+ throw new HttpProblem(409, 'no-mailer', 'No email set up', 'Set SMTP_URL to send receipts by email.');
50
+ const [template] = (await (await env.model('report.template').search({ where: { key: 'pos.receipt' }, limit: 1 })).load([])).read(['id', 'name', 'layout', 'scope']);
51
+ if (template === undefined)
52
+ throw new HttpProblem(409, 'no-template', 'No receipt template', 'Run the apps\u2019 defaults.');
53
+ const rendered = await renderDocument(env, model, template, fields, { ids: [id] }, 'html');
54
+ await mailer.send({
55
+ to: email,
56
+ subject: `Receipt ${String(order['name'])}`,
57
+ text: receiptText(order),
58
+ html: rendered.content.toString('utf8'),
59
+ });
60
+ sent.push('email');
61
+ }
62
+ if (phone !== '') {
63
+ if (messenger === undefined)
64
+ throw new HttpProblem(409, 'no-messenger', 'No text messaging set up', 'Set up Twilio to send receipts by text.');
65
+ await messenger.send({ channel: 'sms', to: phone, text: receiptText(order) });
66
+ sent.push('sms');
67
+ }
68
+ return { sent };
69
+ });
70
+ });
71
+ app.get('/api/v1/counter/shops', async (request) => request.env(async (raw) => {
72
+ const env = raw;
73
+ const shops = await (await env.model('pos.shop').search({})).load({ methods: true });
74
+ return {
75
+ shops: shops.map((s) => ({
76
+ id: s.id,
77
+ name: s.name,
78
+ pricesIncludeTax: s.pricesIncludeTax,
79
+ receiptFooter: s.receiptFooter,
80
+ methods: [...s.methods].map((m) => ({ id: m.id, name: m.name, kind: m.kind })),
81
+ })),
82
+ };
83
+ }));
84
+ app.get('/api/v1/counter/catalogue', async (request) => request.env(async (raw) => {
85
+ const env = raw;
86
+ const products = await (await env
87
+ .model('product.product')
88
+ .search({ where: { saleOk: true }, order: ['name asc'], limit: 2000 })).load({ taxes: true, category: true, uom: true, image: true });
89
+ return {
90
+ products: products.map((p) => ({
91
+ id: p.id,
92
+ name: p.name,
93
+ code: p.code,
94
+ barcode: p.barcode,
95
+ price: p.listPrice.toString(),
96
+ category: p.category?.name ?? null,
97
+ uom: p.uom?.name ?? null,
98
+ image: p.image?.id ?? null,
99
+ taxes: [...p.taxes].map((t) => ({
100
+ id: t.id,
101
+ name: t.name,
102
+ amountType: t.amountType,
103
+ rate: t.rate.toString(),
104
+ fixedAmount: t.fixedAmount.toString(),
105
+ })),
106
+ })),
107
+ };
108
+ }));
109
+ app.get('/api/v1/counter/session', async (request) => request.env(async (raw) => {
110
+ const env = raw;
111
+ const shop = idOf(request.query.shop, 'shop');
112
+ const [session] = await env.model('pos.session').search({
113
+ where: { shop: shop, cashier: env.userId, state: 'open' },
114
+ limit: 1,
115
+ });
116
+ return { session: session === undefined ? null : sessionOut(session) };
117
+ }));
118
+ app.post('/api/v1/counter/sessions', async (request, reply) => {
119
+ const body = (request.body ?? {});
120
+ const shop = idOf(body.shop, 'shop');
121
+ const openingCash = text(body.openingCash) || '0';
122
+ const made = await request.env(async (raw) => {
123
+ const env = raw;
124
+ const created = await env
125
+ .model('pos.session')
126
+ .create([
127
+ { shop: shop, openingCash: orm.toDecimal(openingCash) },
128
+ ]);
129
+ await env.flush();
130
+ const [s] = await env.model('pos.session').browse(created.ids);
131
+ return s === undefined ? null : sessionOut(s);
132
+ }, { write: true });
133
+ return reply.status(201).send({ session: made });
134
+ });
135
+ app.post('/api/v1/counter/orders', async (request, reply) => {
136
+ const input = request.body;
137
+ if (input === null ||
138
+ typeof input !== 'object' ||
139
+ !Array.isArray(input.lines) ||
140
+ !Array.isArray(input.payments))
141
+ throw new HttpProblem(400, 'invalid-body', 'Invalid sale', 'Send the session, the lines and the payments.');
142
+ if (input.lines.length === 0)
143
+ throw new HttpProblem(422, 'empty-sale', 'Nothing to sell', 'Add at least one line.');
144
+ const session = idOf(input.session, 'session');
145
+ const clientId = text(input.clientId);
146
+ const outcome = await request.env(async (raw) => {
147
+ const env = raw;
148
+ // The same sale sent twice (a device back online): the first one, unchanged.
149
+ if (clientId !== '') {
150
+ const [known] = await env.model('pos.order').search({
151
+ where: { session: session, clientId },
152
+ limit: 1,
153
+ });
154
+ if (known !== undefined)
155
+ return { order: await orderOut(env, known.id), repeated: true };
156
+ }
157
+ const [current] = await env
158
+ .model('pos.session')
159
+ .browse([session])
160
+ .load({ shop: true });
161
+ if (current === undefined)
162
+ throw new HttpProblem(404, 'missing-record', 'Not found', 'No such session.');
163
+ const products = await env
164
+ .model('product.product')
165
+ .browse(input.lines.map((l) => idOf(l.product, 'product')))
166
+ .load({ taxes: true });
167
+ const byId = new Map(products.map((p) => [p.id, p]));
168
+ const created = await env.model('pos.order').create([
169
+ {
170
+ session: session,
171
+ pricesIncludeTax: current.shop.pricesIncludeTax === true,
172
+ ...(clientId === '' ? {} : { clientId }),
173
+ ...(input.partner === undefined || input.partner === null
174
+ ? {}
175
+ : { partner: idOf(input.partner, 'partner') }),
176
+ ...(text(input.note) === '' ? {} : { note: text(input.note) }),
177
+ lines: {
178
+ create: input.lines.map((l) => {
179
+ const product = byId.get(idOf(l.product, 'product'));
180
+ if (product === undefined)
181
+ throw new HttpProblem(422, 'unknown-product', 'Unknown product', `No product ${String(l.product)}.`);
182
+ return {
183
+ product: product.id,
184
+ name: product.name,
185
+ quantity: orm.toDecimal(text(l.quantity) || '1'),
186
+ priceUnit: orm.toDecimal(text(l.priceUnit) || product.listPrice.toString()),
187
+ discount: orm.toDecimal(text(l.discount) || '0'),
188
+ taxes: { set: [...product.taxes].map((t) => t.id) },
189
+ };
190
+ }),
191
+ },
192
+ payments: {
193
+ create: input.payments.map((p) => ({
194
+ method: idOf(p.method, 'method'),
195
+ amount: orm.toDecimal(text(p.amount) || '0'),
196
+ ...(text(p.reference) === '' ? {} : { reference: text(p.reference) }),
197
+ })),
198
+ },
199
+ },
200
+ ]);
201
+ await env.flush();
202
+ await env.model('pos.order').browse(created.ids).pay();
203
+ await env.flush();
204
+ return { order: await orderOut(env, created.ids[0]), repeated: false };
205
+ }, { write: true });
206
+ return reply.status(outcome.repeated ? 200 : 201).send(outcome);
207
+ });
208
+ // `POST /api/v1/counter/orders/:id/refund` `{ session, reason, lines?: [{ product, quantity }], method? }`:
209
+ // the sale (or part of it) taken back — a negative sale in the open session, paid back by the method given
210
+ // (the shop's cash by default), linked to the original. Cashiers refund the sales of their own open session;
211
+ // `pos.refund_any` (managers) refunds any sale.
212
+ app.post('/api/v1/counter/orders/:id/refund', async (request, reply) => {
213
+ const id = idOf(request.params.id, 'id');
214
+ const body = (request.body ?? {});
215
+ const session = idOf(body.session, 'session');
216
+ const reason = text(body.reason);
217
+ if (reason === '')
218
+ throw new HttpProblem(422, 'reason-missing', 'Say why', 'A refund needs its reason.');
219
+ const made = await request.env(async (raw) => {
220
+ const env = raw;
221
+ const [original] = await env
222
+ .model('pos.order')
223
+ .browse([id])
224
+ .load({ lines: { taxes: true }, payments: { method: true }, session: true });
225
+ if (original === undefined || original.state !== 'paid')
226
+ throw new HttpProblem(404, 'missing-record', 'Not found', 'No paid sale with this id.');
227
+ const [current] = await env
228
+ .model('pos.session')
229
+ .browse([session])
230
+ .load({ shop: { methods: true } });
231
+ if (current === undefined || current.state !== 'open')
232
+ throw new HttpProblem(409, 'session-closed', 'Session closed', 'Refund in an open session.');
233
+ const mine = original.session.id === current.id;
234
+ let may = mine;
235
+ if (!may) {
236
+ try {
237
+ may = env.access.has('pos.refund_any');
238
+ }
239
+ catch {
240
+ may = false;
241
+ }
242
+ }
243
+ if (!may)
244
+ throw new HttpProblem(403, 'refund-forbidden', 'Not yours to refund', 'Cashiers refund the sales of their own open session; a manager refunds any sale.');
245
+ const wanted = new Map();
246
+ for (const l of body.lines ?? [])
247
+ wanted.set(idOf(l.product, 'product'), text(l.quantity) || '0');
248
+ const lines = [...original.lines]
249
+ .map((l) => {
250
+ const qty = wanted.size === 0 ? l.quantity : orm.toDecimal(wanted.get(l.product.id) ?? '0');
251
+ return { line: l, qty };
252
+ })
253
+ .filter(({ qty }) => qty.gt(0));
254
+ if (lines.length === 0)
255
+ throw new HttpProblem(422, 'nothing-to-refund', 'Nothing to refund', 'Give the quantities taken back.');
256
+ for (const { line, qty } of lines)
257
+ if (qty.gt(line.quantity))
258
+ throw new HttpProblem(422, 'too-much', 'More than sold', `${line.name}: ${qty.toString()} of ${line.quantity.toString()} sold.`);
259
+ const methodId = body.method === undefined
260
+ ? ([...current.shop.methods].find((mm) => mm.kind === 'cash') ??
261
+ [...current.shop.methods][0])?.id
262
+ : idOf(body.method, 'method');
263
+ if (methodId === undefined)
264
+ throw new HttpProblem(409, 'no-method', 'No payment method', 'The shop has no payment method.');
265
+ const created = await env.model('pos.order').create([
266
+ {
267
+ session: session,
268
+ pricesIncludeTax: original.pricesIncludeTax,
269
+ partner: original.partner?.id ?? null,
270
+ refundOf: original.id,
271
+ refundReason: reason,
272
+ note: `Refund of ${original.name}: ${reason}`,
273
+ lines: {
274
+ create: lines.map(({ line, qty }) => ({
275
+ product: line.product.id,
276
+ name: line.name,
277
+ quantity: qty.neg(),
278
+ priceUnit: line.priceUnit,
279
+ discount: line.discount,
280
+ taxes: { set: [...line.taxes].map((t) => t.id) },
281
+ })),
282
+ },
283
+ },
284
+ ]);
285
+ await env.flush();
286
+ const [draft] = await env.model('pos.order').browse(created.ids);
287
+ const total = draft?.total ?? orm.toDecimal(0);
288
+ await env
289
+ .model('pos.order.payment')
290
+ .create([{ order: created.ids[0], method: methodId, amount: total }]);
291
+ await env.flush();
292
+ await env.model('pos.order').browse(created.ids).pay();
293
+ await env.flush();
294
+ return orderOut(env, created.ids[0]);
295
+ }, { write: true });
296
+ return reply.status(201).send({ order: made });
297
+ });
298
+ app.post('/api/v1/counter/sessions/:id/close', async (request) => {
299
+ const id = idOf(request.params.id, 'id');
300
+ const body = (request.body ?? {});
301
+ const closingCash = text(body.closingCash);
302
+ if (closingCash === '')
303
+ throw new HttpProblem(422, 'count-missing', 'Count the cash', 'Send the cash counted in the drawer.');
304
+ return request.env(async (raw) => {
305
+ const env = raw;
306
+ const set = env.model('pos.session').browse([id]);
307
+ await set.write({
308
+ closingCash: orm.toDecimal(closingCash),
309
+ ...(text(body.differenceReason) === ''
310
+ ? {}
311
+ : { differenceReason: text(body.differenceReason) }),
312
+ });
313
+ await env.flush();
314
+ await set.close();
315
+ await env.flush();
316
+ const [s] = await env.model('pos.session').browse([id]);
317
+ return { session: s === undefined ? null : sessionOut(s) };
318
+ }, { write: true });
319
+ });
320
+ }
321
+ const textOrNull = (v) => v === null || v === undefined ? null : String(v);
322
+ /** The receipt in a few lines of text (a text message, an email's plain part). */
323
+ function receiptText(order) {
324
+ const lines = order['lines'].map((l) => `${l.name} × ${l.quantity}: ${l.subtotal}`);
325
+ return [
326
+ `Receipt ${String(order['name'])}`,
327
+ ...lines,
328
+ `Total ${String(order['total'])} (taxes ${String(order['amountTax'])})`,
329
+ `Paid ${String(order['amountPaid'])}, change ${String(order['change'])}`,
330
+ ].join(NEWLINE);
331
+ }
332
+ const NEWLINE = String.fromCharCode(10);
333
+ function sessionOut(raw) {
334
+ const s = raw;
335
+ return {
336
+ id: s['id'],
337
+ name: String(s['name'] ?? ''),
338
+ state: String(s['state'] ?? ''),
339
+ openingCash: String(s['openingCash'] ?? '0'),
340
+ expectedCash: textOrNull(s['expectedCash']),
341
+ closingCash: textOrNull(s['closingCash']),
342
+ closingDifference: textOrNull(s['closingDifference']),
343
+ orderCount: typeof s['orderCount'] === 'number' ? s['orderCount'] : 0,
344
+ total: String(s['total'] ?? '0'),
345
+ };
346
+ }
347
+ async function orderOut(env, id) {
348
+ const [o] = await env.model('pos.order').browse([id]).load({ lines: true, payments: true });
349
+ if (o === undefined)
350
+ throw new HttpProblem(404, 'missing-record', 'Not found', 'No such sale.');
351
+ return {
352
+ id: o.id,
353
+ name: o.name,
354
+ state: o.state,
355
+ total: o.total?.toString() ?? '0',
356
+ amountTax: o.amountTax?.toString() ?? '0',
357
+ amountPaid: o.amountPaid?.toString() ?? '0',
358
+ change: o.change?.toString() ?? '0',
359
+ lines: [...o.lines].map((l) => ({
360
+ product: l.product.id,
361
+ name: l.name,
362
+ quantity: l.quantity.toString(),
363
+ priceUnit: l.priceUnit.toString(),
364
+ subtotal: l.subtotal?.toString() ?? '0',
365
+ })),
366
+ payments: [...o.payments].map((p) => ({ method: p.method.id, amount: p.amount.toString() })),
367
+ };
368
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * The register (RFC 0021, Counter): a page for a tablet or a phone. The shop's products as tiles with pictures,
3
+ * search and a barcode box (a scanner types into it; Enter adds the match), the cart, the payment screen with
4
+ * several methods and the change, the receipt to print, the session opened with its cash and closed with its
5
+ * count. Totals here are the register's own arithmetic for the cashier's eye; the server computes the sale.
6
+ */
7
+ import { type PageProps } from '../../ui/index.js';
8
+ import { type ReactElement } from 'react';
9
+ export declare function Register({ api }: PageProps): ReactElement;
@@ -0,0 +1,252 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The register (RFC 0021, Counter): a page for a tablet or a phone. The shop's products as tiles with pictures,
4
+ * search and a barcode box (a scanner types into it; Enter adds the match), the cart, the payment screen with
5
+ * several methods and the change, the receipt to print, the session opened with its cash and closed with its
6
+ * count. Totals here are the register's own arithmetic for the cashier's eye; the server computes the sale.
7
+ */
8
+ import { Alert, Button, Dialog, Input, Label, NetworkError, Spinner, useKosh, } from '../../ui/index.js';
9
+ import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
10
+ import { useEffect, useMemo, useRef, useState } from 'react';
11
+ import { useTranslation } from 'react-i18next';
12
+ const money = (n) => n.toFixed(2);
13
+ /** The line's amount as the customer pays it (taxes added when the shop's prices do not include them). */
14
+ function lineAmount(line, pricesIncludeTax) {
15
+ const gross = Number(line.product.price) * line.quantity;
16
+ if (pricesIncludeTax)
17
+ return gross;
18
+ const percent = line.product.taxes
19
+ .filter((t) => t.amountType === 'percent')
20
+ .reduce((sum, t) => sum + Number(t.rate), 0);
21
+ const fixed = line.product.taxes
22
+ .filter((t) => t.amountType === 'fixed')
23
+ .reduce((sum, t) => sum + Number(t.fixedAmount) * line.quantity, 0);
24
+ return gross + (gross * percent) / 100 + fixed;
25
+ }
26
+ export function Register({ api }) {
27
+ const { t } = useTranslation('pos');
28
+ const { format, offline } = useKosh();
29
+ const queryClient = useQueryClient();
30
+ const [queued, setQueued] = useState(0);
31
+ const shops = useQuery({
32
+ queryKey: ['counter', 'shops'],
33
+ queryFn: () => api.call('GET', '/api/v1/counter/shops').then((r) => r.shops),
34
+ });
35
+ const [shopId, setShopId] = useState(null);
36
+ const shop = (shops.data ?? []).find((s) => s.id === shopId) ??
37
+ (shops.data?.length === 1 ? shops.data[0] : undefined);
38
+ const session = useQuery({
39
+ queryKey: ['counter', 'session', shop?.id],
40
+ queryFn: () => api
41
+ .call('GET', `/api/v1/counter/session?shop=${shop?.id ?? 0}`)
42
+ .then((r) => r.session),
43
+ enabled: shop !== undefined,
44
+ });
45
+ const catalogue = useQuery({
46
+ queryKey: ['counter', 'catalogue', shop?.id],
47
+ queryFn: () => api
48
+ .call('GET', `/api/v1/counter/catalogue?shop=${shop?.id ?? 0}`)
49
+ .then((r) => r.products),
50
+ enabled: shop !== undefined,
51
+ });
52
+ const [query, setQuery] = useState('');
53
+ const [cart, setCart] = useState([]);
54
+ const [paying, setPaying] = useState(false);
55
+ const [closing, setClosing] = useState(false);
56
+ const [opening, setOpening] = useState('0');
57
+ const [closingCash, setClosingCash] = useState('');
58
+ const [reason, setReason] = useState('');
59
+ const [payments, setPayments] = useState({});
60
+ const [lastSale, setLastSale] = useState(null);
61
+ const [error, setError] = useState(null);
62
+ const clientId = useRef(crypto.randomUUID());
63
+ const search = useRef(null);
64
+ const products = catalogue.data ?? [];
65
+ const shown = useMemo(() => {
66
+ const q = query.trim().toLowerCase();
67
+ if (q === '')
68
+ return products.slice(0, 200);
69
+ return products
70
+ .filter((p) => p.name.toLowerCase().includes(q) ||
71
+ (p.code ?? '').toLowerCase().includes(q) ||
72
+ (p.barcode ?? '').toLowerCase() === q)
73
+ .slice(0, 200);
74
+ }, [products, query]);
75
+ const total = cart.reduce((sum, l) => sum + lineAmount(l, shop?.pricesIncludeTax ?? true), 0);
76
+ const paid = Object.values(payments).reduce((sum, v) => sum + (Number(v) || 0), 0);
77
+ const change = Math.max(0, paid - total);
78
+ const add = (p) => {
79
+ setCart((lines) => {
80
+ const i = lines.findIndex((l) => l.product.id === p.id);
81
+ if (i < 0)
82
+ return [...lines, { product: p, quantity: 1 }];
83
+ const next = [...lines];
84
+ next[i] = { product: p, quantity: (next[i]?.quantity ?? 0) + 1 };
85
+ return next;
86
+ });
87
+ };
88
+ const setQuantity = (id, quantity) => setCart((lines) => lines
89
+ .map((l) => (l.product.id === id ? { product: l.product, quantity } : l))
90
+ .filter((l) => l.quantity > 0 || l.quantity === 0));
91
+ const removeLine = (id) => setCart((lines) => lines.filter((l) => l.product.id !== id));
92
+ // A scanner types the barcode and sends Enter: the exact match goes to the cart, the box clears.
93
+ const onSearchKey = (key) => {
94
+ if (key !== 'Enter')
95
+ return;
96
+ const q = query.trim().toLowerCase();
97
+ const hit = products.find((p) => (p.barcode ?? '').toLowerCase() === q) ??
98
+ (shown.length === 1 ? shown[0] : undefined);
99
+ if (hit !== undefined) {
100
+ add(hit);
101
+ setQuery('');
102
+ }
103
+ };
104
+ useEffect(() => {
105
+ search.current?.focus();
106
+ }, []);
107
+ const open = useMutation({
108
+ mutationFn: () => api.call('POST', '/api/v1/counter/sessions', {
109
+ shop: shop?.id,
110
+ openingCash: opening,
111
+ }),
112
+ onSuccess: () => void queryClient.invalidateQueries({ queryKey: ['counter', 'session'] }),
113
+ onError: (e) => setError(e instanceof Error ? e.message : String(e)),
114
+ });
115
+ const sell = useMutation({
116
+ mutationFn: async () => {
117
+ const body = {
118
+ session: session.data?.id,
119
+ clientId: clientId.current,
120
+ lines: cart.map((l) => ({ product: l.product.id, quantity: String(l.quantity) })),
121
+ payments: Object.entries(payments)
122
+ .filter(([, amount]) => Number(amount) > 0)
123
+ .map(([method, amount]) => ({ method: Number(method), amount })),
124
+ };
125
+ try {
126
+ const r = await api.call('POST', '/api/v1/counter/orders', body);
127
+ return { order: r.order, queued: false };
128
+ }
129
+ catch (error) {
130
+ // Offline: the sale waits on the device, sent in order when the network is back, accepted once by
131
+ // its device reference (offline/queue.ts).
132
+ if (!(error instanceof NetworkError) || offline === undefined)
133
+ throw error;
134
+ await offline.add({
135
+ kind: 'request',
136
+ method: 'POST',
137
+ path: '/api/v1/counter/orders',
138
+ model: 'pos.order',
139
+ modelLabel: t('cart'),
140
+ recordId: null,
141
+ title: `${t('cart')} ${format.decimal(money(total))}`,
142
+ values: body,
143
+ etag: null,
144
+ });
145
+ return { order: null, queued: true };
146
+ }
147
+ },
148
+ onSuccess: (r) => {
149
+ if (r.queued)
150
+ setQueued((n) => n + 1);
151
+ setLastSale(r.order);
152
+ setCart([]);
153
+ setPayments({});
154
+ setPaying(false);
155
+ setError(null);
156
+ clientId.current = crypto.randomUUID();
157
+ void queryClient.invalidateQueries({ queryKey: ['counter', 'session'] });
158
+ search.current?.focus();
159
+ },
160
+ onError: (e) => setError(e instanceof Error ? e.message : String(e)),
161
+ });
162
+ const close = useMutation({
163
+ mutationFn: () => api.call('POST', `/api/v1/counter/sessions/${session.data?.id}/close`, {
164
+ closingCash,
165
+ differenceReason: reason,
166
+ }),
167
+ onSuccess: (r) => {
168
+ setClosing(false);
169
+ setLastSale(null);
170
+ setError(null);
171
+ void queryClient.invalidateQueries({ queryKey: ['counter', 'session'] });
172
+ setClosedInfo(r.session);
173
+ },
174
+ onError: (e) => setError(e instanceof Error ? e.message : String(e)),
175
+ });
176
+ const [closedInfo, setClosedInfo] = useState(null);
177
+ const [sendTo, setSendTo] = useState('');
178
+ const [refundReason, setRefundReason] = useState('');
179
+ // The whole last sale taken back, paid back in cash, with its reason.
180
+ const refund = useMutation({
181
+ mutationFn: (id) => api.call('POST', `/api/v1/counter/orders/${id}/refund`, {
182
+ session: session.data?.id,
183
+ reason: refundReason.trim(),
184
+ }),
185
+ onSuccess: (r) => {
186
+ setLastSale(r.order);
187
+ setRefundReason('');
188
+ setError(null);
189
+ void queryClient.invalidateQueries({ queryKey: ['counter', 'session'] });
190
+ },
191
+ onError: (e) => setError(e instanceof Error ? e.message : String(e)),
192
+ });
193
+ const [sentNote, setSentNote] = useState(null);
194
+ // The receipt by email or text: an address with an @ is an email, anything else a phone number.
195
+ const sendReceipt = useMutation({
196
+ mutationFn: (id) => api.call('POST', `/api/v1/counter/orders/${id}/receipt`, {
197
+ ...(sendTo.includes('@') ? { email: sendTo.trim() } : { phone: sendTo.trim() }),
198
+ }),
199
+ onSuccess: (r) => {
200
+ setSentNote(t('sent', { how: r.sent.join(', ') }));
201
+ setSendTo('');
202
+ },
203
+ onError: (e) => setError(e instanceof Error ? e.message : String(e)),
204
+ });
205
+ const printReceipt = async (sale) => {
206
+ try {
207
+ const { templates } = await api.call('GET', '/api/v1/models/pos.order/documents');
208
+ const template = templates.find((tpl) => tpl.name === 'Receipt') ??
209
+ templates.find((tpl) => tpl.scope === 'record');
210
+ if (template === undefined)
211
+ throw new Error(t('noReceiptTemplate'));
212
+ const file = await api.download(`/api/v1/models/pos.order/documents/${template.id}`, {
213
+ ids: [sale.id],
214
+ });
215
+ const url = URL.createObjectURL(file.blob);
216
+ window.open(url, '_blank', 'noopener');
217
+ setTimeout(() => URL.revokeObjectURL(url), 60_000);
218
+ }
219
+ catch (e) {
220
+ setError(e instanceof Error ? e.message : String(e));
221
+ }
222
+ };
223
+ if (shops.isPending)
224
+ return _jsx(Spinner, { label: t('loading') });
225
+ if ((shops.data ?? []).length === 0)
226
+ return _jsx(Alert, { tone: "info", children: t('noShops') });
227
+ if (shop === undefined)
228
+ return (_jsxs("div", { className: "grid gap-3", children: [_jsx("h2", { className: "text-base font-semibold", children: t('chooseShop') }), _jsx("div", { className: "grid grid-cols-[repeat(auto-fill,minmax(12rem,1fr))] gap-3", children: (shops.data ?? []).map((s) => (_jsx(Button, { size: "sm", variant: "secondary", onClick: () => setShopId(s.id), children: s.name }, s.id))) })] }));
229
+ if (session.isPending)
230
+ return _jsx(Spinner, { label: t('loading') });
231
+ if (session.data === null || session.data === undefined)
232
+ return (_jsxs("div", { className: "mx-auto grid max-w-sm gap-3", children: [_jsx("h2", { className: "text-base font-semibold", children: t('openSession', { shop: shop.name }) }), closedInfo === null ? null : (_jsx(Alert, { tone: "info", children: t('closedSummary', {
233
+ name: closedInfo.name,
234
+ total: format.decimal(closedInfo.total),
235
+ difference: format.decimal(closedInfo.closingDifference ?? '0'),
236
+ }) })), _jsxs("div", { className: "grid gap-1", children: [_jsx(Label, { htmlFor: "counter-opening", children: t('openingCash') }), _jsx(Input, { id: "counter-opening", inputMode: "decimal", value: opening, onChange: (e) => setOpening(e.target.value) })] }), error === null ? null : _jsx(Alert, { children: error }), _jsx(Button, { variant: "primary", busy: open.isPending, onClick: () => open.mutate(), children: t('open') })] }));
237
+ const current = session.data;
238
+ return (_jsxs("div", { className: "grid gap-3 lg:grid-cols-[1fr_22rem]", children: [_jsxs("section", { "aria-label": t('products'), className: "grid content-start gap-3", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Input, { ref: search, "aria-label": t('search'), placeholder: t('searchHint'), value: query, onChange: (e) => setQuery(e.target.value), onKeyDown: (e) => onSearchKey(e.key), className: "max-w-md" }), _jsx("span", { className: "ms-auto text-xs text-muted-foreground", children: t('sessionLine', {
239
+ name: current.name,
240
+ count: current.orderCount,
241
+ total: format.decimal(current.total),
242
+ }) }), _jsx(Button, { size: "sm", variant: "ghost", onClick: () => setClosing(true), children: t('closeSession') })] }), catalogue.isPending ? _jsx(Spinner, { label: t('loading') }) : null, _jsx("ul", { className: "grid grid-cols-[repeat(auto-fill,minmax(9rem,1fr))] gap-2", children: shown.map((p) => (_jsx("li", { children: _jsxs("button", { type: "button", className: "grid w-full gap-1 rounded-lg border bg-surface p-2 text-start hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring", onClick: () => add(p), children: [p.image === null ? (_jsx("span", { className: "aspect-square w-full rounded bg-muted", "aria-hidden": true })) : (_jsx("img", { src: `/api/v1/attachments/${p.image}/download?inline=true&w=256`, alt: "", className: "aspect-square w-full rounded object-cover" })), _jsx("span", { className: "line-clamp-2 text-sm font-medium", children: p.name }), _jsx("span", { className: "tabular-nums text-xs text-muted-foreground", children: format.decimal(p.price) })] }) }, p.id))) })] }), _jsxs("aside", { "aria-label": t('cart'), className: "grid content-start gap-3 rounded-lg border bg-surface p-3", children: [_jsx("h2", { className: "text-sm font-semibold", children: t('cart') }), cart.length === 0 ? (_jsx("p", { className: "text-sm text-muted-foreground", children: t('emptyCart') })) : null, _jsx("ul", { className: "grid gap-2", children: cart.map((l) => (_jsxs("li", { className: "grid grid-cols-[1fr_auto_auto] items-center gap-2 text-sm", children: [_jsx("span", { className: "truncate", children: l.product.name }), _jsxs("span", { className: "inline-flex items-center gap-1", children: [_jsx(Button, { size: "icon", variant: "ghost", "aria-label": t('less', { name: l.product.name }), onClick: () => l.quantity <= 1
243
+ ? removeLine(l.product.id)
244
+ : setQuantity(l.product.id, l.quantity - 1), children: "\u2212" }), _jsx("input", { "aria-label": t('quantityOf', { name: l.product.name }), inputMode: "decimal", className: "w-14 rounded border bg-surface px-1 text-center tabular-nums", value: String(l.quantity), onChange: (e) => {
245
+ const q = Number(e.target.value);
246
+ if (Number.isFinite(q) && q >= 0)
247
+ setQuantity(l.product.id, q);
248
+ } }), _jsx(Button, { size: "icon", variant: "ghost", "aria-label": t('more', { name: l.product.name }), onClick: () => setQuantity(l.product.id, l.quantity + 1), children: "+" })] }), _jsx("span", { className: "tabular-nums", children: format.decimal(money(lineAmount(l, shop.pricesIncludeTax))) })] }, l.product.id))) }), _jsxs("div", { className: "flex items-baseline justify-between border-t pt-2", children: [_jsx("span", { className: "text-sm font-semibold", children: t('total') }), _jsx("span", { className: "text-xl font-semibold tabular-nums", "aria-live": "polite", children: format.decimal(money(total)) })] }), _jsx(Button, { variant: "primary", disabled: cart.length === 0, onClick: () => setPaying(true), children: t('pay') }), queued === 0 ? null : (_jsx("p", { role: "status", className: "text-xs text-muted-foreground", children: t('queued', { count: queued }) })), lastSale === null ? null : (_jsxs("div", { role: "status", className: "grid gap-1 rounded-md border bg-muted/40 p-2 text-sm", children: [_jsx("span", { children: t('soldLine', { name: lastSale.name, total: format.decimal(lastSale.total) }) }), Number(lastSale.change) > 0 ? (_jsx("span", { className: "font-semibold", children: t('changeLine', { change: format.decimal(lastSale.change) }) })) : null, _jsx(Button, { size: "sm", variant: "secondary", onClick: () => void printReceipt(lastSale), children: t('printReceipt') }), _jsxs("div", { className: "grid grid-cols-[1fr_auto] gap-1", children: [_jsx(Input, { "aria-label": t('sendTo'), placeholder: t('sendToHint'), value: sendTo, onChange: (e) => setSendTo(e.target.value) }), _jsx(Button, { size: "sm", variant: "ghost", busy: sendReceipt.isPending, disabled: sendTo.trim() === '', onClick: () => sendReceipt.mutate(lastSale.id), children: t('send') })] }), sentNote === null ? null : (_jsx("span", { className: "text-xs text-muted-foreground", children: sentNote })), _jsxs("div", { className: "grid grid-cols-[1fr_auto] gap-1", children: [_jsx(Input, { "aria-label": t('refundReason'), placeholder: t('refundReasonHint'), value: refundReason, onChange: (e) => setRefundReason(e.target.value) }), _jsx(Button, { size: "sm", variant: "ghost", busy: refund.isPending, disabled: refundReason.trim() === '', onClick: () => refund.mutate(lastSale.id), children: t('refund') })] })] })), error === null ? null : _jsx(Alert, { children: error })] }), _jsx(Dialog, { open: paying, onOpenChange: setPaying, title: t('payTitle', { total: format.decimal(money(total)) }), children: _jsxs("div", { className: "grid gap-3", children: [shop.methods.map((m) => (_jsxs("div", { className: "grid grid-cols-[1fr_8rem] items-center gap-2", children: [_jsx(Label, { htmlFor: `pay-${m.id}`, children: m.name }), _jsx(Input, { id: `pay-${m.id}`, inputMode: "decimal", value: payments[m.id] ?? '', onFocus: () => {
249
+ if ((payments[m.id] ?? '') === '' && paid < total)
250
+ setPayments({ ...payments, [m.id]: money(total - paid) });
251
+ }, onChange: (e) => setPayments({ ...payments, [m.id]: e.target.value }) })] }, m.id))), _jsxs("div", { className: "flex justify-between text-sm", children: [_jsx("span", { children: t('paid') }), _jsx("span", { className: "tabular-nums", children: format.decimal(money(paid)) })] }), _jsxs("div", { className: "flex justify-between text-sm font-semibold", children: [_jsx("span", { children: t('change') }), _jsx("span", { className: "tabular-nums", children: format.decimal(money(change)) })] }), error === null ? null : _jsx(Alert, { children: error }), _jsx(Button, { variant: "primary", disabled: paid + 0.004 < total, busy: sell.isPending, onClick: () => sell.mutate(), children: t('confirmSale') })] }) }), _jsx(Dialog, { open: closing, onOpenChange: setClosing, title: t('closeTitle', { name: current.name }), children: _jsxs("div", { className: "grid gap-3", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: t('expectedCash', { amount: format.decimal(current.expectedCash ?? '0') }) }), _jsxs("div", { className: "grid gap-1", children: [_jsx(Label, { htmlFor: "counter-closing", children: t('closingCash') }), _jsx(Input, { id: "counter-closing", inputMode: "decimal", value: closingCash, onChange: (e) => setClosingCash(e.target.value) })] }), _jsxs("div", { className: "grid gap-1", children: [_jsx(Label, { htmlFor: "counter-reason", children: t('differenceReason') }), _jsx(Input, { id: "counter-reason", value: reason, onChange: (e) => setReason(e.target.value) })] }), error === null ? null : _jsx(Alert, { children: error }), _jsx(Button, { variant: "primary", busy: close.isPending, onClick: () => close.mutate(), children: t('closeSession') })] }) })] }));
252
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * The web contribution of `shutro-erp/pos`: the register page.
4
+ */
5
+ import { type Registry } from '../../ui/index.js';
6
+ import type { i18n } from 'i18next';
7
+ import { messages } from './messages.js';
8
+ export { Register } from './Register.js';
9
+ /** Registers the page (and the messages in `i18n`, when given). */
10
+ export declare function registerPosWeb(registry: Registry, i18n?: i18n): Registry;
11
+ export { messages as posMessages };