shutro-erp 0.3.6 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (623) hide show
  1. package/dist/account_asset/.kosh/bindings.js +1 -1
  2. package/dist/account_asset/.kosh/schema.d.ts +530 -8
  3. package/dist/account_asset/i18n/bn.d.ts +2 -0
  4. package/dist/account_asset/i18n/bn.js +97 -0
  5. package/dist/account_asset/index.js +2 -0
  6. package/dist/account_asset/module.d.ts +4899 -1445
  7. package/dist/account_budget/.kosh/bindings.js +1 -1
  8. package/dist/account_budget/.kosh/schema.d.ts +1130 -56
  9. package/dist/account_budget/i18n/bn.d.ts +2 -0
  10. package/dist/account_budget/i18n/bn.js +63 -0
  11. package/dist/account_budget/index.js +14 -0
  12. package/dist/account_budget/module.d.ts +17958 -4532
  13. package/dist/account_budget/module.js +15 -0
  14. package/dist/account_portal/.kosh/bindings.js +1 -1
  15. package/dist/account_portal/.kosh/schema.d.ts +530 -8
  16. package/dist/account_portal/i18n/bn.d.ts +2 -0
  17. package/dist/account_portal/i18n/bn.js +6 -0
  18. package/dist/account_portal/index.js +2 -0
  19. package/dist/account_portal/module.d.ts +5167 -1693
  20. package/dist/accounting/.kosh/bindings.js +1 -1
  21. package/dist/accounting/.kosh/schema.d.ts +530 -8
  22. package/dist/accounting/closing.js +1 -0
  23. package/dist/accounting/fiscal.d.ts +578 -0
  24. package/dist/accounting/fiscal.js +401 -0
  25. package/dist/accounting/followups.d.ts +618 -0
  26. package/dist/accounting/followups.js +420 -0
  27. package/dist/accounting/i18n/bn.d.ts +2 -0
  28. package/dist/accounting/i18n/bn.js +425 -0
  29. package/dist/accounting/index.d.ts +2 -0
  30. package/dist/accounting/index.js +649 -2
  31. package/dist/accounting/module.d.ts +4998 -1451
  32. package/dist/accounting/module.js +124 -18
  33. package/dist/accounting/statements.d.ts +878 -0
  34. package/dist/accounting/statements.js +728 -0
  35. package/dist/accounting/web/BankReconcilePage.d.ts +9 -0
  36. package/dist/accounting/web/BankReconcilePage.js +138 -0
  37. package/dist/accounting/web/FollowupsPage.d.ts +8 -0
  38. package/dist/accounting/web/FollowupsPage.js +54 -0
  39. package/dist/accounting/web/PostingPreview.d.ts +8 -0
  40. package/dist/accounting/web/PostingPreview.js +26 -0
  41. package/dist/accounting/web/index.d.ts +3 -0
  42. package/dist/accounting/web/index.js +9 -0
  43. package/dist/accounting/web/messages.js +140 -0
  44. package/dist/approvals/.kosh/bindings.js +1 -1
  45. package/dist/approvals/.kosh/schema.d.ts +272 -4
  46. package/dist/approvals/demo.d.ts +7 -0
  47. package/dist/approvals/demo.js +41 -0
  48. package/dist/approvals/i18n/bn.d.ts +2 -0
  49. package/dist/approvals/i18n/bn.js +133 -0
  50. package/dist/approvals/index.d.ts +19 -1
  51. package/dist/approvals/index.js +477 -7
  52. package/dist/approvals/mails.d.ts +7 -2
  53. package/dist/approvals/mails.js +8 -3
  54. package/dist/approvals/module.d.ts +2930 -1
  55. package/dist/approvals/module.js +468 -7
  56. package/dist/approvals/web/Config.d.ts +12 -0
  57. package/dist/approvals/web/Config.js +60 -0
  58. package/dist/approvals/web/Matrix.d.ts +9 -0
  59. package/dist/approvals/web/Matrix.js +26 -0
  60. package/dist/approvals/web/index.d.ts +3 -1
  61. package/dist/approvals/web/index.js +8 -1
  62. package/dist/approvals/web/messages.js +92 -0
  63. package/dist/approvals_purchase/.kosh/bindings.js +15 -0
  64. package/dist/approvals_purchase/.kosh/schema.d.ts +1997 -0
  65. package/dist/approvals_purchase/i18n/bn.d.ts +2 -0
  66. package/dist/approvals_purchase/i18n/bn.js +5 -0
  67. package/dist/approvals_purchase/index.d.ts +3 -0
  68. package/dist/approvals_purchase/index.js +47 -0
  69. package/dist/approvals_purchase/module.d.ts +12670 -0
  70. package/dist/approvals_purchase/module.js +84 -0
  71. package/dist/attendance/.kosh/bindings.js +1 -1
  72. package/dist/attendance/.kosh/schema.d.ts +59 -1
  73. package/dist/attendance/i18n/bn.d.ts +2 -0
  74. package/dist/attendance/i18n/bn.js +147 -0
  75. package/dist/attendance/index.js +3 -1
  76. package/dist/attendance/module.d.ts +452 -8
  77. package/dist/automation/engine.js +1 -1
  78. package/dist/calendar/.kosh/bindings.js +1 -1
  79. package/dist/calendar/.kosh/schema.d.ts +2 -1
  80. package/dist/calendar/i18n/bn.d.ts +2 -0
  81. package/dist/calendar/i18n/bn.js +80 -0
  82. package/dist/calendar/index.js +77 -0
  83. package/dist/calendar/module.d.ts +26 -0
  84. package/dist/calendar/module.js +14 -4
  85. package/dist/cli/apps.js +25 -5
  86. package/dist/cli/init.js +14 -4
  87. package/dist/cli/templates.d.ts +1 -1
  88. package/dist/cli/templates.js +34 -5
  89. package/dist/contacts/.kosh/bindings.js +1 -1
  90. package/dist/contacts/.kosh/schema.d.ts +2 -1
  91. package/dist/contacts/i18n/bn.d.ts +2 -0
  92. package/dist/contacts/i18n/bn.js +67 -0
  93. package/dist/contacts/index.js +3 -0
  94. package/dist/contacts/module.d.ts +42 -0
  95. package/dist/contacts/module.js +6 -1
  96. package/dist/crm/.kosh/bindings.js +1 -1
  97. package/dist/crm/.kosh/schema.d.ts +2 -1
  98. package/dist/crm/i18n/bn.d.ts +2 -0
  99. package/dist/crm/i18n/bn.js +80 -0
  100. package/dist/crm/index.js +22 -0
  101. package/dist/crm/module.d.ts +20 -0
  102. package/dist/crm/module.js +4 -2
  103. package/dist/documents/.kosh/bindings.js +1 -1
  104. package/dist/documents/.kosh/schema.d.ts +2 -1
  105. package/dist/documents/i18n/bn.d.ts +2 -0
  106. package/dist/documents/i18n/bn.js +83 -0
  107. package/dist/documents/index.js +128 -1
  108. package/dist/documents/module.d.ts +20 -0
  109. package/dist/documents/module.js +1 -0
  110. package/dist/event/.kosh/bindings.js +1 -1
  111. package/dist/event/.kosh/schema.d.ts +2 -1
  112. package/dist/event/i18n/bn.d.ts +2 -0
  113. package/dist/event/i18n/bn.js +101 -0
  114. package/dist/event/index.js +66 -0
  115. package/dist/event/module.d.ts +20 -0
  116. package/dist/event/module.js +8 -1
  117. package/dist/event_account/.kosh/bindings.js +1 -1
  118. package/dist/event_account/.kosh/schema.d.ts +530 -8
  119. package/dist/event_account/i18n/bn.d.ts +2 -0
  120. package/dist/event_account/i18n/bn.js +5 -0
  121. package/dist/event_account/index.js +2 -0
  122. package/dist/event_account/module.d.ts +4905 -1431
  123. package/dist/expense/.kosh/bindings.js +1 -1
  124. package/dist/expense/.kosh/schema.d.ts +323 -2
  125. package/dist/expense/i18n/bn.d.ts +2 -0
  126. package/dist/expense/i18n/bn.js +155 -0
  127. package/dist/expense/index.js +8 -1
  128. package/dist/expense/module.d.ts +2715 -694
  129. package/dist/expense/module.js +3 -1
  130. package/dist/expense_account/.kosh/bindings.js +1 -1
  131. package/dist/expense_account/.kosh/schema.d.ts +587 -8
  132. package/dist/expense_account/i18n/bn.d.ts +2 -0
  133. package/dist/expense_account/i18n/bn.js +15 -0
  134. package/dist/expense_account/index.js +2 -0
  135. package/dist/expense_account/module.d.ts +8094 -2619
  136. package/dist/helpdesk/.kosh/bindings.js +1 -1
  137. package/dist/helpdesk/.kosh/schema.d.ts +2 -1
  138. package/dist/helpdesk/i18n/bn.d.ts +2 -0
  139. package/dist/helpdesk/i18n/bn.js +127 -0
  140. package/dist/helpdesk/index.js +2 -0
  141. package/dist/helpdesk/module.d.ts +20 -0
  142. package/dist/hr/.kosh/bindings.js +1 -1
  143. package/dist/hr/.kosh/schema.d.ts +59 -1
  144. package/dist/hr/demo.js +11 -1
  145. package/dist/hr/i18n/bn.d.ts +2 -0
  146. package/dist/hr/i18n/bn.js +126 -0
  147. package/dist/hr/index.d.ts +1 -1
  148. package/dist/hr/index.js +158 -12
  149. package/dist/hr/module.d.ts +932 -59
  150. package/dist/hr/module.js +114 -17
  151. package/dist/{mrp → indent}/.kosh/bindings.js +2 -2
  152. package/dist/indent/.kosh/schema.d.ts +2084 -0
  153. package/dist/indent/demo.d.ts +7 -0
  154. package/dist/indent/demo.js +48 -0
  155. package/dist/indent/i18n/bn.d.ts +2 -0
  156. package/dist/indent/i18n/bn.js +143 -0
  157. package/dist/indent/index.d.ts +3 -0
  158. package/dist/indent/index.js +450 -0
  159. package/dist/{mrp → indent}/module.d.ts +7164 -3075
  160. package/dist/indent/module.js +792 -0
  161. package/dist/indent_purchase/.kosh/bindings.d.ts +22 -0
  162. package/dist/indent_purchase/.kosh/bindings.js +15 -0
  163. package/dist/indent_purchase/.kosh/schema.d.ts +2710 -0
  164. package/dist/indent_purchase/i18n/bn.d.ts +2 -0
  165. package/dist/indent_purchase/i18n/bn.js +13 -0
  166. package/dist/indent_purchase/index.d.ts +3 -0
  167. package/dist/indent_purchase/index.js +61 -0
  168. package/dist/indent_purchase/module.d.ts +27950 -0
  169. package/dist/indent_purchase/module.js +163 -0
  170. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  171. package/dist/l10n_bd/.kosh/schema.d.ts +530 -8
  172. package/dist/l10n_bd/i18n/bn.d.ts +2 -0
  173. package/dist/l10n_bd/i18n/bn.js +69 -0
  174. package/dist/l10n_bd/index.js +2 -0
  175. package/dist/l10n_bd/module.d.ts +5167 -1693
  176. package/dist/l10n_bd_payroll/.kosh/bindings.js +1 -1
  177. package/dist/l10n_bd_payroll/.kosh/schema.d.ts +587 -8
  178. package/dist/l10n_bd_payroll/i18n/bn.d.ts +2 -0
  179. package/dist/l10n_bd_payroll/i18n/bn.js +4 -0
  180. package/dist/l10n_bd_payroll/index.js +2 -0
  181. package/dist/l10n_bd_payroll/module.d.ts +5910 -1548
  182. package/dist/l10n_bd_pos/.kosh/bindings.d.ts +22 -0
  183. package/dist/{mrp_account → l10n_bd_pos}/.kosh/bindings.js +2 -2
  184. package/dist/{mrp_account → l10n_bd_pos}/.kosh/schema.d.ts +693 -651
  185. package/dist/l10n_bd_pos/i18n/bn.d.ts +2 -0
  186. package/dist/l10n_bd_pos/i18n/bn.js +32 -0
  187. package/dist/l10n_bd_pos/index.d.ts +3 -0
  188. package/dist/l10n_bd_pos/index.js +111 -0
  189. package/dist/{mrp_account → l10n_bd_pos}/module.d.ts +7402 -7647
  190. package/dist/l10n_bd_pos/module.js +43 -0
  191. package/dist/l10n_in/.kosh/bindings.js +1 -1
  192. package/dist/l10n_in/.kosh/schema.d.ts +530 -8
  193. package/dist/l10n_in/i18n/bn.d.ts +2 -0
  194. package/dist/l10n_in/i18n/bn.js +95 -0
  195. package/dist/l10n_in/index.js +2 -0
  196. package/dist/l10n_in/module.d.ts +6703 -1652
  197. package/dist/l10n_in/module.js +25 -5
  198. package/dist/l10n_in/web/GstSettings.js +2 -2
  199. package/dist/mail/inbound/handle.js +2 -4
  200. package/dist/mail/inbound/models.js +5 -3
  201. package/dist/mail/index.d.ts +12 -1
  202. package/dist/mail/index.js +139 -2
  203. package/dist/mail/send.d.ts +56 -0
  204. package/dist/mail/send.js +394 -0
  205. package/dist/mail/web/SendButton.d.ts +9 -0
  206. package/dist/mail/web/SendButton.js +71 -0
  207. package/dist/mail/web/SendSelected.d.ts +8 -0
  208. package/dist/mail/web/SendSelected.js +33 -0
  209. package/dist/mail/web/index.d.ts +2 -0
  210. package/dist/mail/web/index.js +6 -0
  211. package/dist/mail/web/messages.js +36 -0
  212. package/dist/maintenance/.kosh/bindings.js +1 -1
  213. package/dist/maintenance/.kosh/schema.d.ts +2 -1
  214. package/dist/maintenance/i18n/bn.d.ts +2 -0
  215. package/dist/maintenance/i18n/bn.js +132 -0
  216. package/dist/maintenance/index.js +85 -0
  217. package/dist/maintenance/module.d.ts +25 -0
  218. package/dist/maintenance/module.js +27 -9
  219. package/dist/maintenance_stock/.kosh/bindings.js +1 -1
  220. package/dist/maintenance_stock/.kosh/schema.d.ts +298 -2
  221. package/dist/maintenance_stock/i18n/bn.d.ts +2 -0
  222. package/dist/maintenance_stock/i18n/bn.js +21 -0
  223. package/dist/maintenance_stock/index.js +2 -0
  224. package/dist/maintenance_stock/module.d.ts +5302 -1256
  225. package/dist/maintenance_stock/module.js +1 -0
  226. package/dist/modules-sdk/apps.d.ts +10 -0
  227. package/dist/modules-sdk/apps.js +32 -0
  228. package/dist/modules-sdk/index.d.ts +1 -1
  229. package/dist/modules-sdk/manifest.d.ts +68 -2
  230. package/dist/payment/.kosh/bindings.js +1 -1
  231. package/dist/payment/.kosh/schema.d.ts +530 -8
  232. package/dist/payment/i18n/bn.d.ts +2 -0
  233. package/dist/payment/i18n/bn.js +59 -0
  234. package/dist/payment/index.js +85 -0
  235. package/dist/payment/module.d.ts +5134 -1660
  236. package/dist/payment/module.js +17 -3
  237. package/dist/payment/settle.js +1 -0
  238. package/dist/payroll/.kosh/bindings.js +1 -1
  239. package/dist/payroll/.kosh/schema.d.ts +59 -1
  240. package/dist/payroll/i18n/bn.d.ts +2 -0
  241. package/dist/payroll/i18n/bn.js +206 -0
  242. package/dist/payroll/index.js +2 -0
  243. package/dist/payroll/module.d.ts +936 -48
  244. package/dist/payroll_account/.kosh/bindings.js +1 -1
  245. package/dist/payroll_account/.kosh/schema.d.ts +587 -8
  246. package/dist/payroll_account/i18n/bn.d.ts +2 -0
  247. package/dist/payroll_account/i18n/bn.js +10 -0
  248. package/dist/payroll_account/index.js +2 -0
  249. package/dist/payroll_account/module.d.ts +7534 -3192
  250. package/dist/permissions/approvals.d.ts +97 -4
  251. package/dist/permissions/approvals.js +780 -31
  252. package/dist/permissions/index.d.ts +1 -1
  253. package/dist/permissions/index.js +2 -2
  254. package/dist/permissions/models.js +177 -5
  255. package/dist/permissions/routes.d.ts +3 -0
  256. package/dist/permissions/routes.js +36 -2
  257. package/dist/permissions/web/ApprovalPanel.js +43 -5
  258. package/dist/permissions/web/messages.js +52 -0
  259. package/dist/portal/.kosh/bindings.js +1 -1
  260. package/dist/portal/.kosh/schema.d.ts +2 -1
  261. package/dist/portal/documents.d.ts +3 -0
  262. package/dist/portal/documents.js +21 -1
  263. package/dist/portal/i18n/bn.d.ts +2 -0
  264. package/dist/portal/i18n/bn.js +33 -0
  265. package/dist/portal/index.js +2 -0
  266. package/dist/portal/module.d.ts +20 -0
  267. package/dist/portal/routes-documents.js +76 -0
  268. package/dist/pos/.kosh/bindings.js +1 -1
  269. package/dist/pos/.kosh/schema.d.ts +531 -8
  270. package/dist/pos/i18n/bn.d.ts +2 -0
  271. package/dist/pos/i18n/bn.js +133 -0
  272. package/dist/pos/index.js +83 -1
  273. package/dist/pos/module.d.ts +6888 -1777
  274. package/dist/pos/module.js +49 -5
  275. package/dist/pos/routes.js +80 -7
  276. package/dist/pos/web/Register.js +2 -5
  277. package/dist/pos_stock/.kosh/bindings.js +1 -1
  278. package/dist/pos_stock/.kosh/schema.d.ts +626 -9
  279. package/dist/pos_stock/i18n/bn.d.ts +2 -0
  280. package/dist/pos_stock/i18n/bn.js +6 -0
  281. package/dist/pos_stock/index.js +2 -0
  282. package/dist/pos_stock/module.d.ts +10798 -3276
  283. package/dist/product/.kosh/bindings.js +1 -1
  284. package/dist/product/.kosh/schema.d.ts +202 -1
  285. package/dist/product/i18n/bn.d.ts +2 -0
  286. package/dist/product/i18n/bn.js +175 -0
  287. package/dist/product/index.d.ts +1 -0
  288. package/dist/product/index.js +288 -1
  289. package/dist/product/module.d.ts +1725 -0
  290. package/dist/product/module.js +66 -6
  291. package/dist/product/pricelist.d.ts +575 -0
  292. package/dist/product/pricelist.js +257 -0
  293. package/dist/product/variants.d.ts +908 -0
  294. package/dist/product/variants.js +251 -0
  295. package/dist/production/.kosh/bindings.d.ts +22 -0
  296. package/dist/production/.kosh/bindings.js +15 -0
  297. package/dist/production/.kosh/schema.d.ts +2345 -0
  298. package/dist/{mrp → production}/demo.d.ts +1 -1
  299. package/dist/{mrp → production}/demo.js +5 -5
  300. package/dist/production/depth.d.ts +790 -0
  301. package/dist/production/depth.js +262 -0
  302. package/dist/production/i18n/bn.d.ts +2 -0
  303. package/dist/production/i18n/bn.js +350 -0
  304. package/dist/production/index.d.ts +6 -0
  305. package/dist/production/index.js +1256 -0
  306. package/dist/production/module.d.ts +17327 -0
  307. package/dist/production/module.js +1333 -0
  308. package/dist/production/plan.d.ts +921 -0
  309. package/dist/production/plan.js +511 -0
  310. package/dist/production/schedule.d.ts +656 -0
  311. package/dist/production/schedule.js +309 -0
  312. package/dist/production_account/.kosh/bindings.d.ts +22 -0
  313. package/dist/production_account/.kosh/bindings.js +15 -0
  314. package/dist/production_account/.kosh/schema.d.ts +3496 -0
  315. package/dist/production_account/i18n/bn.d.ts +2 -0
  316. package/dist/production_account/i18n/bn.js +93 -0
  317. package/dist/production_account/index.d.ts +4 -0
  318. package/dist/production_account/index.js +246 -0
  319. package/dist/production_account/module.d.ts +1192 -0
  320. package/dist/{mrp_account → production_account}/module.js +72 -18
  321. package/dist/production_account/overhead.d.ts +1195 -0
  322. package/dist/production_account/overhead.js +554 -0
  323. package/dist/project/.kosh/bindings.js +1 -1
  324. package/dist/project/.kosh/schema.d.ts +65 -2
  325. package/dist/project/i18n/bn.d.ts +2 -0
  326. package/dist/project/i18n/bn.js +383 -0
  327. package/dist/project/index.js +123 -0
  328. package/dist/project/module.d.ts +20 -0
  329. package/dist/project/module.js +73 -14
  330. package/dist/project_account/.kosh/bindings.js +1 -1
  331. package/dist/project_account/.kosh/schema.d.ts +530 -8
  332. package/dist/project_account/i18n/bn.d.ts +2 -0
  333. package/dist/project_account/i18n/bn.js +6 -0
  334. package/dist/project_account/index.js +2 -0
  335. package/dist/project_account/module.d.ts +4926 -1452
  336. package/dist/project_portal/.kosh/bindings.js +1 -1
  337. package/dist/project_portal/.kosh/schema.d.ts +65 -2
  338. package/dist/project_portal/i18n/bn.d.ts +2 -0
  339. package/dist/project_portal/i18n/bn.js +6 -0
  340. package/dist/project_portal/index.js +2 -0
  341. package/dist/project_portal/module.d.ts +40 -0
  342. package/dist/project_purchase/.kosh/bindings.js +1 -1
  343. package/dist/project_purchase/.kosh/schema.d.ts +1126 -53
  344. package/dist/project_purchase/i18n/bn.d.ts +2 -0
  345. package/dist/project_purchase/i18n/bn.js +4 -0
  346. package/dist/project_purchase/index.js +2 -0
  347. package/dist/project_purchase/module.d.ts +17912 -4446
  348. package/dist/purchase/.kosh/bindings.js +1 -1
  349. package/dist/purchase/.kosh/schema.d.ts +813 -3
  350. package/dist/purchase/comparison.d.ts +993 -0
  351. package/dist/purchase/comparison.js +415 -0
  352. package/dist/purchase/extras.d.ts +1509 -0
  353. package/dist/purchase/extras.js +445 -0
  354. package/dist/purchase/i18n/bn.d.ts +2 -0
  355. package/dist/purchase/i18n/bn.js +413 -0
  356. package/dist/purchase/index.d.ts +5 -0
  357. package/dist/purchase/index.js +1280 -4
  358. package/dist/purchase/module.d.ts +7139 -398
  359. package/dist/purchase/module.js +295 -11
  360. package/dist/purchase/request.d.ts +696 -0
  361. package/dist/purchase/request.js +446 -0
  362. package/dist/purchase/tender.d.ts +1208 -0
  363. package/dist/purchase/tender.js +507 -0
  364. package/dist/purchase/vendorprice.d.ts +340 -0
  365. package/dist/purchase/vendorprice.js +153 -0
  366. package/dist/purchase_account/.kosh/bindings.js +1 -1
  367. package/dist/purchase_account/.kosh/schema.d.ts +1130 -57
  368. package/dist/purchase_account/i18n/bn.d.ts +2 -0
  369. package/dist/purchase_account/i18n/bn.js +22 -0
  370. package/dist/purchase_account/index.js +19 -0
  371. package/dist/purchase_account/module.d.ts +12276 -2247
  372. package/dist/purchase_account/module.js +241 -9
  373. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  374. package/dist/purchase_portal/.kosh/schema.d.ts +813 -3
  375. package/dist/purchase_portal/i18n/bn.d.ts +2 -0
  376. package/dist/purchase_portal/i18n/bn.js +5 -0
  377. package/dist/purchase_portal/index.js +2 -0
  378. package/dist/purchase_portal/module.d.ts +6849 -353
  379. package/dist/purchase_stock/.kosh/bindings.js +1 -1
  380. package/dist/purchase_stock/.kosh/schema.d.ts +910 -4
  381. package/dist/purchase_stock/i18n/bn.d.ts +2 -0
  382. package/dist/purchase_stock/i18n/bn.js +19 -0
  383. package/dist/purchase_stock/index.d.ts +1 -1
  384. package/dist/purchase_stock/index.js +29 -0
  385. package/dist/purchase_stock/module.d.ts +12410 -1864
  386. package/dist/purchase_stock/module.js +81 -9
  387. package/dist/purchase_stock/scorecard.d.ts +22 -0
  388. package/dist/purchase_stock/scorecard.js +77 -0
  389. package/dist/purchase_stock/web/Scorecard.d.ts +7 -0
  390. package/dist/purchase_stock/web/Scorecard.js +22 -0
  391. package/dist/purchase_stock/web/index.d.ts +11 -0
  392. package/dist/purchase_stock/web/index.js +16 -0
  393. package/dist/purchase_stock/web/messages.d.ts +2 -0
  394. package/dist/purchase_stock/web/messages.js +27 -0
  395. package/dist/purchase_stock_account/.kosh/bindings.js +1 -1
  396. package/dist/purchase_stock_account/.kosh/schema.d.ts +1286 -58
  397. package/dist/purchase_stock_account/i18n/bn.d.ts +2 -0
  398. package/dist/purchase_stock_account/i18n/bn.js +8 -0
  399. package/dist/purchase_stock_account/index.js +18 -0
  400. package/dist/purchase_stock_account/module.d.ts +7 -37995
  401. package/dist/purchase_stock_account/module.js +24 -3
  402. package/dist/recruitment/.kosh/bindings.js +1 -1
  403. package/dist/recruitment/.kosh/schema.d.ts +59 -1
  404. package/dist/recruitment/i18n/bn.d.ts +2 -0
  405. package/dist/recruitment/i18n/bn.js +93 -0
  406. package/dist/recruitment/index.js +64 -0
  407. package/dist/recruitment/module.d.ts +458 -8
  408. package/dist/recruitment/module.js +11 -3
  409. package/dist/reporting/documents.d.ts +5 -0
  410. package/dist/reporting/documents.js +11 -1
  411. package/dist/reporting/index.d.ts +1 -1
  412. package/dist/reporting/index.js +1 -1
  413. package/dist/reporting/web/CalendarView.d.ts +18 -5
  414. package/dist/reporting/web/CalendarView.js +163 -29
  415. package/dist/reporting/web/messages.js +18 -0
  416. package/dist/sale_account/.kosh/bindings.js +1 -1
  417. package/dist/sale_account/.kosh/schema.d.ts +642 -16
  418. package/dist/sale_account/i18n/bn.d.ts +2 -0
  419. package/dist/sale_account/i18n/bn.js +48 -0
  420. package/dist/sale_account/index.d.ts +1 -1
  421. package/dist/sale_account/index.js +102 -4
  422. package/dist/sale_account/module.d.ts +8254 -2212
  423. package/dist/sale_account/module.js +535 -46
  424. package/dist/sale_crm/.kosh/bindings.d.ts +22 -0
  425. package/dist/sale_crm/.kosh/bindings.js +15 -0
  426. package/dist/{mrp → sale_crm}/.kosh/schema.d.ts +389 -567
  427. package/dist/sale_crm/i18n/bn.d.ts +2 -0
  428. package/dist/sale_crm/i18n/bn.js +9 -0
  429. package/dist/sale_crm/index.d.ts +7 -0
  430. package/dist/sale_crm/index.js +104 -0
  431. package/dist/sale_crm/module.d.ts +5609 -0
  432. package/dist/sale_crm/module.js +73 -0
  433. package/dist/sale_portal/.kosh/bindings.js +1 -1
  434. package/dist/sale_portal/.kosh/schema.d.ts +291 -4
  435. package/dist/sale_portal/i18n/bn.d.ts +2 -0
  436. package/dist/sale_portal/i18n/bn.js +7 -0
  437. package/dist/sale_portal/index.js +10 -0
  438. package/dist/sale_portal/module.d.ts +2564 -294
  439. package/dist/sale_project/.kosh/bindings.js +1 -1
  440. package/dist/sale_project/.kosh/schema.d.ts +354 -5
  441. package/dist/sale_project/i18n/bn.d.ts +2 -0
  442. package/dist/sale_project/i18n/bn.js +12 -0
  443. package/dist/sale_project/index.js +2 -0
  444. package/dist/sale_project/module.d.ts +6249 -2402
  445. package/dist/sale_stock/.kosh/bindings.js +1 -1
  446. package/dist/sale_stock/.kosh/schema.d.ts +387 -5
  447. package/dist/sale_stock/i18n/bn.d.ts +2 -0
  448. package/dist/sale_stock/i18n/bn.js +15 -0
  449. package/dist/sale_stock/index.js +2 -0
  450. package/dist/sale_stock/module.d.ts +8057 -1759
  451. package/dist/sale_stock/module.js +20 -3
  452. package/dist/sales/.kosh/bindings.js +1 -1
  453. package/dist/sales/.kosh/schema.d.ts +291 -4
  454. package/dist/sales/i18n/bn.d.ts +2 -0
  455. package/dist/sales/i18n/bn.js +128 -0
  456. package/dist/sales/index.js +120 -5
  457. package/dist/sales/module.d.ts +3567 -633
  458. package/dist/sales/module.js +306 -11
  459. package/dist/server/api/duplicate.d.ts +14 -0
  460. package/dist/server/api/duplicate.js +75 -0
  461. package/dist/server/api/explain.d.ts +70 -0
  462. package/dist/server/api/explain.js +111 -0
  463. package/dist/server/api/metadata.d.ts +2 -0
  464. package/dist/server/api/metadata.js +9 -0
  465. package/dist/server/api/openapi.js +15 -0
  466. package/dist/server/api/rest.d.ts +1 -0
  467. package/dist/server/api/rest.js +76 -12
  468. package/dist/server/api/views.js +1 -1
  469. package/dist/server/apps.d.ts +16 -1
  470. package/dist/server/apps.js +164 -2
  471. package/dist/server/auth/base.js +2 -0
  472. package/dist/server/auth/service.d.ts +8 -0
  473. package/dist/server/auth/service.js +19 -0
  474. package/dist/server/events.d.ts +13 -0
  475. package/dist/server/events.js +22 -0
  476. package/dist/server/i18n.d.ts +27 -0
  477. package/dist/server/i18n.js +143 -0
  478. package/dist/server/index.d.ts +2 -0
  479. package/dist/server/index.js +2 -0
  480. package/dist/server/server.js +6 -0
  481. package/dist/settings/company.d.ts +3 -1
  482. package/dist/settings/company.js +26 -5
  483. package/dist/settings/routes.d.ts +10 -0
  484. package/dist/settings/routes.js +19 -3
  485. package/dist/settings/web/AppsPage.d.ts +3 -2
  486. package/dist/settings/web/AppsPage.js +26 -8
  487. package/dist/settings/web/CompaniesPage.js +91 -6
  488. package/dist/settings/web/messages.js +64 -0
  489. package/dist/sign/.kosh/bindings.js +1 -1
  490. package/dist/sign/.kosh/schema.d.ts +2 -1
  491. package/dist/sign/i18n/bn.d.ts +2 -0
  492. package/dist/sign/i18n/bn.js +97 -0
  493. package/dist/sign/index.d.ts +15 -0
  494. package/dist/sign/index.js +129 -1
  495. package/dist/sign/module.d.ts +40 -0
  496. package/dist/sign/module.js +11 -2
  497. package/dist/stock/.kosh/bindings.js +1 -1
  498. package/dist/stock/.kosh/schema.d.ts +298 -2
  499. package/dist/stock/i18n/bn.d.ts +2 -0
  500. package/dist/stock/i18n/bn.js +311 -0
  501. package/dist/stock/index.d.ts +3 -1
  502. package/dist/stock/index.js +442 -3
  503. package/dist/stock/ledger.d.ts +78 -0
  504. package/dist/stock/ledger.js +582 -0
  505. package/dist/stock/module.d.ts +5280 -2044
  506. package/dist/stock/module.js +222 -23
  507. package/dist/stock/replenish.js +16 -1
  508. package/dist/stock/returns.d.ts +157 -0
  509. package/dist/stock/returns.js +163 -0
  510. package/dist/stock/web/Ledger.d.ts +9 -0
  511. package/dist/stock/web/Ledger.js +108 -0
  512. package/dist/stock/web/Operations.d.ts +8 -0
  513. package/dist/stock/web/Operations.js +33 -0
  514. package/dist/stock/web/ReturnButton.d.ts +7 -0
  515. package/dist/stock/web/ReturnButton.js +62 -0
  516. package/dist/stock/web/index.d.ts +3 -0
  517. package/dist/stock/web/index.js +9 -0
  518. package/dist/stock/web/messages.js +154 -0
  519. package/dist/stock_account/.kosh/bindings.js +1 -1
  520. package/dist/stock_account/.kosh/schema.d.ts +682 -9
  521. package/dist/stock_account/i18n/bn.d.ts +2 -0
  522. package/dist/stock_account/i18n/bn.js +73 -0
  523. package/dist/stock_account/index.d.ts +1 -0
  524. package/dist/stock_account/index.js +131 -3
  525. package/dist/stock_account/landed.d.ts +429 -0
  526. package/dist/stock_account/landed.js +335 -0
  527. package/dist/stock_account/module.d.ts +11247 -3363
  528. package/dist/stock_account/module.js +135 -6
  529. package/dist/stock_account/web/LandedPreview.d.ts +7 -0
  530. package/dist/stock_account/web/LandedPreview.js +23 -0
  531. package/dist/stock_account/web/index.d.ts +11 -0
  532. package/dist/stock_account/web/index.js +16 -0
  533. package/dist/stock_account/web/messages.d.ts +2 -0
  534. package/dist/stock_account/web/messages.js +27 -0
  535. package/dist/todo/i18n/bn.d.ts +2 -0
  536. package/dist/todo/i18n/bn.js +66 -0
  537. package/dist/todo/index.js +59 -0
  538. package/dist/todo/module.d.ts +5 -0
  539. package/dist/todo/module.js +5 -3
  540. package/dist/trade_finance/.kosh/bindings.js +1 -1
  541. package/dist/trade_finance/.kosh/schema.d.ts +2 -1
  542. package/dist/trade_finance/i18n/bn.d.ts +2 -0
  543. package/dist/trade_finance/i18n/bn.js +162 -0
  544. package/dist/trade_finance/index.js +93 -0
  545. package/dist/trade_finance/module.d.ts +26 -0
  546. package/dist/trade_finance/module.js +27 -8
  547. package/dist/trade_finance_account/.kosh/bindings.js +1 -1
  548. package/dist/trade_finance_account/.kosh/schema.d.ts +530 -8
  549. package/dist/trade_finance_account/i18n/bn.d.ts +2 -0
  550. package/dist/trade_finance_account/i18n/bn.js +27 -0
  551. package/dist/trade_finance_account/index.js +2 -0
  552. package/dist/trade_finance_account/module.d.ts +4906 -1432
  553. package/dist/trade_finance_account/module.js +1 -0
  554. package/dist/trade_finance_purchase/.kosh/bindings.js +1 -1
  555. package/dist/trade_finance_purchase/.kosh/schema.d.ts +813 -3
  556. package/dist/trade_finance_purchase/i18n/bn.d.ts +2 -0
  557. package/dist/trade_finance_purchase/i18n/bn.js +6 -0
  558. package/dist/trade_finance_purchase/index.js +2 -0
  559. package/dist/trade_finance_purchase/module.d.ts +6909 -413
  560. package/dist/trade_finance_sale/.kosh/bindings.js +1 -1
  561. package/dist/trade_finance_sale/.kosh/schema.d.ts +291 -4
  562. package/dist/trade_finance_sale/i18n/bn.d.ts +2 -0
  563. package/dist/trade_finance_sale/i18n/bn.js +6 -0
  564. package/dist/trade_finance_sale/index.js +2 -0
  565. package/dist/trade_finance_sale/module.d.ts +2601 -331
  566. package/dist/ui/api.d.ts +2 -0
  567. package/dist/ui/app.d.ts +9 -0
  568. package/dist/ui/app.js +24 -4
  569. package/dist/ui/components/AppIcon.js +2 -2
  570. package/dist/ui/components/Explain.d.ts +69 -0
  571. package/dist/ui/components/Explain.js +62 -0
  572. package/dist/ui/context.d.ts +8 -1
  573. package/dist/ui/context.js +34 -1
  574. package/dist/ui/i18n/messages.d.ts +36 -0
  575. package/dist/ui/i18n/messages.js +72 -0
  576. package/dist/ui/index.d.ts +2 -1
  577. package/dist/ui/index.js +1 -0
  578. package/dist/ui/registry.d.ts +14 -0
  579. package/dist/ui/registry.js +10 -0
  580. package/dist/ui/views/FormView.d.ts +6 -0
  581. package/dist/ui/views/FormView.js +47 -10
  582. package/dist/ui/views/ListView.d.ts +8 -0
  583. package/dist/ui/views/ListView.js +10 -3
  584. package/dist/ui/views/StatsRow.js +2 -1
  585. package/dist/views/extend.js +12 -0
  586. package/dist/views/types.d.ts +13 -2
  587. package/dist/views/validate.js +8 -1
  588. package/dist/website/.kosh/bindings.js +1 -1
  589. package/dist/website/.kosh/schema.d.ts +2 -1
  590. package/dist/website/i18n/bn.d.ts +2 -0
  591. package/dist/website/i18n/bn.js +113 -0
  592. package/dist/website/index.js +36 -0
  593. package/dist/website/module.d.ts +20 -0
  594. package/dist/website/module.js +29 -5
  595. package/dist/website_crm/.kosh/bindings.js +1 -1
  596. package/dist/website_crm/.kosh/schema.d.ts +2 -1
  597. package/dist/website_crm/i18n/bn.d.ts +2 -0
  598. package/dist/website_crm/i18n/bn.js +5 -0
  599. package/dist/website_crm/index.js +2 -0
  600. package/dist/website_crm/module.d.ts +40 -0
  601. package/dist/website_sale/.kosh/bindings.js +1 -1
  602. package/dist/website_sale/.kosh/schema.d.ts +642 -17
  603. package/dist/website_sale/i18n/bn.d.ts +2 -0
  604. package/dist/website_sale/i18n/bn.js +132 -0
  605. package/dist/website_sale/index.js +83 -1
  606. package/dist/website_sale/module.d.ts +18705 -5550
  607. package/dist/website_sale/module.js +32 -10
  608. package/dist/website_sale/shop.js +22 -9
  609. package/dist/website_sale_stock/.kosh/bindings.js +1 -1
  610. package/dist/website_sale_stock/.kosh/schema.d.ts +737 -18
  611. package/dist/website_sale_stock/i18n/bn.d.ts +2 -0
  612. package/dist/website_sale_stock/i18n/bn.js +4 -0
  613. package/dist/website_sale_stock/index.js +2 -0
  614. package/dist/website_sale_stock/module.d.ts +10 -24997
  615. package/dist/website_sale_stock/module.js +7 -3
  616. package/package.json +36 -8
  617. package/dist/mrp/index.d.ts +0 -3
  618. package/dist/mrp/index.js +0 -393
  619. package/dist/mrp/module.js +0 -757
  620. package/dist/mrp_account/index.d.ts +0 -3
  621. package/dist/mrp_account/index.js +0 -22
  622. /package/dist/{mrp → approvals_purchase}/.kosh/bindings.d.ts +0 -0
  623. /package/dist/{mrp_account → indent}/.kosh/bindings.d.ts +0 -0
