shutro-erp 0.3.5 → 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 (384) 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 +123 -4
  27. package/dist/account_portal/module.d.ts +146 -0
  28. package/dist/accounting/.kosh/bindings.js +1 -1
  29. package/dist/accounting/.kosh/schema.d.ts +123 -4
  30. package/dist/accounting/index.d.ts +1 -1
  31. package/dist/accounting/index.js +131 -3
  32. package/dist/accounting/module.d.ts +296 -0
  33. package/dist/accounting/module.js +32 -1
  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/cli/init.js +32 -0
  79. package/dist/cli/templates.d.ts +1 -1
  80. package/dist/cli/templates.js +89 -0
  81. package/dist/contacts/module.js +13 -2
  82. package/dist/crm/.kosh/bindings.js +1 -1
  83. package/dist/crm/.kosh/schema.d.ts +2 -1
  84. package/dist/crm/module.d.ts +2 -0
  85. package/dist/crm/module.js +2 -1
  86. package/dist/documents/.kosh/bindings.d.ts +22 -0
  87. package/dist/documents/.kosh/bindings.js +15 -0
  88. package/dist/documents/.kosh/schema.d.ts +862 -0
  89. package/dist/documents/demo.d.ts +6 -0
  90. package/dist/documents/demo.js +22 -0
  91. package/dist/documents/documents.d.ts +98 -0
  92. package/dist/documents/documents.js +318 -0
  93. package/dist/documents/index.d.ts +30 -0
  94. package/dist/documents/index.js +442 -0
  95. package/dist/documents/module.d.ts +2781 -0
  96. package/dist/documents/module.js +214 -0
  97. package/dist/documents/web/Versions.d.ts +7 -0
  98. package/dist/documents/web/Versions.js +45 -0
  99. package/dist/documents/web/index.d.ts +11 -0
  100. package/dist/documents/web/index.js +16 -0
  101. package/dist/documents/web/messages.d.ts +2 -0
  102. package/dist/documents/web/messages.js +21 -0
  103. package/dist/event_account/.kosh/bindings.js +1 -1
  104. package/dist/event_account/.kosh/schema.d.ts +123 -4
  105. package/dist/event_account/module.d.ts +146 -0
  106. package/dist/expense/.kosh/bindings.d.ts +22 -0
  107. package/dist/expense/.kosh/bindings.js +15 -0
  108. package/dist/expense/.kosh/schema.d.ts +1299 -0
  109. package/dist/expense/demo.d.ts +7 -0
  110. package/dist/expense/demo.js +69 -0
  111. package/dist/expense/index.d.ts +3 -0
  112. package/dist/expense/index.js +495 -0
  113. package/dist/expense/module.d.ts +4747 -0
  114. package/dist/expense/module.js +647 -0
  115. package/dist/expense_account/.kosh/bindings.d.ts +22 -0
  116. package/dist/expense_account/.kosh/bindings.js +15 -0
  117. package/dist/expense_account/.kosh/schema.d.ts +1936 -0
  118. package/dist/expense_account/index.d.ts +3 -0
  119. package/dist/expense_account/index.js +74 -0
  120. package/dist/expense_account/module.d.ts +9153 -0
  121. package/dist/expense_account/module.js +318 -0
  122. package/dist/helpdesk/.kosh/bindings.d.ts +22 -0
  123. package/dist/helpdesk/.kosh/bindings.js +15 -0
  124. package/dist/helpdesk/.kosh/schema.d.ts +885 -0
  125. package/dist/helpdesk/demo.d.ts +7 -0
  126. package/dist/helpdesk/demo.js +65 -0
  127. package/dist/helpdesk/hours.d.ts +17 -0
  128. package/dist/helpdesk/hours.js +94 -0
  129. package/dist/helpdesk/index.d.ts +10 -0
  130. package/dist/helpdesk/index.js +461 -0
  131. package/dist/helpdesk/module.d.ts +2940 -0
  132. package/dist/helpdesk/module.js +326 -0
  133. package/dist/helpdesk/routes.d.ts +12 -0
  134. package/dist/helpdesk/routes.js +132 -0
  135. package/dist/helpdesk/sla.d.ts +37 -0
  136. package/dist/helpdesk/sla.js +226 -0
  137. package/dist/hr/index.d.ts +1 -1
  138. package/dist/hr/index.js +1 -1
  139. package/dist/l10n_bd/.kosh/bindings.js +1 -1
  140. package/dist/l10n_bd/.kosh/schema.d.ts +123 -4
  141. package/dist/l10n_bd/module.d.ts +146 -0
  142. package/dist/l10n_bd_payroll/.kosh/bindings.d.ts +22 -0
  143. package/dist/l10n_bd_payroll/.kosh/bindings.js +15 -0
  144. package/dist/l10n_bd_payroll/.kosh/schema.d.ts +2312 -0
  145. package/dist/l10n_bd_payroll/data.d.ts +47 -0
  146. package/dist/l10n_bd_payroll/data.js +347 -0
  147. package/dist/l10n_bd_payroll/index.d.ts +4 -0
  148. package/dist/l10n_bd_payroll/index.js +24 -0
  149. package/dist/l10n_bd_payroll/module.d.ts +12932 -0
  150. package/dist/l10n_bd_payroll/module.js +86 -0
  151. package/dist/l10n_in/.kosh/bindings.js +1 -1
  152. package/dist/l10n_in/.kosh/schema.d.ts +123 -4
  153. package/dist/l10n_in/module.d.ts +147 -0
  154. package/dist/mail/inbound/handle.js +6 -1
  155. package/dist/maintenance_stock/.kosh/bindings.js +1 -1
  156. package/dist/maintenance_stock/.kosh/schema.d.ts +77 -2
  157. package/dist/maintenance_stock/module.d.ts +788 -66
  158. package/dist/modules-sdk/index.d.ts +1 -1
  159. package/dist/modules-sdk/manifest.d.ts +39 -2
  160. package/dist/mrp/.kosh/bindings.js +1 -1
  161. package/dist/mrp/.kosh/schema.d.ts +77 -2
  162. package/dist/mrp/index.d.ts +1 -1
  163. package/dist/mrp/index.js +2 -1
  164. package/dist/mrp/module.d.ts +781 -54
  165. package/dist/mrp/module.js +64 -2
  166. package/dist/mrp_account/.kosh/bindings.js +1 -1
  167. package/dist/mrp_account/.kosh/schema.d.ts +198 -5
  168. package/dist/mrp_account/module.d.ts +4852 -2541
  169. package/dist/payment/.kosh/bindings.js +1 -1
  170. package/dist/payment/.kosh/schema.d.ts +125 -5
  171. package/dist/payment/index.d.ts +41 -0
  172. package/dist/payment/index.js +131 -74
  173. package/dist/payment/module.d.ts +210 -6
  174. package/dist/payment/module.js +19 -3
  175. package/dist/payment/settle.js +10 -1
  176. package/dist/payroll/.kosh/bindings.d.ts +22 -0
  177. package/dist/payroll/.kosh/bindings.js +15 -0
  178. package/dist/payroll/.kosh/schema.d.ts +1390 -0
  179. package/dist/payroll/compute.d.ts +35 -0
  180. package/dist/payroll/compute.js +398 -0
  181. package/dist/payroll/demo.d.ts +55 -0
  182. package/dist/payroll/demo.js +121 -0
  183. package/dist/payroll/formula.d.ts +62 -0
  184. package/dist/payroll/formula.js +320 -0
  185. package/dist/payroll/index.d.ts +230 -0
  186. package/dist/payroll/index.js +713 -0
  187. package/dist/payroll/module.d.ts +9390 -0
  188. package/dist/payroll/module.js +606 -0
  189. package/dist/payroll/routes.d.ts +10 -0
  190. package/dist/payroll/routes.js +112 -0
  191. package/dist/payroll_account/.kosh/bindings.d.ts +22 -0
  192. package/dist/payroll_account/.kosh/bindings.js +15 -0
  193. package/dist/payroll_account/.kosh/schema.d.ts +2308 -0
  194. package/dist/payroll_account/index.d.ts +3 -0
  195. package/dist/payroll_account/index.js +40 -0
  196. package/dist/payroll_account/module.d.ts +12396 -0
  197. package/dist/payroll_account/module.js +256 -0
  198. package/dist/permissions/approvals.d.ts +131 -9
  199. package/dist/permissions/approvals.js +662 -57
  200. package/dist/permissions/index.d.ts +1 -1
  201. package/dist/permissions/index.js +2 -2
  202. package/dist/permissions/models.js +191 -12
  203. package/dist/permissions/routes.d.ts +6 -1
  204. package/dist/permissions/routes.js +37 -4
  205. package/dist/permissions/web/ApprovalPanel.d.ts +5 -4
  206. package/dist/permissions/web/ApprovalPanel.js +39 -13
  207. package/dist/permissions/web/index.d.ts +6 -4
  208. package/dist/permissions/web/index.js +13 -4
  209. package/dist/permissions/web/messages.d.ts +2 -0
  210. package/dist/permissions/web/messages.js +43 -0
  211. package/dist/portal/index.js +1 -1
  212. package/dist/pos/.kosh/bindings.js +1 -1
  213. package/dist/pos/.kosh/schema.d.ts +123 -4
  214. package/dist/pos/index.js +4 -0
  215. package/dist/pos/module.d.ts +147 -0
  216. package/dist/pos_stock/.kosh/bindings.js +1 -1
  217. package/dist/pos_stock/.kosh/schema.d.ts +198 -5
  218. package/dist/pos_stock/module.d.ts +914 -46
  219. package/dist/product/.kosh/bindings.js +1 -1
  220. package/dist/product/.kosh/schema.d.ts +2 -1
  221. package/dist/product/index.js +13 -0
  222. package/dist/product/module.d.ts +2 -0
  223. package/dist/product/module.js +2 -1
  224. package/dist/project/.kosh/bindings.js +1 -1
  225. package/dist/project/.kosh/schema.d.ts +672 -1
  226. package/dist/project/index.d.ts +3 -1
  227. package/dist/project/index.js +901 -14
  228. package/dist/project/module.d.ts +9892 -390
  229. package/dist/project/module.js +1530 -13
  230. package/dist/project/routes.d.ts +44 -0
  231. package/dist/project/routes.js +314 -0
  232. package/dist/project/schedule.d.ts +72 -0
  233. package/dist/project/schedule.js +192 -0
  234. package/dist/project/web/Refresh.d.ts +8 -0
  235. package/dist/project/web/Refresh.js +29 -0
  236. package/dist/project/web/Schedule.d.ts +9 -0
  237. package/dist/project/web/Schedule.js +93 -0
  238. package/dist/project/web/Structure.d.ts +8 -0
  239. package/dist/project/web/Structure.js +86 -0
  240. package/dist/project/web/Workload.d.ts +8 -0
  241. package/dist/project/web/Workload.js +43 -0
  242. package/dist/project/web/index.d.ts +14 -0
  243. package/dist/project/web/index.js +25 -0
  244. package/dist/project/web/messages.d.ts +2 -0
  245. package/dist/project/web/messages.js +85 -0
  246. package/dist/project_account/.kosh/bindings.d.ts +22 -0
  247. package/dist/project_account/.kosh/bindings.js +15 -0
  248. package/dist/project_account/.kosh/schema.d.ts +2241 -0
  249. package/dist/project_account/index.d.ts +3 -0
  250. package/dist/project_account/index.js +27 -0
  251. package/dist/project_account/module.d.ts +11539 -0
  252. package/dist/project_account/module.js +76 -0
  253. package/dist/project_portal/.kosh/bindings.js +1 -1
  254. package/dist/project_portal/.kosh/schema.d.ts +672 -1
  255. package/dist/project_portal/module.d.ts +4798 -129
  256. package/dist/project_purchase/.kosh/bindings.d.ts +22 -0
  257. package/dist/project_purchase/.kosh/bindings.js +15 -0
  258. package/dist/project_purchase/.kosh/schema.d.ts +2320 -0
  259. package/dist/project_purchase/index.d.ts +9 -0
  260. package/dist/project_purchase/index.js +22 -0
  261. package/dist/project_purchase/module.d.ts +25147 -0
  262. package/dist/project_purchase/module.js +56 -0
  263. package/dist/purchase/.kosh/bindings.js +1 -1
  264. package/dist/purchase/.kosh/schema.d.ts +97 -4
  265. package/dist/purchase/module.d.ts +13 -2
  266. package/dist/purchase/module.js +10 -1
  267. package/dist/purchase_account/.kosh/bindings.js +1 -1
  268. package/dist/purchase_account/.kosh/schema.d.ts +126 -6
  269. package/dist/purchase_account/index.js +1 -0
  270. package/dist/purchase_account/module.d.ts +176 -1
  271. package/dist/purchase_account/module.js +13 -2
  272. package/dist/purchase_portal/.kosh/bindings.js +1 -1
  273. package/dist/purchase_portal/.kosh/schema.d.ts +97 -4
  274. package/dist/purchase_portal/module.d.ts +6 -1
  275. package/dist/purchase_stock/.kosh/bindings.js +1 -1
  276. package/dist/purchase_stock/.kosh/schema.d.ts +174 -5
  277. package/dist/purchase_stock/index.d.ts +1 -1
  278. package/dist/purchase_stock/index.js +9 -2
  279. package/dist/purchase_stock/module.d.ts +840 -68
  280. package/dist/purchase_stock/module.js +242 -6
  281. package/dist/purchase_stock_account/.kosh/bindings.js +1 -1
  282. package/dist/purchase_stock_account/.kosh/schema.d.ts +203 -7
  283. package/dist/purchase_stock_account/module.d.ts +8163 -5514
  284. package/dist/purchase_stock_account/module.js +11 -5
  285. package/dist/recruitment/.kosh/bindings.js +1 -1
  286. package/dist/recruitment/.kosh/schema.d.ts +2 -1
  287. package/dist/recruitment/module.d.ts +2 -0
  288. package/dist/recruitment/module.js +2 -1
  289. package/dist/sale_account/.kosh/bindings.js +1 -1
  290. package/dist/sale_account/.kosh/schema.d.ts +123 -4
  291. package/dist/sale_account/module.d.ts +147 -0
  292. package/dist/sale_portal/.kosh/bindings.js +1 -1
  293. package/dist/sale_portal/.kosh/schema.d.ts +2 -1
  294. package/dist/sale_portal/module.d.ts +1 -0
  295. package/dist/sale_project/.kosh/bindings.js +1 -1
  296. package/dist/sale_project/.kosh/schema.d.ts +673 -1
  297. package/dist/sale_project/module.d.ts +4800 -129
  298. package/dist/sale_project/module.js +17 -1
  299. package/dist/sale_stock/.kosh/bindings.js +1 -1
  300. package/dist/sale_stock/.kosh/schema.d.ts +77 -2
  301. package/dist/sale_stock/module.d.ts +789 -66
  302. package/dist/sales/.kosh/bindings.js +1 -1
  303. package/dist/sales/.kosh/schema.d.ts +2 -1
  304. package/dist/sales/index.js +4 -0
  305. package/dist/sales/module.d.ts +1 -0
  306. package/dist/sign/.kosh/bindings.d.ts +22 -0
  307. package/dist/sign/.kosh/bindings.js +15 -0
  308. package/dist/sign/.kosh/schema.d.ts +990 -0
  309. package/dist/sign/index.d.ts +14 -0
  310. package/dist/sign/index.js +350 -0
  311. package/dist/sign/module.d.ts +4478 -0
  312. package/dist/sign/module.js +158 -0
  313. package/dist/sign/sign.d.ts +82 -0
  314. package/dist/sign/sign.js +474 -0
  315. package/dist/stock/.kosh/bindings.js +1 -1
  316. package/dist/stock/.kosh/schema.d.ts +77 -2
  317. package/dist/stock/demo.d.ts +2 -1
  318. package/dist/stock/demo.js +28 -1
  319. package/dist/stock/index.d.ts +2 -1
  320. package/dist/stock/index.js +233 -4
  321. package/dist/stock/module.d.ts +2363 -837
  322. package/dist/stock/module.js +569 -14
  323. package/dist/stock/replenish.d.ts +38 -0
  324. package/dist/stock/replenish.js +176 -0
  325. package/dist/stock/web/Replenishment.d.ts +8 -0
  326. package/dist/stock/web/Replenishment.js +84 -0
  327. package/dist/stock/web/index.d.ts +4 -2
  328. package/dist/stock/web/index.js +6 -2
  329. package/dist/stock/web/messages.js +52 -0
  330. package/dist/stock_account/.kosh/bindings.js +1 -1
  331. package/dist/stock_account/.kosh/schema.d.ts +198 -5
  332. package/dist/stock_account/index.js +10 -0
  333. package/dist/stock_account/module.d.ts +969 -101
  334. package/dist/trade_finance/index.js +2 -0
  335. package/dist/trade_finance/module.js +2 -0
  336. package/dist/trade_finance_account/.kosh/bindings.js +1 -1
  337. package/dist/trade_finance_account/.kosh/schema.d.ts +123 -4
  338. package/dist/trade_finance_account/index.js +1 -0
  339. package/dist/trade_finance_account/module.d.ts +146 -0
  340. package/dist/trade_finance_account/module.js +1 -0
  341. package/dist/trade_finance_purchase/.kosh/bindings.js +1 -1
  342. package/dist/trade_finance_purchase/.kosh/schema.d.ts +97 -4
  343. package/dist/trade_finance_purchase/module.d.ts +6 -1
  344. package/dist/trade_finance_sale/.kosh/bindings.js +1 -1
  345. package/dist/trade_finance_sale/.kosh/schema.d.ts +2 -1
  346. package/dist/trade_finance_sale/module.d.ts +1 -0
  347. package/dist/ui/app.js +24 -12
  348. package/dist/ui/components/AppIcon.js +8 -1
  349. package/dist/ui/i18n/messages.d.ts +4 -0
  350. package/dist/ui/i18n/messages.js +8 -0
  351. package/dist/ui/views/FormView.js +4 -2
  352. package/dist/ui/views/KanbanView.js +9 -1
  353. package/dist/ui/views/ListView.js +4 -2
  354. package/dist/ui/views/widgets.js +7 -1
  355. package/dist/website/index.js +1 -0
  356. package/dist/website/module.js +1 -0
  357. package/dist/website_crm/.kosh/bindings.js +1 -1
  358. package/dist/website_crm/.kosh/schema.d.ts +2 -1
  359. package/dist/website_crm/module.d.ts +1 -0
  360. package/dist/website_sale/.kosh/bindings.d.ts +22 -0
  361. package/dist/website_sale/.kosh/bindings.js +15 -0
  362. package/dist/website_sale/.kosh/schema.d.ts +2284 -0
  363. package/dist/website_sale/couriers.d.ts +50 -0
  364. package/dist/website_sale/couriers.js +59 -0
  365. package/dist/website_sale/demo.d.ts +7 -0
  366. package/dist/website_sale/demo.js +90 -0
  367. package/dist/website_sale/growth.d.ts +31 -0
  368. package/dist/website_sale/growth.js +163 -0
  369. package/dist/website_sale/index.d.ts +49 -0
  370. package/dist/website_sale/index.js +538 -0
  371. package/dist/website_sale/module.d.ts +22027 -0
  372. package/dist/website_sale/module.js +397 -0
  373. package/dist/website_sale/shipments.d.ts +16 -0
  374. package/dist/website_sale/shipments.js +113 -0
  375. package/dist/website_sale/shop.d.ts +81 -0
  376. package/dist/website_sale/shop.js +649 -0
  377. package/dist/website_sale_stock/.kosh/bindings.d.ts +22 -0
  378. package/dist/website_sale_stock/.kosh/bindings.js +15 -0
  379. package/dist/website_sale_stock/.kosh/schema.d.ts +2681 -0
  380. package/dist/website_sale_stock/index.d.ts +9 -0
  381. package/dist/website_sale_stock/index.js +20 -0
  382. package/dist/website_sale_stock/module.d.ts +24998 -0
  383. package/dist/website_sale_stock/module.js +28 -0
  384. package/package.json +83 -2
