@tomei/rental 0.17.9-dev.2 → 0.17.9-staging.2

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