@@ -0,0 +1,728 @@
1
+ /**
2
+ * Bank statements (RFC 0029 §3.1): a statement of a bank or cash journal holds lines — date, label, reference,
3
+ * amount — imported from CSV (a column mapping), OFX or CAMT.053, or pulled from a bank feed (an adapter, like the
4
+ * payment providers). Each line is matched from the reconciliation workspace: the server proposes the open invoices,
5
+ * bills and credit notes it may pay, each with why (the amount, the document's number in the label, the partner);
6
+ * matching registers the payment from the statement's journal, which posts and reconciles it. A line that pays no
7
+ * document is written off to a counterpart account (bank fees, interest). A line not matched yet stays open.
8
+ */
9
+ import { audit, emitting, HttpProblem } from '../server/index.js';
10
+ import { orm } from '../server/orm.js';
11
+ import { defineModel, f, records } from './.kosh/bindings.js';
12
+ import { reconcile, registerPayment, unreconcile } from './payments.js';
13
+ const ALL = ['read', 'write', 'create', 'unlink', 'export', 'import'];
14
+ const zero = orm.toDecimal('0');
15
+ export const AccountBankStatement = defineModel('account.bank.statement', {
16
+ label: 'Bank statement',
17
+ companyScoped: { required: true },
18
+ order: ['date desc', 'id desc'],
19
+ tracking: ['state', 'balanceEnd'],
20
+ access: { 'account.user': ALL, 'account.manager': ALL },
21
+ fields: {
22
+ name: f.char({ required: true, label: 'Statement' }),
23
+ journal: f.many2one('account.journal', {
24
+ required: true,
25
+ checkCompany: true,
26
+ ondelete: 'restrict',
27
+ label: 'Journal',
28
+ }),
29
+ date: f.date({ required: true, label: 'Date' }),
30
+ balanceStart: f.decimal({ scale: 2, default: '0', label: 'Opening balance' }),
31
+ balanceEnd: f.decimal({ scale: 2, default: '0', label: 'Closing balance' }),
32
+ lines: f.one2many('account.bank.statement.line', 'statement', { label: 'Lines' }),
33
+ state: f.state({
34
+ help: 'Open while lines wait to be matched; done once every line is matched and the closing balance agrees.',
35
+ states: { open: 'Open', done: 'Done' },
36
+ initial: 'open',
37
+ transitions: {
38
+ close: emitting('account.bank.statement.closed', {
39
+ from: ['open'],
40
+ to: 'done',
41
+ guard: records('account.bank.statement', async (statements) => {
42
+ const failing = [];
43
+ for (const s of await statements.load({ lines: true })) {
44
+ const lines = [...s.lines];
45
+ const total = lines.reduce((sum, l) => sum.plus(l.amount ?? zero), zero);
46
+ const agrees = (s.balanceStart ?? zero).plus(total).eq(s.balanceEnd ?? zero);
47
+ if (!agrees || lines.some((l) => l.state !== 'matched'))
48
+ failing.push(s.id);
49
+ }
50
+ return failing;
51
+ }),
52
+ message: 'A statement is done when every line is matched and the opening balance plus its lines is the closing balance.',
53
+ }),
54
+ reopen: { from: ['done'], to: 'open' },
55
+ },
56
+ }),
57
+ },
58
+ });
59
+ export const AccountBankStatementLine = defineModel('account.bank.statement.line', {
60
+ label: 'Statement line',
61
+ order: ['date asc', 'id asc'],
62
+ access: { 'account.user': ALL, 'account.manager': ALL },
63
+ fields: {
64
+ statement: f.many2one('account.bank.statement', { required: true, ondelete: 'cascade' }),
65
+ date: f.date({ required: true, label: 'Date' }),
66
+ label: f.char({ required: true, label: 'Label' }),
67
+ reference: f.char({ label: 'Reference' }),
68
+ partner: f.many2one('res.partner', { ondelete: 'set null', label: 'Partner' }),
69
+ /** Money in is positive, money out negative. */
70
+ amount: f.decimal({ scale: 2, required: true, label: 'Amount' }),
71
+ /** The journal, date, amount and reference: a line imported twice is refused. */
72
+ uniqueKey: f.char({ index: 'unique', label: 'Import key' }),
73
+ payment: f.many2one('account.payment', { ondelete: 'set null', label: 'Payment' }),
74
+ move: f.many2one('account.move', { ondelete: 'set null', label: 'Entry' }),
75
+ /** The rule that matched it, or proposes what to do (RFC 0029 §3.2). */
76
+ rule: f.many2one('account.reconcile.rule', { ondelete: 'set null', label: 'Rule' }),
77
+ state: f.selection({ open: 'Open', matched: 'Matched' }, {
78
+ required: true,
79
+ default: 'open',
80
+ label: 'Status',
81
+ help: 'Open until matched to documents (a payment) or written off to an account.',
82
+ }),
83
+ },
84
+ });
85
+ /**
86
+ * A reconciliation rule (RFC 0029 §3.2): which lines (journals, the label, the amount, the direction, the partner)
87
+ * and what to do — match the open document of the same amount, write off to an account, or only suggest. Rules run
88
+ * in sequence on import and on demand; the first that applies wins.
89
+ */
90
+ export const AccountReconcileRule = defineModel('account.reconcile.rule', {
91
+ label: 'Reconciliation rule',
92
+ archivable: true,
93
+ companyScoped: { required: true },
94
+ order: ['sequence asc', 'id asc'],
95
+ access: { 'account.user': ['read'], 'account.manager': ALL },
96
+ fields: {
97
+ name: f.char({ required: true, label: 'Rule' }),
98
+ sequence: f.integer({ default: 10 }),
99
+ journals: f.many2many('account.journal', {
100
+ relation: 'account_rule_journal_rel',
101
+ label: 'Journals',
102
+ help: 'The bank and cash journals it watches; empty: all.',
103
+ }),
104
+ labelContains: f.char({
105
+ label: 'Label contains',
106
+ help: 'Lines whose label or reference contains this text (any case); empty: any label.',
107
+ }),
108
+ direction: f.selection({ both: 'Money in or out', in: 'Money in', out: 'Money out' }, {
109
+ required: true,
110
+ default: 'both',
111
+ label: 'Direction',
112
+ help: 'Which lines: money in, money out, or both.',
113
+ }),
114
+ amountMin: f.decimal({ scale: 2, label: 'From amount' }),
115
+ amountMax: f.decimal({ scale: 2, label: 'To amount' }),
116
+ partner: f.many2one('res.partner', { ondelete: 'cascade', label: 'Partner' }),
117
+ outcome: f.selection({ match: 'Match the document', write_off: 'Write off', suggest: 'Only suggest' }, {
118
+ required: true,
119
+ default: 'match',
120
+ label: 'Then',
121
+ help: 'Match: the open document of the same amount the proposals rank first. Write off: to the account. Suggest: shown on the line, nothing done.',
122
+ }),
123
+ writeOffAccount: f.many2one('account.account', {
124
+ ondelete: 'restrict',
125
+ checkCompany: true,
126
+ label: 'Write off to',
127
+ }),
128
+ writeOffLabel: f.char({ label: 'Write-off label' }),
129
+ hits: f.integer({
130
+ default: 0,
131
+ label: 'Applied',
132
+ help: 'How many lines it matched or wrote off.',
133
+ }),
134
+ },
135
+ constraints: {
136
+ writeOffAccount: {
137
+ depends: ['outcome', 'writeOffAccount'],
138
+ check: async (records) => {
139
+ const rules = records;
140
+ return [...rules]
141
+ .filter((r) => r.outcome === 'write_off' && r.writeOffAccount === null)
142
+ .map((r) => r.id);
143
+ },
144
+ message: 'A write-off rule names its account.',
145
+ },
146
+ },
147
+ });
148
+ const ISO = /^\d{4}-\d{2}-\d{2}$/;
149
+ const bad = (problem, hint) => new HttpProblem(422, 'invalid-statement', 'Invalid statement', problem, hint);
150
+ /** A date in ISO, from `YYYY-MM-DD`, `DD/MM/YYYY` or `YYYYMMDD…` (OFX). */
151
+ function isoDate(text) {
152
+ const t = text.trim();
153
+ if (ISO.test(t))
154
+ return t;
155
+ const dmy = /^(\d{1,2})[/.-](\d{1,2})[/.-](\d{4})$/.exec(t);
156
+ if (dmy !== null)
157
+ return `${dmy[3]}-${dmy[2].padStart(2, '0')}-${dmy[1].padStart(2, '0')}`;
158
+ const compact = /^(\d{4})(\d{2})(\d{2})/.exec(t);
159
+ if (compact !== null)
160
+ return `${compact[1]}-${compact[2]}-${compact[3]}`;
161
+ throw bad(`“${t}” is not a date.`, 'Dates are YYYY-MM-DD or DD/MM/YYYY.');
162
+ }
163
+ /** An amount as a decimal string: thousands separators dropped, `(12.50)` and `12.50-` negative. */
164
+ function amountOf(text) {
165
+ let t = text.trim().replace(/[\s,]/g, '');
166
+ let negative = false;
167
+ if (/^\(.*\)$/.test(t)) {
168
+ negative = true;
169
+ t = t.slice(1, -1);
170
+ }
171
+ if (t.endsWith('-')) {
172
+ negative = true;
173
+ t = t.slice(0, -1);
174
+ }
175
+ if (!/^-?\d+(\.\d+)?$/.test(t))
176
+ throw bad(`“${text}” is not an amount.`, 'Amounts are numbers.');
177
+ return negative ? `-${t.replace(/^-/, '')}` : t;
178
+ }
179
+ /** Splits one CSV line (quotes and doubled quotes understood). */
180
+ function csvCells(line) {
181
+ const out = [];
182
+ let cell = '';
183
+ let quoted = false;
184
+ for (let i = 0; i < line.length; i += 1) {
185
+ const c = line[i];
186
+ if (quoted) {
187
+ if (c === '"' && line[i + 1] === '"') {
188
+ cell += '"';
189
+ i += 1;
190
+ }
191
+ else if (c === '"')
192
+ quoted = false;
193
+ else
194
+ cell += c;
195
+ }
196
+ else if (c === '"')
197
+ quoted = true;
198
+ else if (c === ',' || c === ';') {
199
+ out.push(cell);
200
+ cell = '';
201
+ }
202
+ else
203
+ cell += c;
204
+ }
205
+ out.push(cell);
206
+ return out.map((v) => v.trim());
207
+ }
208
+ /** Lines of a CSV statement with a header row. */
209
+ export function parseCsv(text, mapping) {
210
+ const rows = text.split(/\r?\n/).filter((l) => l.trim() !== '');
211
+ const header = csvCells(rows[0] ?? '');
212
+ const at = (name) => (name === undefined ? -1 : header.indexOf(name));
213
+ const [iDate, iLabel, iAmount, iDebit, iCredit, iRef] = [
214
+ at(mapping.date),
215
+ at(mapping.label),
216
+ at(mapping.amount),
217
+ at(mapping.debit),
218
+ at(mapping.credit),
219
+ at(mapping.reference),
220
+ ];
221
+ if (iDate < 0 || iLabel < 0 || (iAmount < 0 && iDebit < 0 && iCredit < 0))
222
+ throw bad('The mapping names columns the file does not have.', `Columns: ${header.join(', ')}.`);
223
+ return rows.slice(1).map((line) => {
224
+ const cells = csvCells(line);
225
+ const money = iAmount >= 0
226
+ ? amountOf(cells[iAmount] ?? '0')
227
+ : orm
228
+ .toDecimal(amountOf(cells[iCredit] || '0'))
229
+ .minus(orm.toDecimal(amountOf(cells[iDebit] || '0')))
230
+ .toString();
231
+ return {
232
+ date: isoDate(cells[iDate] ?? ''),
233
+ label: cells[iLabel] ?? '',
234
+ amount: money,
235
+ ...(iRef >= 0 && (cells[iRef] ?? '') !== '' ? { reference: cells[iRef] } : {}),
236
+ };
237
+ });
238
+ }
239
+ /** Lines of an OFX statement (`<STMTTRN>` blocks, SGML or XML). */
240
+ export function parseOfx(text) {
241
+ const tag = (block, name) => new RegExp(`<${name}>([^<\\r\\n]*)`, 'i').exec(block)?.[1]?.trim() ?? '';
242
+ return [
243
+ ...text.matchAll(/<STMTTRN>([\s\S]*?)(?:<\/STMTTRN>|(?=<STMTTRN>)|<\/BANKTRANLIST>)/gi),
244
+ ].map((m) => {
245
+ const block = m[1];
246
+ const fitid = tag(block, 'FITID');
247
+ return {
248
+ date: isoDate(tag(block, 'DTPOSTED')),
249
+ label: tag(block, 'NAME') || tag(block, 'MEMO') || fitid,
250
+ amount: amountOf(tag(block, 'TRNAMT')),
251
+ ...(fitid === '' ? {} : { reference: fitid }),
252
+ };
253
+ });
254
+ }
255
+ /** Lines of a CAMT.053 statement (`<Ntry>` entries). */
256
+ export function parseCamt(xml) {
257
+ const inner = (block, name) => new RegExp(`<${name}[^>]*>([\\s\\S]*?)</${name}>`).exec(block)?.[1]?.trim() ?? '';
258
+ return [...xml.matchAll(/<Ntry>([\s\S]*?)<\/Ntry>/g)].map((m) => {
259
+ const block = m[1];
260
+ const amount = amountOf(inner(block, 'Amt'));
261
+ const out = inner(block, 'CdtDbtInd') === 'DBIT';
262
+ const date = inner(inner(block, 'BookgDt'), 'Dt') || inner(inner(block, 'ValDt'), 'Dt');
263
+ const reference = inner(block, 'AcctSvcrRef') || inner(block, 'EndToEndId');
264
+ return {
265
+ date: isoDate(date),
266
+ label: inner(block, 'Ustrd') || inner(block, 'AddtlNtryInf') || reference,
267
+ amount: out ? `-${amount.replace(/^-/, '')}` : amount,
268
+ ...(reference === '' ? {} : { reference }),
269
+ };
270
+ });
271
+ }
272
+ /**
273
+ * A new statement of `journal` from `rows`; lines already imported (the same journal, date, amount and reference
274
+ * or label) are skipped. Returns the statement and how many lines were made and skipped.
275
+ */
276
+ export async function importStatement(env, input) {
277
+ const [journal] = await env
278
+ .model('account.journal')
279
+ .browse([input.journal])
280
+ .load(['type', 'code']);
281
+ if (journal === undefined || (journal.type !== 'bank' && journal.type !== 'cash'))
282
+ throw bad('Statements are for bank and cash journals.', 'Choose a bank or cash journal.');
283
+ const keyOf = (r) => `${input.journal}|${r.date}|${orm.toDecimal(r.amount).toFixed(2)}|${(r.reference ?? r.label).trim()}`;
284
+ const keys = input.rows.map(keyOf);
285
+ const existing = new Set((await (await env.model('account.bank.statement.line').search({
286
+ where: { uniqueKey: { in: keys } },
287
+ })).load(['uniqueKey'])).map((l) => String(l.uniqueKey)));
288
+ const fresh = input.rows.filter((_r, i) => !existing.has(keys[i]));
289
+ const unique = new Map(fresh.map((r) => [keyOf(r), r]));
290
+ if (unique.size === 0)
291
+ return { statement: null, created: 0, skipped: input.rows.length, applied: 0 };
292
+ const dates = [...unique.values()].map((r) => r.date).sort();
293
+ const total = [...unique.values()].reduce((s, r) => s.plus(orm.toDecimal(r.amount)), zero);
294
+ const start = orm.toDecimal(input.balanceStart ?? '0');
295
+ const made = await env.model('account.bank.statement').create([
296
+ {
297
+ name: input.name ?? `${String(journal.code)} ${dates.at(-1)}`,
298
+ journal: input.journal,
299
+ date: dates.at(-1),
300
+ balanceStart: start.toString(),
301
+ balanceEnd: (input.balanceEnd === undefined
302
+ ? start.plus(total)
303
+ : orm.toDecimal(input.balanceEnd)).toString(),
304
+ lines: {
305
+ create: [...unique.entries()].map(([key, r]) => ({
306
+ date: r.date,
307
+ label: r.label,
308
+ reference: r.reference ?? null,
309
+ amount: r.amount,
310
+ uniqueKey: key,
311
+ })),
312
+ },
313
+ },
314
+ ]);
315
+ // The rules run on what was imported (RFC 0029 §3.2).
316
+ const statementId = made.ids[0];
317
+ const lineIds = (await env
318
+ .model('account.bank.statement.line')
319
+ .search({ where: { statement: statementId } })).ids;
320
+ const results = await applyRules(env, lineIds);
321
+ return {
322
+ statement: statementId,
323
+ created: unique.size,
324
+ skipped: input.rows.length - unique.size,
325
+ applied: results.filter((r) => r.outcome !== 'suggest').length,
326
+ };
327
+ }
328
+ /** The active rules of the caller's company, in order. */
329
+ async function rulesOf(env, only) {
330
+ const found = await env.model('account.reconcile.rule').search({
331
+ where: (only === undefined ? {} : { id: only }),
332
+ order: ['sequence asc', 'id asc'],
333
+ });
334
+ return (await found.load({ journals: true, partner: true, writeOffAccount: true })).map((r) => ({
335
+ id: r.id,
336
+ name: String(r.name),
337
+ journals: [...r.journals].map((j) => j.id),
338
+ labelContains: r.labelContains ?? null,
339
+ direction: String(r.direction),
340
+ amountMin: r.amountMin ?? null,
341
+ amountMax: r.amountMax ?? null,
342
+ partner: r.partner?.id ?? null,
343
+ outcome: String(r.outcome),
344
+ writeOffAccount: r.writeOffAccount?.id ?? null,
345
+ writeOffLabel: r.writeOffLabel ?? null,
346
+ }));
347
+ }
348
+ /** Whether a rule's conditions hold for a line. */
349
+ function applies(rule, line) {
350
+ if (rule.journals.length > 0 && !rule.journals.includes(line.journal))
351
+ return false;
352
+ if (rule.direction === 'in' && line.amount.lte(0))
353
+ return false;
354
+ if (rule.direction === 'out' && line.amount.gte(0))
355
+ return false;
356
+ const size = line.amount.abs();
357
+ if (rule.amountMin !== null && size.lt(rule.amountMin))
358
+ return false;
359
+ if (rule.amountMax !== null && size.gt(rule.amountMax))
360
+ return false;
361
+ if (rule.partner !== null && line.partner !== rule.partner)
362
+ return false;
363
+ if (rule.labelContains !== null && rule.labelContains.trim() !== '') {
364
+ const text = `${line.label} ${line.reference ?? ''}`.toLowerCase();
365
+ if (!text.includes(rule.labelContains.trim().toLowerCase()))
366
+ return false;
367
+ }
368
+ return true;
369
+ }
370
+ async function linesOf(env, ids) {
371
+ if (ids.length === 0)
372
+ return [];
373
+ return (await env
374
+ .model('account.bank.statement.line')
375
+ .browse([...ids])
376
+ .load({ statement: true, partner: true })).map((l) => ({
377
+ id: l.id,
378
+ journal: l.statement.journal.id,
379
+ label: String(l.label ?? ''),
380
+ reference: l.reference ?? null,
381
+ amount: l.amount ?? zero,
382
+ partner: l.partner?.id ?? null,
383
+ }));
384
+ }
385
+ /** What a rule does to a line: the document it would match, or the write-off, or a suggestion; null: nothing. */
386
+ async function outcomeOf(env, rule, line) {
387
+ if (rule.outcome === 'write_off')
388
+ return { outcome: 'write_off', detail: `written off: ${rule.writeOffLabel ?? line.label}` };
389
+ if (rule.outcome === 'suggest')
390
+ return { outcome: 'suggest', detail: `suggested by ${rule.name}` };
391
+ const best = (await proposalsFor(env, line.id)).find((p) => p.reasons.includes('the same amount'));
392
+ return best === undefined
393
+ ? null
394
+ : { outcome: 'match', detail: `matched with ${best.name}`, move: best.move };
395
+ }
396
+ /**
397
+ * Runs the rules on open lines (RFC 0029 §3.2): for each, the first rule that applies and can act — matching the
398
+ * document of the same amount, writing off, or suggesting. Applied as the caller (an accountant), each automatic
399
+ * match audited; the rule's count goes up.
400
+ */
401
+ export async function applyRules(env, lineIds) {
402
+ const rules = await rulesOf(env);
403
+ if (rules.length === 0)
404
+ return [];
405
+ const open = (await linesOf(env, lineIds)).filter((l) => l.id > 0);
406
+ const states = new Map((await env
407
+ .model('account.bank.statement.line')
408
+ .browse([...lineIds])
409
+ .load(['state'])).map((l) => [l.id, String(l.state)]));
410
+ const out = [];
411
+ for (const line of open) {
412
+ if (states.get(line.id) !== 'open')
413
+ continue;
414
+ for (const rule of rules) {
415
+ if (!applies(rule, line))
416
+ continue;
417
+ const what = await outcomeOf(env, rule, line);
418
+ if (what === null)
419
+ continue;
420
+ if (what.outcome === 'match' && what.move !== undefined)
421
+ await matchLine(env, line.id, [what.move]);
422
+ else if (what.outcome === 'write_off' && rule.writeOffAccount !== null)
423
+ await writeOffLine(env, line.id, {
424
+ account: rule.writeOffAccount,
425
+ ...(rule.writeOffLabel === null ? {} : { label: rule.writeOffLabel }),
426
+ });
427
+ await env
428
+ .model('account.bank.statement.line')
429
+ .browse([line.id])
430
+ .write({ rule: rule.id });
431
+ if (what.outcome !== 'suggest') {
432
+ await env.asSuperuser({ reason: 'reconciliation rule count' }, async (su) => {
433
+ const [r] = await su
434
+ .model('account.reconcile.rule')
435
+ .browse([rule.id])
436
+ .load(['hits']);
437
+ await su
438
+ .model('account.reconcile.rule')
439
+ .browse([rule.id])
440
+ .write({ hits: Number(r?.hits ?? 0) + 1 });
441
+ });
442
+ await audit(env, {
443
+ kind: 'account.reconcile.rule',
444
+ summary: `${rule.name}: statement line ${line.id} ${what.detail}.`,
445
+ actor: env.uid,
446
+ targetModel: 'account.bank.statement.line',
447
+ targetId: line.id,
448
+ details: { rule: rule.id, outcome: what.outcome },
449
+ });
450
+ }
451
+ out.push({ line: line.id, rule: rule.id, outcome: what.outcome, detail: what.detail });
452
+ break;
453
+ }
454
+ }
455
+ return out;
456
+ }
457
+ /**
458
+ * A rule's dry run (RFC 0029 §3.2): what it would do to the open lines of the last `days` days, and its hit rate —
459
+ * how many lines of those days it applies to. Nothing changes.
460
+ */
461
+ export async function dryRunRule(env, ruleId, days = 90) {
462
+ const [rule] = await rulesOf(env, ruleId);
463
+ if (rule === undefined)
464
+ throw new HttpProblem(404, 'not-found', 'Not found', 'No such rule.');
465
+ const since = new Date(Date.now() - days * 86_400_000).toISOString().slice(0, 10);
466
+ const found = await env.model('account.bank.statement.line').search({
467
+ where: { date: { gte: since } },
468
+ limit: 5000,
469
+ });
470
+ const all = await linesOf(env, found.ids);
471
+ const states = new Map((await found.load(['state'])).map((l) => [l.id, String(l.state)]));
472
+ let hits = 0;
473
+ const examples = [];
474
+ for (const line of all) {
475
+ if (!applies(rule, line))
476
+ continue;
477
+ hits += 1;
478
+ if (states.get(line.id) !== 'open' || examples.length >= 10)
479
+ continue;
480
+ const what = await outcomeOf(env, rule, line);
481
+ if (what !== null)
482
+ examples.push({ line: line.id, label: line.label, detail: what.detail });
483
+ }
484
+ return {
485
+ lines: all.length,
486
+ hits,
487
+ rate: all.length === 0 ? 0 : Math.round((hits / all.length) * 1000) / 10,
488
+ examples,
489
+ };
490
+ }
491
+ /**
492
+ * Undoes a matched line (RFC 0029 §3.2): a payment comes off its documents and is reversed (the payment and its
493
+ * reversal matched together); a write-off is reversed. The line is open again.
494
+ */
495
+ export async function undoLine(env, lineId) {
496
+ if (!env.su && !env.access.has('account.reconcile'))
497
+ throw new HttpProblem(403, 'access', 'Reconciliation', 'Undoing needs the reconcile permission.');
498
+ const [line] = await env
499
+ .model('account.bank.statement.line')
500
+ .browse([lineId])
501
+ .load({ payment: { move: true }, move: true });
502
+ if (line === undefined)
503
+ throw new HttpProblem(404, 'not-found', 'Not found', 'No such line.');
504
+ if (line.state !== 'matched')
505
+ throw new HttpProblem(409, 'open', 'Not matched', 'This line is not matched.');
506
+ const { reverseMove } = await import('./index.js');
507
+ const reconcilable = async (moveId) => (await (await env.model('account.move.line').search({ where: { move: moveId } })).load({ account: true }))
508
+ .filter((l) => l.account?.reconcile === true)
509
+ .map((l) => l.id);
510
+ const paymentMove = line.payment?.move?.id;
511
+ if (paymentMove !== undefined) {
512
+ const own = await reconcilable(paymentMove);
513
+ if (own.length > 0)
514
+ await unreconcile(env, own);
515
+ const reversed = await reverseMove(env, paymentMove, { post: true });
516
+ const back = await reconcilable(reversed.id);
517
+ if (own.length + back.length > 1)
518
+ await reconcile(env, [...own, ...back]);
519
+ }
520
+ else if (line.move !== null)
521
+ await reverseMove(env, line.move.id, { post: true });
522
+ await env
523
+ .model('account.bank.statement.line')
524
+ .browse([lineId])
525
+ .write({ state: 'open', payment: null, move: null, rule: null });
526
+ await audit(env, {
527
+ kind: 'account.statement.undo',
528
+ summary: `Statement line ${lineId} unmatched.`,
529
+ actor: env.uid,
530
+ targetModel: 'account.bank.statement.line',
531
+ targetId: lineId,
532
+ details: {},
533
+ });
534
+ return { line: lineId };
535
+ }
536
+ /**
537
+ * The open documents a line may pay (RFC 0029 §3.1), best first, each with why: money in pays customer invoices
538
+ * (and vendor credit notes), money out vendor bills (and customer credit notes); the exact amount, the document's
539
+ * number or reference in the label, the line's partner.
540
+ */
541
+ export async function proposalsFor(env, lineId, limit = 5) {
542
+ const [line] = await env
543
+ .model('account.bank.statement.line')
544
+ .browse([lineId])
545
+ .load({ statement: { journal: true }, partner: true });
546
+ if (line === undefined)
547
+ throw new HttpProblem(404, 'not-found', 'Not found', 'No such line.');
548
+ const amount = (line.amount ?? zero).abs();
549
+ const incoming = (line.amount ?? zero).gt(0);
550
+ const kinds = incoming
551
+ ? ['out_invoice', 'out_debit', 'in_refund']
552
+ : ['in_invoice', 'in_debit', 'out_refund'];
553
+ const docs = await (await env.model('account.move').search({
554
+ where: {
555
+ state: 'posted',
556
+ kind: { in: kinds },
557
+ company: line.statement.company.id,
558
+ },
559
+ limit: 500,
560
+ })).load({ partner: true });
561
+ const label = `${line.label ?? ''} ${line.reference ?? ''}`.toLowerCase();
562
+ const out = [];
563
+ for (const d of docs) {
564
+ const residual = orm.toDecimal(String(d.amountResidual?.amount ?? '0'));
565
+ if (residual.lte(0))
566
+ continue;
567
+ const reasons = [];
568
+ let score = 0;
569
+ if (residual.eq(amount)) {
570
+ score += 50;
571
+ reasons.push('the same amount');
572
+ }
573
+ const name = String(d.name ?? '');
574
+ const ref = String(d.ref ?? '');
575
+ if (name !== '' && name !== '/' && label.includes(name.toLowerCase())) {
576
+ score += 40;
577
+ reasons.push(`${name} in the label`);
578
+ }
579
+ else if (ref !== '' && label.includes(ref.toLowerCase())) {
580
+ score += 30;
581
+ reasons.push(`its reference ${ref} in the label`);
582
+ }
583
+ if (line.partner !== null && d.partner?.id === line.partner.id) {
584
+ score += 20;
585
+ reasons.push('the same partner');
586
+ }
587
+ else if (d.partner !== null &&
588
+ d.partner !== undefined &&
589
+ label.includes(String(d.partner.name).toLowerCase())) {
590
+ score += 15;
591
+ reasons.push(`${String(d.partner.name)} in the label`);
592
+ }
593
+ if (score === 0)
594
+ continue;
595
+ out.push({
596
+ move: d.id,
597
+ name,
598
+ partner: d.partner === null || d.partner === undefined ? null : String(d.partner.name),
599
+ residual: residual.toFixed(2),
600
+ score,
601
+ reasons,
602
+ });
603
+ }
604
+ return out.sort((a, b) => b.score - a.score || a.move - b.move).slice(0, limit);
605
+ }
606
+ /** Opens a line to be matched, or refuses: it must be open and the caller may reconcile. */
607
+ async function openLine(env, lineId) {
608
+ if (!env.su && !env.access.has('account.reconcile'))
609
+ throw new HttpProblem(403, 'access', 'Reconciliation', 'Matching needs the reconcile permission.');
610
+ const [line] = await env
611
+ .model('account.bank.statement.line')
612
+ .browse([lineId])
613
+ .load({ statement: { journal: true } });
614
+ if (line === undefined)
615
+ throw new HttpProblem(404, 'not-found', 'Not found', 'No such line.');
616
+ if (line.state !== 'open')
617
+ throw new HttpProblem(409, 'matched', 'Matched already', 'This line is matched already.');
618
+ return line;
619
+ }
620
+ /** Matches a line with the documents it pays: the payment from the statement's journal, posted and reconciled. */
621
+ export async function matchLine(env, lineId, moves) {
622
+ const line = await openLine(env, lineId);
623
+ const amount = (line.amount ?? zero).abs();
624
+ const paid = await registerPayment(env, {
625
+ moves,
626
+ journal: line.statement.journal.id,
627
+ date: String(line.date).slice(0, 10),
628
+ amount: amount.toFixed(2),
629
+ memo: String(line.label ?? ''),
630
+ });
631
+ const [payment] = await env
632
+ .model('account.payment')
633
+ .browse([paid.id])
634
+ .load({ partner: true });
635
+ await env
636
+ .model('account.bank.statement.line')
637
+ .browse([lineId])
638
+ .write({
639
+ state: 'matched',
640
+ payment: paid.id,
641
+ ...(payment?.partner ? { partner: payment.partner.id } : {}),
642
+ });
643
+ return { payment: paid.id };
644
+ }
645
+ /** Writes a line off to a counterpart account (fees, interest): an entry in the statement's journal, posted. */
646
+ export async function writeOffLine(env, lineId, input) {
647
+ const line = await openLine(env, lineId);
648
+ const journal = line.statement.journal;
649
+ if (journal.defaultAccount === null)
650
+ throw new HttpProblem(409, 'no-account', 'No bank account', `${String(journal.name)} has no default account.`, 'Set the journal’s account first.');
651
+ const amount = line.amount ?? zero;
652
+ const money = amount.abs().toFixed(2);
653
+ const name = input.label ?? String(line.label ?? '');
654
+ const [company] = await env
655
+ .model('res.company')
656
+ .browse([line.statement.company.id])
657
+ .load({ currency: true });
658
+ const made = await env.model('account.move').create([
659
+ {
660
+ kind: 'entry',
661
+ journal: journal.id,
662
+ currency: company?.currency.id,
663
+ date: String(line.date).slice(0, 10),
664
+ ref: name,
665
+ company: line.statement.company.id,
666
+ lines: {
667
+ create: [
668
+ {
669
+ account: journal.defaultAccount.id,
670
+ name,
671
+ debit: amount.gt(0) ? money : '0',
672
+ credit: amount.gt(0) ? '0' : money,
673
+ },
674
+ {
675
+ account: input.account,
676
+ name,
677
+ debit: amount.gt(0) ? '0' : money,
678
+ credit: amount.gt(0) ? money : '0',
679
+ },
680
+ ],
681
+ },
682
+ },
683
+ ]);
684
+ await env.model('account.move').browse(made.ids).post();
685
+ await env
686
+ .model('account.bank.statement.line')
687
+ .browse([lineId])
688
+ .write({ state: 'matched', move: made.ids[0] });
689
+ return { move: made.ids[0] };
690
+ }
691
+ const feeds = new Map();
692
+ /** Makes a feed available to journals that name it (`account.journal.bankFeed`). */
693
+ export function registerBankFeed(feed) {
694
+ feeds.set(feed.name, feed);
695
+ }
696
+ /** The feed of a name, or undefined. */
697
+ export function bankFeed(name) {
698
+ return feeds.get(name);
699
+ }
700
+ /** A feed for tests and demos: the rows it is given, once each. */
701
+ export function testBankFeed(rows) {
702
+ return {
703
+ name: 'test',
704
+ fetch: async (_journal, cursor) => {
705
+ const from = cursor === null ? 0 : Number(cursor);
706
+ return { rows: rows.slice(from), cursor: String(rows.length) };
707
+ },
708
+ };
709
+ }
710
+ /** Pulls a journal's feed into a new statement; its cursor moves on. */
711
+ export async function pullFeed(env, journalId) {
712
+ const [journal] = await env
713
+ .model('account.journal')
714
+ .browse([journalId])
715
+ .load(['bankFeed', 'bankFeedCursor']);
716
+ const name = journal?.bankFeed ?? null;
717
+ const feed = name === null ? undefined : feeds.get(String(name));
718
+ if (feed === undefined)
719
+ throw new HttpProblem(409, 'no-feed', 'No bank feed', 'This journal has no bank feed.');
720
+ const { rows, cursor } = await feed.fetch(journalId, journal?.bankFeedCursor ?? null);
721
+ const result = await importStatement(env, { journal: journalId, rows });
722
+ // The cursor is the journal's: kept as the system (accountants do not edit journals).
723
+ await env.asSuperuser({ reason: 'bank feed cursor' }, (su) => su
724
+ .model('account.journal')
725
+ .browse([journalId])
726
+ .write({ bankFeedCursor: cursor }));
727
+ return result;
728
+ }