@tomei/rental 0.17.4 → 0.17.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (295) hide show
  1. package/.commitlintrc.json +23 -23
  2. package/.gitlab-ci.yml +16 -16
  3. package/.husky/commit-msg +9 -9
  4. package/.husky/pre-commit +7 -7
  5. package/.prettierrc +4 -4
  6. package/Jenkinsfile +51 -51
  7. package/README.md +8 -8
  8. package/dist/ClassMappings/ItemClassMap.d.ts +4 -0
  9. package/dist/ClassMappings/ItemClassMap.js +8 -0
  10. package/dist/ClassMappings/ItemClassMap.js.map +1 -0
  11. package/dist/ClassMappings/index.d.ts +2 -0
  12. package/dist/ClassMappings/index.js +6 -0
  13. package/dist/ClassMappings/index.js.map +1 -0
  14. package/dist/components/agreement/agreement.d.ts +27 -0
  15. package/dist/components/agreement/agreement.js +120 -0
  16. package/dist/components/agreement/agreement.js.map +1 -0
  17. package/dist/components/agreement/agreement.repository.d.ts +8 -0
  18. package/dist/components/agreement/agreement.repository.js +52 -0
  19. package/dist/components/agreement/agreement.repository.js.map +1 -0
  20. package/dist/components/agreement-history/agreement-history.d.ts +17 -0
  21. package/dist/components/agreement-history/agreement-history.js +40 -0
  22. package/dist/components/agreement-history/agreement-history.js.map +1 -0
  23. package/dist/components/agreement-history/agreement-history.repository.d.ts +8 -0
  24. package/dist/components/agreement-history/agreement-history.repository.js +52 -0
  25. package/dist/components/agreement-history/agreement-history.repository.js.map +1 -0
  26. package/dist/components/agreement-signature/agreement-signature.d.ts +27 -0
  27. package/dist/components/agreement-signature/agreement-signature.js +99 -0
  28. package/dist/components/agreement-signature/agreement-signature.js.map +1 -0
  29. package/dist/components/agreement-signature/agreement-signature.repository.d.ts +8 -0
  30. package/dist/components/agreement-signature/agreement-signature.repository.js +53 -0
  31. package/dist/components/agreement-signature/agreement-signature.repository.js.map +1 -0
  32. package/dist/components/booking/booking.d.ts +46 -0
  33. package/dist/components/booking/booking.js +289 -0
  34. package/dist/components/booking/booking.js.map +1 -0
  35. package/dist/components/booking/booking.repository.d.ts +8 -0
  36. package/dist/components/booking/booking.repository.js +52 -0
  37. package/dist/components/booking/booking.repository.js.map +1 -0
  38. package/dist/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.d.ts +32 -0
  39. package/dist/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.js +106 -0
  40. package/dist/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.js.map +1 -0
  41. package/dist/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.repository.d.ts +9 -0
  42. package/dist/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.repository.js +62 -0
  43. package/dist/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.repository.js.map +1 -0
  44. package/dist/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.d.ts +22 -0
  45. package/dist/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.js +89 -0
  46. package/dist/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.js.map +1 -0
  47. package/dist/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.repository.d.ts +9 -0
  48. package/dist/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.repository.js +63 -0
  49. package/dist/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.repository.js.map +1 -0
  50. package/dist/components/hirer-change-request-signature/hirer-change-request-signature.d.ts +30 -0
  51. package/dist/components/hirer-change-request-signature/hirer-change-request-signature.js +208 -0
  52. package/dist/components/hirer-change-request-signature/hirer-change-request-signature.js.map +1 -0
  53. package/dist/components/hirer-change-request-signature/hirer-change-request-signature.repository.d.ts +9 -0
  54. package/dist/components/hirer-change-request-signature/hirer-change-request-signature.repository.js +63 -0
  55. package/dist/components/hirer-change-request-signature/hirer-change-request-signature.repository.js.map +1 -0
  56. package/dist/components/joint-hirer/joint-hirer.d.ts +26 -0
  57. package/dist/components/joint-hirer/joint-hirer.js +137 -0
  58. package/dist/components/joint-hirer/joint-hirer.js.map +1 -0
  59. package/dist/components/joint-hirer/joint-hirer.repository.d.ts +8 -0
  60. package/dist/components/joint-hirer/joint-hirer.repository.js +52 -0
  61. package/dist/components/joint-hirer/joint-hirer.repository.js.map +1 -0
  62. package/dist/components/rental/rental.d.ts +72 -0
  63. package/dist/components/rental/rental.js +844 -0
  64. package/dist/components/rental/rental.js.map +1 -0
  65. package/dist/components/rental/rental.repository.d.ts +8 -0
  66. package/dist/components/rental/rental.repository.js +52 -0
  67. package/dist/components/rental/rental.repository.js.map +1 -0
  68. package/dist/components/rental-hirer-change-request/rental-hirer-change-request.d.ts +71 -0
  69. package/dist/components/rental-hirer-change-request/rental-hirer-change-request.js +531 -0
  70. package/dist/components/rental-hirer-change-request/rental-hirer-change-request.js.map +1 -0
  71. package/dist/components/rental-hirer-change-request/rental-hirer-change-request.repository.d.ts +9 -0
  72. package/dist/components/rental-hirer-change-request/rental-hirer-change-request.repository.js +62 -0
  73. package/dist/components/rental-hirer-change-request/rental-hirer-change-request.repository.js.map +1 -0
  74. package/dist/components/rental-price/rental-price.d.ts +18 -0
  75. package/dist/components/rental-price/rental-price.js +66 -0
  76. package/dist/components/rental-price/rental-price.js.map +1 -0
  77. package/dist/components/rental-price/rental-price.repository.d.ts +8 -0
  78. package/dist/components/rental-price/rental-price.repository.js +52 -0
  79. package/dist/components/rental-price/rental-price.repository.js.map +1 -0
  80. package/dist/database.d.ts +4 -0
  81. package/dist/database.js +37 -0
  82. package/dist/database.js.map +1 -0
  83. package/dist/enum/account-type.enum.d.ts +4 -0
  84. package/dist/enum/account-type.enum.js +9 -0
  85. package/dist/enum/account-type.enum.js.map +1 -0
  86. package/dist/enum/aggrement-status.enum.d.ts +5 -0
  87. package/dist/enum/aggrement-status.enum.js +10 -0
  88. package/dist/enum/aggrement-status.enum.js.map +1 -0
  89. package/dist/enum/agreement-signature-status.enum.d.ts +4 -0
  90. package/dist/enum/agreement-signature-status.enum.js +9 -0
  91. package/dist/enum/agreement-signature-status.enum.js.map +1 -0
  92. package/dist/enum/agreement-signature-verification-method.enum.d.ts +4 -0
  93. package/dist/enum/agreement-signature-verification-method.enum.js +9 -0
  94. package/dist/enum/agreement-signature-verification-method.enum.js.map +1 -0
  95. package/dist/enum/booking.enum.d.ts +5 -0
  96. package/dist/enum/booking.enum.js +10 -0
  97. package/dist/enum/booking.enum.js.map +1 -0
  98. package/dist/enum/hirer-type.enum.d.ts +4 -0
  99. package/dist/enum/hirer-type.enum.js +9 -0
  100. package/dist/enum/hirer-type.enum.js.map +1 -0
  101. package/dist/enum/index.d.ts +11 -0
  102. package/dist/enum/index.js +24 -0
  103. package/dist/enum/index.js.map +1 -0
  104. package/dist/enum/rental-hirer-change-request-hirer-role.d.ts +4 -0
  105. package/dist/enum/rental-hirer-change-request-hirer-role.js +9 -0
  106. package/dist/enum/rental-hirer-change-request-hirer-role.js.map +1 -0
  107. package/dist/enum/rental-hirer-change-request-status.d.ts +7 -0
  108. package/dist/enum/rental-hirer-change-request-status.js +12 -0
  109. package/dist/enum/rental-hirer-change-request-status.js.map +1 -0
  110. package/dist/enum/rental-hirer-change-request-type.d.ts +4 -0
  111. package/dist/enum/rental-hirer-change-request-type.js +9 -0
  112. package/dist/enum/rental-hirer-change-request-type.js.map +1 -0
  113. package/dist/enum/rental-status.enum.d.ts +9 -0
  114. package/dist/enum/rental-status.enum.js +14 -0
  115. package/dist/enum/rental-status.enum.js.map +1 -0
  116. package/dist/interfaces/agreemeent-signature-attr.interface.d.ts +14 -0
  117. package/dist/interfaces/agreemeent-signature-attr.interface.js +3 -0
  118. package/dist/interfaces/agreemeent-signature-attr.interface.js.map +1 -0
  119. package/dist/interfaces/agreement-attr.interface.d.ts +7 -0
  120. package/dist/interfaces/agreement-attr.interface.js +3 -0
  121. package/dist/interfaces/agreement-attr.interface.js.map +1 -0
  122. package/dist/interfaces/agreement-history-attr.interface.d.ts +7 -0
  123. package/dist/interfaces/agreement-history-attr.interface.js +3 -0
  124. package/dist/interfaces/agreement-history-attr.interface.js.map +1 -0
  125. package/dist/interfaces/booking-attr.interface.d.ts +18 -0
  126. package/dist/interfaces/booking-attr.interface.js +3 -0
  127. package/dist/interfaces/booking-attr.interface.js.map +1 -0
  128. package/dist/interfaces/booking-find-all-search-attr.interface.d.ts +12 -0
  129. package/dist/interfaces/booking-find-all-search-attr.interface.js +3 -0
  130. package/dist/interfaces/booking-find-all-search-attr.interface.js.map +1 -0
  131. package/dist/interfaces/hirer-change-request-new-hirer-attr.interface.d.ts +15 -0
  132. package/dist/interfaces/hirer-change-request-new-hirer-attr.interface.js +3 -0
  133. package/dist/interfaces/hirer-change-request-new-hirer-attr.interface.js.map +1 -0
  134. package/dist/interfaces/hirer-change-request-remove-hirer-attr.interface.d.ts +5 -0
  135. package/dist/interfaces/hirer-change-request-remove-hirer-attr.interface.js +3 -0
  136. package/dist/interfaces/hirer-change-request-remove-hirer-attr.interface.js.map +1 -0
  137. package/dist/interfaces/hirer-change-request-signature-attr.interface.d.ts +14 -0
  138. package/dist/interfaces/hirer-change-request-signature-attr.interface.js +3 -0
  139. package/dist/interfaces/hirer-change-request-signature-attr.interface.js.map +1 -0
  140. package/dist/interfaces/index.d.ts +14 -0
  141. package/dist/interfaces/index.js +3 -0
  142. package/dist/interfaces/index.js.map +1 -0
  143. package/dist/interfaces/joint-hirer-attr.interface.d.ts +11 -0
  144. package/dist/interfaces/joint-hirer-attr.interface.js +3 -0
  145. package/dist/interfaces/joint-hirer-attr.interface.js.map +1 -0
  146. package/dist/interfaces/rental-attr.interface.d.ts +24 -0
  147. package/dist/interfaces/rental-attr.interface.js +3 -0
  148. package/dist/interfaces/rental-attr.interface.js.map +1 -0
  149. package/dist/interfaces/rental-find-all-search-attr.interface.d.ts +10 -0
  150. package/dist/interfaces/rental-find-all-search-attr.interface.js +3 -0
  151. package/dist/interfaces/rental-find-all-search-attr.interface.js.map +1 -0
  152. package/dist/interfaces/rental-hirer-change-request.attr.interface.d.ts +16 -0
  153. package/dist/interfaces/rental-hirer-change-request.attr.interface.js +3 -0
  154. package/dist/interfaces/rental-hirer-change-request.attr.interface.js.map +1 -0
  155. package/dist/interfaces/rental-hirer-change-request.update.interface.d.ts +4 -0
  156. package/dist/interfaces/rental-hirer-change-request.update.interface.js +3 -0
  157. package/dist/interfaces/rental-hirer-change-request.update.interface.js.map +1 -0
  158. package/dist/interfaces/rental-price-attr.interface.d.ts +7 -0
  159. package/dist/interfaces/rental-price-attr.interface.js +3 -0
  160. package/dist/interfaces/rental-price-attr.interface.js.map +1 -0
  161. package/dist/interfaces/response-hirer-signature-attr.interface.d.ts +14 -0
  162. package/dist/interfaces/response-hirer-signature-attr.interface.js +3 -0
  163. package/dist/interfaces/response-hirer-signature-attr.interface.js.map +1 -0
  164. package/dist/models/agreement-history.entity.d.ts +10 -0
  165. package/dist/models/agreement-history.entity.js +65 -0
  166. package/dist/models/agreement-history.entity.js.map +1 -0
  167. package/dist/models/agreement-signature.entity.d.ts +22 -0
  168. package/dist/models/agreement-signature.entity.js +123 -0
  169. package/dist/models/agreement-signature.entity.js.map +1 -0
  170. package/dist/models/agreement.entity.d.ts +14 -0
  171. package/dist/models/agreement.entity.js +70 -0
  172. package/dist/models/agreement.entity.js.map +1 -0
  173. package/dist/models/booking.entity.d.ts +21 -0
  174. package/dist/models/booking.entity.js +128 -0
  175. package/dist/models/booking.entity.js.map +1 -0
  176. package/dist/models/hirer-change-request-new-hirer.entity.d.ts +19 -0
  177. package/dist/models/hirer-change-request-new-hirer.entity.js +121 -0
  178. package/dist/models/hirer-change-request-new-hirer.entity.js.map +1 -0
  179. package/dist/models/hirer-change-request-remove-hirer.entity.d.ts +11 -0
  180. package/dist/models/hirer-change-request-remove-hirer.entity.js +57 -0
  181. package/dist/models/hirer-change-request-remove-hirer.entity.js.map +1 -0
  182. package/dist/models/hirer-change-request-signature.entity.d.ts +18 -0
  183. package/dist/models/hirer-change-request-signature.entity.js +101 -0
  184. package/dist/models/hirer-change-request-signature.entity.js.map +1 -0
  185. package/dist/models/index.d.ts +12 -0
  186. package/dist/models/index.js +26 -0
  187. package/dist/models/index.js.map +1 -0
  188. package/dist/models/joint-hirer.entity.d.ts +14 -0
  189. package/dist/models/joint-hirer.entity.js +91 -0
  190. package/dist/models/joint-hirer.entity.js.map +1 -0
  191. package/dist/models/rental-hirer-change-request.entity.d.ts +22 -0
  192. package/dist/models/rental-hirer-change-request.entity.js +108 -0
  193. package/dist/models/rental-hirer-change-request.entity.js.map +1 -0
  194. package/dist/models/rental-price.entity.d.ts +8 -0
  195. package/dist/models/rental-price.entity.js +59 -0
  196. package/dist/models/rental-price.entity.js.map +1 -0
  197. package/dist/models/rental.entity.d.ts +29 -0
  198. package/dist/models/rental.entity.js +160 -0
  199. package/dist/models/rental.entity.js.map +1 -0
  200. package/dist/src/components/agreement/agreement.js +9 -9
  201. package/dist/src/components/rental/rental.d.ts +2 -1
  202. package/dist/src/components/rental/rental.js +19 -16
  203. package/dist/src/components/rental/rental.js.map +1 -1
  204. package/dist/src/enum/agreement-signature-verification-method.enum.d.ts +4 -0
  205. package/dist/src/enum/agreement-signature-verification-method.enum.js +9 -0
  206. package/dist/src/enum/agreement-signature-verification-method.enum.js.map +1 -0
  207. package/dist/src/enum/index.d.ts +2 -1
  208. package/dist/src/enum/index.js +3 -1
  209. package/dist/src/enum/index.js.map +1 -1
  210. package/dist/src/models/agreement-signature.entity.d.ts +4 -0
  211. package/dist/src/models/agreement-signature.entity.js +22 -0
  212. package/dist/src/models/agreement-signature.entity.js.map +1 -1
  213. package/dist/tsconfig.build.tsbuildinfo +1 -0
  214. package/dist/tsconfig.tsbuildinfo +1 -1
  215. package/eslint.config.mjs +58 -58
  216. package/jest.config.js +10 -10
  217. package/migrations/20250529092130-add-status-to-joint-hirer.js +19 -19
  218. package/migrations/agreement-signature-table-migration.js +76 -64
  219. package/migrations/booking-table-migration.js +79 -79
  220. package/migrations/hirer-change-request-new-hirer-migration.js +72 -72
  221. package/migrations/hirer-change-request-remove-hirer-migration.js +39 -39
  222. package/migrations/hirer-change-request-signature-migration.js +65 -65
  223. package/migrations/joint-hirer-table-migration.js +52 -52
  224. package/migrations/rental-aggreement-history-migration.js +41 -41
  225. package/migrations/rental-aggrement-table-migration.js +30 -30
  226. package/migrations/rental-hirer-change-request-migrations.js +64 -64
  227. package/migrations/rental-price-table-migration.js +32 -32
  228. package/migrations/rental-table-migrations.js +96 -96
  229. package/package.json +80 -80
  230. package/sonar-project.properties +12 -12
  231. package/src/ClassMappings/ItemClassMap.ts +7 -7
  232. package/src/ClassMappings/index.ts +3 -3
  233. package/src/components/agreement/agreement.repository.ts +54 -54
  234. package/src/components/agreement/agreement.ts +187 -187
  235. package/src/components/agreement-history/agreement-history.repository.ts +54 -54
  236. package/src/components/agreement-history/agreement-history.ts +57 -57
  237. package/src/components/agreement-signature/agreement-signature.repository.ts +55 -55
  238. package/src/components/agreement-signature/agreement-signature.ts +143 -143
  239. package/src/components/booking/booking.repository.ts +51 -51
  240. package/src/components/booking/booking.ts +492 -492
  241. package/src/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.repository.ts +64 -64
  242. package/src/components/hirer-change-request-new-hirer/hirer-change-request-new-hirer.ts +153 -153
  243. package/src/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.repository.ts +65 -65
  244. package/src/components/hirer-change-request-remove-hirer/hirer-change-request-remove-hirer.ts +134 -134
  245. package/src/components/hirer-change-request-signature/hirer-change-request-signature.repository.ts +65 -65
  246. package/src/components/hirer-change-request-signature/hirer-change-request-signature.ts +272 -272
  247. package/src/components/joint-hirer/joint-hirer.repository.ts +54 -54
  248. package/src/components/joint-hirer/joint-hirer.ts +252 -252
  249. package/src/components/rental/rental.repository.ts +51 -51
  250. package/src/components/rental/rental.ts +1466 -1460
  251. package/src/components/rental-hirer-change-request/rental-hirer-change-request.repository.ts +64 -64
  252. package/src/components/rental-hirer-change-request/rental-hirer-change-request.ts +907 -907
  253. package/src/components/rental-price/rental-price.repository.ts +54 -54
  254. package/src/components/rental-price/rental-price.ts +100 -100
  255. package/src/database.ts +39 -39
  256. package/src/enum/account-type.enum.ts +4 -4
  257. package/src/enum/agreement-signature-status.enum.ts +4 -4
  258. package/src/enum/agreement-signature-verification-method.enum.ts +4 -0
  259. package/src/enum/booking.enum.ts +5 -5
  260. package/src/enum/hirer-type.enum.ts +4 -4
  261. package/src/enum/index.ts +23 -21
  262. package/src/enum/rental-hirer-change-request-hirer-role.ts +4 -4
  263. package/src/enum/rental-hirer-change-request-status.ts +7 -7
  264. package/src/enum/rental-hirer-change-request-type.ts +4 -4
  265. package/src/enum/rental-status.enum.ts +39 -39
  266. package/src/index.ts +54 -54
  267. package/src/interfaces/agreemeent-signature-attr.interface.ts +15 -15
  268. package/src/interfaces/agreement-attr.interface.ts +7 -7
  269. package/src/interfaces/agreement-history-attr.interface.ts +7 -7
  270. package/src/interfaces/booking-attr.interface.ts +19 -19
  271. package/src/interfaces/booking-find-all-search-attr.interface.ts +12 -12
  272. package/src/interfaces/hirer-change-request-new-hirer-attr.interface.ts +15 -15
  273. package/src/interfaces/hirer-change-request-remove-hirer-attr.interface.ts +5 -5
  274. package/src/interfaces/hirer-change-request-signature-attr.interface.ts +15 -15
  275. package/src/interfaces/index.ts +29 -29
  276. package/src/interfaces/joint-hirer-attr.interface.ts +11 -11
  277. package/src/interfaces/rental-attr.interface.ts +25 -25
  278. package/src/interfaces/rental-find-all-search-attr.interface.ts +11 -11
  279. package/src/interfaces/rental-hirer-change-request.attr.interface.ts +17 -17
  280. package/src/interfaces/rental-hirer-change-request.update.interface.ts +4 -4
  281. package/src/interfaces/rental-price-attr.interface.ts +7 -7
  282. package/src/interfaces/response-hirer-signature-attr.interface.ts +15 -15
  283. package/src/models/agreement-history.entity.ts +51 -51
  284. package/src/models/agreement-signature.entity.ts +105 -86
  285. package/src/models/agreement.entity.ts +47 -47
  286. package/src/models/booking.entity.ts +105 -105
  287. package/src/models/hirer-change-request-new-hirer.entity.ts +102 -102
  288. package/src/models/hirer-change-request-remove-hirer.entity.ts +47 -47
  289. package/src/models/hirer-change-request-signature.entity.ts +86 -86
  290. package/src/models/index.ts +25 -25
  291. package/src/models/joint-hirer.entity.ts +70 -70
  292. package/src/models/rental-price.entity.ts +38 -38
  293. package/src/models/rental.entity.ts +133 -133
  294. package/tsconfig.build.json +5 -5
  295. package/tsconfig.json +24 -24
