@tomei/rental 0.16.1 → 0.16.3

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 (65) hide show
  1. package/.commitlintrc.json +22 -22
  2. package/.gitlab-ci.yml +16 -16
  3. package/.husky/commit-msg +15 -15
  4. package/.husky/pre-commit +7 -7
  5. package/.prettierrc +4 -4
  6. package/Jenkinsfile +51 -51
  7. package/README.md +8 -8
  8. package/dist/src/components/agreement/agreement.js +8 -8
  9. package/dist/src/components/agreement/agreement.js.map +1 -1
  10. package/dist/src/components/rental/rental.js +15 -15
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/eslint.config.mjs +58 -58
  13. package/jest.config.js +10 -10
  14. package/migrations/booking-table-migration.js +79 -79
  15. package/migrations/hirer-signature-table-migration.js +64 -64
  16. package/migrations/joint-hirer-table-migration.js +52 -52
  17. package/migrations/rental-aggreement-history-migration.js +41 -41
  18. package/migrations/rental-aggrement-table-migration.js +30 -30
  19. package/migrations/rental-price-table-migration.js +32 -32
  20. package/migrations/rental-table-migrations.js +96 -96
  21. package/package.json +75 -75
  22. package/sonar-project.properties +12 -12
  23. package/src/components/agreement/agreement.repository.ts +54 -54
  24. package/src/components/agreement/agreement.ts +180 -180
  25. package/src/components/agreement-history/agreement-history.repository.ts +54 -54
  26. package/src/components/agreement-history/agreement-history.ts +57 -57
  27. package/src/components/booking/booking.repository.ts +51 -51
  28. package/src/components/booking/booking.ts +492 -492
  29. package/src/components/hirer-signature/hirer-signature.repository.ts +54 -54
  30. package/src/components/hirer-signature/hirer-signature.ts +140 -140
  31. package/src/components/joint-hirer/joint-hirer.repository.ts +54 -54
  32. package/src/components/joint-hirer/joint-hirer.ts +137 -137
  33. package/src/components/rental/rental.repository.ts +51 -51
  34. package/src/components/rental/rental.ts +1116 -1116
  35. package/src/components/rental-price/rental-price.repository.ts +54 -54
  36. package/src/components/rental-price/rental-price.ts +100 -100
  37. package/src/database.ts +31 -31
  38. package/src/enum/account-type.enum.ts +4 -4
  39. package/src/enum/booking.enum.ts +5 -5
  40. package/src/enum/hirer-signature-status.enum.ts +4 -4
  41. package/src/enum/hirer-type.enum.ts +4 -4
  42. package/src/enum/index.ts +15 -15
  43. package/src/enum/rental-status.enum.ts +39 -39
  44. package/src/index.ts +36 -36
  45. package/src/interfaces/agreement-attr.interface.ts +7 -7
  46. package/src/interfaces/agreement-history-attr.interface.ts +7 -7
  47. package/src/interfaces/booking-attr.interface.ts +19 -19
  48. package/src/interfaces/booking-find-all-search-attr.interface.ts +12 -12
  49. package/src/interfaces/hirer-signature-attr.interface.ts +15 -15
  50. package/src/interfaces/index.ts +19 -19
  51. package/src/interfaces/joint-hirer-attr.interface.ts +10 -10
  52. package/src/interfaces/rental-attr.interface.ts +25 -25
  53. package/src/interfaces/rental-find-all-search-attr.interface.ts +11 -11
  54. package/src/interfaces/rental-price-attr.interface.ts +7 -7
  55. package/src/interfaces/response-hirer-signature-attr.interface.ts +15 -15
  56. package/src/models/agreement-history.entity.ts +51 -51
  57. package/src/models/agreement.entity.ts +47 -47
  58. package/src/models/booking.entity.ts +105 -105
  59. package/src/models/hirer-signature.entity.ts +81 -81
  60. package/src/models/index.ts +17 -17
  61. package/src/models/joint-hirer.entity.ts +63 -63
  62. package/src/models/rental-price.entity.ts +38 -38
  63. package/src/models/rental.entity.ts +133 -133
  64. package/tsconfig.build.json +5 -5
  65. package/tsconfig.json +24 -24