@@ -0,0 +1,606 @@
1
+ /**
2
+ * The data layer of Payroll (RFC 0024 §2.3): salary **structures** of **rules** (a code, a category, a formula in
3
+ * the payroll language, an optional condition, effective dates), versioned **parameters** and progressive **slab
4
+ * tables** (rates and thresholds are data, never code), one-off **inputs**, **loans** repaid run by run, monthly
5
+ * **runs** with a **payslip** per person whose **lines** carry their explanation, and the employee's pay details.
6
+ *
7
+ * People read their own payslips; payroll officers prepare runs; payroll managers approve them, which locks them —
8
+ * a correction is a new run that refers to the old one, never an edit.
9
+ */
10
+ import { attendanceShutro } from '../attendance/index.js';
11
+ import { hrShutro } from '../hr/index.js';
12
+ import { orm } from '../server/orm.js';
13
+ import { check, compute, defineModel, defineModule, extendModel, f, method, records, } from './.kosh/bindings.js';
14
+ import { computeRuns, recordRepayments } from './compute.js';
15
+ import { FormulaError, parse } from './formula.js';
16
+ const READ = ['read'];
17
+ const ALL = ['read', 'write', 'create', 'unlink'];
18
+ const OFFICERS = ['payroll.officer'];
19
+ const me = { $env: 'uid' };
20
+ const ZERO = orm.toDecimal(0);
21
+ const CODE = orm.sql `~ '^[A-Z][A-Z0-9_]*$'`;
22
+ export const CATEGORIES = {
23
+ basic: 'Basic',
24
+ allowance: 'Allowance',
25
+ deduction: 'Deduction',
26
+ contribution: 'Company contribution',
27
+ info: 'For information',
28
+ };
29
+ /** The formulas that do not parse, by record id. */
30
+ function badFormulas(rows) {
31
+ const out = [];
32
+ for (const r of rows)
33
+ for (const text of [r.formula, r.condition])
34
+ if (text != null && text.trim() !== '')
35
+ try {
36
+ parse(text);
37
+ }
38
+ catch (error) {
39
+ if (error instanceof FormulaError) {
40
+ out.push(r.id);
41
+ break;
42
+ }
43
+ throw error;
44
+ }
45
+ return out;
46
+ }
47
+ export const PayrollStructure = defineModel('payroll.structure', {
48
+ label: 'Salary structure',
49
+ recName: 'name',
50
+ order: ['name asc'],
51
+ archivable: true,
52
+ tracking: ['name'],
53
+ access: { 'payroll.officer': READ, 'payroll.manager': [...ALL, 'export', 'import'] },
54
+ fields: {
55
+ name: f.char({ required: true, translate: true }),
56
+ rules: f.one2many('payroll.rule', 'structure'),
57
+ note: f.text(),
58
+ },
59
+ });
60
+ export const PayrollRule = defineModel('payroll.rule', {
61
+ label: 'Salary rule',
62
+ recName: 'name',
63
+ order: ['sequence asc', 'id asc'],
64
+ tracking: ['formula', 'condition', 'dateFrom', 'dateTo'],
65
+ access: { 'payroll.officer': READ, 'payroll.manager': [...ALL, 'export', 'import'] },
66
+ fields: {
67
+ structure: f.many2one('payroll.structure', { required: true, ondelete: 'cascade' }),
68
+ sequence: f.integer({ required: true, default: 10 }),
69
+ code: f.char({
70
+ size: 40,
71
+ required: true,
72
+ help: 'In capitals; later rules read this rule’s amount by it (BASIC, HOUSE_RENT…).',
73
+ }),
74
+ name: f.char({ required: true, translate: true }),
75
+ category: f.selection(CATEGORIES, {
76
+ required: true,
77
+ default: 'allowance',
78
+ help: 'Basic and allowances make the gross; deductions come off it; company contributions are paid on top.',
79
+ }),
80
+ formula: f.char({
81
+ size: 500,
82
+ required: true,
83
+ default: '0',
84
+ help: "In the payroll language: WAGE * 0.6, max(0, ABSENT_DAYS) * BASIC / 30, slab('TAX', …).",
85
+ }),
86
+ condition: f.char({
87
+ size: 500,
88
+ label: 'Applies when',
89
+ help: 'Empty: always; otherwise a formula that is not zero (SERVICE_MONTHS >= 12).',
90
+ }),
91
+ appearsOnSlip: f.boolean({ default: true, label: 'On the payslip' }),
92
+ dateFrom: f.date({ label: 'Valid from' }),
93
+ dateTo: f.date({ label: 'Valid to' }),
94
+ note: f.text(),
95
+ },
96
+ sqlConstraints: {
97
+ code: { check: orm.sql `code ${CODE}`, message: 'A code is written in capitals, digits and _.' },
98
+ ordered: {
99
+ check: orm.sql `date_to IS NULL OR date_from IS NULL OR date_to >= date_from`,
100
+ message: 'A rule ends on or after it starts.',
101
+ },
102
+ },
103
+ constraints: {
104
+ formulas: {
105
+ ...check('payroll.rule', ['formula', 'condition'], (rules) => badFormulas(rules)),
106
+ message: 'The formula or the condition is not in the payroll language.',
107
+ },
108
+ },
109
+ });
110
+ export const PayrollParameter = defineModel('payroll.parameter', {
111
+ label: 'Payroll parameter',
112
+ recName: 'name',
113
+ order: ['code asc', 'dateFrom desc'],
114
+ tracking: ['value', 'dateFrom', 'dateTo'],
115
+ access: { 'payroll.officer': READ, 'payroll.manager': [...ALL, 'export', 'import'] },
116
+ fields: {
117
+ code: f.char({
118
+ size: 40,
119
+ required: true,
120
+ help: 'Read in formulas as P_ and the code (P_TAX_FREE).',
121
+ }),
122
+ name: f.char({ required: true, translate: true }),
123
+ value: f.decimal({ scale: 4, required: true }),
124
+ dateFrom: f.date({ label: 'Valid from' }),
125
+ dateTo: f.date({ label: 'Valid to' }),
126
+ note: f.text({ help: 'Where the value comes from (the law, the circular, the year).' }),
127
+ },
128
+ sqlConstraints: {
129
+ code: { check: orm.sql `code ${CODE}`, message: 'A code is written in capitals, digits and _.' },
130
+ },
131
+ });
132
+ export const PayrollSlab = defineModel('payroll.slab', {
133
+ label: 'Slab table',
134
+ recName: 'name',
135
+ order: ['code asc', 'dateFrom desc'],
136
+ archivable: true,
137
+ tracking: ['dateFrom', 'dateTo'],
138
+ access: { 'payroll.officer': READ, 'payroll.manager': [...ALL, 'export', 'import'] },
139
+ fields: {
140
+ code: f.char({ size: 40, required: true, help: "Read in formulas as slab('CODE', amount)." }),
141
+ name: f.char({ required: true, translate: true }),
142
+ dateFrom: f.date({ label: 'Valid from' }),
143
+ dateTo: f.date({ label: 'Valid to' }),
144
+ bands: f.one2many('payroll.slab.band', 'slab'),
145
+ note: f.text({ help: 'Where the bands come from (the Finance Act, the year).' }),
146
+ },
147
+ sqlConstraints: {
148
+ code: { check: orm.sql `code ${CODE}`, message: 'A code is written in capitals, digits and _.' },
149
+ },
150
+ });
151
+ export const PayrollSlabBand = defineModel('payroll.slab.band', {
152
+ label: 'Slab band',
153
+ order: ['sequence asc', 'id asc'],
154
+ access: { 'payroll.officer': READ, 'payroll.manager': ALL },
155
+ fields: {
156
+ slab: f.many2one('payroll.slab', { required: true, ondelete: 'cascade' }),
157
+ sequence: f.integer({ required: true, default: 10 }),
158
+ upTo: f.decimal({
159
+ scale: 2,
160
+ label: 'Up to',
161
+ help: 'The band ends at this amount; empty: the rest.',
162
+ }),
163
+ rate: f.decimal({ scale: 2, required: true, default: '0', label: 'Rate (%)' }),
164
+ },
165
+ });
166
+ export const PayrollEmployeeValue = defineModel('payroll.employee.value', {
167
+ label: 'Per-person payroll value',
168
+ order: ['code asc', 'dateFrom desc'],
169
+ companyScoped: { required: true },
170
+ tracking: ['value', 'dateFrom', 'dateTo'],
171
+ access: { 'payroll.user': READ, 'payroll.officer': [...ALL, 'export', 'import'] },
172
+ rules: {
173
+ own: { groups: ['payroll.user'], perms: ['read'], domain: { 'employee.user': me } },
174
+ officers: { groups: ['payroll.officer'], perms: [...ALL], domain: {} },
175
+ },
176
+ fields: {
177
+ employee: f.many2one('hr.employee', { required: true, ondelete: 'cascade' }),
178
+ code: f.char({
179
+ size: 40,
180
+ required: true,
181
+ help: 'Read in formulas as E_ and the code (E_PF_MEMBER, E_INVESTMENT, E_MOBILE_ALLOWANCE).',
182
+ }),
183
+ value: f.decimal({ scale: 4, required: true }),
184
+ dateFrom: f.date({ label: 'Valid from' }),
185
+ dateTo: f.date({ label: 'Valid to' }),
186
+ note: f.char(),
187
+ },
188
+ sqlConstraints: {
189
+ code: { check: orm.sql `code ${CODE}`, message: 'A code is written in capitals, digits and _.' },
190
+ },
191
+ });
192
+ export const PayrollInput = defineModel('payroll.input', {
193
+ label: 'Payroll input',
194
+ order: ['date desc', 'id desc'],
195
+ companyScoped: { required: true },
196
+ tracking: ['amount'],
197
+ access: { 'payroll.user': READ, 'payroll.officer': [...ALL, 'export', 'import'] },
198
+ rules: {
199
+ own: { groups: ['payroll.user'], perms: ['read'], domain: { 'employee.user': me } },
200
+ officers: { groups: ['payroll.officer'], perms: [...ALL], domain: {} },
201
+ },
202
+ fields: {
203
+ employee: f.many2one('hr.employee', { required: true, ondelete: 'cascade' }),
204
+ date: f.date({ required: true, help: 'The month it is paid in.' }),
205
+ code: f.char({
206
+ size: 40,
207
+ required: true,
208
+ help: 'Read in formulas as INPUT_ and the code (INPUT_BONUS).',
209
+ }),
210
+ amount: f.decimal({ scale: 2, required: true }),
211
+ note: f.char(),
212
+ },
213
+ sqlConstraints: {
214
+ code: { check: orm.sql `code ${CODE}`, message: 'A code is written in capitals, digits and _.' },
215
+ },
216
+ });
217
+ export const PayrollLoan = defineModel('payroll.loan', {
218
+ label: 'Salary loan',
219
+ recName: 'name',
220
+ order: ['id desc'],
221
+ companyScoped: { required: true },
222
+ tracking: ['state', 'amount', 'installment'],
223
+ access: { 'payroll.user': READ, 'payroll.officer': [...ALL, 'export', 'import'] },
224
+ rules: {
225
+ own: { groups: ['payroll.user'], perms: ['read'], domain: { 'employee.user': me } },
226
+ officers: { groups: ['payroll.officer'], perms: [...ALL], domain: {} },
227
+ },
228
+ fields: {
229
+ name: f.char({ required: true, sequence: 'payroll.loan', label: 'Reference' }),
230
+ employee: f.many2one('hr.employee', { required: true, ondelete: 'restrict' }),
231
+ amount: f.decimal({ scale: 2, required: true }),
232
+ installment: f.decimal({ scale: 2, required: true, label: 'Per month' }),
233
+ dateStart: f.date({ required: true, label: 'First deduction in' }),
234
+ repayments: f.one2many('payroll.loan.repayment', 'loan'),
235
+ repaid: f.decimal({
236
+ scale: 2,
237
+ label: 'Repaid',
238
+ help: 'The instalments taken by approved payroll runs.',
239
+ ...compute('payroll.loan', 'repaid', ['repayments.amount'], (loans) => {
240
+ for (const l of loans)
241
+ l.repaid = [...l.repayments].reduce((s, r) => s.plus(r.amount), ZERO);
242
+ }),
243
+ store: true,
244
+ }),
245
+ remaining: f.decimal({
246
+ scale: 2,
247
+ label: 'Remaining',
248
+ help: 'The loan less what was repaid; the loan ends when nothing remains.',
249
+ ...compute('payroll.loan', 'remaining', ['amount', 'repaid'], (loans) => {
250
+ for (const l of loans)
251
+ l.remaining = l.amount.minus(l.repaid ?? ZERO);
252
+ }),
253
+ store: true,
254
+ }),
255
+ state: f.state({
256
+ states: { draft: 'Draft', running: 'Being repaid', done: 'Repaid', cancelled: 'Cancelled' },
257
+ initial: 'draft',
258
+ help: 'Being repaid: each approved run deducts the instalment until nothing remains.',
259
+ transitions: {
260
+ start: {
261
+ from: ['draft'],
262
+ to: 'running',
263
+ requires: 'payroll.approve',
264
+ after: records('payroll.loan', async (loans) => {
265
+ for (const id of loans.ids)
266
+ await loans.env.emit('payroll.loan.started', { id });
267
+ }),
268
+ },
269
+ finish: { from: ['running'], to: 'done' },
270
+ cancel: { from: ['draft'], to: 'cancelled' },
271
+ },
272
+ }),
273
+ },
274
+ sqlConstraints: {
275
+ positive: {
276
+ check: orm.sql `amount > 0 AND installment > 0 AND installment <= amount`,
277
+ message: 'A loan and its instalment are positive, the instalment no more than the loan.',
278
+ },
279
+ },
280
+ });
281
+ export const PayrollLoanRepayment = defineModel('payroll.loan.repayment', {
282
+ label: 'Loan repayment',
283
+ order: ['date asc', 'id asc'],
284
+ access: { 'payroll.user': READ, 'payroll.officer': READ, 'payroll.manager': ALL },
285
+ rules: {
286
+ own: { groups: ['payroll.user'], perms: ['read'], domain: { 'loan.employee.user': me } },
287
+ officers: { groups: ['payroll.officer'], perms: ['read'], domain: {} },
288
+ managers: { groups: ['payroll.manager'], perms: [...ALL], domain: {} },
289
+ },
290
+ fields: {
291
+ loan: f.many2one('payroll.loan', { required: true, ondelete: 'cascade' }),
292
+ slip: f.many2one('payroll.slip', { ondelete: 'set null', label: 'Payslip' }),
293
+ date: f.date({ required: true }),
294
+ amount: f.decimal({ scale: 2, required: true }),
295
+ },
296
+ });
297
+ const RUN_STATES = {
298
+ draft: 'Draft',
299
+ computed: 'Computed',
300
+ approved: 'Approved',
301
+ paid: 'Paid',
302
+ cancelled: 'Cancelled',
303
+ };
304
+ export const PayrollRun = defineModel('payroll.run', {
305
+ label: 'Payroll run',
306
+ recName: 'name',
307
+ order: ['dateFrom desc', 'id desc'],
308
+ companyScoped: { required: true },
309
+ tracking: ['state', 'gross', 'net'],
310
+ access: { 'payroll.officer': [...ALL, 'export'], 'payroll.manager': [...ALL, 'export'] },
311
+ fields: {
312
+ name: f.char({ required: true, sequence: 'payroll.run', label: 'Reference' }),
313
+ title: f.char({ label: 'Title', help: 'October 2026 salaries, the Eid bonus…' }),
314
+ kind: f.selection({ regular: 'Regular', correction: 'Correction' }, {
315
+ required: true,
316
+ default: 'regular',
317
+ help: 'A correction pays the difference to an approved run of the same period, which stays as it was.',
318
+ }),
319
+ correctionOf: f.many2one('payroll.run', { ondelete: 'restrict', label: 'Corrects' }),
320
+ dateFrom: f.date({ required: true, label: 'From' }),
321
+ dateTo: f.date({ required: true, label: 'To' }),
322
+ paymentDate: f.date({ label: 'Paid on' }),
323
+ festival: f.boolean({
324
+ default: false,
325
+ label: 'Festival bonus run',
326
+ help: 'Read in formulas as FESTIVAL (1 or 0): the run that pays a festival bonus (Eid, Puja…).',
327
+ }),
328
+ slips: f.one2many('payroll.slip', 'run', { label: 'Payslips' }),
329
+ slipCount: f.integer({
330
+ label: 'Payslips',
331
+ ...compute('payroll.run', 'slipCount', ['slips'], (runs) => {
332
+ for (const r of runs)
333
+ r.slipCount = r.slips.length;
334
+ }),
335
+ }),
336
+ gross: f.decimal({
337
+ scale: 2,
338
+ label: 'Gross',
339
+ help: 'The payslips’ basic and allowances.',
340
+ ...compute('payroll.run', 'gross', ['slips.gross'], (runs) => {
341
+ for (const r of runs)
342
+ r.gross = [...r.slips].reduce((s, x) => s.plus(x.gross), ZERO);
343
+ }),
344
+ store: true,
345
+ }),
346
+ net: f.decimal({
347
+ scale: 2,
348
+ label: 'Net',
349
+ help: 'What the payslips pay out.',
350
+ ...compute('payroll.run', 'net', ['slips.net'], (runs) => {
351
+ for (const r of runs)
352
+ r.net = [...r.slips].reduce((s, x) => s.plus(x.net), ZERO);
353
+ }),
354
+ store: true,
355
+ }),
356
+ state: f.state({
357
+ states: RUN_STATES,
358
+ initial: 'draft',
359
+ help: 'Computed, checked, approved (then locked), paid.',
360
+ transitions: {
361
+ compute: {
362
+ from: ['draft', 'computed'],
363
+ to: 'computed',
364
+ // The payslips, from the contracts, attendance, inputs and loans (read as the system: wages are HR's).
365
+ after: records('payroll.run', async (runs) => {
366
+ const env = runs.env;
367
+ await env.flush();
368
+ await env.asSuperuser({ reason: 'compute payroll' }, async (su) => {
369
+ await computeRuns(su, runs.ids);
370
+ await su.flush();
371
+ });
372
+ }),
373
+ },
374
+ redraft: { from: ['computed'], to: 'draft' },
375
+ approve: {
376
+ from: ['computed'],
377
+ to: 'approved',
378
+ requires: 'payroll.approve',
379
+ guard: records('payroll.run', async (runs) => [...(await runs.load({ slips: true }))]
380
+ .filter((r) => r.slips.length === 0)
381
+ .map((r) => r.id)),
382
+ message: 'Compute the payslips before approving the run.',
383
+ after: records('payroll.run', async (runs) => {
384
+ const env = runs.env;
385
+ await env.asSuperuser({ reason: 'loan repayments' }, async (su) => {
386
+ await recordRepayments(su, runs.ids);
387
+ await su.flush();
388
+ });
389
+ await env.flush();
390
+ await env
391
+ .model('payroll.run')
392
+ .browse(runs.ids)
393
+ .approvedOut();
394
+ for (const id of runs.ids)
395
+ await env.emit('payroll.run.approved', { id });
396
+ }),
397
+ },
398
+ pay: {
399
+ from: ['approved'],
400
+ to: 'paid',
401
+ requires: 'payroll.approve',
402
+ after: records('payroll.run', async (runs) => {
403
+ const env = runs.env;
404
+ await env.flush();
405
+ await env
406
+ .model('payroll.run')
407
+ .browse(runs.ids)
408
+ .paidOut();
409
+ }),
410
+ },
411
+ cancel: { from: ['draft', 'computed'], to: 'cancelled' },
412
+ },
413
+ extensible: true,
414
+ }),
415
+ },
416
+ methods: {
417
+ /** After the approval: nothing here; Accounting books the run (`payroll_account`). */
418
+ approvedOut: method('payroll.run', async () => undefined),
419
+ /** After the payment: nothing here; Accounting books it. */
420
+ paidOut: method('payroll.run', async () => undefined),
421
+ },
422
+ sqlConstraints: {
423
+ ordered: { check: orm.sql `date_to >= date_from`, message: 'A run ends on or after it starts.' },
424
+ correction: {
425
+ check: orm.sql `(kind = 'correction') = (correction_of_id IS NOT NULL)`,
426
+ message: 'A correction names the run it corrects; a regular run none.',
427
+ },
428
+ },
429
+ });
430
+ export const PayrollSlip = defineModel('payroll.slip', {
431
+ label: 'Payslip',
432
+ recName: 'name',
433
+ order: ['id desc'],
434
+ companyScoped: { required: true },
435
+ access: {
436
+ 'payroll.user': READ,
437
+ 'payroll.officer': [...ALL, 'export'],
438
+ 'payroll.manager': [...ALL, 'export'],
439
+ },
440
+ rules: {
441
+ own: {
442
+ groups: ['payroll.user'],
443
+ perms: ['read'],
444
+ // Their own, once the run is approved.
445
+ domain: { 'employee.user': me, 'run.state': { in: ['approved', 'paid'] } },
446
+ },
447
+ officers: { groups: ['payroll.officer'], perms: [...ALL], domain: {} },
448
+ },
449
+ fields: {
450
+ name: f.char({ required: true, label: 'Reference' }),
451
+ run: f.many2one('payroll.run', { required: true, ondelete: 'cascade' }),
452
+ employee: f.many2one('hr.employee', { required: true, ondelete: 'restrict' }),
453
+ contract: f.many2one('hr.contract', { ondelete: 'set null' }),
454
+ structure: f.many2one('payroll.structure', { ondelete: 'set null' }),
455
+ dateFrom: f.date({ required: true, label: 'From' }),
456
+ dateTo: f.date({ required: true, label: 'To' }),
457
+ lines: f.one2many('payroll.slip.line', 'slip'),
458
+ gross: f.decimal({ scale: 2, required: true, default: '0', help: 'Basic and allowances.' }),
459
+ deductions: f.decimal({ scale: 2, required: true, default: '0' }),
460
+ contributions: f.decimal({
461
+ scale: 2,
462
+ required: true,
463
+ default: '0',
464
+ label: 'Company contributions',
465
+ help: 'Paid by the company on top of the gross (its share of the provident fund…).',
466
+ }),
467
+ net: f.decimal({
468
+ scale: 2,
469
+ required: true,
470
+ default: '0',
471
+ help: 'Gross less deductions: what is paid out.',
472
+ }),
473
+ previousNet: f.decimal({
474
+ scale: 2,
475
+ label: 'Net paid before',
476
+ help: 'A correction: the net of the person’s payslip in the corrected run.',
477
+ }),
478
+ difference: f.decimal({
479
+ scale: 2,
480
+ label: 'To pay now',
481
+ help: 'The net, less what the corrected payslip paid.',
482
+ ...compute('payroll.slip', 'difference', ['net', 'previousNet'], (slips) => {
483
+ for (const s of slips)
484
+ s.difference = s.net.minus(s.previousNet ?? ZERO);
485
+ }),
486
+ store: true,
487
+ }),
488
+ warnings: f.text({
489
+ help: 'What to check before approving: days not yet approved, a missing contract…',
490
+ }),
491
+ },
492
+ sqlConstraints: {
493
+ onePerRun: { unique: ['run', 'employee'], message: 'One payslip per person in a run.' },
494
+ },
495
+ });
496
+ export const PayrollSlipLine = defineModel('payroll.slip.line', {
497
+ label: 'Payslip line',
498
+ order: ['sequence asc', 'id asc'],
499
+ access: {
500
+ 'payroll.user': READ,
501
+ 'payroll.officer': [...ALL, 'export'],
502
+ 'payroll.manager': [...ALL, 'export'],
503
+ },
504
+ rules: {
505
+ own: {
506
+ groups: ['payroll.user'],
507
+ perms: ['read'],
508
+ domain: { 'slip.employee.user': me, 'slip.run.state': { in: ['approved', 'paid'] } },
509
+ },
510
+ officers: { groups: ['payroll.officer'], perms: [...ALL], domain: {} },
511
+ },
512
+ fields: {
513
+ slip: f.many2one('payroll.slip', { required: true, ondelete: 'cascade' }),
514
+ rule: f.many2one('payroll.rule', { ondelete: 'set null' }),
515
+ sequence: f.integer({ required: true, default: 10 }),
516
+ code: f.char({ size: 40, required: true }),
517
+ name: f.char({ required: true }),
518
+ category: f.selection(CATEGORIES, {
519
+ required: true,
520
+ default: 'allowance',
521
+ help: 'From the rule: whether the amount adds to the gross, comes off it, or is paid by the company on top.',
522
+ }),
523
+ amount: f.decimal({ scale: 2, required: true, default: '0' }),
524
+ explanation: f.char({
525
+ size: 1000,
526
+ help: 'The formula with its values: how the amount came about.',
527
+ }),
528
+ appearsOnSlip: f.boolean({ default: true, label: 'On the payslip' }),
529
+ },
530
+ });
531
+ /** The structure a contract is paid by. */
532
+ export const ContractStructure = extendModel('hr.contract', {
533
+ fields: {
534
+ structure: f.many2one('payroll.structure', {
535
+ ondelete: 'restrict',
536
+ label: 'Salary structure',
537
+ help: 'The rules this contract is paid by; without one, the person is left out of payroll runs.',
538
+ }),
539
+ },
540
+ });
541
+ /** How each employee is paid: bank transfer, mobile wallet or cash. Payroll officers only. */
542
+ export const EmployeePay = extendModel('hr.employee', {
543
+ fields: {
544
+ payMethod: f.selection({ bank: 'Bank transfer', wallet: 'Mobile wallet', cash: 'Cash' }, {
545
+ default: 'bank',
546
+ label: 'Paid by',
547
+ help: 'Which payment file the person’s net pay goes into.',
548
+ groups: { read: OFFICERS, write: OFFICERS },
549
+ }),
550
+ bankName: f.char({ label: 'Bank', groups: { read: OFFICERS, write: OFFICERS } }),
551
+ bankBranch: f.char({ label: 'Branch', groups: { read: OFFICERS, write: OFFICERS } }),
552
+ bankAccount: f.char({ label: 'Account number', groups: { read: OFFICERS, write: OFFICERS } }),
553
+ routingNumber: f.char({ label: 'Routing number', groups: { read: OFFICERS, write: OFFICERS } }),
554
+ walletProvider: f.selection({ bkash: 'bKash', nagad: 'Nagad', rocket: 'Rocket' }, { label: 'Wallet', groups: { read: OFFICERS, write: OFFICERS } }),
555
+ walletNumber: f.char({ label: 'Wallet number', groups: { read: OFFICERS, write: OFFICERS } }),
556
+ },
557
+ });
558
+ export const payrollShutro = defineModule({
559
+ name: 'payroll',
560
+ version: '0.1.0',
561
+ depends: [hrShutro, attendanceShutro],
562
+ groups: [
563
+ orm.defineGroup({ code: 'payroll.user', label: 'Payroll: own payslips', implies: ['hr.user'] }),
564
+ orm.defineGroup({
565
+ code: 'payroll.officer',
566
+ label: 'Payroll officer (prepares runs)',
567
+ implies: ['payroll.user'],
568
+ }),
569
+ orm.defineGroup({
570
+ code: 'payroll.manager',
571
+ label: 'Payroll manager (approves runs)',
572
+ implies: ['payroll.officer'],
573
+ }),
574
+ ],
575
+ permissions: [
576
+ orm.definePermission({
577
+ code: 'payroll.approve',
578
+ label: 'Approve and pay payroll runs, start loans',
579
+ groups: ['payroll.manager'],
580
+ }),
581
+ ],
582
+ sequences: [
583
+ orm.defineSequence({ code: 'payroll.run', prefix: 'PAY/{year}/', padding: 4, reset: 'yearly' }),
584
+ orm.defineSequence({
585
+ code: 'payroll.loan',
586
+ prefix: 'LOAN/{year}/',
587
+ padding: 4,
588
+ reset: 'yearly',
589
+ }),
590
+ ],
591
+ models: [
592
+ PayrollStructure,
593
+ PayrollRule,
594
+ PayrollParameter,
595
+ PayrollSlab,
596
+ PayrollSlabBand,
597
+ PayrollEmployeeValue,
598
+ PayrollInput,
599
+ PayrollLoan,
600
+ PayrollLoanRepayment,
601
+ PayrollRun,
602
+ PayrollSlip,
603
+ PayrollSlipLine,
604
+ ],
605
+ extensions: [ContractStructure, EmployeePay],
606
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Payroll's routes (RFC 0024 §2.3):
3
+ *
4
+ * - `POST /api/v1/payroll/formula` `{ formula }`: whether it parses, and the variables it reads
5
+ * - `POST /api/v1/payroll/runs/:id/correction` managers: a correction run of an approved run (computed)
6
+ * - `GET /api/v1/payroll/payment-formats` the payment file formats
7
+ * - `GET /api/v1/payroll/runs/:id/payment-file?format=` officers: an approved run's file (bank, bkash, nagad…)
8
+ */
9
+ import { type ModuleContext } from '../server/index.js';
10
+ export declare function registerPayrollRoutes({ app }: ModuleContext): void;