@@ -1,1460 +1,1466 @@
1
- import { IRentalAttr } from '../../interfaces/rental-attr.interface';
2
- import { RentalStatusEnum } from '../../enum/rental-status.enum';
3
- import { RentalRepository } from './rental.repository';
4
- import { ClassError, ObjectBase } from '@tomei/general';
5
- import { ApplicationConfig } from '@tomei/config';
6
- import { LoginUser } from '@tomei/sso';
7
- import { RentalPrice } from '../rental-price/rental-price';
8
- import { Op, QueryTypes } from 'sequelize';
9
- import { ActionEnum, Activity } from '@tomei/activity-history';
10
- import { IRentalFindAllSearchAttr } from '../../interfaces/rental-find-all-search-attr.interface';
11
- import { RentalAccountTypeEnum } from '../../enum/account-type.enum';
12
- import { JointHirer } from '../joint-hirer/joint-hirer';
13
- import { JointHirerModel } from '../../models/joint-hirer.entity';
14
- import { AgreementRepository } from '../agreement/agreement.repository';
15
- import * as rentalDb from '../../database';
16
- import { RentalModel } from '../../models/rental.entity';
17
- import { JointHirerRepository } from '../joint-hirer/joint-hirer.repository';
18
- import { AgreementModel } from '../../models';
19
- import { Agreement } from '../agreement/agreement';
20
- import { AggrementStatusEnum } from '../../enum/aggrement-status.enum';
21
- import { AgreementSignatureRepository } from '../agreement-signature/agreement-signature.repository';
22
- import { AgreementSignatureStatusEnum } from '../../enum/agreement-signature-status.enum';
23
- import { HirerTypeEnum } from '../../enum/hirer-type.enum';
24
- import { AgreementHistoryRepository } from '../agreement-history/agreement-history.repository';
25
- import { DocType, Document, IAccountSystem } from '@tomei/finance';
26
- import { ItemClassMap } from '../../ClassMappings/ItemClassMap';
27
-
28
- export class Rental extends ObjectBase {
29
- ObjectId: string;
30
- ObjectName: string;
31
- ObjectType = 'Rental';
32
- TableName: string;
33
- CustomerId: string;
34
- CustomerType: string;
35
- ItemId: string;
36
- ItemType: string;
37
- PriceId: string;
38
- StartDateTime: Date;
39
- EndDateTime: Date;
40
- CancelRemarks: string;
41
- TerminateRemarks: string;
42
- AgreementNo: string;
43
- AccountType: RentalAccountTypeEnum;
44
- JointHirers: JointHirer[] = [];
45
- Invoices: Document[] = [];
46
- Item: any;
47
- protected _Status: RentalStatusEnum | string;
48
- protected _EscheatmentYN: string = 'N';
49
- protected _CreatedById: string;
50
- protected _CreatedAt: Date;
51
- protected _UpdatedById: string;
52
- protected _UpdatedAt: Date;
53
- protected static _Repo = new RentalRepository();
54
- protected static _AgreementRepo = new AgreementRepository();
55
- protected static _JointHirerRepo = new JointHirerRepository();
56
- protected static _AgreementSignatureRepo = new AgreementSignatureRepository();
57
- protected static _AgreementHistoryRepo = new AgreementHistoryRepository();
58
-
59
- get RentalId(): string {
60
- return this.ObjectId;
61
- }
62
-
63
- set RentalId(value: string) {
64
- this.ObjectId = value;
65
- }
66
-
67
- get Status(): RentalStatusEnum | string {
68
- return this._Status;
69
- }
70
-
71
- get EscheatmentYN(): string {
72
- return this._EscheatmentYN;
73
- }
74
-
75
- get CreatedById(): string {
76
- return this._CreatedById;
77
- }
78
-
79
- get CreatedAt(): Date {
80
- return this._CreatedAt;
81
- }
82
-
83
- get UpdatedById(): string {
84
- return this._UpdatedById;
85
- }
86
-
87
- get UpdatedAt(): Date {
88
- return this._UpdatedAt;
89
- }
90
-
91
- private setTerminated() {
92
- this._Status = RentalStatusEnum.TERMINATED;
93
- }
94
-
95
- protected constructor(rentalAttr?: IRentalAttr) {
96
- super();
97
- if (rentalAttr) {
98
- this.RentalId = rentalAttr.RentalId;
99
- this.CustomerId = rentalAttr.CustomerId;
100
- this.CustomerType = rentalAttr.CustomerType;
101
- this.ItemId = rentalAttr.ItemId;
102
- this.ItemType = rentalAttr.ItemType;
103
- this.PriceId = rentalAttr.PriceId;
104
- this.StartDateTime = rentalAttr.StartDateTime;
105
- this.EndDateTime = rentalAttr.EndDateTime;
106
- this.CancelRemarks = rentalAttr.CancelRemarks;
107
- this.TerminateRemarks = rentalAttr.TerminateRemarks;
108
- this.AgreementNo = rentalAttr.AgreementNo;
109
- this.AccountType = rentalAttr.AccountType;
110
- this._Status = rentalAttr.Status;
111
- this._EscheatmentYN = rentalAttr.EscheatmentYN;
112
- this._CreatedById = rentalAttr.CreatedById;
113
- this._CreatedAt = rentalAttr.CreatedAt;
114
- this._UpdatedById = rentalAttr.UpdatedById;
115
- this._UpdatedAt = rentalAttr.UpdatedAt;
116
- }
117
- }
118
-
119
- public static async init(dbTransaction?: any, rentalId?: string) {
120
- try {
121
- if (rentalId) {
122
- const rental = await Rental._Repo.findByPk(rentalId, dbTransaction);
123
- if (rental) {
124
- return new Rental(rental);
125
- } else {
126
- throw new ClassError('Rental', 'RentalErrMsg00', 'Rental Not Found');
127
- }
128
- }
129
- return new Rental();
130
- } catch (error) {
131
- throw new ClassError(
132
- 'Rental',
133
- 'RentalErrMsg00',
134
- 'Failed To Initialize Rental',
135
- );
136
- }
137
- }
138
-
139
- /**
140
- * Create a new Rental record.
141
- * @param {RentalPrice} rentalPrice - The rental pricing information.
142
- * @param {LoginUser} loginUser - The user initiating the creation.
143
- * @param {any} [dbTransaction] - Optional database transaction for atomic operations.
144
- * @throws {ClassError} Throws an error if:
145
- * 1. loginUser does not have 'Rental - Create' privilege.
146
- * 2. Rental item is not available at the current date.
147
- * @returns {Promise<any>} A Promise resolving to the created rental record.
148
- */
149
- public async create(
150
- rentalPrice: RentalPrice,
151
- loginUser: LoginUser,
152
- jointHirers?: JointHirer[],
153
- dbTransaction?: any,
154
- ): Promise<any> {
155
- try {
156
- /*Part 1: Check Privilege*/
157
- const systemCode =
158
- ApplicationConfig.getComponentConfigValue('system-code');
159
- const isPrivileged = await loginUser.checkPrivileges(
160
- systemCode,
161
- 'Rental - Create',
162
- );
163
-
164
- if (!isPrivileged) {
165
- throw new ClassError(
166
- 'Rental',
167
- 'RentalErrMsg01',
168
- "You do not have 'Rental - Create' privilege.",
169
- );
170
- }
171
-
172
- /*Part 2: Make Sure Rental Item Available*/
173
- const isItemAvailable = await Rental.isItemAvailable(
174
- this.ItemId,
175
- this.ItemType,
176
- this.StartDateTime,
177
- this.EndDateTime,
178
- dbTransaction,
179
- );
180
-
181
- if (!isItemAvailable) {
182
- throw new ClassError(
183
- 'Rental',
184
- 'RentalErrMsg02',
185
- 'Rental Item is not available at current date.',
186
- );
187
- }
188
-
189
- // Part 3: Create Rental Agreement Record
190
- // Call Rental._AgreementRepo create method by passing:
191
- // AgreementNo: this.AgreementNo
192
- // Status: "Not Generated"
193
- // dbTransaction
194
- await Rental._AgreementRepo.create(
195
- {
196
- AgreementNo: this.AgreementNo,
197
- Status: 'Not Generated',
198
- },
199
- {
200
- transaction: dbTransaction,
201
- },
202
- );
203
-
204
- //Create a record into rental_AgreementSignature table
205
- //Create the main customer's signature record
206
- await Rental._AgreementSignatureRepo.create(
207
- {
208
- SignatureId: this.createId(),
209
- AgreementNo: this.AgreementNo,
210
- Party: HirerTypeEnum.PRIMARY,
211
- PartyId: this.CustomerId,
212
- PartyType: 'Customer',
213
- SignatureStatus: AgreementSignatureStatusEnum.PENDING,
214
- SignedAt: null,
215
- CreatedById: loginUser.ObjectId,
216
- CreatedAt: new Date(),
217
- UpdatedById: loginUser.ObjectId,
218
- UpdatedAt: new Date(),
219
- },
220
- { transaction: dbTransaction },
221
- );
222
-
223
- // Create signatures record for joint hirers if any
224
- if (jointHirers && jointHirers.length > 0) {
225
- for (const jointHirer of jointHirers) {
226
- await Rental._AgreementSignatureRepo.create(
227
- {
228
- SignatureId: this.createId(),
229
- AgreementNo: this.AgreementNo,
230
- Party: HirerTypeEnum.JOINT,
231
- PartyId: jointHirer.CustomerId,
232
- PartyType: 'Customer',
233
- SignatureStatus: AgreementSignatureStatusEnum.PENDING,
234
- SignedAt: null,
235
- CreatedById: loginUser.ObjectId,
236
- CreatedAt: new Date(),
237
- UpdatedById: loginUser.ObjectId,
238
- UpdatedAt: new Date(),
239
- },
240
- { transaction: dbTransaction },
241
- );
242
- }
243
- }
244
-
245
- /*Part 4: Insert Rental & The Agreed Rental Price*/
246
- if (!rentalPrice.PriceId) {
247
- const rentalPriceData = await rentalPrice.create(
248
- loginUser,
249
- dbTransaction,
250
- );
251
- this.PriceId = rentalPriceData.PriceId;
252
- } else {
253
- this.PriceId = rentalPrice.PriceId;
254
- }
255
- this.RentalId = this.createId();
256
- // if (!this.Status) {
257
- this._Status = RentalStatusEnum.PENDING_SIGNING;
258
- // }
259
- this._CreatedById = loginUser.ObjectId;
260
- this._UpdatedById = loginUser.ObjectId;
261
- this._CreatedAt = new Date();
262
- this._UpdatedAt = new Date();
263
-
264
- const data = {
265
- RentalId: this.RentalId,
266
- CustomerId: this.CustomerId,
267
- CustomerType: this.CustomerType,
268
- ItemId: this.ItemId,
269
- ItemType: this.ItemType,
270
- PriceId: this.PriceId,
271
- StartDateTime: this.StartDateTime,
272
- EndDateTime: this.EndDateTime,
273
- CancelRemarks: this.CancelRemarks,
274
- TerminateRemarks: this.TerminateRemarks,
275
- AgreementNo: this.AgreementNo,
276
- AccountType: this.AccountType,
277
- Status: this.Status,
278
- EscheatmentYN: this.EscheatmentYN,
279
- CreatedById: this.CreatedById,
280
- CreatedAt: this.CreatedAt,
281
- UpdatedById: this.UpdatedById,
282
- UpdatedAt: this.UpdatedAt,
283
- };
284
-
285
- await Rental._Repo.create(data, {
286
- transaction: dbTransaction,
287
- });
288
-
289
- //For every data inside Param.jointHirers, update the rentalId and call jointHirer.create
290
- if (jointHirers) {
291
- for (let i = 0; i < jointHirers.length; i++) {
292
- jointHirers[i].RentalId = this.RentalId;
293
- await jointHirers[i].create(loginUser, dbTransaction);
294
- this.JointHirers.push(jointHirers[i]);
295
- }
296
- }
297
-
298
- /*Part 5: Record Create Rental Activity*/
299
- const activity = new Activity();
300
- activity.ActivityId = activity.createId();
301
- activity.Action = ActionEnum.CREATE;
302
- activity.Description = 'Add Rental';
303
- activity.EntityType = 'Rental';
304
- activity.EntityId = this.RentalId;
305
- activity.EntityValueBefore = JSON.stringify({});
306
- activity.EntityValueAfter = JSON.stringify(data);
307
- await activity.create(loginUser.ObjectId, dbTransaction);
308
-
309
- /*Part 6: Return Result*/
310
- return this;
311
- } catch (error) {
312
- throw error;
313
- }
314
- }
315
-
316
- public static async isItemAvailable(
317
- itemId: string,
318
- itemType: string,
319
- startDateTime: Date,
320
- endDateTime: Date,
321
- dbTransaction?: any,
322
- ) {
323
- try {
324
- const rental = await Rental._Repo.findOne({
325
- where: {
326
- ItemId: itemId,
327
- ItemType: itemType,
328
- StartDateTime: {
329
- [Op.lte]: endDateTime,
330
- },
331
- EndDateTime: {
332
- [Op.gte]: startDateTime,
333
- },
334
- },
335
- transaction: dbTransaction,
336
- });
337
-
338
- if (!rental) {
339
- // No rental found → item is available
340
- return true;
341
- }
342
-
343
- if (
344
- rental.Status === RentalStatusEnum.TERMINATED ||
345
- rental.Status === RentalStatusEnum.CANCELLED
346
- ) {
347
- // Rental found but status is Terminated or Cancelled → item is available
348
- return true;
349
- }
350
-
351
- // Rental exists and is active → item is not available
352
- return false;
353
- } catch (error) {
354
- throw error;
355
- }
356
- }
357
-
358
- public static async findAll(
359
- loginUser: LoginUser,
360
- dbTransaction: any,
361
- page?: number,
362
- row?: number,
363
- search?: IRentalFindAllSearchAttr,
364
- ) {
365
- try {
366
- const systemCode =
367
- await ApplicationConfig.getComponentConfigValue('system-code');
368
-
369
- const isPrivileged = await loginUser.checkPrivileges(
370
- systemCode,
371
- 'Rental - View',
372
- );
373
-
374
- if (!isPrivileged) {
375
- throw new ClassError(
376
- 'Rental',
377
- 'RentalErrMsg01',
378
- "You do not have 'Rental - View' privilege.",
379
- );
380
- }
381
-
382
- const queryObj: any = {};
383
-
384
- let options: any = {
385
- transaction: dbTransaction,
386
- order: [['CreatedAt', 'DESC']],
387
- };
388
-
389
- if (page && row) {
390
- options = {
391
- ...options,
392
- limit: row,
393
- offset: row * (page - 1),
394
- };
395
- }
396
-
397
- if (search) {
398
- Object.entries(search).forEach(([key, value]) => {
399
- if (key === 'StartDateTime') {
400
- queryObj[key] = {
401
- [Op.gte]: value,
402
- };
403
- } else if (key === 'EndDateTime') {
404
- queryObj[key] = {
405
- [Op.lte]: value,
406
- };
407
- } else if (key === 'CustomerId') {
408
- queryObj[key] = {
409
- [Op.substring]: value,
410
- };
411
-
412
- options.include = [
413
- {
414
- model: JointHirerModel,
415
- required: false,
416
- where: {
417
- CustomerId: {
418
- [Op.substring]: value,
419
- },
420
- },
421
- },
422
- ];
423
- } else {
424
- queryObj[key] = {
425
- [Op.substring]: value,
426
- };
427
- }
428
- });
429
- if (options?.include?.length > 1) {
430
- options.include.push({
431
- model: AgreementModel,
432
- required: false,
433
- });
434
- } else {
435
- options.include = [
436
- {
437
- model: AgreementModel,
438
- required: false,
439
- },
440
- ];
441
- }
442
-
443
- options = {
444
- ...options,
445
- where: queryObj,
446
- };
447
- }
448
-
449
- return await Rental._Repo.findAndCountAll(options);
450
- } catch (err) {
451
- throw err;
452
- }
453
- }
454
-
455
- public static async checkActiveByItemId(
456
- loginUser: LoginUser,
457
- itemId: string,
458
- itemType: string,
459
- dbTransaction?: any,
460
- ) {
461
- try {
462
- const systemCode =
463
- await ApplicationConfig.getComponentConfigValue('system-code');
464
-
465
- const isPrivileged = await loginUser.checkPrivileges(
466
- systemCode,
467
- 'Rental - View',
468
- );
469
-
470
- if (!isPrivileged) {
471
- throw new ClassError(
472
- 'Rental',
473
- 'RentalErrMsg01',
474
- "You do not have 'Rental - View' privilege.",
475
- );
476
- }
477
-
478
- const queryObj: any = {
479
- ItemId: itemId,
480
- ItemType: itemType,
481
- Status: RentalStatusEnum.ACTIVE,
482
- };
483
-
484
- const options: any = {
485
- transaction: dbTransaction,
486
- where: queryObj,
487
- };
488
-
489
- const rental = await Rental._Repo.findOne(options);
490
-
491
- if (rental) {
492
- return true;
493
- } else {
494
- return false;
495
- }
496
- } catch (err) {
497
- throw err;
498
- }
499
- }
500
-
501
- /**
502
- * Sets the StartDateTime property of the Rental class as custom setter.
503
- * @param {Date} startDateTime - The start date and time of the rental.
504
- * @throws {ClassError} Throws an error if:
505
- * 1. startDateTime is a past date.
506
- * 2. startDateTime is more than the EndDateTime.
507
- * @returns {void}
508
- */
509
- setStartDateTime(startDateTime: Date): void {
510
- const startDateTimeObject = new Date(startDateTime);
511
- startDateTimeObject.setHours(0, 0, 0, 0);
512
-
513
- /*Part 1: Make Sure Future Date Time*/
514
- if (startDateTimeObject < new Date(new Date().setHours(0, 0, 0, 0))) {
515
- throw new ClassError(
516
- 'Rental',
517
- 'RentalErrMsg02',
518
- 'StartDateTime cannot be a past datetime.',
519
- );
520
- }
521
-
522
- /*Part 2: Make Sure Less Than End Date Time*/
523
- if (this.EndDateTime && startDateTimeObject > this.EndDateTime) {
524
- throw new ClassError(
525
- 'Rental',
526
- 'RentalErrMsg03',
527
- 'StartDateTime cannot be more than EndDateTime.',
528
- );
529
- }
530
-
531
- /*Part 3: Set Status Whether Reserved or Active*/
532
- if (startDateTimeObject > new Date(new Date().setHours(0, 0, 0, 0))) {
533
- this._Status = RentalStatusEnum.RESERVED;
534
- } else {
535
- this._Status = RentalStatusEnum.ACTIVE;
536
- }
537
- }
538
-
539
- public async periodEndProcess(
540
- loginUser: LoginUser,
541
- dbTransaction: any,
542
- stockInventory: any,
543
- ) {
544
- try {
545
- // Privilege Checking
546
- const systemCode =
547
- await ApplicationConfig.getComponentConfigValue('system-code');
548
-
549
- const isPrivileged = await loginUser.checkPrivileges(
550
- systemCode,
551
- 'Rental – PeriodEndProcess',
552
- );
553
-
554
- if (!isPrivileged) {
555
- throw new ClassError(
556
- 'Rental',
557
- 'RentalErrMsg04',
558
- "You do not have 'Rental - PeriodEndProcess' privilege.",
559
- );
560
- }
561
-
562
- // Validate Existing Rental
563
- if (this.AgreementNo === undefined || this.AgreementNo === null) {
564
- throw new ClassError(
565
- 'Rental',
566
- 'RentalErrMsg05',
567
- 'Rental must be existing rental.',
568
- );
569
- }
570
-
571
- // Validate Rental End Period
572
- if (this.EndDateTime === new Date(new Date().setHours(0, 0, 0, 0))) {
573
- throw new ClassError(
574
- 'Rental',
575
- 'RentalErrMsg06',
576
- 'Rental period is not ending today.',
577
- );
578
- }
579
-
580
- // Mark Rental Item as Available
581
- await stockInventory.setAvailable(loginUser, dbTransaction);
582
-
583
- // Capture Record Info Before Changes
584
- const entityValueBefore = {
585
- RentalId: this.RentalId,
586
- CustomerId: this.CustomerId,
587
- CustomerType: this.CustomerType,
588
- ItemId: this.ItemId,
589
- ItemType: this.ItemType,
590
- PriceId: this.PriceId,
591
- StartDateTime: this.StartDateTime,
592
- EndDateTime: this.EndDateTime,
593
- CancelRemarks: this.CancelRemarks,
594
- TerminateRemarks: this.TerminateRemarks,
595
- AgreementNo: this.AgreementNo,
596
- AccountType: this.AccountType,
597
- Status: this.Status,
598
- EscheatmentYN: this.EscheatmentYN,
599
- CreatedById: this.CreatedById,
600
- CreatedAt: this.CreatedAt,
601
- UpdatedById: this.UpdatedById,
602
- UpdatedAt: this.UpdatedAt,
603
- };
604
-
605
- // Mark Rental as Terminated and Capture Updater Info
606
- this.setTerminated();
607
- this._UpdatedById = loginUser.ObjectId;
608
- this._UpdatedAt = new Date();
609
-
610
- // Capture Record Info after Changes
611
- const entityValueAfter = {
612
- RentalId: this.RentalId,
613
- CustomerId: this.CustomerId,
614
- CustomerType: this.CustomerType,
615
- ItemId: this.ItemId,
616
- ItemType: this.ItemType,
617
- PriceId: this.PriceId,
618
- StartDateTime: this.StartDateTime,
619
- EndDateTime: this.EndDateTime,
620
- CancelRemarks: this.CancelRemarks,
621
- TerminateRemarks: this.TerminateRemarks,
622
- AgreementNo: this.AgreementNo,
623
- AccountType: this.AccountType,
624
- Status: this.Status,
625
- EscheatmentYN: this.EscheatmentYN,
626
- CreatedById: this.CreatedById,
627
- CreatedAt: this.CreatedAt,
628
- UpdatedById: this.UpdatedById,
629
- UpdatedAt: this.UpdatedAt,
630
- };
631
-
632
- // Record the Update Activity
633
- const activity = new Activity();
634
- activity.ActivityId = activity.createId();
635
- activity.Action = ActionEnum.UPDATE;
636
- activity.Description = 'Set Rental as Terminated';
637
- activity.EntityType = 'Rental';
638
- activity.EntityId = this.RentalId;
639
- activity.EntityValueBefore = JSON.stringify(entityValueBefore);
640
- activity.EntityValueAfter = JSON.stringify(entityValueAfter);
641
- await activity.create(loginUser.ObjectId, dbTransaction);
642
-
643
- return this;
644
- } catch (err) {
645
- throw err;
646
- }
647
- }
648
-
649
- async getCustomerActiveRentals(
650
- loginUser: LoginUser,
651
- dbTransaction: any,
652
- CustomerId: string,
653
- ): Promise<IRentalAttr[]> {
654
- try {
655
- // Part 1: Privilege Checking
656
- // Call loginUser.checkPrivileges() by passing:
657
- // SystemCode: <get_from_app_config>
658
- // PrivilegeCode: "RENTAL_VIEW"
659
- const systemCode =
660
- ApplicationConfig.getComponentConfigValue('system-code');
661
- const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
662
-
663
- if (!isPrivileged) {
664
- throw new ClassError(
665
- 'Rental',
666
- 'RentalErrMsg01',
667
- "You do not have 'Rental - View' privilege.",
668
- );
669
- }
670
-
671
- // Part 2: Retrieve Rentals and Returns
672
- // Call Rental._Repo findAll method by passing:
673
- // where:
674
- // Status: "Active"
675
- // [Op.OR]:
676
- // CustomerId: Params.CustomerId
677
- // '$JointHirers.CustomerId$': Params.CustomerId
678
- // include:
679
- // model: JointHirerModel
680
- // attributes: []
681
- const query = `
682
- SELECT
683
- r.*
684
- FROM
685
- rental_Rental r
686
- LEFT JOIN
687
- rental_JointHirer jh ON r.RentalId = jh.RentalId
688
- WHERE
689
- r.Status = 'Active'
690
- AND (
691
- r.CustomerId = '${CustomerId}'
692
- OR jh.CustomerId = '${CustomerId}'
693
- )
694
- GROUP BY
695
- r.RentalId
696
- `;
697
- const db = rentalDb.getConnection();
698
- const result = await db.query(query, {
699
- type: QueryTypes.SELECT,
700
- transaction: dbTransaction,
701
- model: RentalModel,
702
- mapToModel: true,
703
- });
704
-
705
- // Format the returned records
706
- const records: IRentalAttr[] = result.map((record: RentalModel) => {
707
- return {
708
- RentalId: record.RentalId,
709
- CustomerId: record.CustomerId,
710
- CustomerType: record.CustomerType,
711
- ItemId: record.ItemId,
712
- ItemType: record.ItemType,
713
- PriceId: record.PriceId,
714
- StartDateTime: record.StartDateTime,
715
- EndDateTime: record.EndDateTime,
716
- CancelRemarks: record.CancelRemarks,
717
- TerminateRemarks: record.TerminateRemarks,
718
- AgreementNo: record.AgreementNo,
719
- AccountType: record.AccountType,
720
- Status: record.Status,
721
- EscheatmentYN: record.EscheatmentYN,
722
- CreatedById: record.CreatedById,
723
- CreatedAt: record.CreatedAt,
724
- UpdatedById: record.UpdatedById,
725
- UpdatedAt: record.UpdatedAt,
726
- };
727
- });
728
- // Return the returned records.
729
- return records;
730
- } catch (error) {
731
- throw error;
732
- }
733
- }
734
-
735
- async getJointHirers(dbTransaction: any) {
736
- try {
737
- if (!this.RentalId) {
738
- throw new ClassError('Rental', 'RentalErrMsg01', 'RentalId is missing');
739
- }
740
- if (this.AccountType !== RentalAccountTypeEnum.JOINT) {
741
- throw new ClassError(
742
- 'Rental',
743
- 'RentalErrMsg07',
744
- 'This rental does not have joint hirers.',
745
- );
746
- }
747
-
748
- const jointHirers = await Rental._JointHirerRepo.findAll({
749
- where: {
750
- RentalId: this.RentalId,
751
- },
752
- transaction: dbTransaction,
753
- });
754
- // Map the jointHirers to JointHirer instances
755
- const jointHirerInstances = Promise.all(
756
- jointHirers.map(async (hirer: JointHirerModel) => {
757
- return await JointHirer.init(hirer.HirerId, dbTransaction);
758
- }),
759
- );
760
- return jointHirerInstances;
761
- } catch (error) {
762
- console.error('Error in getJointHirers:', error);
763
- throw error;
764
- }
765
- }
766
-
767
- async getCustomerIds(loginUser: LoginUser, dbTransaction: any) {
768
- try {
769
- // Part 1: Privilege Checking
770
- // Call loginUser.checkPrivileges() by passing:
771
- // SystemCode: <get_from_app_config>
772
- // PrivilegeCode: "RENTAL_VIEW"
773
- const systemCode =
774
- ApplicationConfig.getComponentConfigValue('system-code');
775
- const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
776
-
777
- if (!isPrivileged) {
778
- throw new ClassError(
779
- 'Rental',
780
- 'RentalErrMsg01',
781
- "You do not have 'Rental - View' privilege.",
782
- );
783
- }
784
-
785
- // Part 2: Validation
786
- // Make sure this.RentalId exists, if not throw new ClassError by passing
787
- if (!this.RentalId) {
788
- throw new ClassError('Rental', 'RentalErrMsg01', 'RentalId is missing');
789
- }
790
-
791
- //Part 3: Retrieve Listing and Returns
792
- // 3.1 Call Rental._JointHirerRepo findAll method by passing:
793
- // where:
794
- // RentalId: this.RentalId
795
- // dbTransaction
796
- const options: any = {
797
- where: {
798
- RentalId: this.RentalId,
799
- Status: 'Active',
800
- },
801
- transaction: dbTransaction,
802
- };
803
-
804
- const joinHirers = await Rental._JointHirerRepo.findAll(options);
805
-
806
- // 3.2 Create a new array variable and set its value to joint hirer and Rental.CustomerId
807
- // array translated to only CustomerId. Then, append this.CustomerId to the array.
808
- const customerIds: string[] = [this.CustomerId];
809
- for (let i = 0; i < joinHirers.length; i++) {
810
- const jointHirer = joinHirers[i];
811
- customerIds.push(jointHirer.CustomerId);
812
- }
813
-
814
- // 3.3 Return the array.
815
- return customerIds;
816
- } catch (error) {
817
- throw error;
818
- }
819
- }
820
-
821
- async signAgreement(
822
- loginUser: LoginUser,
823
- party: string,
824
- PartyId: string,
825
- PartyType: string,
826
- dbTransaction: any,
827
- statusAfterSign?: RentalStatusEnum | string,
828
- ) {
829
- try {
830
- // Part 1: Privilege Checking
831
- // Call loginUser.checkPrivileges() by passing:
832
- // SystemCode: <get_from_app_config>
833
- // PrivilegeCode: "RENTAL_SIGN"
834
- const systemCode =
835
- ApplicationConfig.getComponentConfigValue('system-code');
836
- const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_SIGN');
837
-
838
- if (!isPrivileged) {
839
- throw new ClassError(
840
- 'Rental',
841
- 'RentalErrMsg01',
842
- "You do not have 'RENTAL_SIGN' privilege.",
843
- );
844
- }
845
-
846
- // Part 2: Validation
847
- // Make sure this.Status is "Pending Signing" and agreementStatus is "Generated", if not throw new ClassError
848
- const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
849
- if (
850
- this.Status !== RentalStatusEnum.PENDING_SIGNING &&
851
- agreement.Status !== AggrementStatusEnum.GENERATED
852
- ) {
853
- throw new ClassError(
854
- 'Rental',
855
- 'RentalErrMsg01',
856
- 'Rental is not ready to be signed yet.',
857
- );
858
- }
859
-
860
- //Make sure CustomerId inside the listing and SignatureStatus is "Pending"
861
- const signatureList = await Agreement.getSignatureList(
862
- loginUser,
863
- this.AgreementNo,
864
- dbTransaction,
865
- );
866
- const customerSignature = signatureList.find(
867
- (sig) =>
868
- sig.PartyId === PartyId &&
869
- sig.PartyType === PartyType &&
870
- sig.Party === party &&
871
- sig.SignatureStatus === AgreementSignatureStatusEnum.PENDING,
872
- );
873
-
874
- if (!customerSignature) {
875
- throw new ClassError(
876
- 'Rental',
877
- 'RentalErrMsg01',
878
- 'Signature is not pending.',
879
- );
880
- }
881
-
882
- //Update rental_AgreementSignature
883
- const signaturePayload = {
884
- SignatureStatus: AgreementSignatureStatusEnum.SIGNED,
885
- SignedAt: new Date(),
886
- UpdatedById: loginUser.ObjectId,
887
- UpdatedAt: new Date(),
888
- };
889
-
890
- await Rental._AgreementSignatureRepo.update(signaturePayload, {
891
- where: {
892
- AgreementNo: this.AgreementNo,
893
- Party: party,
894
- PartyId: PartyId,
895
- PartyType: PartyType,
896
- },
897
- transaction: dbTransaction,
898
- });
899
-
900
- const signatureActivity = new Activity();
901
- signatureActivity.ActivityId = signatureActivity.createId();
902
- signatureActivity.Action = ActionEnum.UPDATE;
903
- signatureActivity.Description = 'Update hirer signature';
904
- signatureActivity.EntityType = 'AgreementSignature';
905
- signatureActivity.EntityId = this.AgreementNo;
906
- signatureActivity.EntityValueBefore = JSON.stringify(customerSignature);
907
- signatureActivity.EntityValueAfter = JSON.stringify(signaturePayload);
908
- await signatureActivity.create(loginUser.ObjectId, dbTransaction);
909
-
910
- const updatedSignatureList = await Agreement.getSignatureList(
911
- loginUser,
912
- this.AgreementNo,
913
- dbTransaction,
914
- );
915
-
916
- const pendingSignatures = updatedSignatureList.filter(
917
- (sig) => sig.SignatureStatus === 'Pending',
918
- );
919
- if (pendingSignatures.length > 0) {
920
- return;
921
- }
922
-
923
- //Part 3: Update Agreement Status
924
- // 3.1 Set EntityValueBefore to current agreement instance.
925
- const entityValueAgreementBefore = {
926
- AgreementNo: agreement.AgreementNo,
927
- Status: agreement.Status,
928
- DateSigned: agreement.DateSigned,
929
- };
930
-
931
- // 3.2 Set below agreement attributes:
932
- // DateSigned: current date & time
933
- // Status: "Signed"
934
- const payload = {
935
- DateSigned: new Date(),
936
- Status: AggrementStatusEnum.SIGNED,
937
- };
938
-
939
- // 3.3 Call Rental._AgreementRepo update()
940
- await Rental._AgreementRepo.update(payload, {
941
- where: {
942
- AgreementNo: this.AgreementNo,
943
- },
944
- transaction: dbTransaction,
945
- });
946
-
947
- const entityValueAgreementAfter = {
948
- entityValueAgreementBefore,
949
- ...payload,
950
- };
951
-
952
- // Part 4: Record Update Agreement Activity
953
- const activity = new Activity();
954
- activity.ActivityId = activity.createId();
955
- activity.Action = ActionEnum.UPDATE;
956
- activity.Description = 'Update agreement';
957
- activity.EntityType = 'RentalAgreement';
958
- activity.EntityId = this.AgreementNo;
959
- activity.EntityValueBefore = JSON.stringify(entityValueAgreementBefore);
960
- activity.EntityValueAfter = JSON.stringify(entityValueAgreementAfter);
961
- await activity.create(loginUser.ObjectId, dbTransaction);
962
-
963
- // Part 5: Update Rental Status
964
- // 5.1 Set EntityValueBefore to current rental instance.
965
- const entityValueRentalBefore = {
966
- RentalId: this.RentalId,
967
- CustomerId: this.CustomerId,
968
- CustomerType: this.CustomerType,
969
- ItemId: this.ItemId,
970
- ItemType: this.ItemType,
971
- PriceId: this.PriceId,
972
- StartDateTime: this.StartDateTime,
973
- EndDateTime: this.EndDateTime,
974
- CancelRemarks: this.CancelRemarks,
975
- TerminateRemarks: this.TerminateRemarks,
976
- AgreementNo: this.AgreementNo,
977
- AccountType: this.AccountType,
978
- Status: this.Status,
979
- EscheatmentYN: this.EscheatmentYN,
980
- CreatedById: this.CreatedById,
981
- CreatedAt: this.CreatedAt,
982
- UpdatedById: this.UpdatedById,
983
- UpdatedAt: this.UpdatedAt,
984
- };
985
- // 5.2: Set below rental attributes:
986
- // Status: "Pending Key Collection"
987
- // UpdatedById: loginUser.ObjectId
988
- // UpdatedAt: current date & time
989
-
990
- this._Status = statusAfterSign
991
- ? statusAfterSign
992
- : RentalStatusEnum.ACTIVE;
993
-
994
- const payloadRental = {
995
- Status: this._Status,
996
- UpdatedById: loginUser.ObjectId,
997
- UpdatedAt: new Date(),
998
- };
999
-
1000
- // 5.3: Call Rental._Repo update() method by passing:
1001
- // populate rental instance attributes
1002
- // dbTransaction
1003
- await Rental._Repo.update(payloadRental, {
1004
- where: {
1005
- RentalId: this.RentalId,
1006
- },
1007
- transaction: dbTransaction,
1008
- });
1009
-
1010
- const entityValueRentalAfter = {
1011
- entityValueRentalBefore,
1012
- ...payloadRental,
1013
- };
1014
-
1015
- // Part 6: Record Update Agreement Activity
1016
- const rentalActivity = new Activity();
1017
- rentalActivity.ActivityId = activity.createId();
1018
- rentalActivity.Action = ActionEnum.UPDATE;
1019
- rentalActivity.Description = 'Sign rental agreement';
1020
- rentalActivity.EntityType = 'Rental';
1021
- rentalActivity.EntityId = this.RentalId;
1022
- rentalActivity.EntityValueBefore = JSON.stringify(
1023
- entityValueRentalBefore,
1024
- );
1025
- rentalActivity.EntityValueAfter = JSON.stringify(entityValueRentalAfter);
1026
- await rentalActivity.create(loginUser.ObjectId, dbTransaction);
1027
- } catch (error) {
1028
- throw error;
1029
- }
1030
- }
1031
-
1032
- async generateAgreement(
1033
- loginUser: LoginUser,
1034
- dbTransaction: any,
1035
- MediaId: string,
1036
- ) {
1037
- //This method will generate a new rental agreement.
1038
- try {
1039
- // Part 1: Privilege Checking
1040
- // Call loginUser.checkPrivileges() by passing:
1041
- // SystemCode: "<get_from_app_config>"
1042
- // PrivilegeCode: "RENTAL_AGREEMENT_CREATE"
1043
-
1044
- const systemCode =
1045
- ApplicationConfig.getComponentConfigValue('system-code');
1046
- const isPrivileged = loginUser.checkPrivileges(
1047
- systemCode,
1048
- 'RENTAL_AGREEMENT_CREATE',
1049
- );
1050
-
1051
- if (!isPrivileged) {
1052
- throw new ClassError(
1053
- 'Rental',
1054
- 'RentalErrMsg01',
1055
- "You do not have 'RENTAL_AGREEMENT_CREATE' privilege.",
1056
- );
1057
- }
1058
-
1059
- // Part 2: Validation
1060
- // Instantiate existing RentalAgreement by passing:
1061
- // dbTransaction
1062
- // AgreementNo: this.AgreementNo
1063
- // Make sure Params.MediaId exists, if not throw new ClassError by passing:
1064
- // Classname: "Rental"
1065
- // MessageCode: "RentalErrMsg0X"
1066
- // Message: "MediaId is required."
1067
- const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
1068
- if (!MediaId) {
1069
- throw new ClassError(
1070
- 'Rental',
1071
- 'RentalErrMsg0X',
1072
- 'MediaId is required.',
1073
- );
1074
- }
1075
-
1076
- // Part 3: Update Agreement Record
1077
- // Set EntityValueBefore to the agreement instance.
1078
- // Set below agreement attributes:
1079
- // Status: "Generated"
1080
- // MediaId: Params.MediaId
1081
-
1082
- const EntityValueBefore = agreement;
1083
- agreement.Status = AggrementStatusEnum.GENERATED;
1084
- agreement.MediaId = MediaId;
1085
-
1086
- // Call Rental._AgreementRepo update method by passing:
1087
- // Status: agreement.Status
1088
- // MediaId: agreement.MediaId
1089
- // dbTransaction
1090
-
1091
- await Rental._AgreementRepo.update(
1092
- {
1093
- Status: agreement.Status,
1094
- MediaId: agreement.MediaId,
1095
- },
1096
- {
1097
- where: {
1098
- AgreementNo: this.AgreementNo,
1099
- },
1100
- transaction: dbTransaction,
1101
- },
1102
- );
1103
-
1104
- // Part 4: Record Update Agreement Activity
1105
- // Initialise EntityValueAfter variable and set to agreement instance
1106
- const EntityValueAfter = agreement;
1107
- // Instantiate new activity from Activity class, call createId() method, then set:
1108
- const activity = new Activity();
1109
- // Action: ActionEnum.UPDATE
1110
- // Description: "Generate agreement."
1111
- // EntityType: "RentalAgreement"
1112
- // EntityId: this.AgreementNo
1113
- // EntityValueBefore: EntityValueBefore
1114
- // EntityValueAfter: EntityValueAfter
1115
- activity.ActivityId = activity.createId();
1116
- activity.Action = ActionEnum.UPDATE;
1117
- activity.Description = 'Generate agreement.';
1118
- activity.EntityType = 'RentalAgreement';
1119
- activity.EntityId = this.AgreementNo;
1120
- activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
1121
- activity.EntityValueAfter = JSON.stringify(EntityValueAfter);
1122
-
1123
- // Call new activity create method by passing:
1124
- // dbTransaction
1125
- // userId: loginUser.ObjectId
1126
- await activity.create(loginUser.ObjectId, dbTransaction);
1127
-
1128
- // Part 5: Add Record To Agreement History Table
1129
- // Make sure there is a private static readonly _AgreementHistoryRepo variable.
1130
- // Set newRecord to an object with below key-value:
1131
- // AgreementNo: this.AgreementNo
1132
- // MediaId: params.MediaId
1133
- // ActivityCompleted: "Agreement Generated"
1134
- // CreatedAt: Current Timestamp
1135
- // Use _AgreementHistoryRepo.create() method and pass newRecord and dbTransaction.
1136
- const agreementHistory = await Rental._AgreementHistoryRepo.create(
1137
- {
1138
- AgreementNo: this.AgreementNo,
1139
- MediaId,
1140
- ActivityCompleted: 'Agreement Generated',
1141
- CreatedAt: new Date(),
1142
- },
1143
- { transaction: dbTransaction },
1144
- );
1145
-
1146
- // Part 6: Record Agreement History Activity
1147
- // Initialise EntityValueAfter variable and set to newRecord from previous part.
1148
- // Instantiate new activity from Activity class, call createId() method, then set:
1149
- // Action: ActionEnum.CREATE
1150
- // Description: "Generate agreement."
1151
- // EntityType: "RentalAgreementHistory"
1152
- // EntityId: HistoryId from previous part.
1153
- // EntityValueBefore: empty object.
1154
- // EntityValueAfter: EntityValueAfter
1155
- // Call new activity create method by passing:
1156
- // dbTransaction
1157
- // userId: loginUser.ObjectId
1158
- const entityValueAfter = agreementHistory.get({ plain: true });
1159
- const activityHistory = new Activity();
1160
- activityHistory.ActivityId = activityHistory.createId();
1161
- activityHistory.Action = ActionEnum.CREATE;
1162
- activityHistory.Description = 'Generate agreement.';
1163
- activityHistory.EntityType = 'RentalAgreementHistory';
1164
- activityHistory.EntityId = agreementHistory.HistoryId.toString();
1165
- activityHistory.EntityValueBefore = JSON.stringify({});
1166
- activityHistory.EntityValueAfter = JSON.stringify(entityValueAfter);
1167
- await activityHistory.create(loginUser.ObjectId, dbTransaction);
1168
- } catch (error) {
1169
- throw error;
1170
- }
1171
- }
1172
-
1173
- public toJSON(): IRentalAttr {
1174
- return {
1175
- RentalId: this.RentalId,
1176
- CustomerId: this.CustomerId,
1177
- CustomerType: this.CustomerType,
1178
- ItemId: this.ItemId,
1179
- ItemType: this.ItemType,
1180
- PriceId: this.PriceId,
1181
- StartDateTime: this.StartDateTime,
1182
- EndDateTime: this.EndDateTime,
1183
- Status: this.Status,
1184
- CancelRemarks: this.CancelRemarks,
1185
- TerminateRemarks: this.TerminateRemarks,
1186
- EscheatmentYN: this.EscheatmentYN,
1187
- AgreementNo: this.AgreementNo,
1188
- AccountType: this.AccountType,
1189
- CreatedById: this.CreatedById,
1190
- CreatedAt: this.CreatedAt,
1191
- UpdatedById: this.UpdatedById,
1192
- UpdatedAt: this.UpdatedAt,
1193
- };
1194
- }
1195
-
1196
- public async getInvoices(
1197
- loginUser: LoginUser,
1198
- dbTransaction: any,
1199
- accountingSystem?: IAccountSystem,
1200
- ) {
1201
- try {
1202
- // Part 1: Privilege Checking
1203
- // Call loginUser.checkPrivileges() by passing:
1204
- // SystemCode: <get_from_app_config>
1205
- // PrivilegeCode: "RENTAL_VIEW_INVOICE"
1206
- const systemCode =
1207
- ApplicationConfig.getComponentConfigValue('system-code');
1208
- const isPrivileged = loginUser.checkPrivileges(
1209
- systemCode,
1210
- 'RENTAL_VIEW_INVOICE',
1211
- );
1212
-
1213
- if (!isPrivileged) {
1214
- throw new ClassError(
1215
- 'Rental',
1216
- 'RentalErrMsg01',
1217
- "You do not have 'RENTAL_VIEW_INVOICE' privilege.",
1218
- );
1219
- }
1220
-
1221
- // Part 2: Validation
1222
- // Make sure this._ObjectId exists, if not throw new ClassError by passing
1223
- if (!this.ObjectId) {
1224
- throw new ClassError(
1225
- 'Rental',
1226
- 'RentalErrMsg01',
1227
- 'ObjectId is missing.',
1228
- );
1229
- }
1230
-
1231
- // Part 3: Check Existing Invoices
1232
- if (this.Invoices.length > 0) {
1233
- return this.Invoices;
1234
- }
1235
-
1236
- // Part 4: Retrieve Invoices and Returns
1237
- const search = {
1238
- DocType: DocType.INVOICE,
1239
- RelatedObjectId: this.ObjectId,
1240
- RelatedObjectType: this.ObjectType,
1241
- };
1242
-
1243
- const documents = await Document.findAll(
1244
- loginUser,
1245
- dbTransaction,
1246
- undefined,
1247
- undefined,
1248
- search,
1249
- undefined,
1250
- accountingSystem,
1251
- );
1252
-
1253
- this.Invoices = documents.rows.map((doc) => {
1254
- const document = new Document(dbTransaction, doc);
1255
- return document;
1256
- });
1257
-
1258
- return this.Invoices;
1259
- } catch (error) {
1260
- throw error;
1261
- }
1262
- }
1263
-
1264
- public async getItem(dbTransaction: any): Promise<any> {
1265
- if (this.Item) {
1266
- return this.Item;
1267
- } else {
1268
- const ItemClass = ItemClassMap[this.ItemType];
1269
-
1270
- if (!ItemClass || typeof ItemClass.init !== 'function') {
1271
- throw new Error(`Invalid or unregistered ItemType: ${this.ItemType}`);
1272
- }
1273
-
1274
- ItemClass._Repo = ItemClass._Repo || ItemClass.getRepo();
1275
-
1276
- if (!ItemClass._Repo) {
1277
- throw new Error(
1278
- `ItemType ${this.ItemType} does not have a repository.`,
1279
- );
1280
- }
1281
-
1282
- const itemInstance = await ItemClass.init(this.ItemId, dbTransaction);
1283
-
1284
- if (!itemInstance) {
1285
- throw new Error(`${this.ItemType} not found with ID ${this.ItemId}`);
1286
- }
1287
- this.Item = itemInstance;
1288
-
1289
- // Make sure item has setAvailable method
1290
- if (typeof this.Item.setAvailable !== 'function') {
1291
- throw new Error(`${this.ItemType} does not implement setAvailable()`);
1292
- }
1293
- return this.Item;
1294
- }
1295
- }
1296
-
1297
- public async cancel(
1298
- loginUser: LoginUser,
1299
- dbTransaction: any,
1300
- remarks: string,
1301
- ): Promise<Rental> {
1302
- try {
1303
- // Part 1: Privilege Checking
1304
- const systemCode =
1305
- ApplicationConfig.getComponentConfigValue('system-code');
1306
- const isPrivileged = await loginUser.checkPrivileges(
1307
- systemCode,
1308
- 'RENTAL_CANCEL',
1309
- );
1310
-
1311
- if (!isPrivileged) {
1312
- throw new ClassError(
1313
- 'Rental',
1314
- 'RentalErrMsg01',
1315
- "You do not have 'RENTAL_CANCEL' privilege.",
1316
- );
1317
- }
1318
-
1319
- // Part 2: Validation
1320
- if (!this.ObjectId) {
1321
- throw new ClassError(
1322
- 'Rental',
1323
- 'RentalErrMsg01',
1324
- 'Rental not found. Rental Id is empty.',
1325
- );
1326
- }
1327
-
1328
- if (
1329
- this.Status === RentalStatusEnum.ACTIVE ||
1330
- this.Status === RentalStatusEnum.SUSPENDED
1331
- ) {
1332
- throw new ClassError(
1333
- 'Rental',
1334
- 'RentalErrMsg01',
1335
- `Rental already ${this.Status}, please do termination process.`,
1336
- );
1337
- }
1338
-
1339
- if (!remarks) {
1340
- throw new ClassError(
1341
- 'Rental',
1342
- 'RentalErrMsg01',
1343
- 'Cancellation remarks is required.',
1344
- );
1345
- }
1346
-
1347
- // Part 3: Capture Record Info Before Changes
1348
- const entityValueBefore = this.toJSON();
1349
-
1350
- // Part 4: Update Rental Status and Remarks
1351
- this._Status = RentalStatusEnum.CANCELLED;
1352
- this.CancelRemarks = remarks;
1353
- this._UpdatedById = loginUser.ObjectId;
1354
- this._UpdatedAt = new Date();
1355
-
1356
- // Part 5: Update Rental Record
1357
- await Rental._Repo.update(
1358
- {
1359
- Status: this.Status,
1360
- CancelRemarks: this.CancelRemarks,
1361
- UpdatedById: this.UpdatedById,
1362
- UpdatedAt: this.UpdatedAt,
1363
- },
1364
- {
1365
- where: {
1366
- RentalId: this.RentalId,
1367
- },
1368
- transaction: dbTransaction,
1369
- },
1370
- );
1371
-
1372
- // Part 6: Mark Item as Available
1373
- const item = await this.getItem(dbTransaction);
1374
-
1375
- await item.setAvailable(loginUser, dbTransaction);
1376
-
1377
- // Part 7: Capture Record Info After Changes
1378
- const entityValueAfter = this.toJSON();
1379
- // Part 8: Record Activity
1380
- const activity = new Activity();
1381
- activity.ActivityId = activity.createId();
1382
- activity.Action = ActionEnum.UPDATE;
1383
- activity.Description = 'Rental Cancellation';
1384
- activity.EntityType = this.ObjectType;
1385
- activity.EntityId = this.ObjectId;
1386
- activity.EntityValueBefore = JSON.stringify(entityValueBefore);
1387
- activity.EntityValueAfter = JSON.stringify(entityValueAfter);
1388
- await activity.create(loginUser.ObjectId, dbTransaction);
1389
- return this;
1390
- } catch (error) {
1391
- throw error;
1392
- }
1393
- }
1394
-
1395
- public async update(
1396
- loginUser: LoginUser,
1397
- dbTransaction: any,
1398
- updates: Partial<IRentalAttr>,
1399
- ): Promise<Rental> {
1400
- try {
1401
- // Part 1: Privilege Checking
1402
- const systemCode =
1403
- ApplicationConfig.getComponentConfigValue('system-code');
1404
- const isPrivileged = await loginUser.checkPrivileges(
1405
- systemCode,
1406
- 'RENTAL_UPDATE',
1407
- );
1408
-
1409
- if (!isPrivileged) {
1410
- throw new ClassError(
1411
- 'Rental',
1412
- 'RentalErrMsg01',
1413
- "You do not have 'RENTAL_UPDATE' privilege.",
1414
- );
1415
- }
1416
-
1417
- // Part 2: Validation
1418
- if (!this.ObjectId) {
1419
- throw new ClassError(
1420
- 'Rental',
1421
- 'RentalErrMsg01',
1422
- 'Rental not found. Rental Id is empty.',
1423
- );
1424
- }
1425
-
1426
- // Part 3: Capture Record Info Before Changes
1427
- const entityValueBefore = this.toJSON();
1428
-
1429
- // Part 4: Update Rental Record
1430
- await Rental._Repo.update(updates, {
1431
- where: {
1432
- RentalId: this.RentalId,
1433
- },
1434
- transaction: dbTransaction,
1435
- });
1436
-
1437
- // Part 5: Update object instance properties and capture after changes
1438
- Object.assign(this, updates);
1439
- this._UpdatedById = loginUser.ObjectId;
1440
- this._UpdatedAt = new Date();
1441
-
1442
- const entityValueAfter = this.toJSON();
1443
-
1444
- // Part 6: Record Activity
1445
- const activity = new Activity();
1446
- activity.ActivityId = activity.createId();
1447
- activity.Action = ActionEnum.UPDATE;
1448
- activity.Description = 'Update Rental';
1449
- activity.EntityType = this.ObjectType;
1450
- activity.EntityId = this.ObjectId;
1451
- activity.EntityValueBefore = JSON.stringify(entityValueBefore);
1452
- activity.EntityValueAfter = JSON.stringify(entityValueAfter);
1453
- await activity.create(loginUser.ObjectId, dbTransaction);
1454
-
1455
- return this;
1456
- } catch (error) {
1457
- throw error;
1458
- }
1459
- }
1460
- }
1
+ import { IRentalAttr } from '../../interfaces/rental-attr.interface';
2
+ import { RentalStatusEnum } from '../../enum/rental-status.enum';
3
+ import { RentalRepository } from './rental.repository';
4
+ import { ClassError, ObjectBase } from '@tomei/general';
5
+ import { ApplicationConfig } from '@tomei/config';
6
+ import { LoginUser } from '@tomei/sso';
7
+ import { RentalPrice } from '../rental-price/rental-price';
8
+ import { Op, QueryTypes } from 'sequelize';
9
+ import { ActionEnum, Activity } from '@tomei/activity-history';
10
+ import { IRentalFindAllSearchAttr } from '../../interfaces/rental-find-all-search-attr.interface';
11
+ import { RentalAccountTypeEnum } from '../../enum/account-type.enum';
12
+ import { JointHirer } from '../joint-hirer/joint-hirer';
13
+ import { JointHirerModel } from '../../models/joint-hirer.entity';
14
+ import { AgreementRepository } from '../agreement/agreement.repository';
15
+ import * as rentalDb from '../../database';
16
+ import { RentalModel } from '../../models/rental.entity';
17
+ import { JointHirerRepository } from '../joint-hirer/joint-hirer.repository';
18
+ import { AgreementModel } from '../../models';
19
+ import { Agreement } from '../agreement/agreement';
20
+ import { AggrementStatusEnum } from '../../enum/aggrement-status.enum';
21
+ import { AgreementSignatureRepository } from '../agreement-signature/agreement-signature.repository';
22
+ import { AgreementSignatureStatusEnum } from '../../enum/agreement-signature-status.enum';
23
+ import { HirerTypeEnum } from '../../enum/hirer-type.enum';
24
+ import { AgreementHistoryRepository } from '../agreement-history/agreement-history.repository';
25
+ import { DocType, Document, IAccountSystem } from '@tomei/finance';
26
+ import { ItemClassMap } from '../../ClassMappings/ItemClassMap';
27
+ import { AgreementSignatureVerificationMethodEnum } from '../../enum/agreement-signature-verification-method.enum';
28
+
29
+ export class Rental extends ObjectBase {
30
+ ObjectId: string;
31
+ ObjectName: string;
32
+ ObjectType = 'Rental';
33
+ TableName: string;
34
+ CustomerId: string;
35
+ CustomerType: string;
36
+ ItemId: string;
37
+ ItemType: string;
38
+ PriceId: string;
39
+ StartDateTime: Date;
40
+ EndDateTime: Date;
41
+ CancelRemarks: string;
42
+ TerminateRemarks: string;
43
+ AgreementNo: string;
44
+ AccountType: RentalAccountTypeEnum;
45
+ JointHirers: JointHirer[] = [];
46
+ Invoices: Document[] = [];
47
+ Item: any;
48
+ protected _Status: RentalStatusEnum | string;
49
+ protected _EscheatmentYN: string = 'N';
50
+ protected _CreatedById: string;
51
+ protected _CreatedAt: Date;
52
+ protected _UpdatedById: string;
53
+ protected _UpdatedAt: Date;
54
+ protected static _Repo = new RentalRepository();
55
+ protected static _AgreementRepo = new AgreementRepository();
56
+ protected static _JointHirerRepo = new JointHirerRepository();
57
+ protected static _AgreementSignatureRepo = new AgreementSignatureRepository();
58
+ protected static _AgreementHistoryRepo = new AgreementHistoryRepository();
59
+
60
+ get RentalId(): string {
61
+ return this.ObjectId;
62
+ }
63
+
64
+ set RentalId(value: string) {
65
+ this.ObjectId = value;
66
+ }
67
+
68
+ get Status(): RentalStatusEnum | string {
69
+ return this._Status;
70
+ }
71
+
72
+ get EscheatmentYN(): string {
73
+ return this._EscheatmentYN;
74
+ }
75
+
76
+ get CreatedById(): string {
77
+ return this._CreatedById;
78
+ }
79
+
80
+ get CreatedAt(): Date {
81
+ return this._CreatedAt;
82
+ }
83
+
84
+ get UpdatedById(): string {
85
+ return this._UpdatedById;
86
+ }
87
+
88
+ get UpdatedAt(): Date {
89
+ return this._UpdatedAt;
90
+ }
91
+
92
+ private setTerminated() {
93
+ this._Status = RentalStatusEnum.TERMINATED;
94
+ }
95
+
96
+ protected constructor(rentalAttr?: IRentalAttr) {
97
+ super();
98
+ if (rentalAttr) {
99
+ this.RentalId = rentalAttr.RentalId;
100
+ this.CustomerId = rentalAttr.CustomerId;
101
+ this.CustomerType = rentalAttr.CustomerType;
102
+ this.ItemId = rentalAttr.ItemId;
103
+ this.ItemType = rentalAttr.ItemType;
104
+ this.PriceId = rentalAttr.PriceId;
105
+ this.StartDateTime = rentalAttr.StartDateTime;
106
+ this.EndDateTime = rentalAttr.EndDateTime;
107
+ this.CancelRemarks = rentalAttr.CancelRemarks;
108
+ this.TerminateRemarks = rentalAttr.TerminateRemarks;
109
+ this.AgreementNo = rentalAttr.AgreementNo;
110
+ this.AccountType = rentalAttr.AccountType;
111
+ this._Status = rentalAttr.Status;
112
+ this._EscheatmentYN = rentalAttr.EscheatmentYN;
113
+ this._CreatedById = rentalAttr.CreatedById;
114
+ this._CreatedAt = rentalAttr.CreatedAt;
115
+ this._UpdatedById = rentalAttr.UpdatedById;
116
+ this._UpdatedAt = rentalAttr.UpdatedAt;
117
+ }
118
+ }
119
+
120
+ public static async init(dbTransaction?: any, rentalId?: string) {
121
+ try {
122
+ if (rentalId) {
123
+ const rental = await Rental._Repo.findByPk(rentalId, dbTransaction);
124
+ if (rental) {
125
+ return new Rental(rental);
126
+ } else {
127
+ throw new ClassError('Rental', 'RentalErrMsg00', 'Rental Not Found');
128
+ }
129
+ }
130
+ return new Rental();
131
+ } catch (error) {
132
+ throw new ClassError(
133
+ 'Rental',
134
+ 'RentalErrMsg00',
135
+ 'Failed To Initialize Rental',
136
+ );
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Create a new Rental record.
142
+ * @param {RentalPrice} rentalPrice - The rental pricing information.
143
+ * @param {LoginUser} loginUser - The user initiating the creation.
144
+ * @param {any} [dbTransaction] - Optional database transaction for atomic operations.
145
+ * @throws {ClassError} Throws an error if:
146
+ * 1. loginUser does not have 'Rental - Create' privilege.
147
+ * 2. Rental item is not available at the current date.
148
+ * @returns {Promise<any>} A Promise resolving to the created rental record.
149
+ */
150
+ public async create(
151
+ rentalPrice: RentalPrice,
152
+ loginUser: LoginUser,
153
+ jointHirers?: JointHirer[],
154
+ dbTransaction?: any,
155
+ ): Promise<any> {
156
+ try {
157
+ /*Part 1: Check Privilege*/
158
+ const systemCode =
159
+ ApplicationConfig.getComponentConfigValue('system-code');
160
+ const isPrivileged = await loginUser.checkPrivileges(
161
+ systemCode,
162
+ 'Rental - Create',
163
+ );
164
+
165
+ if (!isPrivileged) {
166
+ throw new ClassError(
167
+ 'Rental',
168
+ 'RentalErrMsg01',
169
+ "You do not have 'Rental - Create' privilege.",
170
+ );
171
+ }
172
+
173
+ /*Part 2: Make Sure Rental Item Available*/
174
+ const isItemAvailable = await Rental.isItemAvailable(
175
+ this.ItemId,
176
+ this.ItemType,
177
+ this.StartDateTime,
178
+ this.EndDateTime,
179
+ dbTransaction,
180
+ );
181
+
182
+ if (!isItemAvailable) {
183
+ throw new ClassError(
184
+ 'Rental',
185
+ 'RentalErrMsg02',
186
+ 'Rental Item is not available at current date.',
187
+ );
188
+ }
189
+
190
+ // Part 3: Create Rental Agreement Record
191
+ // Call Rental._AgreementRepo create method by passing:
192
+ // AgreementNo: this.AgreementNo
193
+ // Status: "Not Generated"
194
+ // dbTransaction
195
+ await Rental._AgreementRepo.create(
196
+ {
197
+ AgreementNo: this.AgreementNo,
198
+ Status: 'Not Generated',
199
+ },
200
+ {
201
+ transaction: dbTransaction,
202
+ },
203
+ );
204
+
205
+ //Create a record into rental_AgreementSignature table
206
+ //Create the main customer's signature record
207
+ await Rental._AgreementSignatureRepo.create(
208
+ {
209
+ SignatureId: this.createId(),
210
+ AgreementNo: this.AgreementNo,
211
+ Party: HirerTypeEnum.PRIMARY,
212
+ PartyId: this.CustomerId,
213
+ PartyType: 'Customer',
214
+ SignatureStatus: AgreementSignatureStatusEnum.PENDING,
215
+ SignedAt: null,
216
+ CreatedById: loginUser.ObjectId,
217
+ CreatedAt: new Date(),
218
+ UpdatedById: loginUser.ObjectId,
219
+ UpdatedAt: new Date(),
220
+ },
221
+ { transaction: dbTransaction },
222
+ );
223
+
224
+ // Create signatures record for joint hirers if any
225
+ if (jointHirers && jointHirers.length > 0) {
226
+ for (const jointHirer of jointHirers) {
227
+ await Rental._AgreementSignatureRepo.create(
228
+ {
229
+ SignatureId: this.createId(),
230
+ AgreementNo: this.AgreementNo,
231
+ Party: HirerTypeEnum.JOINT,
232
+ PartyId: jointHirer.CustomerId,
233
+ PartyType: 'Customer',
234
+ SignatureStatus: AgreementSignatureStatusEnum.PENDING,
235
+ SignedAt: null,
236
+ CreatedById: loginUser.ObjectId,
237
+ CreatedAt: new Date(),
238
+ UpdatedById: loginUser.ObjectId,
239
+ UpdatedAt: new Date(),
240
+ },
241
+ { transaction: dbTransaction },
242
+ );
243
+ }
244
+ }
245
+
246
+ /*Part 4: Insert Rental & The Agreed Rental Price*/
247
+ if (!rentalPrice.PriceId) {
248
+ const rentalPriceData = await rentalPrice.create(
249
+ loginUser,
250
+ dbTransaction,
251
+ );
252
+ this.PriceId = rentalPriceData.PriceId;
253
+ } else {
254
+ this.PriceId = rentalPrice.PriceId;
255
+ }
256
+ this.RentalId = this.createId();
257
+ // if (!this.Status) {
258
+ this._Status = RentalStatusEnum.PENDING_SIGNING;
259
+ // }
260
+ this._CreatedById = loginUser.ObjectId;
261
+ this._UpdatedById = loginUser.ObjectId;
262
+ this._CreatedAt = new Date();
263
+ this._UpdatedAt = new Date();
264
+
265
+ const data = {
266
+ RentalId: this.RentalId,
267
+ CustomerId: this.CustomerId,
268
+ CustomerType: this.CustomerType,
269
+ ItemId: this.ItemId,
270
+ ItemType: this.ItemType,
271
+ PriceId: this.PriceId,
272
+ StartDateTime: this.StartDateTime,
273
+ EndDateTime: this.EndDateTime,
274
+ CancelRemarks: this.CancelRemarks,
275
+ TerminateRemarks: this.TerminateRemarks,
276
+ AgreementNo: this.AgreementNo,
277
+ AccountType: this.AccountType,
278
+ Status: this.Status,
279
+ EscheatmentYN: this.EscheatmentYN,
280
+ CreatedById: this.CreatedById,
281
+ CreatedAt: this.CreatedAt,
282
+ UpdatedById: this.UpdatedById,
283
+ UpdatedAt: this.UpdatedAt,
284
+ };
285
+
286
+ await Rental._Repo.create(data, {
287
+ transaction: dbTransaction,
288
+ });
289
+
290
+ //For every data inside Param.jointHirers, update the rentalId and call jointHirer.create
291
+ if (jointHirers) {
292
+ for (let i = 0; i < jointHirers.length; i++) {
293
+ jointHirers[i].RentalId = this.RentalId;
294
+ await jointHirers[i].create(loginUser, dbTransaction);
295
+ this.JointHirers.push(jointHirers[i]);
296
+ }
297
+ }
298
+
299
+ /*Part 5: Record Create Rental Activity*/
300
+ const activity = new Activity();
301
+ activity.ActivityId = activity.createId();
302
+ activity.Action = ActionEnum.CREATE;
303
+ activity.Description = 'Add Rental';
304
+ activity.EntityType = 'Rental';
305
+ activity.EntityId = this.RentalId;
306
+ activity.EntityValueBefore = JSON.stringify({});
307
+ activity.EntityValueAfter = JSON.stringify(data);
308
+ await activity.create(loginUser.ObjectId, dbTransaction);
309
+
310
+ /*Part 6: Return Result*/
311
+ return this;
312
+ } catch (error) {
313
+ throw error;
314
+ }
315
+ }
316
+
317
+ public static async isItemAvailable(
318
+ itemId: string,
319
+ itemType: string,
320
+ startDateTime: Date,
321
+ endDateTime: Date,
322
+ dbTransaction?: any,
323
+ ) {
324
+ try {
325
+ const rental = await Rental._Repo.findOne({
326
+ where: {
327
+ ItemId: itemId,
328
+ ItemType: itemType,
329
+ StartDateTime: {
330
+ [Op.lte]: endDateTime,
331
+ },
332
+ EndDateTime: {
333
+ [Op.gte]: startDateTime,
334
+ },
335
+ },
336
+ transaction: dbTransaction,
337
+ });
338
+
339
+ if (!rental) {
340
+ // No rental found → item is available
341
+ return true;
342
+ }
343
+
344
+ if (
345
+ rental.Status === RentalStatusEnum.TERMINATED ||
346
+ rental.Status === RentalStatusEnum.CANCELLED
347
+ ) {
348
+ // Rental found but status is Terminated or Cancelled → item is available
349
+ return true;
350
+ }
351
+
352
+ // Rental exists and is active → item is not available
353
+ return false;
354
+ } catch (error) {
355
+ throw error;
356
+ }
357
+ }
358
+
359
+ public static async findAll(
360
+ loginUser: LoginUser,
361
+ dbTransaction: any,
362
+ page?: number,
363
+ row?: number,
364
+ search?: IRentalFindAllSearchAttr,
365
+ ) {
366
+ try {
367
+ const systemCode =
368
+ await ApplicationConfig.getComponentConfigValue('system-code');
369
+
370
+ const isPrivileged = await loginUser.checkPrivileges(
371
+ systemCode,
372
+ 'Rental - View',
373
+ );
374
+
375
+ if (!isPrivileged) {
376
+ throw new ClassError(
377
+ 'Rental',
378
+ 'RentalErrMsg01',
379
+ "You do not have 'Rental - View' privilege.",
380
+ );
381
+ }
382
+
383
+ const queryObj: any = {};
384
+
385
+ let options: any = {
386
+ transaction: dbTransaction,
387
+ order: [['CreatedAt', 'DESC']],
388
+ };
389
+
390
+ if (page && row) {
391
+ options = {
392
+ ...options,
393
+ limit: row,
394
+ offset: row * (page - 1),
395
+ };
396
+ }
397
+
398
+ if (search) {
399
+ Object.entries(search).forEach(([key, value]) => {
400
+ if (key === 'StartDateTime') {
401
+ queryObj[key] = {
402
+ [Op.gte]: value,
403
+ };
404
+ } else if (key === 'EndDateTime') {
405
+ queryObj[key] = {
406
+ [Op.lte]: value,
407
+ };
408
+ } else if (key === 'CustomerId') {
409
+ queryObj[key] = {
410
+ [Op.substring]: value,
411
+ };
412
+
413
+ options.include = [
414
+ {
415
+ model: JointHirerModel,
416
+ required: false,
417
+ where: {
418
+ CustomerId: {
419
+ [Op.substring]: value,
420
+ },
421
+ },
422
+ },
423
+ ];
424
+ } else {
425
+ queryObj[key] = {
426
+ [Op.substring]: value,
427
+ };
428
+ }
429
+ });
430
+ if (options?.include?.length > 1) {
431
+ options.include.push({
432
+ model: AgreementModel,
433
+ required: false,
434
+ });
435
+ } else {
436
+ options.include = [
437
+ {
438
+ model: AgreementModel,
439
+ required: false,
440
+ },
441
+ ];
442
+ }
443
+
444
+ options = {
445
+ ...options,
446
+ where: queryObj,
447
+ };
448
+ }
449
+
450
+ return await Rental._Repo.findAndCountAll(options);
451
+ } catch (err) {
452
+ throw err;
453
+ }
454
+ }
455
+
456
+ public static async checkActiveByItemId(
457
+ loginUser: LoginUser,
458
+ itemId: string,
459
+ itemType: string,
460
+ dbTransaction?: any,
461
+ ) {
462
+ try {
463
+ const systemCode =
464
+ await ApplicationConfig.getComponentConfigValue('system-code');
465
+
466
+ const isPrivileged = await loginUser.checkPrivileges(
467
+ systemCode,
468
+ 'Rental - View',
469
+ );
470
+
471
+ if (!isPrivileged) {
472
+ throw new ClassError(
473
+ 'Rental',
474
+ 'RentalErrMsg01',
475
+ "You do not have 'Rental - View' privilege.",
476
+ );
477
+ }
478
+
479
+ const queryObj: any = {
480
+ ItemId: itemId,
481
+ ItemType: itemType,
482
+ Status: RentalStatusEnum.ACTIVE,
483
+ };
484
+
485
+ const options: any = {
486
+ transaction: dbTransaction,
487
+ where: queryObj,
488
+ };
489
+
490
+ const rental = await Rental._Repo.findOne(options);
491
+
492
+ if (rental) {
493
+ return true;
494
+ } else {
495
+ return false;
496
+ }
497
+ } catch (err) {
498
+ throw err;
499
+ }
500
+ }
501
+
502
+ /**
503
+ * Sets the StartDateTime property of the Rental class as custom setter.
504
+ * @param {Date} startDateTime - The start date and time of the rental.
505
+ * @throws {ClassError} Throws an error if:
506
+ * 1. startDateTime is a past date.
507
+ * 2. startDateTime is more than the EndDateTime.
508
+ * @returns {void}
509
+ */
510
+ setStartDateTime(startDateTime: Date): void {
511
+ const startDateTimeObject = new Date(startDateTime);
512
+ startDateTimeObject.setHours(0, 0, 0, 0);
513
+
514
+ /*Part 1: Make Sure Future Date Time*/
515
+ if (startDateTimeObject < new Date(new Date().setHours(0, 0, 0, 0))) {
516
+ throw new ClassError(
517
+ 'Rental',
518
+ 'RentalErrMsg02',
519
+ 'StartDateTime cannot be a past datetime.',
520
+ );
521
+ }
522
+
523
+ /*Part 2: Make Sure Less Than End Date Time*/
524
+ if (this.EndDateTime && startDateTimeObject > this.EndDateTime) {
525
+ throw new ClassError(
526
+ 'Rental',
527
+ 'RentalErrMsg03',
528
+ 'StartDateTime cannot be more than EndDateTime.',
529
+ );
530
+ }
531
+
532
+ /*Part 3: Set Status Whether Reserved or Active*/
533
+ if (startDateTimeObject > new Date(new Date().setHours(0, 0, 0, 0))) {
534
+ this._Status = RentalStatusEnum.RESERVED;
535
+ } else {
536
+ this._Status = RentalStatusEnum.ACTIVE;
537
+ }
538
+ }
539
+
540
+ public async periodEndProcess(
541
+ loginUser: LoginUser,
542
+ dbTransaction: any,
543
+ stockInventory: any,
544
+ ) {
545
+ try {
546
+ // Privilege Checking
547
+ const systemCode =
548
+ await ApplicationConfig.getComponentConfigValue('system-code');
549
+
550
+ const isPrivileged = await loginUser.checkPrivileges(
551
+ systemCode,
552
+ 'Rental – PeriodEndProcess',
553
+ );
554
+
555
+ if (!isPrivileged) {
556
+ throw new ClassError(
557
+ 'Rental',
558
+ 'RentalErrMsg04',
559
+ "You do not have 'Rental - PeriodEndProcess' privilege.",
560
+ );
561
+ }
562
+
563
+ // Validate Existing Rental
564
+ if (this.AgreementNo === undefined || this.AgreementNo === null) {
565
+ throw new ClassError(
566
+ 'Rental',
567
+ 'RentalErrMsg05',
568
+ 'Rental must be existing rental.',
569
+ );
570
+ }
571
+
572
+ // Validate Rental End Period
573
+ if (this.EndDateTime === new Date(new Date().setHours(0, 0, 0, 0))) {
574
+ throw new ClassError(
575
+ 'Rental',
576
+ 'RentalErrMsg06',
577
+ 'Rental period is not ending today.',
578
+ );
579
+ }
580
+
581
+ // Mark Rental Item as Available
582
+ await stockInventory.setAvailable(loginUser, dbTransaction);
583
+
584
+ // Capture Record Info Before Changes
585
+ const entityValueBefore = {
586
+ RentalId: this.RentalId,
587
+ CustomerId: this.CustomerId,
588
+ CustomerType: this.CustomerType,
589
+ ItemId: this.ItemId,
590
+ ItemType: this.ItemType,
591
+ PriceId: this.PriceId,
592
+ StartDateTime: this.StartDateTime,
593
+ EndDateTime: this.EndDateTime,
594
+ CancelRemarks: this.CancelRemarks,
595
+ TerminateRemarks: this.TerminateRemarks,
596
+ AgreementNo: this.AgreementNo,
597
+ AccountType: this.AccountType,
598
+ Status: this.Status,
599
+ EscheatmentYN: this.EscheatmentYN,
600
+ CreatedById: this.CreatedById,
601
+ CreatedAt: this.CreatedAt,
602
+ UpdatedById: this.UpdatedById,
603
+ UpdatedAt: this.UpdatedAt,
604
+ };
605
+
606
+ // Mark Rental as Terminated and Capture Updater Info
607
+ this.setTerminated();
608
+ this._UpdatedById = loginUser.ObjectId;
609
+ this._UpdatedAt = new Date();
610
+
611
+ // Capture Record Info after Changes
612
+ const entityValueAfter = {
613
+ RentalId: this.RentalId,
614
+ CustomerId: this.CustomerId,
615
+ CustomerType: this.CustomerType,
616
+ ItemId: this.ItemId,
617
+ ItemType: this.ItemType,
618
+ PriceId: this.PriceId,
619
+ StartDateTime: this.StartDateTime,
620
+ EndDateTime: this.EndDateTime,
621
+ CancelRemarks: this.CancelRemarks,
622
+ TerminateRemarks: this.TerminateRemarks,
623
+ AgreementNo: this.AgreementNo,
624
+ AccountType: this.AccountType,
625
+ Status: this.Status,
626
+ EscheatmentYN: this.EscheatmentYN,
627
+ CreatedById: this.CreatedById,
628
+ CreatedAt: this.CreatedAt,
629
+ UpdatedById: this.UpdatedById,
630
+ UpdatedAt: this.UpdatedAt,
631
+ };
632
+
633
+ // Record the Update Activity
634
+ const activity = new Activity();
635
+ activity.ActivityId = activity.createId();
636
+ activity.Action = ActionEnum.UPDATE;
637
+ activity.Description = 'Set Rental as Terminated';
638
+ activity.EntityType = 'Rental';
639
+ activity.EntityId = this.RentalId;
640
+ activity.EntityValueBefore = JSON.stringify(entityValueBefore);
641
+ activity.EntityValueAfter = JSON.stringify(entityValueAfter);
642
+ await activity.create(loginUser.ObjectId, dbTransaction);
643
+
644
+ return this;
645
+ } catch (err) {
646
+ throw err;
647
+ }
648
+ }
649
+
650
+ async getCustomerActiveRentals(
651
+ loginUser: LoginUser,
652
+ dbTransaction: any,
653
+ CustomerId: string,
654
+ ): Promise<IRentalAttr[]> {
655
+ try {
656
+ // Part 1: Privilege Checking
657
+ // Call loginUser.checkPrivileges() by passing:
658
+ // SystemCode: <get_from_app_config>
659
+ // PrivilegeCode: "RENTAL_VIEW"
660
+ const systemCode =
661
+ ApplicationConfig.getComponentConfigValue('system-code');
662
+ const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
663
+
664
+ if (!isPrivileged) {
665
+ throw new ClassError(
666
+ 'Rental',
667
+ 'RentalErrMsg01',
668
+ "You do not have 'Rental - View' privilege.",
669
+ );
670
+ }
671
+
672
+ // Part 2: Retrieve Rentals and Returns
673
+ // Call Rental._Repo findAll method by passing:
674
+ // where:
675
+ // Status: "Active"
676
+ // [Op.OR]:
677
+ // CustomerId: Params.CustomerId
678
+ // '$JointHirers.CustomerId$': Params.CustomerId
679
+ // include:
680
+ // model: JointHirerModel
681
+ // attributes: []
682
+ const query = `
683
+ SELECT
684
+ r.*
685
+ FROM
686
+ rental_Rental r
687
+ LEFT JOIN
688
+ rental_JointHirer jh ON r.RentalId = jh.RentalId
689
+ WHERE
690
+ r.Status = 'Active'
691
+ AND (
692
+ r.CustomerId = '${CustomerId}'
693
+ OR jh.CustomerId = '${CustomerId}'
694
+ )
695
+ GROUP BY
696
+ r.RentalId
697
+ `;
698
+ const db = rentalDb.getConnection();
699
+ const result = await db.query(query, {
700
+ type: QueryTypes.SELECT,
701
+ transaction: dbTransaction,
702
+ model: RentalModel,
703
+ mapToModel: true,
704
+ });
705
+
706
+ // Format the returned records
707
+ const records: IRentalAttr[] = result.map((record: RentalModel) => {
708
+ return {
709
+ RentalId: record.RentalId,
710
+ CustomerId: record.CustomerId,
711
+ CustomerType: record.CustomerType,
712
+ ItemId: record.ItemId,
713
+ ItemType: record.ItemType,
714
+ PriceId: record.PriceId,
715
+ StartDateTime: record.StartDateTime,
716
+ EndDateTime: record.EndDateTime,
717
+ CancelRemarks: record.CancelRemarks,
718
+ TerminateRemarks: record.TerminateRemarks,
719
+ AgreementNo: record.AgreementNo,
720
+ AccountType: record.AccountType,
721
+ Status: record.Status,
722
+ EscheatmentYN: record.EscheatmentYN,
723
+ CreatedById: record.CreatedById,
724
+ CreatedAt: record.CreatedAt,
725
+ UpdatedById: record.UpdatedById,
726
+ UpdatedAt: record.UpdatedAt,
727
+ };
728
+ });
729
+ // Return the returned records.
730
+ return records;
731
+ } catch (error) {
732
+ throw error;
733
+ }
734
+ }
735
+
736
+ async getJointHirers(dbTransaction: any) {
737
+ try {
738
+ if (!this.RentalId) {
739
+ throw new ClassError('Rental', 'RentalErrMsg01', 'RentalId is missing');
740
+ }
741
+ if (this.AccountType !== RentalAccountTypeEnum.JOINT) {
742
+ throw new ClassError(
743
+ 'Rental',
744
+ 'RentalErrMsg07',
745
+ 'This rental does not have joint hirers.',
746
+ );
747
+ }
748
+
749
+ const jointHirers = await Rental._JointHirerRepo.findAll({
750
+ where: {
751
+ RentalId: this.RentalId,
752
+ },
753
+ transaction: dbTransaction,
754
+ });
755
+ // Map the jointHirers to JointHirer instances
756
+ const jointHirerInstances = Promise.all(
757
+ jointHirers.map(async (hirer: JointHirerModel) => {
758
+ return await JointHirer.init(hirer.HirerId, dbTransaction);
759
+ }),
760
+ );
761
+ return jointHirerInstances;
762
+ } catch (error) {
763
+ console.error('Error in getJointHirers:', error);
764
+ throw error;
765
+ }
766
+ }
767
+
768
+ async getCustomerIds(loginUser: LoginUser, dbTransaction: any) {
769
+ try {
770
+ // Part 1: Privilege Checking
771
+ // Call loginUser.checkPrivileges() by passing:
772
+ // SystemCode: <get_from_app_config>
773
+ // PrivilegeCode: "RENTAL_VIEW"
774
+ const systemCode =
775
+ ApplicationConfig.getComponentConfigValue('system-code');
776
+ const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
777
+
778
+ if (!isPrivileged) {
779
+ throw new ClassError(
780
+ 'Rental',
781
+ 'RentalErrMsg01',
782
+ "You do not have 'Rental - View' privilege.",
783
+ );
784
+ }
785
+
786
+ // Part 2: Validation
787
+ // Make sure this.RentalId exists, if not throw new ClassError by passing
788
+ if (!this.RentalId) {
789
+ throw new ClassError('Rental', 'RentalErrMsg01', 'RentalId is missing');
790
+ }
791
+
792
+ //Part 3: Retrieve Listing and Returns
793
+ // 3.1 Call Rental._JointHirerRepo findAll method by passing:
794
+ // where:
795
+ // RentalId: this.RentalId
796
+ // dbTransaction
797
+ const options: any = {
798
+ where: {
799
+ RentalId: this.RentalId,
800
+ Status: 'Active',
801
+ },
802
+ transaction: dbTransaction,
803
+ };
804
+
805
+ const joinHirers = await Rental._JointHirerRepo.findAll(options);
806
+
807
+ // 3.2 Create a new array variable and set its value to joint hirer and Rental.CustomerId
808
+ // array translated to only CustomerId. Then, append this.CustomerId to the array.
809
+ const customerIds: string[] = [this.CustomerId];
810
+ for (let i = 0; i < joinHirers.length; i++) {
811
+ const jointHirer = joinHirers[i];
812
+ customerIds.push(jointHirer.CustomerId);
813
+ }
814
+
815
+ // 3.3 Return the array.
816
+ return customerIds;
817
+ } catch (error) {
818
+ throw error;
819
+ }
820
+ }
821
+
822
+ async signAgreement(
823
+ loginUser: LoginUser,
824
+ party: string,
825
+ PartyId: string,
826
+ PartyType: string,
827
+ dbTransaction: any,
828
+ statusAfterSign?: RentalStatusEnum | string,
829
+ method?: AgreementSignatureVerificationMethodEnum,
830
+ justification?: string,
831
+ ) {
832
+ try {
833
+ // Part 1: Privilege Checking
834
+ // Call loginUser.checkPrivileges() by passing:
835
+ // SystemCode: <get_from_app_config>
836
+ // PrivilegeCode: "RENTAL_SIGN"
837
+ const systemCode =
838
+ ApplicationConfig.getComponentConfigValue('system-code');
839
+ const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_SIGN');
840
+
841
+ if (!isPrivileged) {
842
+ throw new ClassError(
843
+ 'Rental',
844
+ 'RentalErrMsg01',
845
+ "You do not have 'RENTAL_SIGN' privilege.",
846
+ );
847
+ }
848
+
849
+ // Part 2: Validation
850
+ // Make sure this.Status is "Pending Signing" and agreementStatus is "Generated", if not throw new ClassError
851
+ const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
852
+ if (
853
+ this.Status !== RentalStatusEnum.PENDING_SIGNING &&
854
+ agreement.Status !== AggrementStatusEnum.GENERATED
855
+ ) {
856
+ throw new ClassError(
857
+ 'Rental',
858
+ 'RentalErrMsg01',
859
+ 'Rental is not ready to be signed yet.',
860
+ );
861
+ }
862
+
863
+ //Make sure CustomerId inside the listing and SignatureStatus is "Pending"
864
+ const signatureList = await Agreement.getSignatureList(
865
+ loginUser,
866
+ this.AgreementNo,
867
+ dbTransaction,
868
+ );
869
+ const customerSignature = signatureList.find(
870
+ (sig) =>
871
+ sig.PartyId === PartyId &&
872
+ sig.PartyType === PartyType &&
873
+ sig.Party === party &&
874
+ sig.SignatureStatus === AgreementSignatureStatusEnum.PENDING,
875
+ );
876
+
877
+ if (!customerSignature) {
878
+ throw new ClassError(
879
+ 'Rental',
880
+ 'RentalErrMsg01',
881
+ 'Signature is not pending.',
882
+ );
883
+ }
884
+
885
+ //Update rental_AgreementSignature
886
+ const signaturePayload = {
887
+ SignatureStatus: AgreementSignatureStatusEnum.SIGNED,
888
+ SignedAt: new Date(),
889
+ UpdatedById: loginUser.ObjectId,
890
+ UpdatedAt: new Date(),
891
+ VerificationMethod: method,
892
+ VerificationJustification: justification,
893
+ VerifiedById: loginUser.ObjectId,
894
+ };
895
+
896
+ await Rental._AgreementSignatureRepo.update(signaturePayload, {
897
+ where: {
898
+ AgreementNo: this.AgreementNo,
899
+ Party: party,
900
+ PartyId: PartyId,
901
+ PartyType: PartyType,
902
+ },
903
+ transaction: dbTransaction,
904
+ });
905
+
906
+ const signatureActivity = new Activity();
907
+ signatureActivity.ActivityId = signatureActivity.createId();
908
+ signatureActivity.Action = ActionEnum.UPDATE;
909
+ signatureActivity.Description = 'Update hirer signature';
910
+ signatureActivity.EntityType = 'AgreementSignature';
911
+ signatureActivity.EntityId = this.AgreementNo;
912
+ signatureActivity.EntityValueBefore = JSON.stringify(customerSignature);
913
+ signatureActivity.EntityValueAfter = JSON.stringify(signaturePayload);
914
+ await signatureActivity.create(loginUser.ObjectId, dbTransaction);
915
+
916
+ const updatedSignatureList = await Agreement.getSignatureList(
917
+ loginUser,
918
+ this.AgreementNo,
919
+ dbTransaction,
920
+ );
921
+
922
+ const pendingSignatures = updatedSignatureList.filter(
923
+ (sig) => sig.SignatureStatus === 'Pending',
924
+ );
925
+ if (pendingSignatures.length > 0) {
926
+ return;
927
+ }
928
+
929
+ //Part 3: Update Agreement Status
930
+ // 3.1 Set EntityValueBefore to current agreement instance.
931
+ const entityValueAgreementBefore = {
932
+ AgreementNo: agreement.AgreementNo,
933
+ Status: agreement.Status,
934
+ DateSigned: agreement.DateSigned,
935
+ };
936
+
937
+ // 3.2 Set below agreement attributes:
938
+ // DateSigned: current date & time
939
+ // Status: "Signed"
940
+ const payload = {
941
+ DateSigned: new Date(),
942
+ Status: AggrementStatusEnum.SIGNED,
943
+ };
944
+
945
+ // 3.3 Call Rental._AgreementRepo update()
946
+ await Rental._AgreementRepo.update(payload, {
947
+ where: {
948
+ AgreementNo: this.AgreementNo,
949
+ },
950
+ transaction: dbTransaction,
951
+ });
952
+
953
+ const entityValueAgreementAfter = {
954
+ entityValueAgreementBefore,
955
+ ...payload,
956
+ };
957
+
958
+ // Part 4: Record Update Agreement Activity
959
+ const activity = new Activity();
960
+ activity.ActivityId = activity.createId();
961
+ activity.Action = ActionEnum.UPDATE;
962
+ activity.Description = 'Update agreement';
963
+ activity.EntityType = 'RentalAgreement';
964
+ activity.EntityId = this.AgreementNo;
965
+ activity.EntityValueBefore = JSON.stringify(entityValueAgreementBefore);
966
+ activity.EntityValueAfter = JSON.stringify(entityValueAgreementAfter);
967
+ await activity.create(loginUser.ObjectId, dbTransaction);
968
+
969
+ // Part 5: Update Rental Status
970
+ // 5.1 Set EntityValueBefore to current rental instance.
971
+ const entityValueRentalBefore = {
972
+ RentalId: this.RentalId,
973
+ CustomerId: this.CustomerId,
974
+ CustomerType: this.CustomerType,
975
+ ItemId: this.ItemId,
976
+ ItemType: this.ItemType,
977
+ PriceId: this.PriceId,
978
+ StartDateTime: this.StartDateTime,
979
+ EndDateTime: this.EndDateTime,
980
+ CancelRemarks: this.CancelRemarks,
981
+ TerminateRemarks: this.TerminateRemarks,
982
+ AgreementNo: this.AgreementNo,
983
+ AccountType: this.AccountType,
984
+ Status: this.Status,
985
+ EscheatmentYN: this.EscheatmentYN,
986
+ CreatedById: this.CreatedById,
987
+ CreatedAt: this.CreatedAt,
988
+ UpdatedById: this.UpdatedById,
989
+ UpdatedAt: this.UpdatedAt,
990
+ };
991
+ // 5.2: Set below rental attributes:
992
+ // Status: "Pending Key Collection"
993
+ // UpdatedById: loginUser.ObjectId
994
+ // UpdatedAt: current date & time
995
+
996
+ this._Status = statusAfterSign
997
+ ? statusAfterSign
998
+ : RentalStatusEnum.ACTIVE;
999
+
1000
+ const payloadRental = {
1001
+ Status: this._Status,
1002
+ UpdatedById: loginUser.ObjectId,
1003
+ UpdatedAt: new Date(),
1004
+ };
1005
+
1006
+ // 5.3: Call Rental._Repo update() method by passing:
1007
+ // populate rental instance attributes
1008
+ // dbTransaction
1009
+ await Rental._Repo.update(payloadRental, {
1010
+ where: {
1011
+ RentalId: this.RentalId,
1012
+ },
1013
+ transaction: dbTransaction,
1014
+ });
1015
+
1016
+ const entityValueRentalAfter = {
1017
+ entityValueRentalBefore,
1018
+ ...payloadRental,
1019
+ };
1020
+
1021
+ // Part 6: Record Update Agreement Activity
1022
+ const rentalActivity = new Activity();
1023
+ rentalActivity.ActivityId = activity.createId();
1024
+ rentalActivity.Action = ActionEnum.UPDATE;
1025
+ rentalActivity.Description = 'Sign rental agreement';
1026
+ rentalActivity.EntityType = 'Rental';
1027
+ rentalActivity.EntityId = this.RentalId;
1028
+ rentalActivity.EntityValueBefore = JSON.stringify(
1029
+ entityValueRentalBefore,
1030
+ );
1031
+ rentalActivity.EntityValueAfter = JSON.stringify(entityValueRentalAfter);
1032
+ await rentalActivity.create(loginUser.ObjectId, dbTransaction);
1033
+ } catch (error) {
1034
+ throw error;
1035
+ }
1036
+ }
1037
+
1038
+ async generateAgreement(
1039
+ loginUser: LoginUser,
1040
+ dbTransaction: any,
1041
+ MediaId: string,
1042
+ ) {
1043
+ //This method will generate a new rental agreement.
1044
+ try {
1045
+ // Part 1: Privilege Checking
1046
+ // Call loginUser.checkPrivileges() by passing:
1047
+ // SystemCode: "<get_from_app_config>"
1048
+ // PrivilegeCode: "RENTAL_AGREEMENT_CREATE"
1049
+
1050
+ const systemCode =
1051
+ ApplicationConfig.getComponentConfigValue('system-code');
1052
+ const isPrivileged = loginUser.checkPrivileges(
1053
+ systemCode,
1054
+ 'RENTAL_AGREEMENT_CREATE',
1055
+ );
1056
+
1057
+ if (!isPrivileged) {
1058
+ throw new ClassError(
1059
+ 'Rental',
1060
+ 'RentalErrMsg01',
1061
+ "You do not have 'RENTAL_AGREEMENT_CREATE' privilege.",
1062
+ );
1063
+ }
1064
+
1065
+ // Part 2: Validation
1066
+ // Instantiate existing RentalAgreement by passing:
1067
+ // dbTransaction
1068
+ // AgreementNo: this.AgreementNo
1069
+ // Make sure Params.MediaId exists, if not throw new ClassError by passing:
1070
+ // Classname: "Rental"
1071
+ // MessageCode: "RentalErrMsg0X"
1072
+ // Message: "MediaId is required."
1073
+ const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
1074
+ if (!MediaId) {
1075
+ throw new ClassError(
1076
+ 'Rental',
1077
+ 'RentalErrMsg0X',
1078
+ 'MediaId is required.',
1079
+ );
1080
+ }
1081
+
1082
+ // Part 3: Update Agreement Record
1083
+ // Set EntityValueBefore to the agreement instance.
1084
+ // Set below agreement attributes:
1085
+ // Status: "Generated"
1086
+ // MediaId: Params.MediaId
1087
+
1088
+ const EntityValueBefore = agreement;
1089
+ agreement.Status = AggrementStatusEnum.GENERATED;
1090
+ agreement.MediaId = MediaId;
1091
+
1092
+ // Call Rental._AgreementRepo update method by passing:
1093
+ // Status: agreement.Status
1094
+ // MediaId: agreement.MediaId
1095
+ // dbTransaction
1096
+
1097
+ await Rental._AgreementRepo.update(
1098
+ {
1099
+ Status: agreement.Status,
1100
+ MediaId: agreement.MediaId,
1101
+ },
1102
+ {
1103
+ where: {
1104
+ AgreementNo: this.AgreementNo,
1105
+ },
1106
+ transaction: dbTransaction,
1107
+ },
1108
+ );
1109
+
1110
+ // Part 4: Record Update Agreement Activity
1111
+ // Initialise EntityValueAfter variable and set to agreement instance
1112
+ const EntityValueAfter = agreement;
1113
+ // Instantiate new activity from Activity class, call createId() method, then set:
1114
+ const activity = new Activity();
1115
+ // Action: ActionEnum.UPDATE
1116
+ // Description: "Generate agreement."
1117
+ // EntityType: "RentalAgreement"
1118
+ // EntityId: this.AgreementNo
1119
+ // EntityValueBefore: EntityValueBefore
1120
+ // EntityValueAfter: EntityValueAfter
1121
+ activity.ActivityId = activity.createId();
1122
+ activity.Action = ActionEnum.UPDATE;
1123
+ activity.Description = 'Generate agreement.';
1124
+ activity.EntityType = 'RentalAgreement';
1125
+ activity.EntityId = this.AgreementNo;
1126
+ activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
1127
+ activity.EntityValueAfter = JSON.stringify(EntityValueAfter);
1128
+
1129
+ // Call new activity create method by passing:
1130
+ // dbTransaction
1131
+ // userId: loginUser.ObjectId
1132
+ await activity.create(loginUser.ObjectId, dbTransaction);
1133
+
1134
+ // Part 5: Add Record To Agreement History Table
1135
+ // Make sure there is a private static readonly _AgreementHistoryRepo variable.
1136
+ // Set newRecord to an object with below key-value:
1137
+ // AgreementNo: this.AgreementNo
1138
+ // MediaId: params.MediaId
1139
+ // ActivityCompleted: "Agreement Generated"
1140
+ // CreatedAt: Current Timestamp
1141
+ // Use _AgreementHistoryRepo.create() method and pass newRecord and dbTransaction.
1142
+ const agreementHistory = await Rental._AgreementHistoryRepo.create(
1143
+ {
1144
+ AgreementNo: this.AgreementNo,
1145
+ MediaId,
1146
+ ActivityCompleted: 'Agreement Generated',
1147
+ CreatedAt: new Date(),
1148
+ },
1149
+ { transaction: dbTransaction },
1150
+ );
1151
+
1152
+ // Part 6: Record Agreement History Activity
1153
+ // Initialise EntityValueAfter variable and set to newRecord from previous part.
1154
+ // Instantiate new activity from Activity class, call createId() method, then set:
1155
+ // Action: ActionEnum.CREATE
1156
+ // Description: "Generate agreement."
1157
+ // EntityType: "RentalAgreementHistory"
1158
+ // EntityId: HistoryId from previous part.
1159
+ // EntityValueBefore: empty object.
1160
+ // EntityValueAfter: EntityValueAfter
1161
+ // Call new activity create method by passing:
1162
+ // dbTransaction
1163
+ // userId: loginUser.ObjectId
1164
+ const entityValueAfter = agreementHistory.get({ plain: true });
1165
+ const activityHistory = new Activity();
1166
+ activityHistory.ActivityId = activityHistory.createId();
1167
+ activityHistory.Action = ActionEnum.CREATE;
1168
+ activityHistory.Description = 'Generate agreement.';
1169
+ activityHistory.EntityType = 'RentalAgreementHistory';
1170
+ activityHistory.EntityId = agreementHistory.HistoryId.toString();
1171
+ activityHistory.EntityValueBefore = JSON.stringify({});
1172
+ activityHistory.EntityValueAfter = JSON.stringify(entityValueAfter);
1173
+ await activityHistory.create(loginUser.ObjectId, dbTransaction);
1174
+ } catch (error) {
1175
+ throw error;
1176
+ }
1177
+ }
1178
+
1179
+ public toJSON(): IRentalAttr {
1180
+ return {
1181
+ RentalId: this.RentalId,
1182
+ CustomerId: this.CustomerId,
1183
+ CustomerType: this.CustomerType,
1184
+ ItemId: this.ItemId,
1185
+ ItemType: this.ItemType,
1186
+ PriceId: this.PriceId,
1187
+ StartDateTime: this.StartDateTime,
1188
+ EndDateTime: this.EndDateTime,
1189
+ Status: this.Status,
1190
+ CancelRemarks: this.CancelRemarks,
1191
+ TerminateRemarks: this.TerminateRemarks,
1192
+ EscheatmentYN: this.EscheatmentYN,
1193
+ AgreementNo: this.AgreementNo,
1194
+ AccountType: this.AccountType,
1195
+ CreatedById: this.CreatedById,
1196
+ CreatedAt: this.CreatedAt,
1197
+ UpdatedById: this.UpdatedById,
1198
+ UpdatedAt: this.UpdatedAt,
1199
+ };
1200
+ }
1201
+
1202
+ public async getInvoices(
1203
+ loginUser: LoginUser,
1204
+ dbTransaction: any,
1205
+ accountingSystem?: IAccountSystem,
1206
+ ) {
1207
+ try {
1208
+ // Part 1: Privilege Checking
1209
+ // Call loginUser.checkPrivileges() by passing:
1210
+ // SystemCode: <get_from_app_config>
1211
+ // PrivilegeCode: "RENTAL_VIEW_INVOICE"
1212
+ const systemCode =
1213
+ ApplicationConfig.getComponentConfigValue('system-code');
1214
+ const isPrivileged = loginUser.checkPrivileges(
1215
+ systemCode,
1216
+ 'RENTAL_VIEW_INVOICE',
1217
+ );
1218
+
1219
+ if (!isPrivileged) {
1220
+ throw new ClassError(
1221
+ 'Rental',
1222
+ 'RentalErrMsg01',
1223
+ "You do not have 'RENTAL_VIEW_INVOICE' privilege.",
1224
+ );
1225
+ }
1226
+
1227
+ // Part 2: Validation
1228
+ // Make sure this._ObjectId exists, if not throw new ClassError by passing
1229
+ if (!this.ObjectId) {
1230
+ throw new ClassError(
1231
+ 'Rental',
1232
+ 'RentalErrMsg01',
1233
+ 'ObjectId is missing.',
1234
+ );
1235
+ }
1236
+
1237
+ // Part 3: Check Existing Invoices
1238
+ if (this.Invoices.length > 0) {
1239
+ return this.Invoices;
1240
+ }
1241
+
1242
+ // Part 4: Retrieve Invoices and Returns
1243
+ const search = {
1244
+ DocType: DocType.INVOICE,
1245
+ RelatedObjectId: this.ObjectId,
1246
+ RelatedObjectType: this.ObjectType,
1247
+ };
1248
+
1249
+ const documents = await Document.findAll(
1250
+ loginUser,
1251
+ dbTransaction,
1252
+ undefined,
1253
+ undefined,
1254
+ search,
1255
+ undefined,
1256
+ accountingSystem,
1257
+ );
1258
+
1259
+ this.Invoices = documents.rows.map((doc) => {
1260
+ const document = new Document(dbTransaction, doc);
1261
+ return document;
1262
+ });
1263
+
1264
+ return this.Invoices;
1265
+ } catch (error) {
1266
+ throw error;
1267
+ }
1268
+ }
1269
+
1270
+ public async getItem(dbTransaction: any): Promise<any> {
1271
+ if (this.Item) {
1272
+ return this.Item;
1273
+ } else {
1274
+ const ItemClass = ItemClassMap[this.ItemType];
1275
+
1276
+ if (!ItemClass || typeof ItemClass.init !== 'function') {
1277
+ throw new Error(`Invalid or unregistered ItemType: ${this.ItemType}`);
1278
+ }
1279
+
1280
+ ItemClass._Repo = ItemClass._Repo || ItemClass.getRepo();
1281
+
1282
+ if (!ItemClass._Repo) {
1283
+ throw new Error(
1284
+ `ItemType ${this.ItemType} does not have a repository.`,
1285
+ );
1286
+ }
1287
+
1288
+ const itemInstance = await ItemClass.init(this.ItemId, dbTransaction);
1289
+
1290
+ if (!itemInstance) {
1291
+ throw new Error(`${this.ItemType} not found with ID ${this.ItemId}`);
1292
+ }
1293
+ this.Item = itemInstance;
1294
+
1295
+ // Make sure item has setAvailable method
1296
+ if (typeof this.Item.setAvailable !== 'function') {
1297
+ throw new Error(`${this.ItemType} does not implement setAvailable()`);
1298
+ }
1299
+ return this.Item;
1300
+ }
1301
+ }
1302
+
1303
+ public async cancel(
1304
+ loginUser: LoginUser,
1305
+ dbTransaction: any,
1306
+ remarks: string,
1307
+ ): Promise<Rental> {
1308
+ try {
1309
+ // Part 1: Privilege Checking
1310
+ const systemCode =
1311
+ ApplicationConfig.getComponentConfigValue('system-code');
1312
+ const isPrivileged = await loginUser.checkPrivileges(
1313
+ systemCode,
1314
+ 'RENTAL_CANCEL',
1315
+ );
1316
+
1317
+ if (!isPrivileged) {
1318
+ throw new ClassError(
1319
+ 'Rental',
1320
+ 'RentalErrMsg01',
1321
+ "You do not have 'RENTAL_CANCEL' privilege.",
1322
+ );
1323
+ }
1324
+
1325
+ // Part 2: Validation
1326
+ if (!this.ObjectId) {
1327
+ throw new ClassError(
1328
+ 'Rental',
1329
+ 'RentalErrMsg01',
1330
+ 'Rental not found. Rental Id is empty.',
1331
+ );
1332
+ }
1333
+
1334
+ if (
1335
+ this.Status === RentalStatusEnum.ACTIVE ||
1336
+ this.Status === RentalStatusEnum.SUSPENDED
1337
+ ) {
1338
+ throw new ClassError(
1339
+ 'Rental',
1340
+ 'RentalErrMsg01',
1341
+ `Rental already ${this.Status}, please do termination process.`,
1342
+ );
1343
+ }
1344
+
1345
+ if (!remarks) {
1346
+ throw new ClassError(
1347
+ 'Rental',
1348
+ 'RentalErrMsg01',
1349
+ 'Cancellation remarks is required.',
1350
+ );
1351
+ }
1352
+
1353
+ // Part 3: Capture Record Info Before Changes
1354
+ const entityValueBefore = this.toJSON();
1355
+
1356
+ // Part 4: Update Rental Status and Remarks
1357
+ this._Status = RentalStatusEnum.CANCELLED;
1358
+ this.CancelRemarks = remarks;
1359
+ this._UpdatedById = loginUser.ObjectId;
1360
+ this._UpdatedAt = new Date();
1361
+
1362
+ // Part 5: Update Rental Record
1363
+ await Rental._Repo.update(
1364
+ {
1365
+ Status: this.Status,
1366
+ CancelRemarks: this.CancelRemarks,
1367
+ UpdatedById: this.UpdatedById,
1368
+ UpdatedAt: this.UpdatedAt,
1369
+ },
1370
+ {
1371
+ where: {
1372
+ RentalId: this.RentalId,
1373
+ },
1374
+ transaction: dbTransaction,
1375
+ },
1376
+ );
1377
+
1378
+ // Part 6: Mark Item as Available
1379
+ const item = await this.getItem(dbTransaction);
1380
+
1381
+ await item.setAvailable(loginUser, dbTransaction);
1382
+
1383
+ // Part 7: Capture Record Info After Changes
1384
+ const entityValueAfter = this.toJSON();
1385
+ // Part 8: Record Activity
1386
+ const activity = new Activity();
1387
+ activity.ActivityId = activity.createId();
1388
+ activity.Action = ActionEnum.UPDATE;
1389
+ activity.Description = 'Rental Cancellation';
1390
+ activity.EntityType = this.ObjectType;
1391
+ activity.EntityId = this.ObjectId;
1392
+ activity.EntityValueBefore = JSON.stringify(entityValueBefore);
1393
+ activity.EntityValueAfter = JSON.stringify(entityValueAfter);
1394
+ await activity.create(loginUser.ObjectId, dbTransaction);
1395
+ return this;
1396
+ } catch (error) {
1397
+ throw error;
1398
+ }
1399
+ }
1400
+
1401
+ public async update(
1402
+ loginUser: LoginUser,
1403
+ dbTransaction: any,
1404
+ updates: Partial<IRentalAttr>,
1405
+ ): Promise<Rental> {
1406
+ try {
1407
+ // Part 1: Privilege Checking
1408
+ const systemCode =
1409
+ ApplicationConfig.getComponentConfigValue('system-code');
1410
+ const isPrivileged = await loginUser.checkPrivileges(
1411
+ systemCode,
1412
+ 'RENTAL_UPDATE',
1413
+ );
1414
+
1415
+ if (!isPrivileged) {
1416
+ throw new ClassError(
1417
+ 'Rental',
1418
+ 'RentalErrMsg01',
1419
+ "You do not have 'RENTAL_UPDATE' privilege.",
1420
+ );
1421
+ }
1422
+
1423
+ // Part 2: Validation
1424
+ if (!this.ObjectId) {
1425
+ throw new ClassError(
1426
+ 'Rental',
1427
+ 'RentalErrMsg01',
1428
+ 'Rental not found. Rental Id is empty.',
1429
+ );
1430
+ }
1431
+
1432
+ // Part 3: Capture Record Info Before Changes
1433
+ const entityValueBefore = this.toJSON();
1434
+
1435
+ // Part 4: Update Rental Record
1436
+ await Rental._Repo.update(updates, {
1437
+ where: {
1438
+ RentalId: this.RentalId,
1439
+ },
1440
+ transaction: dbTransaction,
1441
+ });
1442
+
1443
+ // Part 5: Update object instance properties and capture after changes
1444
+ Object.assign(this, updates);
1445
+ this._UpdatedById = loginUser.ObjectId;
1446
+ this._UpdatedAt = new Date();
1447
+
1448
+ const entityValueAfter = this.toJSON();
1449
+
1450
+ // Part 6: Record Activity
1451
+ const activity = new Activity();
1452
+ activity.ActivityId = activity.createId();
1453
+ activity.Action = ActionEnum.UPDATE;
1454
+ activity.Description = 'Update Rental';
1455
+ activity.EntityType = this.ObjectType;
1456
+ activity.EntityId = this.ObjectId;
1457
+ activity.EntityValueBefore = JSON.stringify(entityValueBefore);
1458
+ activity.EntityValueAfter = JSON.stringify(entityValueAfter);
1459
+ await activity.create(loginUser.ObjectId, dbTransaction);
1460
+
1461
+ return this;
1462
+ } catch (error) {
1463
+ throw error;
1464
+ }
1465
+ }
1466
+ }