@@ -1,1116 +1,1116 @@
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 { HirerSignatureRepository } from '../hirer-signature/hirer-signature.repository';
22
- import { HirerSignatureStatusEnum } from '../../enum/hirer-signature-status.enum';
23
- import { HirerTypeEnum } from '../../enum/hirer-type.enum';
24
- import { AgreementHistoryRepository } from '../agreement-history/agreement-history.repository';
25
-
26
- export class Rental extends ObjectBase {
27
- ObjectId: string;
28
- ObjectName: string;
29
- ObjectType = 'Rental';
30
- TableName: string;
31
- CustomerId: string;
32
- CustomerType: string;
33
- ItemId: string;
34
- ItemType: string;
35
- PriceId: string;
36
- StartDateTime: Date;
37
- EndDateTime: Date;
38
- CancelRemarks: string;
39
- TerminateRemarks: string;
40
- AgreementNo: string;
41
- AccountType: RentalAccountTypeEnum;
42
- JointHirers: JointHirer[] = [];
43
- protected _Status: RentalStatusEnum;
44
- protected _EscheatmentYN: string = 'N';
45
- protected _CreatedById: string;
46
- protected _CreatedAt: Date;
47
- protected _UpdatedById: string;
48
- protected _UpdatedAt: Date;
49
- protected static _Repo = new RentalRepository();
50
- protected static _AgreementRepo = new AgreementRepository();
51
- protected static _JointHirerRepo = new JointHirerRepository();
52
- protected static _HirerSignatureRepo = new HirerSignatureRepository();
53
- protected static _AgreementHistoryRepo = new AgreementHistoryRepository();
54
-
55
- get RentalId(): string {
56
- return this.ObjectId;
57
- }
58
-
59
- set RentalId(value: string) {
60
- this.ObjectId = value;
61
- }
62
-
63
- get Status(): RentalStatusEnum {
64
- return this._Status;
65
- }
66
-
67
- get EscheatmentYN(): string {
68
- return this._EscheatmentYN;
69
- }
70
-
71
- get CreatedById(): string {
72
- return this._CreatedById;
73
- }
74
-
75
- get CreatedAt(): Date {
76
- return this._CreatedAt;
77
- }
78
-
79
- get UpdatedById(): string {
80
- return this._UpdatedById;
81
- }
82
-
83
- get UpdatedAt(): Date {
84
- return this._UpdatedAt;
85
- }
86
-
87
- private setTerminated() {
88
- this._Status = RentalStatusEnum.TERMINATED;
89
- }
90
-
91
- protected constructor(rentalAttr?: IRentalAttr) {
92
- super();
93
- if (rentalAttr) {
94
- this.RentalId = rentalAttr.RentalId;
95
- this.CustomerId = rentalAttr.CustomerId;
96
- this.CustomerType = rentalAttr.CustomerType;
97
- this.ItemId = rentalAttr.ItemId;
98
- this.ItemType = rentalAttr.ItemType;
99
- this.PriceId = rentalAttr.PriceId;
100
- this.StartDateTime = rentalAttr.StartDateTime;
101
- this.EndDateTime = rentalAttr.EndDateTime;
102
- this.CancelRemarks = rentalAttr.CancelRemarks;
103
- this.TerminateRemarks = rentalAttr.TerminateRemarks;
104
- this.AgreementNo = rentalAttr.AgreementNo;
105
- this.AccountType = rentalAttr.AccountType;
106
- this._Status = rentalAttr.Status;
107
- this._EscheatmentYN = rentalAttr.EscheatmentYN;
108
- this._CreatedById = rentalAttr.CreatedById;
109
- this._CreatedAt = rentalAttr.CreatedAt;
110
- this._UpdatedById = rentalAttr.UpdatedById;
111
- this._UpdatedAt = rentalAttr.UpdatedAt;
112
- }
113
- }
114
-
115
- public static async init(dbTransaction?: any, rentalId?: string) {
116
- try {
117
- if (rentalId) {
118
- const rental = await Rental._Repo.findByPk(rentalId, dbTransaction);
119
- if (rental) {
120
- return new Rental(rental);
121
- } else {
122
- throw new ClassError('Rental', 'RentalErrMsg00', 'Rental Not Found');
123
- }
124
- }
125
- return new Rental();
126
- } catch (error) {
127
- throw new ClassError(
128
- 'Rental',
129
- 'RentalErrMsg00',
130
- 'Failed To Initialize Price',
131
- );
132
- }
133
- }
134
-
135
- /**
136
- * Create a new Rental record.
137
- * @param {RentalPrice} rentalPrice - The rental pricing information.
138
- * @param {LoginUser} loginUser - The user initiating the creation.
139
- * @param {any} [dbTransaction] - Optional database transaction for atomic operations.
140
- * @throws {ClassError} Throws an error if:
141
- * 1. loginUser does not have 'Rental - Create' privilege.
142
- * 2. Rental item is not available at the current date.
143
- * @returns {Promise<any>} A Promise resolving to the created rental record.
144
- */
145
- public async create(
146
- rentalPrice: RentalPrice,
147
- loginUser: LoginUser,
148
- jointHirers?: JointHirer[],
149
- dbTransaction?: any,
150
- ): Promise<any> {
151
- try {
152
- /*Part 1: Check Privilege*/
153
- const systemCode =
154
- ApplicationConfig.getComponentConfigValue('system-code');
155
- const isPrivileged = await loginUser.checkPrivileges(
156
- systemCode,
157
- 'Rental - Create',
158
- );
159
-
160
- if (!isPrivileged) {
161
- throw new ClassError(
162
- 'Rental',
163
- 'RentalErrMsg01',
164
- "You do not have 'Rental - Create' privilege.",
165
- );
166
- }
167
-
168
- /*Part 2: Make Sure Rental Item Available*/
169
- const isItemAvailable = await Rental.isItemAvailable(
170
- this.ItemId,
171
- this.ItemType,
172
- this.StartDateTime,
173
- this.EndDateTime,
174
- dbTransaction,
175
- );
176
-
177
- if (!isItemAvailable) {
178
- throw new ClassError(
179
- 'Rental',
180
- 'RentalErrMsg02',
181
- 'Rental Item is not available at current date.',
182
- );
183
- }
184
-
185
- // Part 3: Create Rental Agreement Record
186
- // Call Rental._AgreementRepo create method by passing:
187
- // AgreementNo: this.AgreementNo
188
- // Status: "Not Generated"
189
- // dbTransaction
190
- await Rental._AgreementRepo.create(
191
- {
192
- AgreementNo: this.AgreementNo,
193
- Status: 'Not Generated',
194
- },
195
- {
196
- transaction: dbTransaction,
197
- },
198
- );
199
-
200
- //Create a record into rental_HirerSignature table
201
- //Create the main customer's signature record
202
- await Rental._HirerSignatureRepo.create(
203
- {
204
- HirerSignatureId: this.createId(),
205
- AgreementNo: this.AgreementNo,
206
- HirerType: HirerTypeEnum.PRIMARY,
207
- CustomerId: this.CustomerId,
208
- CustomerType: 'Customer',
209
- SignatureStatus: HirerSignatureStatusEnum.PENDING,
210
- SignedAt: new Date(),
211
- CreatedById: loginUser.ObjectId,
212
- CreatedAt: new Date(),
213
- UpdatedById: loginUser.ObjectId,
214
- UpdatedAt: new Date(),
215
- },
216
- { transaction: dbTransaction },
217
- );
218
-
219
- // Create signatures record for joint hirers if any
220
- if (jointHirers && jointHirers.length > 0) {
221
- for (const jointHirer of jointHirers) {
222
- await Rental._HirerSignatureRepo.create(
223
- {
224
- HirerSignatureId: this.createId(),
225
- AgreementNo: this.AgreementNo,
226
- HirerType: HirerTypeEnum.JOINT,
227
- CustomerId: jointHirer.CustomerId,
228
- CustomerType: 'SDBCustomer',
229
- SignatureStatus: HirerSignatureStatusEnum.PENDING,
230
- SignedAt: new Date(),
231
- CreatedById: loginUser.ObjectId,
232
- CreatedAt: new Date(),
233
- UpdatedById: loginUser.ObjectId,
234
- UpdatedAt: new Date(),
235
- },
236
- { transaction: dbTransaction },
237
- );
238
- }
239
- }
240
-
241
- /*Part 4: Insert Rental & The Agreed Rental Price*/
242
- if (!rentalPrice.PriceId) {
243
- const rentalPriceData = await rentalPrice.create(
244
- loginUser,
245
- dbTransaction,
246
- );
247
- this.PriceId = rentalPriceData.PriceId;
248
- } else {
249
- this.PriceId = rentalPrice.PriceId;
250
- }
251
- this.RentalId = this.createId();
252
- // if (!this.Status) {
253
- this._Status = RentalStatusEnum.PENDING_SIGNING;
254
- // }
255
- this._CreatedById = loginUser.ObjectId;
256
- this._UpdatedById = loginUser.ObjectId;
257
- this._CreatedAt = new Date();
258
- this._UpdatedAt = new Date();
259
-
260
- const data = {
261
- RentalId: this.RentalId,
262
- CustomerId: this.CustomerId,
263
- CustomerType: this.CustomerType,
264
- ItemId: this.ItemId,
265
- ItemType: this.ItemType,
266
- PriceId: this.PriceId,
267
- StartDateTime: this.StartDateTime,
268
- EndDateTime: this.EndDateTime,
269
- CancelRemarks: this.CancelRemarks,
270
- TerminateRemarks: this.TerminateRemarks,
271
- AgreementNo: this.AgreementNo,
272
- AccountType: this.AccountType,
273
- Status: this.Status,
274
- EscheatmentYN: this.EscheatmentYN,
275
- CreatedById: this.CreatedById,
276
- CreatedAt: this.CreatedAt,
277
- UpdatedById: this.UpdatedById,
278
- UpdatedAt: this.UpdatedAt,
279
- };
280
-
281
- await Rental._Repo.create(data, {
282
- transaction: dbTransaction,
283
- });
284
-
285
- //For every data inside Param.jointHirers, update the rentalId and call jointHirer.create
286
- if (jointHirers) {
287
- for (let i = 0; i < jointHirers.length; i++) {
288
- jointHirers[i].RentalId = this.RentalId;
289
- await jointHirers[i].create(loginUser, dbTransaction);
290
- this.JointHirers.push(jointHirers[i]);
291
- }
292
- }
293
-
294
- /*Part 5: Record Create Rental Activity*/
295
- const activity = new Activity();
296
- activity.ActivityId = activity.createId();
297
- activity.Action = ActionEnum.CREATE;
298
- activity.Description = 'Add Rental';
299
- activity.EntityType = 'Rental';
300
- activity.EntityId = this.RentalId;
301
- activity.EntityValueBefore = JSON.stringify({});
302
- activity.EntityValueAfter = JSON.stringify(data);
303
- await activity.create(loginUser.ObjectId, dbTransaction);
304
-
305
- /*Part 6: Return Result*/
306
- return this;
307
- } catch (error) {
308
- throw error;
309
- }
310
- }
311
-
312
- public static async isItemAvailable(
313
- itemId: string,
314
- itemType: string,
315
- startDateTime: Date,
316
- endDateTime: Date,
317
- dbTransaction?: any,
318
- ) {
319
- try {
320
- const item = await Rental._Repo.findOne({
321
- where: {
322
- ItemId: itemId,
323
- ItemType: itemType,
324
- StartDateTime: {
325
- [Op.lte]: endDateTime,
326
- },
327
- EndDateTime: {
328
- [Op.gte]: startDateTime,
329
- },
330
- },
331
- transaction: dbTransaction,
332
- });
333
-
334
- if (item) {
335
- return false;
336
- } else {
337
- return true;
338
- }
339
- } catch (error) {
340
- throw error;
341
- }
342
- }
343
-
344
- public static async findAll(
345
- loginUser: LoginUser,
346
- dbTransaction: any,
347
- page?: number,
348
- row?: number,
349
- search?: IRentalFindAllSearchAttr,
350
- ) {
351
- try {
352
- const systemCode =
353
- await ApplicationConfig.getComponentConfigValue('system-code');
354
-
355
- const isPrivileged = await loginUser.checkPrivileges(
356
- systemCode,
357
- 'Rental - View',
358
- );
359
-
360
- if (!isPrivileged) {
361
- throw new ClassError(
362
- 'Rental',
363
- 'RentalErrMsg01',
364
- "You do not have 'Rental - View' privilege.",
365
- );
366
- }
367
-
368
- const queryObj: any = {};
369
-
370
- let options: any = {
371
- transaction: dbTransaction,
372
- order: [['CreatedAt', 'DESC']],
373
- };
374
-
375
- if (page && row) {
376
- options = {
377
- ...options,
378
- limit: row,
379
- offset: row * (page - 1),
380
- };
381
- }
382
-
383
- if (search) {
384
- Object.entries(search).forEach(([key, value]) => {
385
- if (key === 'StartDateTime') {
386
- queryObj[key] = {
387
- [Op.gte]: value,
388
- };
389
- } else if (key === 'EndDateTime') {
390
- queryObj[key] = {
391
- [Op.lte]: value,
392
- };
393
- } else if (key === 'CustomerId') {
394
- queryObj[key] = {
395
- [Op.substring]: value,
396
- };
397
-
398
- options.include = [
399
- {
400
- model: JointHirerModel,
401
- required: false,
402
- where: {
403
- CustomerId: {
404
- [Op.substring]: value,
405
- },
406
- },
407
- },
408
- ];
409
- } else {
410
- queryObj[key] = {
411
- [Op.substring]: value,
412
- };
413
- }
414
- });
415
- if (options?.include?.length > 1) {
416
- options.include.push({
417
- model: AgreementModel,
418
- required: false,
419
- });
420
- } else {
421
- options.include = [
422
- {
423
- model: AgreementModel,
424
- required: false,
425
- },
426
- ];
427
- }
428
-
429
- options = {
430
- ...options,
431
- where: queryObj,
432
- };
433
- }
434
-
435
- return await Rental._Repo.findAndCountAll(options);
436
- } catch (err) {
437
- throw err;
438
- }
439
- }
440
-
441
- public static async checkActiveByItemId(
442
- loginUser: LoginUser,
443
- itemId: string,
444
- itemType: string,
445
- dbTransaction?: any,
446
- ) {
447
- try {
448
- const systemCode =
449
- await ApplicationConfig.getComponentConfigValue('system-code');
450
-
451
- const isPrivileged = await loginUser.checkPrivileges(
452
- systemCode,
453
- 'Rental - View',
454
- );
455
-
456
- if (!isPrivileged) {
457
- throw new ClassError(
458
- 'Rental',
459
- 'RentalErrMsg01',
460
- "You do not have 'Rental - View' privilege.",
461
- );
462
- }
463
-
464
- const queryObj: any = {
465
- ItemId: itemId,
466
- ItemType: itemType,
467
- Status: RentalStatusEnum.ACTIVE,
468
- };
469
-
470
- const options: any = {
471
- transaction: dbTransaction,
472
- where: queryObj,
473
- };
474
-
475
- const rental = await Rental._Repo.findOne(options);
476
-
477
- if (rental) {
478
- return true;
479
- } else {
480
- return false;
481
- }
482
- } catch (err) {
483
- throw err;
484
- }
485
- }
486
-
487
- /**
488
- * Sets the StartDateTime property of the Rental class as custom setter.
489
- * @param {Date} startDateTime - The start date and time of the rental.
490
- * @throws {ClassError} Throws an error if:
491
- * 1. startDateTime is a past date.
492
- * 2. startDateTime is more than the EndDateTime.
493
- * @returns {void}
494
- */
495
- setStartDateTime(startDateTime: Date): void {
496
- const startDateTimeObject = new Date(startDateTime);
497
- startDateTimeObject.setHours(0, 0, 0, 0);
498
-
499
- /*Part 1: Make Sure Future Date Time*/
500
- if (startDateTimeObject < new Date(new Date().setHours(0, 0, 0, 0))) {
501
- throw new ClassError(
502
- 'Rental',
503
- 'RentalErrMsg02',
504
- 'StartDateTime cannot be a past datetime.',
505
- );
506
- }
507
-
508
- /*Part 2: Make Sure Less Than End Date Time*/
509
- if (this.EndDateTime && startDateTimeObject > this.EndDateTime) {
510
- throw new ClassError(
511
- 'Rental',
512
- 'RentalErrMsg03',
513
- 'StartDateTime cannot be more than EndDateTime.',
514
- );
515
- }
516
-
517
- /*Part 3: Set Status Whether Reserved or Active*/
518
- if (startDateTimeObject > new Date(new Date().setHours(0, 0, 0, 0))) {
519
- this._Status = RentalStatusEnum.RESERVED;
520
- } else {
521
- this._Status = RentalStatusEnum.ACTIVE;
522
- }
523
- }
524
-
525
- public async periodEndProcess(
526
- loginUser: LoginUser,
527
- dbTransaction: any,
528
- stockInventory: any,
529
- ) {
530
- try {
531
- // Privilege Checking
532
- const systemCode =
533
- await ApplicationConfig.getComponentConfigValue('system-code');
534
-
535
- const isPrivileged = await loginUser.checkPrivileges(
536
- systemCode,
537
- 'Rental – PeriodEndProcess',
538
- );
539
-
540
- if (!isPrivileged) {
541
- throw new ClassError(
542
- 'Rental',
543
- 'RentalErrMsg04',
544
- "You do not have 'Rental - PeriodEndProcess' privilege.",
545
- );
546
- }
547
-
548
- // Validate Existing Rental
549
- if (this.AgreementNo === undefined || this.AgreementNo === null) {
550
- throw new ClassError(
551
- 'Rental',
552
- 'RentalErrMsg05',
553
- 'Rental must be existing rental.',
554
- );
555
- }
556
-
557
- // Validate Rental End Period
558
- if (this.EndDateTime === new Date(new Date().setHours(0, 0, 0, 0))) {
559
- throw new ClassError(
560
- 'Rental',
561
- 'RentalErrMsg06',
562
- 'Rental period is not ending today.',
563
- );
564
- }
565
-
566
- // Mark Rental Item as Available
567
- await stockInventory.setAvailable(loginUser, dbTransaction);
568
-
569
- // Capture Record Info Before Changes
570
- const entityValueBefore = {
571
- RentalId: this.RentalId,
572
- CustomerId: this.CustomerId,
573
- CustomerType: this.CustomerType,
574
- ItemId: this.ItemId,
575
- ItemType: this.ItemType,
576
- PriceId: this.PriceId,
577
- StartDateTime: this.StartDateTime,
578
- EndDateTime: this.EndDateTime,
579
- CancelRemarks: this.CancelRemarks,
580
- TerminateRemarks: this.TerminateRemarks,
581
- AgreementNo: this.AgreementNo,
582
- AccountType: this.AccountType,
583
- Status: this.Status,
584
- EscheatmentYN: this.EscheatmentYN,
585
- CreatedById: this.CreatedById,
586
- CreatedAt: this.CreatedAt,
587
- UpdatedById: this.UpdatedById,
588
- UpdatedAt: this.UpdatedAt,
589
- };
590
-
591
- // Mark Rental as Terminated and Capture Updater Info
592
- this.setTerminated();
593
- this._UpdatedById = loginUser.ObjectId;
594
- this._UpdatedAt = new Date();
595
-
596
- // Capture Record Info after Changes
597
- const entityValueAfter = {
598
- RentalId: this.RentalId,
599
- CustomerId: this.CustomerId,
600
- CustomerType: this.CustomerType,
601
- ItemId: this.ItemId,
602
- ItemType: this.ItemType,
603
- PriceId: this.PriceId,
604
- StartDateTime: this.StartDateTime,
605
- EndDateTime: this.EndDateTime,
606
- CancelRemarks: this.CancelRemarks,
607
- TerminateRemarks: this.TerminateRemarks,
608
- AgreementNo: this.AgreementNo,
609
- AccountType: this.AccountType,
610
- Status: this.Status,
611
- EscheatmentYN: this.EscheatmentYN,
612
- CreatedById: this.CreatedById,
613
- CreatedAt: this.CreatedAt,
614
- UpdatedById: this.UpdatedById,
615
- UpdatedAt: this.UpdatedAt,
616
- };
617
-
618
- // Record the Update Activity
619
- const activity = new Activity();
620
- activity.ActivityId = activity.createId();
621
- activity.Action = ActionEnum.UPDATE;
622
- activity.Description = 'Set Rental as Terminated';
623
- activity.EntityType = 'Rental';
624
- activity.EntityId = this.RentalId;
625
- activity.EntityValueBefore = JSON.stringify(entityValueBefore);
626
- activity.EntityValueAfter = JSON.stringify(entityValueAfter);
627
- await activity.create(loginUser.ObjectId, dbTransaction);
628
-
629
- return this;
630
- } catch (err) {
631
- throw err;
632
- }
633
- }
634
-
635
- async getCustomerActiveRentals(
636
- loginUser: LoginUser,
637
- dbTransaction: any,
638
- CustomerId: string,
639
- ): Promise<IRentalAttr[]> {
640
- try {
641
- // Part 1: Privilege Checking
642
- // Call loginUser.checkPrivileges() by passing:
643
- // SystemCode: <get_from_app_config>
644
- // PrivilegeCode: "RENTAL_VIEW"
645
- const systemCode =
646
- ApplicationConfig.getComponentConfigValue('system-code');
647
- const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
648
-
649
- if (!isPrivileged) {
650
- throw new ClassError(
651
- 'Rental',
652
- 'RentalErrMsg01',
653
- "You do not have 'Rental - View' privilege.",
654
- );
655
- }
656
-
657
- // Part 2: Retrieve Rentals and Returns
658
- // Call Rental._Repo findAll method by passing:
659
- // where:
660
- // Status: "Active"
661
- // [Op.OR]:
662
- // CustomerId: Params.CustomerId
663
- // '$JointHirers.CustomerId$': Params.CustomerId
664
- // include:
665
- // model: JointHirerModel
666
- // attributes: []
667
- const query = `
668
- SELECT
669
- r.*
670
- FROM
671
- rental_Rental r
672
- LEFT JOIN
673
- rental_JointHirer jh ON r.RentalId = jh.RentalId
674
- WHERE
675
- r.Status = 'Active'
676
- AND (
677
- r.CustomerId = '${CustomerId}'
678
- OR jh.CustomerId = '${CustomerId}'
679
- )
680
- GROUP BY
681
- r.RentalId
682
- `;
683
- const db = rentalDb.getConnection();
684
- const result = await db.query(query, {
685
- type: QueryTypes.SELECT,
686
- transaction: dbTransaction,
687
- model: RentalModel,
688
- mapToModel: true,
689
- });
690
-
691
- // Format the returned records
692
- const records: IRentalAttr[] = result.map((record: RentalModel) => {
693
- return {
694
- RentalId: record.RentalId,
695
- CustomerId: record.CustomerId,
696
- CustomerType: record.CustomerType,
697
- ItemId: record.ItemId,
698
- ItemType: record.ItemType,
699
- PriceId: record.PriceId,
700
- StartDateTime: record.StartDateTime,
701
- EndDateTime: record.EndDateTime,
702
- CancelRemarks: record.CancelRemarks,
703
- TerminateRemarks: record.TerminateRemarks,
704
- AgreementNo: record.AgreementNo,
705
- AccountType: record.AccountType,
706
- Status: record.Status,
707
- EscheatmentYN: record.EscheatmentYN,
708
- CreatedById: record.CreatedById,
709
- CreatedAt: record.CreatedAt,
710
- UpdatedById: record.UpdatedById,
711
- UpdatedAt: record.UpdatedAt,
712
- };
713
- });
714
- // Return the returned records.
715
- return records;
716
- } catch (error) {
717
- throw error;
718
- }
719
- }
720
-
721
- async getCustomerIds(loginUser: LoginUser, dbTransaction: any) {
722
- try {
723
- // Part 1: Privilege Checking
724
- // Call loginUser.checkPrivileges() by passing:
725
- // SystemCode: <get_from_app_config>
726
- // PrivilegeCode: "RENTAL_VIEW"
727
- const systemCode =
728
- ApplicationConfig.getComponentConfigValue('system-code');
729
- const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
730
-
731
- if (!isPrivileged) {
732
- throw new ClassError(
733
- 'Rental',
734
- 'RentalErrMsg01',
735
- "You do not have 'Rental - View' privilege.",
736
- );
737
- }
738
-
739
- // Part 2: Validation
740
- // Make sure this.RentalId exists, if not throw new ClassError by passing
741
- if (!this.RentalId) {
742
- throw new ClassError('Rental', 'RentalErrMsg01', 'RentalId is missing');
743
- }
744
-
745
- //Part 3: Retrieve Listing and Returns
746
- // 3.1 Call Rental._JointHirerRepo findAll method by passing:
747
- // where:
748
- // RentalId: this.RentalId
749
- // dbTransaction
750
- const options: any = {
751
- where: {
752
- RentalId: this.RentalId,
753
- },
754
- transaction: dbTransaction,
755
- };
756
-
757
- const joinHirers = await Rental._JointHirerRepo.findAll(options);
758
-
759
- // 3.2 Create a new array variable and set its value to joint hirer and Rental.CustomerId
760
- // array translated to only CustomerId. Then, append this.CustomerId to the array.
761
- const customerIds: string[] = [this.CustomerId];
762
- for (let i = 0; i < joinHirers.length; i++) {
763
- const jointHirer = joinHirers[i];
764
- customerIds.push(jointHirer.CustomerId);
765
- }
766
-
767
- // 3.3 Return the array.
768
- return customerIds;
769
- } catch (error) {
770
- throw error;
771
- }
772
- }
773
-
774
- async signAgreement(
775
- loginUser: LoginUser,
776
- CustomerId: string,
777
- dbTransaction: any,
778
- ) {
779
- try {
780
- // Part 1: Privilege Checking
781
- // Call loginUser.checkPrivileges() by passing:
782
- // SystemCode: <get_from_app_config>
783
- // PrivilegeCode: "RENTAL_SIGN"
784
- const systemCode =
785
- ApplicationConfig.getComponentConfigValue('system-code');
786
- const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_SIGN');
787
-
788
- if (!isPrivileged) {
789
- throw new ClassError(
790
- 'Rental',
791
- 'RentalErrMsg01',
792
- "You do not have 'RENTAL_SIGN' privilege.",
793
- );
794
- }
795
-
796
- // Part 2: Validation
797
- // Make sure this.Status is "Pending Signing" and agreementStatus is "Generated", if not throw new ClassError
798
- const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
799
- if (
800
- this.Status !== RentalStatusEnum.PENDING_SIGNING &&
801
- agreement.Status !== AggrementStatusEnum.GENERATED
802
- ) {
803
- throw new ClassError(
804
- 'Rental',
805
- 'RentalErrMsg01',
806
- 'Rental is not ready to be signed yet.',
807
- );
808
- }
809
-
810
- //Make sure CustomerId inside the listing and SignatureStatus is "Pending"
811
- const signatureList = await Agreement.getSignatureList(
812
- loginUser,
813
- this.AgreementNo,
814
- dbTransaction,
815
- );
816
- const customerSignature = signatureList.find(
817
- (sig) =>
818
- sig.CustomerId === CustomerId &&
819
- sig.SignatureStatus === HirerSignatureStatusEnum.PENDING,
820
- );
821
-
822
- if (!customerSignature) {
823
- throw new ClassError(
824
- 'Rental',
825
- 'RentalErrMsg01',
826
- 'Customer signature is not pending.',
827
- );
828
- }
829
-
830
- //Update rental_HirerSignature
831
- const signaturePayload = {
832
- SignatureStatus: HirerSignatureStatusEnum.SIGNED,
833
- SignedAt: new Date(),
834
- UpdatedById: loginUser.ObjectId,
835
- UpdatedAt: new Date(),
836
- };
837
-
838
- await Rental._HirerSignatureRepo.update(signaturePayload, {
839
- where: {
840
- AgreementNo: this.AgreementNo,
841
- CustomerId: CustomerId,
842
- },
843
- transaction: dbTransaction,
844
- });
845
-
846
- const signatureActivity = new Activity();
847
- signatureActivity.ActivityId = signatureActivity.createId();
848
- signatureActivity.Action = ActionEnum.UPDATE;
849
- signatureActivity.Description = 'Update hirer signature';
850
- signatureActivity.EntityType = 'HirerSignature';
851
- signatureActivity.EntityId = this.AgreementNo;
852
- signatureActivity.EntityValueBefore = JSON.stringify(customerSignature);
853
- signatureActivity.EntityValueAfter = JSON.stringify(signaturePayload);
854
- await signatureActivity.create(loginUser.ObjectId, dbTransaction);
855
-
856
- const updatedSignatureList = await Agreement.getSignatureList(
857
- loginUser,
858
- this.AgreementNo,
859
- dbTransaction,
860
- );
861
-
862
- const pendingSignatures = updatedSignatureList.filter(
863
- (sig) => sig.SignatureStatus === 'Pending',
864
- );
865
- if (pendingSignatures.length > 0) {
866
- return;
867
- }
868
-
869
- //Part 3: Update Agreement Status
870
- // 3.1 Set EntityValueBefore to current agreement instance.
871
- const entityValueAgreementBefore = {
872
- AgreementNo: agreement.AgreementNo,
873
- Status: agreement.Status,
874
- DateSigned: agreement.DateSigned,
875
- };
876
-
877
- // 3.2 Set below agreement attributes:
878
- // DateSigned: current date & time
879
- // Status: "Signed"
880
- const payload = {
881
- DateSigned: new Date(),
882
- Status: AggrementStatusEnum.SIGNED,
883
- };
884
-
885
- // 3.3 Call Rental._AgreementRepo update()
886
- await Rental._AgreementRepo.update(payload, {
887
- where: {
888
- AgreementNo: this.AgreementNo,
889
- },
890
- transaction: dbTransaction,
891
- });
892
-
893
- const entityValueAgreementAfter = {
894
- entityValueAgreementBefore,
895
- ...payload,
896
- };
897
-
898
- // Part 4: Record Update Agreement Activity
899
- const activity = new Activity();
900
- activity.ActivityId = activity.createId();
901
- activity.Action = ActionEnum.UPDATE;
902
- activity.Description = 'Update agreement';
903
- activity.EntityType = 'RentalAgreement';
904
- activity.EntityId = this.AgreementNo;
905
- activity.EntityValueBefore = JSON.stringify(entityValueAgreementBefore);
906
- activity.EntityValueAfter = JSON.stringify(entityValueAgreementAfter);
907
- await activity.create(loginUser.ObjectId, dbTransaction);
908
-
909
- // Part 5: Update Rental Status
910
- // 5.1 Set EntityValueBefore to current rental instance.
911
- const entityValueRentalBefore = {
912
- RentalId: this.RentalId,
913
- CustomerId: this.CustomerId,
914
- CustomerType: this.CustomerType,
915
- ItemId: this.ItemId,
916
- ItemType: this.ItemType,
917
- PriceId: this.PriceId,
918
- StartDateTime: this.StartDateTime,
919
- EndDateTime: this.EndDateTime,
920
- CancelRemarks: this.CancelRemarks,
921
- TerminateRemarks: this.TerminateRemarks,
922
- AgreementNo: this.AgreementNo,
923
- AccountType: this.AccountType,
924
- Status: this.Status,
925
- EscheatmentYN: this.EscheatmentYN,
926
- CreatedById: this.CreatedById,
927
- CreatedAt: this.CreatedAt,
928
- UpdatedById: this.UpdatedById,
929
- UpdatedAt: this.UpdatedAt,
930
- };
931
- // 5.2: Set below rental attributes:
932
- // Status: "Pending Key Collection"
933
- // UpdatedById: loginUser.ObjectId
934
- // UpdatedAt: current date & time
935
-
936
- const payloadRental = {
937
- Status: RentalStatusEnum.PENDING_KEY_COLLECTION,
938
- UpdatedById: loginUser.ObjectId,
939
- UpdatedAt: new Date(),
940
- };
941
-
942
- // 5.3: Call Rental._Repo update() method by passing:
943
- // populate rental instance attributes
944
- // dbTransaction
945
- await Rental._Repo.update(payloadRental, {
946
- where: {
947
- RentalId: this.RentalId,
948
- },
949
- transaction: dbTransaction,
950
- });
951
-
952
- const entityValueRentalAfter = {
953
- entityValueRentalBefore,
954
- ...payloadRental,
955
- };
956
-
957
- this._Status = RentalStatusEnum.PENDING_KEY_COLLECTION;
958
-
959
- // Part 6: Record Update Agreement Activity
960
- const rentalActivity = new Activity();
961
- rentalActivity.ActivityId = activity.createId();
962
- rentalActivity.Action = ActionEnum.UPDATE;
963
- rentalActivity.Description = 'Sign rental agreement';
964
- rentalActivity.EntityType = 'Rental';
965
- rentalActivity.EntityId = this.RentalId;
966
- rentalActivity.EntityValueBefore = JSON.stringify(
967
- entityValueRentalBefore,
968
- );
969
- rentalActivity.EntityValueAfter = JSON.stringify(entityValueRentalAfter);
970
- await rentalActivity.create(loginUser.ObjectId, dbTransaction);
971
- } catch (error) {
972
- throw error;
973
- }
974
- }
975
-
976
- async generateAgreement(
977
- loginUser: LoginUser,
978
- dbTransaction: any,
979
- MediaId: string,
980
- ) {
981
- //This method will generate a new rental agreement.
982
- try {
983
- // Part 1: Privilege Checking
984
- // Call loginUser.checkPrivileges() by passing:
985
- // SystemCode: "<get_from_app_config>"
986
- // PrivilegeCode: "RENTAL_AGREEMENT_CREATE"
987
-
988
- const systemCode =
989
- ApplicationConfig.getComponentConfigValue('system-code');
990
- const isPrivileged = loginUser.checkPrivileges(
991
- systemCode,
992
- 'RENTAL_AGREEMENT_CREATE',
993
- );
994
-
995
- if (!isPrivileged) {
996
- throw new ClassError(
997
- 'Rental',
998
- 'RentalErrMsg01',
999
- "You do not have 'RENTAL_AGREEMENT_CREATE' privilege.",
1000
- );
1001
- }
1002
-
1003
- // Part 2: Validation
1004
- // Instantiate existing RentalAgreement by passing:
1005
- // dbTransaction
1006
- // AgreementNo: this.AgreementNo
1007
- // Make sure Params.MediaId exists, if not throw new ClassError by passing:
1008
- // Classname: "Rental"
1009
- // MessageCode: "RentalErrMsg0X"
1010
- // Message: "MediaId is required."
1011
- const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
1012
- if (!MediaId) {
1013
- throw new ClassError(
1014
- 'Rental',
1015
- 'RentalErrMsg0X',
1016
- 'MediaId is required.',
1017
- );
1018
- }
1019
-
1020
- // Part 3: Update Agreement Record
1021
- // Set EntityValueBefore to the agreement instance.
1022
- // Set below agreement attributes:
1023
- // Status: "Generated"
1024
- // MediaId: Params.MediaId
1025
-
1026
- const EntityValueBefore = agreement;
1027
- agreement.Status = AggrementStatusEnum.GENERATED;
1028
- agreement.MediaId = MediaId;
1029
-
1030
- // Call Rental._AgreementRepo update method by passing:
1031
- // Status: agreement.Status
1032
- // MediaId: agreement.MediaId
1033
- // dbTransaction
1034
-
1035
- await Rental._AgreementRepo.update(
1036
- {
1037
- Status: agreement.Status,
1038
- MediaId: agreement.MediaId,
1039
- },
1040
- {
1041
- where: {
1042
- AgreementNo: this.AgreementNo,
1043
- },
1044
- transaction: dbTransaction,
1045
- },
1046
- );
1047
-
1048
- // Part 4: Record Update Agreement Activity
1049
- // Initialise EntityValueAfter variable and set to agreement instance
1050
- const EntityValueAfter = agreement;
1051
- // Instantiate new activity from Activity class, call createId() method, then set:
1052
- const activity = new Activity();
1053
- // Action: ActionEnum.UPDATE
1054
- // Description: "Generate agreement."
1055
- // EntityType: "RentalAgreement"
1056
- // EntityId: this.AgreementNo
1057
- // EntityValueBefore: EntityValueBefore
1058
- // EntityValueAfter: EntityValueAfter
1059
- activity.ActivityId = activity.createId();
1060
- activity.Action = ActionEnum.UPDATE;
1061
- activity.Description = 'Generate agreement.';
1062
- activity.EntityType = 'RentalAgreement';
1063
- activity.EntityId = this.AgreementNo;
1064
- activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
1065
- activity.EntityValueAfter = JSON.stringify(EntityValueAfter);
1066
-
1067
- // Call new activity create method by passing:
1068
- // dbTransaction
1069
- // userId: loginUser.ObjectId
1070
- await activity.create(loginUser.ObjectId, dbTransaction);
1071
-
1072
- // Part 5: Add Record To Agreement History Table
1073
- // Make sure there is a private static readonly _AgreementHistoryRepo variable.
1074
- // Set newRecord to an object with below key-value:
1075
- // AgreementNo: this.AgreementNo
1076
- // MediaId: params.MediaId
1077
- // ActivityCompleted: "Agreement Generated"
1078
- // CreatedAt: Current Timestamp
1079
- // Use _AgreementHistoryRepo.create() method and pass newRecord and dbTransaction.
1080
- const agreementHistory = await Rental._AgreementHistoryRepo.create(
1081
- {
1082
- AgreementNo: this.AgreementNo,
1083
- MediaId,
1084
- ActivityCompleted: 'Agreement Generated',
1085
- CreatedAt: new Date(),
1086
- },
1087
- { transaction: dbTransaction },
1088
- );
1089
-
1090
- // Part 6: Record Agreement History Activity
1091
- // Initialise EntityValueAfter variable and set to newRecord from previous part.
1092
- // Instantiate new activity from Activity class, call createId() method, then set:
1093
- // Action: ActionEnum.CREATE
1094
- // Description: "Generate agreement."
1095
- // EntityType: "RentalAgreementHistory"
1096
- // EntityId: HistoryId from previous part.
1097
- // EntityValueBefore: empty object.
1098
- // EntityValueAfter: EntityValueAfter
1099
- // Call new activity create method by passing:
1100
- // dbTransaction
1101
- // userId: loginUser.ObjectId
1102
- const entityValueAfter = agreementHistory.get({ plain: true });
1103
- const activityHistory = new Activity();
1104
- activityHistory.ActivityId = activityHistory.createId();
1105
- activityHistory.Action = ActionEnum.CREATE;
1106
- activityHistory.Description = 'Generate agreement.';
1107
- activityHistory.EntityType = 'RentalAgreementHistory';
1108
- activityHistory.EntityId = agreementHistory.HistoryId.toString();
1109
- activityHistory.EntityValueBefore = JSON.stringify({});
1110
- activityHistory.EntityValueAfter = JSON.stringify(entityValueAfter);
1111
- await activityHistory.create(loginUser.ObjectId, dbTransaction);
1112
- } catch (error) {
1113
- throw error;
1114
- }
1115
- }
1116
- }
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 { HirerSignatureRepository } from '../hirer-signature/hirer-signature.repository';
22
+ import { HirerSignatureStatusEnum } from '../../enum/hirer-signature-status.enum';
23
+ import { HirerTypeEnum } from '../../enum/hirer-type.enum';
24
+ import { AgreementHistoryRepository } from '../agreement-history/agreement-history.repository';
25
+
26
+ export class Rental extends ObjectBase {
27
+ ObjectId: string;
28
+ ObjectName: string;
29
+ ObjectType = 'Rental';
30
+ TableName: string;
31
+ CustomerId: string;
32
+ CustomerType: string;
33
+ ItemId: string;
34
+ ItemType: string;
35
+ PriceId: string;
36
+ StartDateTime: Date;
37
+ EndDateTime: Date;
38
+ CancelRemarks: string;
39
+ TerminateRemarks: string;
40
+ AgreementNo: string;
41
+ AccountType: RentalAccountTypeEnum;
42
+ JointHirers: JointHirer[] = [];
43
+ protected _Status: RentalStatusEnum;
44
+ protected _EscheatmentYN: string = 'N';
45
+ protected _CreatedById: string;
46
+ protected _CreatedAt: Date;
47
+ protected _UpdatedById: string;
48
+ protected _UpdatedAt: Date;
49
+ protected static _Repo = new RentalRepository();
50
+ protected static _AgreementRepo = new AgreementRepository();
51
+ protected static _JointHirerRepo = new JointHirerRepository();
52
+ protected static _HirerSignatureRepo = new HirerSignatureRepository();
53
+ protected static _AgreementHistoryRepo = new AgreementHistoryRepository();
54
+
55
+ get RentalId(): string {
56
+ return this.ObjectId;
57
+ }
58
+
59
+ set RentalId(value: string) {
60
+ this.ObjectId = value;
61
+ }
62
+
63
+ get Status(): RentalStatusEnum {
64
+ return this._Status;
65
+ }
66
+
67
+ get EscheatmentYN(): string {
68
+ return this._EscheatmentYN;
69
+ }
70
+
71
+ get CreatedById(): string {
72
+ return this._CreatedById;
73
+ }
74
+
75
+ get CreatedAt(): Date {
76
+ return this._CreatedAt;
77
+ }
78
+
79
+ get UpdatedById(): string {
80
+ return this._UpdatedById;
81
+ }
82
+
83
+ get UpdatedAt(): Date {
84
+ return this._UpdatedAt;
85
+ }
86
+
87
+ private setTerminated() {
88
+ this._Status = RentalStatusEnum.TERMINATED;
89
+ }
90
+
91
+ protected constructor(rentalAttr?: IRentalAttr) {
92
+ super();
93
+ if (rentalAttr) {
94
+ this.RentalId = rentalAttr.RentalId;
95
+ this.CustomerId = rentalAttr.CustomerId;
96
+ this.CustomerType = rentalAttr.CustomerType;
97
+ this.ItemId = rentalAttr.ItemId;
98
+ this.ItemType = rentalAttr.ItemType;
99
+ this.PriceId = rentalAttr.PriceId;
100
+ this.StartDateTime = rentalAttr.StartDateTime;
101
+ this.EndDateTime = rentalAttr.EndDateTime;
102
+ this.CancelRemarks = rentalAttr.CancelRemarks;
103
+ this.TerminateRemarks = rentalAttr.TerminateRemarks;
104
+ this.AgreementNo = rentalAttr.AgreementNo;
105
+ this.AccountType = rentalAttr.AccountType;
106
+ this._Status = rentalAttr.Status;
107
+ this._EscheatmentYN = rentalAttr.EscheatmentYN;
108
+ this._CreatedById = rentalAttr.CreatedById;
109
+ this._CreatedAt = rentalAttr.CreatedAt;
110
+ this._UpdatedById = rentalAttr.UpdatedById;
111
+ this._UpdatedAt = rentalAttr.UpdatedAt;
112
+ }
113
+ }
114
+
115
+ public static async init(dbTransaction?: any, rentalId?: string) {
116
+ try {
117
+ if (rentalId) {
118
+ const rental = await Rental._Repo.findByPk(rentalId, dbTransaction);
119
+ if (rental) {
120
+ return new Rental(rental);
121
+ } else {
122
+ throw new ClassError('Rental', 'RentalErrMsg00', 'Rental Not Found');
123
+ }
124
+ }
125
+ return new Rental();
126
+ } catch (error) {
127
+ throw new ClassError(
128
+ 'Rental',
129
+ 'RentalErrMsg00',
130
+ 'Failed To Initialize Price',
131
+ );
132
+ }
133
+ }
134
+
135
+ /**
136
+ * Create a new Rental record.
137
+ * @param {RentalPrice} rentalPrice - The rental pricing information.
138
+ * @param {LoginUser} loginUser - The user initiating the creation.
139
+ * @param {any} [dbTransaction] - Optional database transaction for atomic operations.
140
+ * @throws {ClassError} Throws an error if:
141
+ * 1. loginUser does not have 'Rental - Create' privilege.
142
+ * 2. Rental item is not available at the current date.
143
+ * @returns {Promise<any>} A Promise resolving to the created rental record.
144
+ */
145
+ public async create(
146
+ rentalPrice: RentalPrice,
147
+ loginUser: LoginUser,
148
+ jointHirers?: JointHirer[],
149
+ dbTransaction?: any,
150
+ ): Promise<any> {
151
+ try {
152
+ /*Part 1: Check Privilege*/
153
+ const systemCode =
154
+ ApplicationConfig.getComponentConfigValue('system-code');
155
+ const isPrivileged = await loginUser.checkPrivileges(
156
+ systemCode,
157
+ 'Rental - Create',
158
+ );
159
+
160
+ if (!isPrivileged) {
161
+ throw new ClassError(
162
+ 'Rental',
163
+ 'RentalErrMsg01',
164
+ "You do not have 'Rental - Create' privilege.",
165
+ );
166
+ }
167
+
168
+ /*Part 2: Make Sure Rental Item Available*/
169
+ const isItemAvailable = await Rental.isItemAvailable(
170
+ this.ItemId,
171
+ this.ItemType,
172
+ this.StartDateTime,
173
+ this.EndDateTime,
174
+ dbTransaction,
175
+ );
176
+
177
+ if (!isItemAvailable) {
178
+ throw new ClassError(
179
+ 'Rental',
180
+ 'RentalErrMsg02',
181
+ 'Rental Item is not available at current date.',
182
+ );
183
+ }
184
+
185
+ // Part 3: Create Rental Agreement Record
186
+ // Call Rental._AgreementRepo create method by passing:
187
+ // AgreementNo: this.AgreementNo
188
+ // Status: "Not Generated"
189
+ // dbTransaction
190
+ await Rental._AgreementRepo.create(
191
+ {
192
+ AgreementNo: this.AgreementNo,
193
+ Status: 'Not Generated',
194
+ },
195
+ {
196
+ transaction: dbTransaction,
197
+ },
198
+ );
199
+
200
+ //Create a record into rental_HirerSignature table
201
+ //Create the main customer's signature record
202
+ await Rental._HirerSignatureRepo.create(
203
+ {
204
+ HirerSignatureId: this.createId(),
205
+ AgreementNo: this.AgreementNo,
206
+ HirerType: HirerTypeEnum.PRIMARY,
207
+ CustomerId: this.CustomerId,
208
+ CustomerType: 'Customer',
209
+ SignatureStatus: HirerSignatureStatusEnum.PENDING,
210
+ SignedAt: new Date(),
211
+ CreatedById: loginUser.ObjectId,
212
+ CreatedAt: new Date(),
213
+ UpdatedById: loginUser.ObjectId,
214
+ UpdatedAt: new Date(),
215
+ },
216
+ { transaction: dbTransaction },
217
+ );
218
+
219
+ // Create signatures record for joint hirers if any
220
+ if (jointHirers && jointHirers.length > 0) {
221
+ for (const jointHirer of jointHirers) {
222
+ await Rental._HirerSignatureRepo.create(
223
+ {
224
+ HirerSignatureId: this.createId(),
225
+ AgreementNo: this.AgreementNo,
226
+ HirerType: HirerTypeEnum.JOINT,
227
+ CustomerId: jointHirer.CustomerId,
228
+ CustomerType: 'SDBCustomer',
229
+ SignatureStatus: HirerSignatureStatusEnum.PENDING,
230
+ SignedAt: new Date(),
231
+ CreatedById: loginUser.ObjectId,
232
+ CreatedAt: new Date(),
233
+ UpdatedById: loginUser.ObjectId,
234
+ UpdatedAt: new Date(),
235
+ },
236
+ { transaction: dbTransaction },
237
+ );
238
+ }
239
+ }
240
+
241
+ /*Part 4: Insert Rental & The Agreed Rental Price*/
242
+ if (!rentalPrice.PriceId) {
243
+ const rentalPriceData = await rentalPrice.create(
244
+ loginUser,
245
+ dbTransaction,
246
+ );
247
+ this.PriceId = rentalPriceData.PriceId;
248
+ } else {
249
+ this.PriceId = rentalPrice.PriceId;
250
+ }
251
+ this.RentalId = this.createId();
252
+ // if (!this.Status) {
253
+ this._Status = RentalStatusEnum.PENDING_SIGNING;
254
+ // }
255
+ this._CreatedById = loginUser.ObjectId;
256
+ this._UpdatedById = loginUser.ObjectId;
257
+ this._CreatedAt = new Date();
258
+ this._UpdatedAt = new Date();
259
+
260
+ const data = {
261
+ RentalId: this.RentalId,
262
+ CustomerId: this.CustomerId,
263
+ CustomerType: this.CustomerType,
264
+ ItemId: this.ItemId,
265
+ ItemType: this.ItemType,
266
+ PriceId: this.PriceId,
267
+ StartDateTime: this.StartDateTime,
268
+ EndDateTime: this.EndDateTime,
269
+ CancelRemarks: this.CancelRemarks,
270
+ TerminateRemarks: this.TerminateRemarks,
271
+ AgreementNo: this.AgreementNo,
272
+ AccountType: this.AccountType,
273
+ Status: this.Status,
274
+ EscheatmentYN: this.EscheatmentYN,
275
+ CreatedById: this.CreatedById,
276
+ CreatedAt: this.CreatedAt,
277
+ UpdatedById: this.UpdatedById,
278
+ UpdatedAt: this.UpdatedAt,
279
+ };
280
+
281
+ await Rental._Repo.create(data, {
282
+ transaction: dbTransaction,
283
+ });
284
+
285
+ //For every data inside Param.jointHirers, update the rentalId and call jointHirer.create
286
+ if (jointHirers) {
287
+ for (let i = 0; i < jointHirers.length; i++) {
288
+ jointHirers[i].RentalId = this.RentalId;
289
+ await jointHirers[i].create(loginUser, dbTransaction);
290
+ this.JointHirers.push(jointHirers[i]);
291
+ }
292
+ }
293
+
294
+ /*Part 5: Record Create Rental Activity*/
295
+ const activity = new Activity();
296
+ activity.ActivityId = activity.createId();
297
+ activity.Action = ActionEnum.CREATE;
298
+ activity.Description = 'Add Rental';
299
+ activity.EntityType = 'Rental';
300
+ activity.EntityId = this.RentalId;
301
+ activity.EntityValueBefore = JSON.stringify({});
302
+ activity.EntityValueAfter = JSON.stringify(data);
303
+ await activity.create(loginUser.ObjectId, dbTransaction);
304
+
305
+ /*Part 6: Return Result*/
306
+ return this;
307
+ } catch (error) {
308
+ throw error;
309
+ }
310
+ }
311
+
312
+ public static async isItemAvailable(
313
+ itemId: string,
314
+ itemType: string,
315
+ startDateTime: Date,
316
+ endDateTime: Date,
317
+ dbTransaction?: any,
318
+ ) {
319
+ try {
320
+ const item = await Rental._Repo.findOne({
321
+ where: {
322
+ ItemId: itemId,
323
+ ItemType: itemType,
324
+ StartDateTime: {
325
+ [Op.lte]: endDateTime,
326
+ },
327
+ EndDateTime: {
328
+ [Op.gte]: startDateTime,
329
+ },
330
+ },
331
+ transaction: dbTransaction,
332
+ });
333
+
334
+ if (item) {
335
+ return false;
336
+ } else {
337
+ return true;
338
+ }
339
+ } catch (error) {
340
+ throw error;
341
+ }
342
+ }
343
+
344
+ public static async findAll(
345
+ loginUser: LoginUser,
346
+ dbTransaction: any,
347
+ page?: number,
348
+ row?: number,
349
+ search?: IRentalFindAllSearchAttr,
350
+ ) {
351
+ try {
352
+ const systemCode =
353
+ await ApplicationConfig.getComponentConfigValue('system-code');
354
+
355
+ const isPrivileged = await loginUser.checkPrivileges(
356
+ systemCode,
357
+ 'Rental - View',
358
+ );
359
+
360
+ if (!isPrivileged) {
361
+ throw new ClassError(
362
+ 'Rental',
363
+ 'RentalErrMsg01',
364
+ "You do not have 'Rental - View' privilege.",
365
+ );
366
+ }
367
+
368
+ const queryObj: any = {};
369
+
370
+ let options: any = {
371
+ transaction: dbTransaction,
372
+ order: [['CreatedAt', 'DESC']],
373
+ };
374
+
375
+ if (page && row) {
376
+ options = {
377
+ ...options,
378
+ limit: row,
379
+ offset: row * (page - 1),
380
+ };
381
+ }
382
+
383
+ if (search) {
384
+ Object.entries(search).forEach(([key, value]) => {
385
+ if (key === 'StartDateTime') {
386
+ queryObj[key] = {
387
+ [Op.gte]: value,
388
+ };
389
+ } else if (key === 'EndDateTime') {
390
+ queryObj[key] = {
391
+ [Op.lte]: value,
392
+ };
393
+ } else if (key === 'CustomerId') {
394
+ queryObj[key] = {
395
+ [Op.substring]: value,
396
+ };
397
+
398
+ options.include = [
399
+ {
400
+ model: JointHirerModel,
401
+ required: false,
402
+ where: {
403
+ CustomerId: {
404
+ [Op.substring]: value,
405
+ },
406
+ },
407
+ },
408
+ ];
409
+ } else {
410
+ queryObj[key] = {
411
+ [Op.substring]: value,
412
+ };
413
+ }
414
+ });
415
+ if (options?.include?.length > 1) {
416
+ options.include.push({
417
+ model: AgreementModel,
418
+ required: false,
419
+ });
420
+ } else {
421
+ options.include = [
422
+ {
423
+ model: AgreementModel,
424
+ required: false,
425
+ },
426
+ ];
427
+ }
428
+
429
+ options = {
430
+ ...options,
431
+ where: queryObj,
432
+ };
433
+ }
434
+
435
+ return await Rental._Repo.findAndCountAll(options);
436
+ } catch (err) {
437
+ throw err;
438
+ }
439
+ }
440
+
441
+ public static async checkActiveByItemId(
442
+ loginUser: LoginUser,
443
+ itemId: string,
444
+ itemType: string,
445
+ dbTransaction?: any,
446
+ ) {
447
+ try {
448
+ const systemCode =
449
+ await ApplicationConfig.getComponentConfigValue('system-code');
450
+
451
+ const isPrivileged = await loginUser.checkPrivileges(
452
+ systemCode,
453
+ 'Rental - View',
454
+ );
455
+
456
+ if (!isPrivileged) {
457
+ throw new ClassError(
458
+ 'Rental',
459
+ 'RentalErrMsg01',
460
+ "You do not have 'Rental - View' privilege.",
461
+ );
462
+ }
463
+
464
+ const queryObj: any = {
465
+ ItemId: itemId,
466
+ ItemType: itemType,
467
+ Status: RentalStatusEnum.ACTIVE,
468
+ };
469
+
470
+ const options: any = {
471
+ transaction: dbTransaction,
472
+ where: queryObj,
473
+ };
474
+
475
+ const rental = await Rental._Repo.findOne(options);
476
+
477
+ if (rental) {
478
+ return true;
479
+ } else {
480
+ return false;
481
+ }
482
+ } catch (err) {
483
+ throw err;
484
+ }
485
+ }
486
+
487
+ /**
488
+ * Sets the StartDateTime property of the Rental class as custom setter.
489
+ * @param {Date} startDateTime - The start date and time of the rental.
490
+ * @throws {ClassError} Throws an error if:
491
+ * 1. startDateTime is a past date.
492
+ * 2. startDateTime is more than the EndDateTime.
493
+ * @returns {void}
494
+ */
495
+ setStartDateTime(startDateTime: Date): void {
496
+ const startDateTimeObject = new Date(startDateTime);
497
+ startDateTimeObject.setHours(0, 0, 0, 0);
498
+
499
+ /*Part 1: Make Sure Future Date Time*/
500
+ if (startDateTimeObject < new Date(new Date().setHours(0, 0, 0, 0))) {
501
+ throw new ClassError(
502
+ 'Rental',
503
+ 'RentalErrMsg02',
504
+ 'StartDateTime cannot be a past datetime.',
505
+ );
506
+ }
507
+
508
+ /*Part 2: Make Sure Less Than End Date Time*/
509
+ if (this.EndDateTime && startDateTimeObject > this.EndDateTime) {
510
+ throw new ClassError(
511
+ 'Rental',
512
+ 'RentalErrMsg03',
513
+ 'StartDateTime cannot be more than EndDateTime.',
514
+ );
515
+ }
516
+
517
+ /*Part 3: Set Status Whether Reserved or Active*/
518
+ if (startDateTimeObject > new Date(new Date().setHours(0, 0, 0, 0))) {
519
+ this._Status = RentalStatusEnum.RESERVED;
520
+ } else {
521
+ this._Status = RentalStatusEnum.ACTIVE;
522
+ }
523
+ }
524
+
525
+ public async periodEndProcess(
526
+ loginUser: LoginUser,
527
+ dbTransaction: any,
528
+ stockInventory: any,
529
+ ) {
530
+ try {
531
+ // Privilege Checking
532
+ const systemCode =
533
+ await ApplicationConfig.getComponentConfigValue('system-code');
534
+
535
+ const isPrivileged = await loginUser.checkPrivileges(
536
+ systemCode,
537
+ 'Rental – PeriodEndProcess',
538
+ );
539
+
540
+ if (!isPrivileged) {
541
+ throw new ClassError(
542
+ 'Rental',
543
+ 'RentalErrMsg04',
544
+ "You do not have 'Rental - PeriodEndProcess' privilege.",
545
+ );
546
+ }
547
+
548
+ // Validate Existing Rental
549
+ if (this.AgreementNo === undefined || this.AgreementNo === null) {
550
+ throw new ClassError(
551
+ 'Rental',
552
+ 'RentalErrMsg05',
553
+ 'Rental must be existing rental.',
554
+ );
555
+ }
556
+
557
+ // Validate Rental End Period
558
+ if (this.EndDateTime === new Date(new Date().setHours(0, 0, 0, 0))) {
559
+ throw new ClassError(
560
+ 'Rental',
561
+ 'RentalErrMsg06',
562
+ 'Rental period is not ending today.',
563
+ );
564
+ }
565
+
566
+ // Mark Rental Item as Available
567
+ await stockInventory.setAvailable(loginUser, dbTransaction);
568
+
569
+ // Capture Record Info Before Changes
570
+ const entityValueBefore = {
571
+ RentalId: this.RentalId,
572
+ CustomerId: this.CustomerId,
573
+ CustomerType: this.CustomerType,
574
+ ItemId: this.ItemId,
575
+ ItemType: this.ItemType,
576
+ PriceId: this.PriceId,
577
+ StartDateTime: this.StartDateTime,
578
+ EndDateTime: this.EndDateTime,
579
+ CancelRemarks: this.CancelRemarks,
580
+ TerminateRemarks: this.TerminateRemarks,
581
+ AgreementNo: this.AgreementNo,
582
+ AccountType: this.AccountType,
583
+ Status: this.Status,
584
+ EscheatmentYN: this.EscheatmentYN,
585
+ CreatedById: this.CreatedById,
586
+ CreatedAt: this.CreatedAt,
587
+ UpdatedById: this.UpdatedById,
588
+ UpdatedAt: this.UpdatedAt,
589
+ };
590
+
591
+ // Mark Rental as Terminated and Capture Updater Info
592
+ this.setTerminated();
593
+ this._UpdatedById = loginUser.ObjectId;
594
+ this._UpdatedAt = new Date();
595
+
596
+ // Capture Record Info after Changes
597
+ const entityValueAfter = {
598
+ RentalId: this.RentalId,
599
+ CustomerId: this.CustomerId,
600
+ CustomerType: this.CustomerType,
601
+ ItemId: this.ItemId,
602
+ ItemType: this.ItemType,
603
+ PriceId: this.PriceId,
604
+ StartDateTime: this.StartDateTime,
605
+ EndDateTime: this.EndDateTime,
606
+ CancelRemarks: this.CancelRemarks,
607
+ TerminateRemarks: this.TerminateRemarks,
608
+ AgreementNo: this.AgreementNo,
609
+ AccountType: this.AccountType,
610
+ Status: this.Status,
611
+ EscheatmentYN: this.EscheatmentYN,
612
+ CreatedById: this.CreatedById,
613
+ CreatedAt: this.CreatedAt,
614
+ UpdatedById: this.UpdatedById,
615
+ UpdatedAt: this.UpdatedAt,
616
+ };
617
+
618
+ // Record the Update Activity
619
+ const activity = new Activity();
620
+ activity.ActivityId = activity.createId();
621
+ activity.Action = ActionEnum.UPDATE;
622
+ activity.Description = 'Set Rental as Terminated';
623
+ activity.EntityType = 'Rental';
624
+ activity.EntityId = this.RentalId;
625
+ activity.EntityValueBefore = JSON.stringify(entityValueBefore);
626
+ activity.EntityValueAfter = JSON.stringify(entityValueAfter);
627
+ await activity.create(loginUser.ObjectId, dbTransaction);
628
+
629
+ return this;
630
+ } catch (err) {
631
+ throw err;
632
+ }
633
+ }
634
+
635
+ async getCustomerActiveRentals(
636
+ loginUser: LoginUser,
637
+ dbTransaction: any,
638
+ CustomerId: string,
639
+ ): Promise<IRentalAttr[]> {
640
+ try {
641
+ // Part 1: Privilege Checking
642
+ // Call loginUser.checkPrivileges() by passing:
643
+ // SystemCode: <get_from_app_config>
644
+ // PrivilegeCode: "RENTAL_VIEW"
645
+ const systemCode =
646
+ ApplicationConfig.getComponentConfigValue('system-code');
647
+ const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
648
+
649
+ if (!isPrivileged) {
650
+ throw new ClassError(
651
+ 'Rental',
652
+ 'RentalErrMsg01',
653
+ "You do not have 'Rental - View' privilege.",
654
+ );
655
+ }
656
+
657
+ // Part 2: Retrieve Rentals and Returns
658
+ // Call Rental._Repo findAll method by passing:
659
+ // where:
660
+ // Status: "Active"
661
+ // [Op.OR]:
662
+ // CustomerId: Params.CustomerId
663
+ // '$JointHirers.CustomerId$': Params.CustomerId
664
+ // include:
665
+ // model: JointHirerModel
666
+ // attributes: []
667
+ const query = `
668
+ SELECT
669
+ r.*
670
+ FROM
671
+ rental_Rental r
672
+ LEFT JOIN
673
+ rental_JointHirer jh ON r.RentalId = jh.RentalId
674
+ WHERE
675
+ r.Status = 'Active'
676
+ AND (
677
+ r.CustomerId = '${CustomerId}'
678
+ OR jh.CustomerId = '${CustomerId}'
679
+ )
680
+ GROUP BY
681
+ r.RentalId
682
+ `;
683
+ const db = rentalDb.getConnection();
684
+ const result = await db.query(query, {
685
+ type: QueryTypes.SELECT,
686
+ transaction: dbTransaction,
687
+ model: RentalModel,
688
+ mapToModel: true,
689
+ });
690
+
691
+ // Format the returned records
692
+ const records: IRentalAttr[] = result.map((record: RentalModel) => {
693
+ return {
694
+ RentalId: record.RentalId,
695
+ CustomerId: record.CustomerId,
696
+ CustomerType: record.CustomerType,
697
+ ItemId: record.ItemId,
698
+ ItemType: record.ItemType,
699
+ PriceId: record.PriceId,
700
+ StartDateTime: record.StartDateTime,
701
+ EndDateTime: record.EndDateTime,
702
+ CancelRemarks: record.CancelRemarks,
703
+ TerminateRemarks: record.TerminateRemarks,
704
+ AgreementNo: record.AgreementNo,
705
+ AccountType: record.AccountType,
706
+ Status: record.Status,
707
+ EscheatmentYN: record.EscheatmentYN,
708
+ CreatedById: record.CreatedById,
709
+ CreatedAt: record.CreatedAt,
710
+ UpdatedById: record.UpdatedById,
711
+ UpdatedAt: record.UpdatedAt,
712
+ };
713
+ });
714
+ // Return the returned records.
715
+ return records;
716
+ } catch (error) {
717
+ throw error;
718
+ }
719
+ }
720
+
721
+ async getCustomerIds(loginUser: LoginUser, dbTransaction: any) {
722
+ try {
723
+ // Part 1: Privilege Checking
724
+ // Call loginUser.checkPrivileges() by passing:
725
+ // SystemCode: <get_from_app_config>
726
+ // PrivilegeCode: "RENTAL_VIEW"
727
+ const systemCode =
728
+ ApplicationConfig.getComponentConfigValue('system-code');
729
+ const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_VIEW');
730
+
731
+ if (!isPrivileged) {
732
+ throw new ClassError(
733
+ 'Rental',
734
+ 'RentalErrMsg01',
735
+ "You do not have 'Rental - View' privilege.",
736
+ );
737
+ }
738
+
739
+ // Part 2: Validation
740
+ // Make sure this.RentalId exists, if not throw new ClassError by passing
741
+ if (!this.RentalId) {
742
+ throw new ClassError('Rental', 'RentalErrMsg01', 'RentalId is missing');
743
+ }
744
+
745
+ //Part 3: Retrieve Listing and Returns
746
+ // 3.1 Call Rental._JointHirerRepo findAll method by passing:
747
+ // where:
748
+ // RentalId: this.RentalId
749
+ // dbTransaction
750
+ const options: any = {
751
+ where: {
752
+ RentalId: this.RentalId,
753
+ },
754
+ transaction: dbTransaction,
755
+ };
756
+
757
+ const joinHirers = await Rental._JointHirerRepo.findAll(options);
758
+
759
+ // 3.2 Create a new array variable and set its value to joint hirer and Rental.CustomerId
760
+ // array translated to only CustomerId. Then, append this.CustomerId to the array.
761
+ const customerIds: string[] = [this.CustomerId];
762
+ for (let i = 0; i < joinHirers.length; i++) {
763
+ const jointHirer = joinHirers[i];
764
+ customerIds.push(jointHirer.CustomerId);
765
+ }
766
+
767
+ // 3.3 Return the array.
768
+ return customerIds;
769
+ } catch (error) {
770
+ throw error;
771
+ }
772
+ }
773
+
774
+ async signAgreement(
775
+ loginUser: LoginUser,
776
+ CustomerId: string,
777
+ dbTransaction: any,
778
+ ) {
779
+ try {
780
+ // Part 1: Privilege Checking
781
+ // Call loginUser.checkPrivileges() by passing:
782
+ // SystemCode: <get_from_app_config>
783
+ // PrivilegeCode: "RENTAL_SIGN"
784
+ const systemCode =
785
+ ApplicationConfig.getComponentConfigValue('system-code');
786
+ const isPrivileged = loginUser.checkPrivileges(systemCode, 'RENTAL_SIGN');
787
+
788
+ if (!isPrivileged) {
789
+ throw new ClassError(
790
+ 'Rental',
791
+ 'RentalErrMsg01',
792
+ "You do not have 'RENTAL_SIGN' privilege.",
793
+ );
794
+ }
795
+
796
+ // Part 2: Validation
797
+ // Make sure this.Status is "Pending Signing" and agreementStatus is "Generated", if not throw new ClassError
798
+ const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
799
+ if (
800
+ this.Status !== RentalStatusEnum.PENDING_SIGNING &&
801
+ agreement.Status !== AggrementStatusEnum.GENERATED
802
+ ) {
803
+ throw new ClassError(
804
+ 'Rental',
805
+ 'RentalErrMsg01',
806
+ 'Rental is not ready to be signed yet.',
807
+ );
808
+ }
809
+
810
+ //Make sure CustomerId inside the listing and SignatureStatus is "Pending"
811
+ const signatureList = await Agreement.getSignatureList(
812
+ loginUser,
813
+ this.AgreementNo,
814
+ dbTransaction,
815
+ );
816
+ const customerSignature = signatureList.find(
817
+ (sig) =>
818
+ sig.CustomerId === CustomerId &&
819
+ sig.SignatureStatus === HirerSignatureStatusEnum.PENDING,
820
+ );
821
+
822
+ if (!customerSignature) {
823
+ throw new ClassError(
824
+ 'Rental',
825
+ 'RentalErrMsg01',
826
+ 'Customer signature is not pending.',
827
+ );
828
+ }
829
+
830
+ //Update rental_HirerSignature
831
+ const signaturePayload = {
832
+ SignatureStatus: HirerSignatureStatusEnum.SIGNED,
833
+ SignedAt: new Date(),
834
+ UpdatedById: loginUser.ObjectId,
835
+ UpdatedAt: new Date(),
836
+ };
837
+
838
+ await Rental._HirerSignatureRepo.update(signaturePayload, {
839
+ where: {
840
+ AgreementNo: this.AgreementNo,
841
+ CustomerId: CustomerId,
842
+ },
843
+ transaction: dbTransaction,
844
+ });
845
+
846
+ const signatureActivity = new Activity();
847
+ signatureActivity.ActivityId = signatureActivity.createId();
848
+ signatureActivity.Action = ActionEnum.UPDATE;
849
+ signatureActivity.Description = 'Update hirer signature';
850
+ signatureActivity.EntityType = 'HirerSignature';
851
+ signatureActivity.EntityId = this.AgreementNo;
852
+ signatureActivity.EntityValueBefore = JSON.stringify(customerSignature);
853
+ signatureActivity.EntityValueAfter = JSON.stringify(signaturePayload);
854
+ await signatureActivity.create(loginUser.ObjectId, dbTransaction);
855
+
856
+ const updatedSignatureList = await Agreement.getSignatureList(
857
+ loginUser,
858
+ this.AgreementNo,
859
+ dbTransaction,
860
+ );
861
+
862
+ const pendingSignatures = updatedSignatureList.filter(
863
+ (sig) => sig.SignatureStatus === 'Pending',
864
+ );
865
+ if (pendingSignatures.length > 0) {
866
+ return;
867
+ }
868
+
869
+ //Part 3: Update Agreement Status
870
+ // 3.1 Set EntityValueBefore to current agreement instance.
871
+ const entityValueAgreementBefore = {
872
+ AgreementNo: agreement.AgreementNo,
873
+ Status: agreement.Status,
874
+ DateSigned: agreement.DateSigned,
875
+ };
876
+
877
+ // 3.2 Set below agreement attributes:
878
+ // DateSigned: current date & time
879
+ // Status: "Signed"
880
+ const payload = {
881
+ DateSigned: new Date(),
882
+ Status: AggrementStatusEnum.SIGNED,
883
+ };
884
+
885
+ // 3.3 Call Rental._AgreementRepo update()
886
+ await Rental._AgreementRepo.update(payload, {
887
+ where: {
888
+ AgreementNo: this.AgreementNo,
889
+ },
890
+ transaction: dbTransaction,
891
+ });
892
+
893
+ const entityValueAgreementAfter = {
894
+ entityValueAgreementBefore,
895
+ ...payload,
896
+ };
897
+
898
+ // Part 4: Record Update Agreement Activity
899
+ const activity = new Activity();
900
+ activity.ActivityId = activity.createId();
901
+ activity.Action = ActionEnum.UPDATE;
902
+ activity.Description = 'Update agreement';
903
+ activity.EntityType = 'RentalAgreement';
904
+ activity.EntityId = this.AgreementNo;
905
+ activity.EntityValueBefore = JSON.stringify(entityValueAgreementBefore);
906
+ activity.EntityValueAfter = JSON.stringify(entityValueAgreementAfter);
907
+ await activity.create(loginUser.ObjectId, dbTransaction);
908
+
909
+ // Part 5: Update Rental Status
910
+ // 5.1 Set EntityValueBefore to current rental instance.
911
+ const entityValueRentalBefore = {
912
+ RentalId: this.RentalId,
913
+ CustomerId: this.CustomerId,
914
+ CustomerType: this.CustomerType,
915
+ ItemId: this.ItemId,
916
+ ItemType: this.ItemType,
917
+ PriceId: this.PriceId,
918
+ StartDateTime: this.StartDateTime,
919
+ EndDateTime: this.EndDateTime,
920
+ CancelRemarks: this.CancelRemarks,
921
+ TerminateRemarks: this.TerminateRemarks,
922
+ AgreementNo: this.AgreementNo,
923
+ AccountType: this.AccountType,
924
+ Status: this.Status,
925
+ EscheatmentYN: this.EscheatmentYN,
926
+ CreatedById: this.CreatedById,
927
+ CreatedAt: this.CreatedAt,
928
+ UpdatedById: this.UpdatedById,
929
+ UpdatedAt: this.UpdatedAt,
930
+ };
931
+ // 5.2: Set below rental attributes:
932
+ // Status: "Pending Key Collection"
933
+ // UpdatedById: loginUser.ObjectId
934
+ // UpdatedAt: current date & time
935
+
936
+ const payloadRental = {
937
+ Status: RentalStatusEnum.PENDING_KEY_COLLECTION,
938
+ UpdatedById: loginUser.ObjectId,
939
+ UpdatedAt: new Date(),
940
+ };
941
+
942
+ // 5.3: Call Rental._Repo update() method by passing:
943
+ // populate rental instance attributes
944
+ // dbTransaction
945
+ await Rental._Repo.update(payloadRental, {
946
+ where: {
947
+ RentalId: this.RentalId,
948
+ },
949
+ transaction: dbTransaction,
950
+ });
951
+
952
+ const entityValueRentalAfter = {
953
+ entityValueRentalBefore,
954
+ ...payloadRental,
955
+ };
956
+
957
+ this._Status = RentalStatusEnum.PENDING_KEY_COLLECTION;
958
+
959
+ // Part 6: Record Update Agreement Activity
960
+ const rentalActivity = new Activity();
961
+ rentalActivity.ActivityId = activity.createId();
962
+ rentalActivity.Action = ActionEnum.UPDATE;
963
+ rentalActivity.Description = 'Sign rental agreement';
964
+ rentalActivity.EntityType = 'Rental';
965
+ rentalActivity.EntityId = this.RentalId;
966
+ rentalActivity.EntityValueBefore = JSON.stringify(
967
+ entityValueRentalBefore,
968
+ );
969
+ rentalActivity.EntityValueAfter = JSON.stringify(entityValueRentalAfter);
970
+ await rentalActivity.create(loginUser.ObjectId, dbTransaction);
971
+ } catch (error) {
972
+ throw error;
973
+ }
974
+ }
975
+
976
+ async generateAgreement(
977
+ loginUser: LoginUser,
978
+ dbTransaction: any,
979
+ MediaId: string,
980
+ ) {
981
+ //This method will generate a new rental agreement.
982
+ try {
983
+ // Part 1: Privilege Checking
984
+ // Call loginUser.checkPrivileges() by passing:
985
+ // SystemCode: "<get_from_app_config>"
986
+ // PrivilegeCode: "RENTAL_AGREEMENT_CREATE"
987
+
988
+ const systemCode =
989
+ ApplicationConfig.getComponentConfigValue('system-code');
990
+ const isPrivileged = loginUser.checkPrivileges(
991
+ systemCode,
992
+ 'RENTAL_AGREEMENT_CREATE',
993
+ );
994
+
995
+ if (!isPrivileged) {
996
+ throw new ClassError(
997
+ 'Rental',
998
+ 'RentalErrMsg01',
999
+ "You do not have 'RENTAL_AGREEMENT_CREATE' privilege.",
1000
+ );
1001
+ }
1002
+
1003
+ // Part 2: Validation
1004
+ // Instantiate existing RentalAgreement by passing:
1005
+ // dbTransaction
1006
+ // AgreementNo: this.AgreementNo
1007
+ // Make sure Params.MediaId exists, if not throw new ClassError by passing:
1008
+ // Classname: "Rental"
1009
+ // MessageCode: "RentalErrMsg0X"
1010
+ // Message: "MediaId is required."
1011
+ const agreement = await Agreement.init(this.AgreementNo, dbTransaction);
1012
+ if (!MediaId) {
1013
+ throw new ClassError(
1014
+ 'Rental',
1015
+ 'RentalErrMsg0X',
1016
+ 'MediaId is required.',
1017
+ );
1018
+ }
1019
+
1020
+ // Part 3: Update Agreement Record
1021
+ // Set EntityValueBefore to the agreement instance.
1022
+ // Set below agreement attributes:
1023
+ // Status: "Generated"
1024
+ // MediaId: Params.MediaId
1025
+
1026
+ const EntityValueBefore = agreement;
1027
+ agreement.Status = AggrementStatusEnum.GENERATED;
1028
+ agreement.MediaId = MediaId;
1029
+
1030
+ // Call Rental._AgreementRepo update method by passing:
1031
+ // Status: agreement.Status
1032
+ // MediaId: agreement.MediaId
1033
+ // dbTransaction
1034
+
1035
+ await Rental._AgreementRepo.update(
1036
+ {
1037
+ Status: agreement.Status,
1038
+ MediaId: agreement.MediaId,
1039
+ },
1040
+ {
1041
+ where: {
1042
+ AgreementNo: this.AgreementNo,
1043
+ },
1044
+ transaction: dbTransaction,
1045
+ },
1046
+ );
1047
+
1048
+ // Part 4: Record Update Agreement Activity
1049
+ // Initialise EntityValueAfter variable and set to agreement instance
1050
+ const EntityValueAfter = agreement;
1051
+ // Instantiate new activity from Activity class, call createId() method, then set:
1052
+ const activity = new Activity();
1053
+ // Action: ActionEnum.UPDATE
1054
+ // Description: "Generate agreement."
1055
+ // EntityType: "RentalAgreement"
1056
+ // EntityId: this.AgreementNo
1057
+ // EntityValueBefore: EntityValueBefore
1058
+ // EntityValueAfter: EntityValueAfter
1059
+ activity.ActivityId = activity.createId();
1060
+ activity.Action = ActionEnum.UPDATE;
1061
+ activity.Description = 'Generate agreement.';
1062
+ activity.EntityType = 'RentalAgreement';
1063
+ activity.EntityId = this.AgreementNo;
1064
+ activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
1065
+ activity.EntityValueAfter = JSON.stringify(EntityValueAfter);
1066
+
1067
+ // Call new activity create method by passing:
1068
+ // dbTransaction
1069
+ // userId: loginUser.ObjectId
1070
+ await activity.create(loginUser.ObjectId, dbTransaction);
1071
+
1072
+ // Part 5: Add Record To Agreement History Table
1073
+ // Make sure there is a private static readonly _AgreementHistoryRepo variable.
1074
+ // Set newRecord to an object with below key-value:
1075
+ // AgreementNo: this.AgreementNo
1076
+ // MediaId: params.MediaId
1077
+ // ActivityCompleted: "Agreement Generated"
1078
+ // CreatedAt: Current Timestamp
1079
+ // Use _AgreementHistoryRepo.create() method and pass newRecord and dbTransaction.
1080
+ const agreementHistory = await Rental._AgreementHistoryRepo.create(
1081
+ {
1082
+ AgreementNo: this.AgreementNo,
1083
+ MediaId,
1084
+ ActivityCompleted: 'Agreement Generated',
1085
+ CreatedAt: new Date(),
1086
+ },
1087
+ { transaction: dbTransaction },
1088
+ );
1089
+
1090
+ // Part 6: Record Agreement History Activity
1091
+ // Initialise EntityValueAfter variable and set to newRecord from previous part.
1092
+ // Instantiate new activity from Activity class, call createId() method, then set:
1093
+ // Action: ActionEnum.CREATE
1094
+ // Description: "Generate agreement."
1095
+ // EntityType: "RentalAgreementHistory"
1096
+ // EntityId: HistoryId from previous part.
1097
+ // EntityValueBefore: empty object.
1098
+ // EntityValueAfter: EntityValueAfter
1099
+ // Call new activity create method by passing:
1100
+ // dbTransaction
1101
+ // userId: loginUser.ObjectId
1102
+ const entityValueAfter = agreementHistory.get({ plain: true });
1103
+ const activityHistory = new Activity();
1104
+ activityHistory.ActivityId = activityHistory.createId();
1105
+ activityHistory.Action = ActionEnum.CREATE;
1106
+ activityHistory.Description = 'Generate agreement.';
1107
+ activityHistory.EntityType = 'RentalAgreementHistory';
1108
+ activityHistory.EntityId = agreementHistory.HistoryId.toString();
1109
+ activityHistory.EntityValueBefore = JSON.stringify({});
1110
+ activityHistory.EntityValueAfter = JSON.stringify(entityValueAfter);
1111
+ await activityHistory.create(loginUser.ObjectId, dbTransaction);
1112
+ } catch (error) {
1113
+ throw error;
1114
+ }
1115
+ }
1116
+ }