@tomei/rental 0.12.2 → 0.12.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 (147) hide show
  1. package/.commitlintrc.json +22 -22
  2. package/.eslintrc +16 -16
  3. package/.eslintrc.js +35 -35
  4. package/.gitlab-ci.yml +16 -16
  5. package/.husky/commit-msg +15 -15
  6. package/.husky/pre-commit +7 -7
  7. package/.prettierrc +4 -4
  8. package/Jenkinsfile +51 -51
  9. package/README.md +8 -8
  10. package/eslint.config.mjs +37 -37
  11. package/jest.config.js +10 -10
  12. package/migrations/booking-table-migration.js +79 -79
  13. package/migrations/joint-hirer-table-migration.js +52 -52
  14. package/migrations/rental-aggrement-table-migration.js +30 -30
  15. package/migrations/rental-price-table-migration.js +32 -32
  16. package/migrations/rental-table-migrations.js +96 -96
  17. package/package.json +78 -78
  18. package/sonar-project.properties +12 -12
  19. package/src/components/agreement/agreement.repository.ts +54 -54
  20. package/src/components/agreement/agreement.ts +49 -49
  21. package/src/components/booking/booking.repository.ts +51 -51
  22. package/src/components/booking/booking.ts +492 -492
  23. package/src/components/joint-hirer/joint-hirer.repository.ts +54 -54
  24. package/src/components/joint-hirer/joint-hirer.ts +137 -137
  25. package/src/components/rental/rental.repository.ts +51 -51
  26. package/src/components/rental/rental.ts +963 -963
  27. package/src/components/rental-price/rental-price.repository.ts +54 -54
  28. package/src/components/rental-price/rental-price.ts +100 -100
  29. package/src/database.ts +27 -27
  30. package/src/enum/account-type.enum.ts +4 -4
  31. package/src/enum/booking.enum.ts +5 -5
  32. package/src/enum/index.ts +11 -11
  33. package/src/enum/rental-status.enum.ts +39 -39
  34. package/src/index.ts +28 -28
  35. package/src/interfaces/agreement-attr.interface.ts +7 -7
  36. package/src/interfaces/booking-attr.interface.ts +19 -19
  37. package/src/interfaces/booking-find-all-search-attr.interface.ts +12 -12
  38. package/src/interfaces/index.ts +15 -15
  39. package/src/interfaces/joint-hirer-attr.interface.ts +10 -10
  40. package/src/interfaces/rental-attr.interface.ts +25 -25
  41. package/src/interfaces/rental-find-all-search-attr.interface.ts +11 -11
  42. package/src/interfaces/rental-price-attr.interface.ts +7 -7
  43. package/src/models/agreement.entity.ts +39 -39
  44. package/src/models/booking.entity.ts +105 -105
  45. package/src/models/index.ts +13 -13
  46. package/src/models/joint-hirer.entity.ts +63 -63
  47. package/src/models/rental-price.entity.ts +38 -38
  48. package/src/models/rental.entity.ts +133 -133
  49. package/tsconfig.build.json +5 -5
  50. package/tsconfig.json +23 -23
  51. package/dist/index.d.ts +0 -1
  52. package/dist/index.js +0 -18
  53. package/dist/index.js.map +0 -1
  54. package/dist/src/components/agreement/agreement.d.ts +0 -17
  55. package/dist/src/components/agreement/agreement.js +0 -50
  56. package/dist/src/components/agreement/agreement.js.map +0 -1
  57. package/dist/src/components/agreement/agreement.repository.d.ts +0 -8
  58. package/dist/src/components/agreement/agreement.repository.js +0 -67
  59. package/dist/src/components/agreement/agreement.repository.js.map +0 -1
  60. package/dist/src/components/booking/booking.d.ts +0 -46
  61. package/dist/src/components/booking/booking.js +0 -314
  62. package/dist/src/components/booking/booking.js.map +0 -1
  63. package/dist/src/components/booking/booking.repository.d.ts +0 -8
  64. package/dist/src/components/booking/booking.repository.js +0 -67
  65. package/dist/src/components/booking/booking.repository.js.map +0 -1
  66. package/dist/src/components/joint-hirer/joint-hirer.d.ts +0 -23
  67. package/dist/src/components/joint-hirer/joint-hirer.js +0 -106
  68. package/dist/src/components/joint-hirer/joint-hirer.js.map +0 -1
  69. package/dist/src/components/joint-hirer/joint-hirer.repository.d.ts +0 -8
  70. package/dist/src/components/joint-hirer/joint-hirer.repository.js +0 -67
  71. package/dist/src/components/joint-hirer/joint-hirer.repository.js.map +0 -1
  72. package/dist/src/components/rental/rental.d.ts +0 -59
  73. package/dist/src/components/rental/rental.js +0 -619
  74. package/dist/src/components/rental/rental.js.map +0 -1
  75. package/dist/src/components/rental/rental.repository.d.ts +0 -8
  76. package/dist/src/components/rental/rental.repository.js +0 -67
  77. package/dist/src/components/rental/rental.repository.js.map +0 -1
  78. package/dist/src/components/rental-price/rental-price.d.ts +0 -18
  79. package/dist/src/components/rental-price/rental-price.js +0 -79
  80. package/dist/src/components/rental-price/rental-price.js.map +0 -1
  81. package/dist/src/components/rental-price/rental-price.repository.d.ts +0 -8
  82. package/dist/src/components/rental-price/rental-price.repository.js +0 -67
  83. package/dist/src/components/rental-price/rental-price.repository.js.map +0 -1
  84. package/dist/src/database.d.ts +0 -4
  85. package/dist/src/database.js +0 -25
  86. package/dist/src/database.js.map +0 -1
  87. package/dist/src/enum/account-type.enum.d.ts +0 -4
  88. package/dist/src/enum/account-type.enum.js +0 -9
  89. package/dist/src/enum/account-type.enum.js.map +0 -1
  90. package/dist/src/enum/aggrement-status.enum.d.ts +0 -5
  91. package/dist/src/enum/aggrement-status.enum.js +0 -10
  92. package/dist/src/enum/aggrement-status.enum.js.map +0 -1
  93. package/dist/src/enum/booking.enum.d.ts +0 -5
  94. package/dist/src/enum/booking.enum.js +0 -10
  95. package/dist/src/enum/booking.enum.js.map +0 -1
  96. package/dist/src/enum/index.d.ts +0 -5
  97. package/dist/src/enum/index.js +0 -12
  98. package/dist/src/enum/index.js.map +0 -1
  99. package/dist/src/enum/rental-status.enum.d.ts +0 -9
  100. package/dist/src/enum/rental-status.enum.js +0 -14
  101. package/dist/src/enum/rental-status.enum.js.map +0 -1
  102. package/dist/src/index.d.ts +0 -15
  103. package/dist/src/index.js +0 -43
  104. package/dist/src/index.js.map +0 -1
  105. package/dist/src/interfaces/agreement-attr.interface.d.ts +0 -7
  106. package/dist/src/interfaces/agreement-attr.interface.js +0 -3
  107. package/dist/src/interfaces/agreement-attr.interface.js.map +0 -1
  108. package/dist/src/interfaces/booking-attr.interface.d.ts +0 -18
  109. package/dist/src/interfaces/booking-attr.interface.js +0 -3
  110. package/dist/src/interfaces/booking-attr.interface.js.map +0 -1
  111. package/dist/src/interfaces/booking-find-all-search-attr.interface.d.ts +0 -12
  112. package/dist/src/interfaces/booking-find-all-search-attr.interface.js +0 -3
  113. package/dist/src/interfaces/booking-find-all-search-attr.interface.js.map +0 -1
  114. package/dist/src/interfaces/index.d.ts +0 -7
  115. package/dist/src/interfaces/index.js +0 -3
  116. package/dist/src/interfaces/index.js.map +0 -1
  117. package/dist/src/interfaces/joint-hirer-attr.interface.d.ts +0 -10
  118. package/dist/src/interfaces/joint-hirer-attr.interface.js +0 -3
  119. package/dist/src/interfaces/joint-hirer-attr.interface.js.map +0 -1
  120. package/dist/src/interfaces/rental-attr.interface.d.ts +0 -24
  121. package/dist/src/interfaces/rental-attr.interface.js +0 -3
  122. package/dist/src/interfaces/rental-attr.interface.js.map +0 -1
  123. package/dist/src/interfaces/rental-find-all-search-attr.interface.d.ts +0 -10
  124. package/dist/src/interfaces/rental-find-all-search-attr.interface.js +0 -3
  125. package/dist/src/interfaces/rental-find-all-search-attr.interface.js.map +0 -1
  126. package/dist/src/interfaces/rental-price-attr.interface.d.ts +0 -7
  127. package/dist/src/interfaces/rental-price-attr.interface.js +0 -3
  128. package/dist/src/interfaces/rental-price-attr.interface.js.map +0 -1
  129. package/dist/src/models/agreement.entity.d.ts +0 -10
  130. package/dist/src/models/agreement.entity.js +0 -60
  131. package/dist/src/models/agreement.entity.js.map +0 -1
  132. package/dist/src/models/booking.entity.d.ts +0 -21
  133. package/dist/src/models/booking.entity.js +0 -128
  134. package/dist/src/models/booking.entity.js.map +0 -1
  135. package/dist/src/models/index.d.ts +0 -6
  136. package/dist/src/models/index.js +0 -14
  137. package/dist/src/models/index.js.map +0 -1
  138. package/dist/src/models/joint-hirer.entity.d.ts +0 -13
  139. package/dist/src/models/joint-hirer.entity.js +0 -79
  140. package/dist/src/models/joint-hirer.entity.js.map +0 -1
  141. package/dist/src/models/rental-price.entity.d.ts +0 -8
  142. package/dist/src/models/rental-price.entity.js +0 -59
  143. package/dist/src/models/rental-price.entity.js.map +0 -1
  144. package/dist/src/models/rental.entity.d.ts +0 -29
  145. package/dist/src/models/rental.entity.js +0 -160
  146. package/dist/src/models/rental.entity.js.map +0 -1
  147. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -1,492 +1,492 @@
1
- import { ClassError, ObjectBase } from '@tomei/general';
2
- import { BookingStatusEnum } from '../../enum/booking.enum';
3
- import { IBookingAttr } from '../../interfaces/booking-attr.interface';
4
- import { BookingRepository } from './booking.repository';
5
- import { DateTime } from 'luxon';
6
- import { LoginUser } from '@tomei/sso';
7
- import { RentalPrice } from '../rental-price/rental-price';
8
- import { ApplicationConfig } from '@tomei/config';
9
- import { Op, where, WhereOptions } from 'sequelize';
10
- import { Rental } from '../rental/rental';
11
- import { ActionEnum, Activity } from '@tomei/activity-history';
12
- import { IBookingFindAllSearchAttr } from '../../interfaces/booking-find-all-search-attr.interface';
13
-
14
- export class Booking extends ObjectBase {
15
- ObjectId: string;
16
- ObjectName: string;
17
- ObjectType: string = 'Booking';
18
- TableName = 'booking_Booking';
19
- CustomerId: string;
20
- CustomerType: string;
21
- ItemId: string;
22
- ItemType: string;
23
- PriceId: string;
24
- protected _ScheduledStartDateTime: Date;
25
- protected _ScheduledEndDateTime: Date;
26
- BookingFee: number;
27
- Status: BookingStatusEnum;
28
- CancelRemarks: string;
29
- protected _CreatedById: string;
30
- protected _CreatedAt: Date;
31
- protected _UpdatedById: string;
32
- protected _UpdatedAt: Date;
33
-
34
- protected static _Repo = new BookingRepository();
35
-
36
- get BookingNo(): string {
37
- return this.ObjectId;
38
- }
39
-
40
- set BookingNo(value: string) {
41
- this.ObjectId = value;
42
- }
43
-
44
- get CreatedById(): string {
45
- return this._CreatedById;
46
- }
47
-
48
- get CreatedAt(): Date {
49
- return this._CreatedAt;
50
- }
51
-
52
- get UpdatedById(): string {
53
- return this._UpdatedById;
54
- }
55
-
56
- get UpdatedAt(): Date {
57
- return this._UpdatedAt;
58
- }
59
-
60
- get ScheduledStartDateTime(): Date {
61
- return this._ScheduledStartDateTime;
62
- }
63
-
64
- get ScheduledEndDateTime(): Date {
65
- return this._ScheduledEndDateTime;
66
- }
67
-
68
- async setScheduledStartDateTime(datetime: Date) {
69
- const dateTime = DateTime.fromJSDate(datetime);
70
- const currentDateTime = DateTime.now();
71
- if (dateTime <= currentDateTime) {
72
- throw new ClassError(
73
- 'Booking',
74
- 'BookingErrMsg01',
75
- 'Booking must be for future date time.',
76
- );
77
- }
78
- this._ScheduledStartDateTime = datetime;
79
- }
80
-
81
- async setScheduledEndDateTime(datetime: Date) {
82
- const dateTime = DateTime.fromJSDate(datetime);
83
- const currentDateTime = DateTime.now();
84
- if (dateTime <= currentDateTime) {
85
- throw new ClassError(
86
- 'Booking',
87
- 'BookingErrMsg01',
88
- 'Booking must be for future date time.',
89
- );
90
- }
91
-
92
- if (!this._ScheduledStartDateTime) {
93
- throw new ClassError(
94
- 'Booking',
95
- 'BookingErrMsg01',
96
- 'Booking start date time must be set before setting end date time.',
97
- );
98
- }
99
-
100
- const startDateTime = DateTime.fromJSDate(this._ScheduledStartDateTime);
101
- if (dateTime <= startDateTime) {
102
- throw new ClassError(
103
- 'Booking',
104
- 'BookingErrMsg03',
105
- 'Booking end date time must be more than start date time.',
106
- );
107
- }
108
-
109
- this._ScheduledEndDateTime = datetime;
110
- }
111
-
112
- protected constructor(bookingAttr?: IBookingAttr) {
113
- super();
114
- if (bookingAttr) {
115
- this.ObjectId = bookingAttr.BookingNo;
116
- this.CustomerId = bookingAttr.CustomerId;
117
- this.CustomerType = bookingAttr.CustomerType;
118
- this.ItemId = bookingAttr.ItemId;
119
- this.ItemType = bookingAttr.ItemType;
120
- this.PriceId = bookingAttr.PriceId;
121
- this._ScheduledStartDateTime = bookingAttr.ScheduledStartDateTime;
122
- this._ScheduledEndDateTime = bookingAttr.ScheduledEndDateTime;
123
- this.BookingFee = bookingAttr.BookingFee;
124
- this.Status = bookingAttr.Status;
125
- this.CancelRemarks = bookingAttr.CancelRemarks;
126
- this._CreatedById = bookingAttr.CreatedById;
127
- this._CreatedAt = bookingAttr.CreatedAt;
128
- this._UpdatedById = bookingAttr.UpdatedById;
129
- this._UpdatedAt = bookingAttr.UpdatedAt;
130
- }
131
- }
132
-
133
- public static async init(dbTransaction?: any, bookingNo?: string) {
134
- try {
135
- if (bookingNo) {
136
- const booking = await Booking._Repo.findOne({
137
- where: {
138
- BookingNo: bookingNo,
139
- },
140
- transaction: dbTransaction,
141
- });
142
-
143
- if (booking) {
144
- return new Booking(booking.get({ plain: true }));
145
- } else {
146
- throw new Error('Booking not found');
147
- }
148
- }
149
- return new Booking();
150
- } catch (error) {
151
- throw error;
152
- }
153
- }
154
-
155
- public async create(
156
- dbTransaction: any,
157
- loginUser: LoginUser,
158
- rentalPrice: RentalPrice,
159
- ) {
160
- //This method will create new booking record.
161
- try {
162
- //Part 1: Check Privilege
163
- const systemCode =
164
- ApplicationConfig.getComponentConfigValue('system-code');
165
- const isPrivileged = await loginUser.checkPrivileges(
166
- systemCode,
167
- 'Booking - Create',
168
- );
169
-
170
- if (!isPrivileged) {
171
- throw new ClassError(
172
- 'Booking',
173
- 'BookingErrMsg02',
174
- "You do not have 'Booking - Create' privilege.",
175
- );
176
- }
177
-
178
- //Part 2: Booking Validation
179
- //check if item and booking available
180
- const isRentalAvailable = await Rental.isItemAvailable(
181
- this.ItemId,
182
- this.ItemType,
183
- this._ScheduledStartDateTime,
184
- this._ScheduledEndDateTime,
185
- dbTransaction,
186
- );
187
-
188
- const isBookingAvailable = await Booking.isBookingItemAvailable(
189
- dbTransaction,
190
- this.ItemId,
191
- this.ItemType,
192
- this._ScheduledStartDateTime,
193
- this._ScheduledEndDateTime,
194
- );
195
-
196
- if (!isRentalAvailable || !isBookingAvailable) {
197
- throw new ClassError(
198
- 'Booking',
199
- 'BookingErrMsg02',
200
- 'Booking is not available for the item on the chosen date.',
201
- );
202
- }
203
-
204
- //Part 3: Insert Booking & The Agreed Rental Price
205
- const rp = await rentalPrice.create(loginUser, dbTransaction);
206
-
207
- //Set below Booking attributes:
208
- this.BookingNo = this.createId();
209
- this.PriceId = rp.PriceId;
210
- this._CreatedById = loginUser.ObjectId;
211
- this._CreatedAt = DateTime.now().toJSDate();
212
- this._UpdatedById = loginUser.ObjectId;
213
- this._UpdatedAt = DateTime.now().toJSDate();
214
-
215
- //call this class repo create
216
- const data: IBookingAttr = {
217
- BookingNo: this.BookingNo,
218
- CustomerId: this.CustomerId,
219
- CustomerType: this.CustomerType,
220
- ItemId: this.ItemId,
221
- ItemType: this.ItemType,
222
- PriceId: this.PriceId,
223
- ScheduledStartDateTime: this._ScheduledStartDateTime,
224
- ScheduledEndDateTime: this._ScheduledEndDateTime,
225
- BookingFee: this.BookingFee,
226
- Status: this.Status,
227
- CancelRemarks: this.CancelRemarks,
228
- CreatedById: this._CreatedById,
229
- CreatedAt: this._CreatedAt,
230
- UpdatedById: this._UpdatedById,
231
- UpdatedAt: this._UpdatedAt,
232
- };
233
-
234
- await Booking._Repo.create(data, {
235
- transaction: dbTransaction,
236
- });
237
-
238
- //Part 4: Record Create Booking Activity
239
- const activity = new Activity();
240
- activity.ActivityId = activity.createId();
241
- activity.Action = ActionEnum.CREATE;
242
- activity.Description = 'Add Booking';
243
- activity.EntityId = this.BookingNo;
244
- activity.EntityType = this.ObjectType;
245
- activity.EntityValueBefore = JSON.stringify({});
246
- activity.EntityValueAfter = JSON.stringify(data);
247
-
248
- await activity.create(loginUser.ObjectId, dbTransaction);
249
-
250
- return this;
251
- } catch (error) {
252
- throw error;
253
- }
254
- }
255
-
256
- public static async isBookingItemAvailable(
257
- dbTransaction: any,
258
- itemId: string,
259
- itemType: string,
260
- startDateTime: Date,
261
- endDateTime: Date,
262
- ) {
263
- //This method will check if booking item available.
264
- try {
265
- //call this class repo findOne method
266
- const booking = await Booking._Repo.findOne({
267
- where: {
268
- ItemId: itemId,
269
- ItemType: itemType,
270
- [Op.and]: [
271
- {
272
- ScheduledStartDateTime: {
273
- [Op.lte]: endDateTime,
274
- },
275
- },
276
- {
277
- ScheduledEndDateTime: {
278
- [Op.gte]: startDateTime,
279
- },
280
- },
281
- ],
282
- },
283
- transaction: dbTransaction,
284
- });
285
-
286
- //if booking record exists, return false.
287
- if (booking) {
288
- return false;
289
- }
290
- return true;
291
- } catch (error) {
292
- throw error;
293
- }
294
- }
295
-
296
- public static async findAll(
297
- dbTransaction: any,
298
- page?: number,
299
- row?: number,
300
- search?: IBookingFindAllSearchAttr,
301
- ) {
302
- try {
303
- const queryObj: any = {};
304
-
305
- let options: any = {
306
- transaction: dbTransaction,
307
- order: [['CreatedAt', 'DESC']],
308
- };
309
-
310
- if (page && row) {
311
- options = {
312
- ...options,
313
- limit: row,
314
- offset: row * (page - 1),
315
- };
316
- }
317
-
318
- if (search) {
319
- Object.entries(search).forEach(([key, value]) => {
320
- if (key === 'ScheduledStartDateTime') {
321
- queryObj[key] = {
322
- [Op.gte]: value,
323
- };
324
- } else if (key === 'ScheduledEndDateTime') {
325
- queryObj[key] = {
326
- [Op.lte]: value,
327
- };
328
- } else {
329
- queryObj[key] = {
330
- [Op.substring]: value,
331
- };
332
- }
333
- });
334
-
335
- options = {
336
- ...options,
337
- where: queryObj,
338
- };
339
- }
340
-
341
- return await Booking._Repo.findAndCountAll(options);
342
- } catch (err) {
343
- throw err;
344
- }
345
- }
346
-
347
- public static async findOne(
348
- loginUser: LoginUser,
349
- dbTransaction: any,
350
- searchOptions: WhereOptions,
351
- ) {
352
- //This method will create new booking record.
353
- try {
354
- // Part 1: Privilege Checking
355
- // Call loginUser.checkPrivileges() by passing:
356
- // SystemCode: "<get_from_app_config>"
357
- // PrivilegeCode: "BOOKING_VIEW"
358
-
359
- const systemCode =
360
- ApplicationConfig.getComponentConfigValue('system-code');
361
- const isPrivileged = await loginUser.checkPrivileges(
362
- systemCode,
363
- 'BOOKING_VIEW',
364
- );
365
-
366
- if (!isPrivileged) {
367
- throw new ClassError(
368
- 'Booking',
369
- 'BookingErrMsg3',
370
- "You do not have 'BOOKING_VIEW' privilege.",
371
- );
372
- }
373
-
374
- // Part 2: Find Booking
375
- // 2.1 Call Booking._Repo findOne by passing:
376
- // where: Params.searchOptions
377
- // dbTransaction
378
-
379
- const record = await Booking._Repo.findOne({
380
- where: searchOptions,
381
- transaction: dbTransaction,
382
- });
383
-
384
- // 2.2 If record found, call Booking.init() method by passing:
385
- // dbTransaction
386
- // BookingNo: record.BookingNo
387
- // Return the booking instance.
388
- // If record not found, return null.
389
- if (record) {
390
- const booking = await Booking.init(dbTransaction, record.BookingNo);
391
- return booking;
392
- } else {
393
- return null;
394
- }
395
- } catch (error) {
396
- throw error;
397
- }
398
- }
399
-
400
- public static async updateLeadToCustomer(
401
- loginUser: LoginUser, //The user performing the operation. Used for tracking who initiated the update.
402
- dbTransaction: any, //The database transaction object to ensure atomicity of the operation.
403
- LeadId: string, //The identifier for the lead whose bookings will be updated.
404
- CustomerId: string, //The identifier of the customer to replace the lead in the booking records.
405
- ) {
406
- // This method updates all booking records where LeadId matches the provided LeadId by replacing the LeadId with the CustomerId and updating the CustomerType from 'Lead' to 'Customer'.
407
- try {
408
- // Part 1: Privilege Checking
409
- // Call loginUser.checkPrivileges() with parameters:
410
- // SystemCode: Retrieve from app config.
411
- // PrivilegeCode: 'BOOKING_UPDATE'.
412
- const systemCode =
413
- ApplicationConfig.getComponentConfigValue('system-code');
414
- const isPrivileged = await loginUser.checkPrivileges(
415
- systemCode,
416
- 'BOOKING_UPDATE',
417
- );
418
- if (!isPrivileged) {
419
- throw new ClassError(
420
- 'Booking',
421
- 'BookingErrMsg02',
422
- "You do not have 'BOOKING_UPDATE' privilege",
423
- );
424
- }
425
- // Part 2: Validation
426
- // Validate the LeadId and CustomerId to ensure they are not null or invalid.
427
- if (!LeadId || !CustomerId) {
428
- throw new ClassError(
429
- 'Booking',
430
- 'BookingErrMsg02',
431
- 'LeadId and CustomerId cannot be null or invalid',
432
- );
433
- }
434
- // Part 3: Retrieve Records
435
- // Retrieve all booking records:
436
- // where
437
- // [Op.AND]:
438
- // CustomerId: Params.LeadId
439
- // CustomerType: "Lead"
440
- // dbTransaction
441
- const bookings = await Booking._Repo.findAll({
442
- where: {
443
- [Op.and]: [{ CustomerId: LeadId }, { CustomerType: 'Lead' }],
444
- },
445
- transaction: dbTransaction,
446
- });
447
- // Part 5: Update Records and Record Activity
448
- // For each matching booking record:
449
- for (const booking of bookings) {
450
- // Update the CustomerId from LeadId to CustomerId.
451
- // Update the CustomerType from 'Lead' to 'Customer'.
452
- // Ensure the UpdatedById and UpdatedAt fields in the SDB booking record are updated to reflect the loginUser and the current timestamp.
453
- const EntityValueBefore = {
454
- ...booking.get({ plain: true }),
455
- };
456
- booking.CustomerId = CustomerId;
457
- booking.CustomerType = 'Customer';
458
- booking.UpdatedById = loginUser.ObjectId;
459
- booking.UpdatedAt = DateTime.now().toJSDate();
460
- await booking.save({
461
- transaction: dbTransaction,
462
- });
463
- // Record Update Activity:
464
- // Initialise EntityValueAfter variable and set it to the updated SDB booking record.
465
- // Instantiate a new activity from the Activity class, and set:
466
- // ActivityId: activity.createId()
467
- // Action: ActionEnum.Update
468
- // Description: "Update Lead to Customer"
469
- // EntityType: "Booking"
470
- // EntityId: <UpdatedBookingRecord>.BookingId
471
- // EntityValueBefore: Stringified representation of the original SDB booking record.
472
- // EntityValueAfter: EntityValueAfter (stringified representation of the updated booking record).
473
- const activity = new Activity();
474
- activity.ActivityId = activity.createId();
475
- activity.Action = ActionEnum.UPDATE;
476
- activity.Description = 'Update Lead to Customer';
477
- activity.EntityType = 'Booking';
478
- activity.EntityId = booking.BookingNo;
479
- activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
480
- activity.EntityValueAfter = JSON.stringify(
481
- booking.get({ plain: true }),
482
- );
483
- // Call the activity create() method by passing:
484
- // dbTransaction
485
- // userId: loginUser.UserId
486
- await activity.create(loginUser.ObjectId, dbTransaction);
487
- }
488
- } catch (error) {
489
- throw error;
490
- }
491
- }
492
- }
1
+ import { ClassError, ObjectBase } from '@tomei/general';
2
+ import { BookingStatusEnum } from '../../enum/booking.enum';
3
+ import { IBookingAttr } from '../../interfaces/booking-attr.interface';
4
+ import { BookingRepository } from './booking.repository';
5
+ import { DateTime } from 'luxon';
6
+ import { LoginUser } from '@tomei/sso';
7
+ import { RentalPrice } from '../rental-price/rental-price';
8
+ import { ApplicationConfig } from '@tomei/config';
9
+ import { Op, where, WhereOptions } from 'sequelize';
10
+ import { Rental } from '../rental/rental';
11
+ import { ActionEnum, Activity } from '@tomei/activity-history';
12
+ import { IBookingFindAllSearchAttr } from '../../interfaces/booking-find-all-search-attr.interface';
13
+
14
+ export class Booking extends ObjectBase {
15
+ ObjectId: string;
16
+ ObjectName: string;
17
+ ObjectType: string = 'Booking';
18
+ TableName = 'booking_Booking';
19
+ CustomerId: string;
20
+ CustomerType: string;
21
+ ItemId: string;
22
+ ItemType: string;
23
+ PriceId: string;
24
+ protected _ScheduledStartDateTime: Date;
25
+ protected _ScheduledEndDateTime: Date;
26
+ BookingFee: number;
27
+ Status: BookingStatusEnum;
28
+ CancelRemarks: string;
29
+ protected _CreatedById: string;
30
+ protected _CreatedAt: Date;
31
+ protected _UpdatedById: string;
32
+ protected _UpdatedAt: Date;
33
+
34
+ protected static _Repo = new BookingRepository();
35
+
36
+ get BookingNo(): string {
37
+ return this.ObjectId;
38
+ }
39
+
40
+ set BookingNo(value: string) {
41
+ this.ObjectId = value;
42
+ }
43
+
44
+ get CreatedById(): string {
45
+ return this._CreatedById;
46
+ }
47
+
48
+ get CreatedAt(): Date {
49
+ return this._CreatedAt;
50
+ }
51
+
52
+ get UpdatedById(): string {
53
+ return this._UpdatedById;
54
+ }
55
+
56
+ get UpdatedAt(): Date {
57
+ return this._UpdatedAt;
58
+ }
59
+
60
+ get ScheduledStartDateTime(): Date {
61
+ return this._ScheduledStartDateTime;
62
+ }
63
+
64
+ get ScheduledEndDateTime(): Date {
65
+ return this._ScheduledEndDateTime;
66
+ }
67
+
68
+ async setScheduledStartDateTime(datetime: Date) {
69
+ const dateTime = DateTime.fromJSDate(datetime);
70
+ const currentDateTime = DateTime.now();
71
+ if (dateTime <= currentDateTime) {
72
+ throw new ClassError(
73
+ 'Booking',
74
+ 'BookingErrMsg01',
75
+ 'Booking must be for future date time.',
76
+ );
77
+ }
78
+ this._ScheduledStartDateTime = datetime;
79
+ }
80
+
81
+ async setScheduledEndDateTime(datetime: Date) {
82
+ const dateTime = DateTime.fromJSDate(datetime);
83
+ const currentDateTime = DateTime.now();
84
+ if (dateTime <= currentDateTime) {
85
+ throw new ClassError(
86
+ 'Booking',
87
+ 'BookingErrMsg01',
88
+ 'Booking must be for future date time.',
89
+ );
90
+ }
91
+
92
+ if (!this._ScheduledStartDateTime) {
93
+ throw new ClassError(
94
+ 'Booking',
95
+ 'BookingErrMsg01',
96
+ 'Booking start date time must be set before setting end date time.',
97
+ );
98
+ }
99
+
100
+ const startDateTime = DateTime.fromJSDate(this._ScheduledStartDateTime);
101
+ if (dateTime <= startDateTime) {
102
+ throw new ClassError(
103
+ 'Booking',
104
+ 'BookingErrMsg03',
105
+ 'Booking end date time must be more than start date time.',
106
+ );
107
+ }
108
+
109
+ this._ScheduledEndDateTime = datetime;
110
+ }
111
+
112
+ protected constructor(bookingAttr?: IBookingAttr) {
113
+ super();
114
+ if (bookingAttr) {
115
+ this.ObjectId = bookingAttr.BookingNo;
116
+ this.CustomerId = bookingAttr.CustomerId;
117
+ this.CustomerType = bookingAttr.CustomerType;
118
+ this.ItemId = bookingAttr.ItemId;
119
+ this.ItemType = bookingAttr.ItemType;
120
+ this.PriceId = bookingAttr.PriceId;
121
+ this._ScheduledStartDateTime = bookingAttr.ScheduledStartDateTime;
122
+ this._ScheduledEndDateTime = bookingAttr.ScheduledEndDateTime;
123
+ this.BookingFee = bookingAttr.BookingFee;
124
+ this.Status = bookingAttr.Status;
125
+ this.CancelRemarks = bookingAttr.CancelRemarks;
126
+ this._CreatedById = bookingAttr.CreatedById;
127
+ this._CreatedAt = bookingAttr.CreatedAt;
128
+ this._UpdatedById = bookingAttr.UpdatedById;
129
+ this._UpdatedAt = bookingAttr.UpdatedAt;
130
+ }
131
+ }
132
+
133
+ public static async init(dbTransaction?: any, bookingNo?: string) {
134
+ try {
135
+ if (bookingNo) {
136
+ const booking = await Booking._Repo.findOne({
137
+ where: {
138
+ BookingNo: bookingNo,
139
+ },
140
+ transaction: dbTransaction,
141
+ });
142
+
143
+ if (booking) {
144
+ return new Booking(booking.get({ plain: true }));
145
+ } else {
146
+ throw new Error('Booking not found');
147
+ }
148
+ }
149
+ return new Booking();
150
+ } catch (error) {
151
+ throw error;
152
+ }
153
+ }
154
+
155
+ public async create(
156
+ dbTransaction: any,
157
+ loginUser: LoginUser,
158
+ rentalPrice: RentalPrice,
159
+ ) {
160
+ //This method will create new booking record.
161
+ try {
162
+ //Part 1: Check Privilege
163
+ const systemCode =
164
+ ApplicationConfig.getComponentConfigValue('system-code');
165
+ const isPrivileged = await loginUser.checkPrivileges(
166
+ systemCode,
167
+ 'Booking - Create',
168
+ );
169
+
170
+ if (!isPrivileged) {
171
+ throw new ClassError(
172
+ 'Booking',
173
+ 'BookingErrMsg02',
174
+ "You do not have 'Booking - Create' privilege.",
175
+ );
176
+ }
177
+
178
+ //Part 2: Booking Validation
179
+ //check if item and booking available
180
+ const isRentalAvailable = await Rental.isItemAvailable(
181
+ this.ItemId,
182
+ this.ItemType,
183
+ this._ScheduledStartDateTime,
184
+ this._ScheduledEndDateTime,
185
+ dbTransaction,
186
+ );
187
+
188
+ const isBookingAvailable = await Booking.isBookingItemAvailable(
189
+ dbTransaction,
190
+ this.ItemId,
191
+ this.ItemType,
192
+ this._ScheduledStartDateTime,
193
+ this._ScheduledEndDateTime,
194
+ );
195
+
196
+ if (!isRentalAvailable || !isBookingAvailable) {
197
+ throw new ClassError(
198
+ 'Booking',
199
+ 'BookingErrMsg02',
200
+ 'Booking is not available for the item on the chosen date.',
201
+ );
202
+ }
203
+
204
+ //Part 3: Insert Booking & The Agreed Rental Price
205
+ const rp = await rentalPrice.create(loginUser, dbTransaction);
206
+
207
+ //Set below Booking attributes:
208
+ this.BookingNo = this.createId();
209
+ this.PriceId = rp.PriceId;
210
+ this._CreatedById = loginUser.ObjectId;
211
+ this._CreatedAt = DateTime.now().toJSDate();
212
+ this._UpdatedById = loginUser.ObjectId;
213
+ this._UpdatedAt = DateTime.now().toJSDate();
214
+
215
+ //call this class repo create
216
+ const data: IBookingAttr = {
217
+ BookingNo: this.BookingNo,
218
+ CustomerId: this.CustomerId,
219
+ CustomerType: this.CustomerType,
220
+ ItemId: this.ItemId,
221
+ ItemType: this.ItemType,
222
+ PriceId: this.PriceId,
223
+ ScheduledStartDateTime: this._ScheduledStartDateTime,
224
+ ScheduledEndDateTime: this._ScheduledEndDateTime,
225
+ BookingFee: this.BookingFee,
226
+ Status: this.Status,
227
+ CancelRemarks: this.CancelRemarks,
228
+ CreatedById: this._CreatedById,
229
+ CreatedAt: this._CreatedAt,
230
+ UpdatedById: this._UpdatedById,
231
+ UpdatedAt: this._UpdatedAt,
232
+ };
233
+
234
+ await Booking._Repo.create(data, {
235
+ transaction: dbTransaction,
236
+ });
237
+
238
+ //Part 4: Record Create Booking Activity
239
+ const activity = new Activity();
240
+ activity.ActivityId = activity.createId();
241
+ activity.Action = ActionEnum.CREATE;
242
+ activity.Description = 'Add Booking';
243
+ activity.EntityId = this.BookingNo;
244
+ activity.EntityType = this.ObjectType;
245
+ activity.EntityValueBefore = JSON.stringify({});
246
+ activity.EntityValueAfter = JSON.stringify(data);
247
+
248
+ await activity.create(loginUser.ObjectId, dbTransaction);
249
+
250
+ return this;
251
+ } catch (error) {
252
+ throw error;
253
+ }
254
+ }
255
+
256
+ public static async isBookingItemAvailable(
257
+ dbTransaction: any,
258
+ itemId: string,
259
+ itemType: string,
260
+ startDateTime: Date,
261
+ endDateTime: Date,
262
+ ) {
263
+ //This method will check if booking item available.
264
+ try {
265
+ //call this class repo findOne method
266
+ const booking = await Booking._Repo.findOne({
267
+ where: {
268
+ ItemId: itemId,
269
+ ItemType: itemType,
270
+ [Op.and]: [
271
+ {
272
+ ScheduledStartDateTime: {
273
+ [Op.lte]: endDateTime,
274
+ },
275
+ },
276
+ {
277
+ ScheduledEndDateTime: {
278
+ [Op.gte]: startDateTime,
279
+ },
280
+ },
281
+ ],
282
+ },
283
+ transaction: dbTransaction,
284
+ });
285
+
286
+ //if booking record exists, return false.
287
+ if (booking) {
288
+ return false;
289
+ }
290
+ return true;
291
+ } catch (error) {
292
+ throw error;
293
+ }
294
+ }
295
+
296
+ public static async findAll(
297
+ dbTransaction: any,
298
+ page?: number,
299
+ row?: number,
300
+ search?: IBookingFindAllSearchAttr,
301
+ ) {
302
+ try {
303
+ const queryObj: any = {};
304
+
305
+ let options: any = {
306
+ transaction: dbTransaction,
307
+ order: [['CreatedAt', 'DESC']],
308
+ };
309
+
310
+ if (page && row) {
311
+ options = {
312
+ ...options,
313
+ limit: row,
314
+ offset: row * (page - 1),
315
+ };
316
+ }
317
+
318
+ if (search) {
319
+ Object.entries(search).forEach(([key, value]) => {
320
+ if (key === 'ScheduledStartDateTime') {
321
+ queryObj[key] = {
322
+ [Op.gte]: value,
323
+ };
324
+ } else if (key === 'ScheduledEndDateTime') {
325
+ queryObj[key] = {
326
+ [Op.lte]: value,
327
+ };
328
+ } else {
329
+ queryObj[key] = {
330
+ [Op.substring]: value,
331
+ };
332
+ }
333
+ });
334
+
335
+ options = {
336
+ ...options,
337
+ where: queryObj,
338
+ };
339
+ }
340
+
341
+ return await Booking._Repo.findAndCountAll(options);
342
+ } catch (err) {
343
+ throw err;
344
+ }
345
+ }
346
+
347
+ public static async findOne(
348
+ loginUser: LoginUser,
349
+ dbTransaction: any,
350
+ searchOptions: WhereOptions,
351
+ ) {
352
+ //This method will create new booking record.
353
+ try {
354
+ // Part 1: Privilege Checking
355
+ // Call loginUser.checkPrivileges() by passing:
356
+ // SystemCode: "<get_from_app_config>"
357
+ // PrivilegeCode: "BOOKING_VIEW"
358
+
359
+ const systemCode =
360
+ ApplicationConfig.getComponentConfigValue('system-code');
361
+ const isPrivileged = await loginUser.checkPrivileges(
362
+ systemCode,
363
+ 'BOOKING_VIEW',
364
+ );
365
+
366
+ if (!isPrivileged) {
367
+ throw new ClassError(
368
+ 'Booking',
369
+ 'BookingErrMsg3',
370
+ "You do not have 'BOOKING_VIEW' privilege.",
371
+ );
372
+ }
373
+
374
+ // Part 2: Find Booking
375
+ // 2.1 Call Booking._Repo findOne by passing:
376
+ // where: Params.searchOptions
377
+ // dbTransaction
378
+
379
+ const record = await Booking._Repo.findOne({
380
+ where: searchOptions,
381
+ transaction: dbTransaction,
382
+ });
383
+
384
+ // 2.2 If record found, call Booking.init() method by passing:
385
+ // dbTransaction
386
+ // BookingNo: record.BookingNo
387
+ // Return the booking instance.
388
+ // If record not found, return null.
389
+ if (record) {
390
+ const booking = await Booking.init(dbTransaction, record.BookingNo);
391
+ return booking;
392
+ } else {
393
+ return null;
394
+ }
395
+ } catch (error) {
396
+ throw error;
397
+ }
398
+ }
399
+
400
+ public static async updateLeadToCustomer(
401
+ loginUser: LoginUser, //The user performing the operation. Used for tracking who initiated the update.
402
+ dbTransaction: any, //The database transaction object to ensure atomicity of the operation.
403
+ LeadId: string, //The identifier for the lead whose bookings will be updated.
404
+ CustomerId: string, //The identifier of the customer to replace the lead in the booking records.
405
+ ) {
406
+ // This method updates all booking records where LeadId matches the provided LeadId by replacing the LeadId with the CustomerId and updating the CustomerType from 'Lead' to 'Customer'.
407
+ try {
408
+ // Part 1: Privilege Checking
409
+ // Call loginUser.checkPrivileges() with parameters:
410
+ // SystemCode: Retrieve from app config.
411
+ // PrivilegeCode: 'BOOKING_UPDATE'.
412
+ const systemCode =
413
+ ApplicationConfig.getComponentConfigValue('system-code');
414
+ const isPrivileged = await loginUser.checkPrivileges(
415
+ systemCode,
416
+ 'BOOKING_UPDATE',
417
+ );
418
+ if (!isPrivileged) {
419
+ throw new ClassError(
420
+ 'Booking',
421
+ 'BookingErrMsg02',
422
+ "You do not have 'BOOKING_UPDATE' privilege",
423
+ );
424
+ }
425
+ // Part 2: Validation
426
+ // Validate the LeadId and CustomerId to ensure they are not null or invalid.
427
+ if (!LeadId || !CustomerId) {
428
+ throw new ClassError(
429
+ 'Booking',
430
+ 'BookingErrMsg02',
431
+ 'LeadId and CustomerId cannot be null or invalid',
432
+ );
433
+ }
434
+ // Part 3: Retrieve Records
435
+ // Retrieve all booking records:
436
+ // where
437
+ // [Op.AND]:
438
+ // CustomerId: Params.LeadId
439
+ // CustomerType: "Lead"
440
+ // dbTransaction
441
+ const bookings = await Booking._Repo.findAll({
442
+ where: {
443
+ [Op.and]: [{ CustomerId: LeadId }, { CustomerType: 'Lead' }],
444
+ },
445
+ transaction: dbTransaction,
446
+ });
447
+ // Part 5: Update Records and Record Activity
448
+ // For each matching booking record:
449
+ for (const booking of bookings) {
450
+ // Update the CustomerId from LeadId to CustomerId.
451
+ // Update the CustomerType from 'Lead' to 'Customer'.
452
+ // Ensure the UpdatedById and UpdatedAt fields in the SDB booking record are updated to reflect the loginUser and the current timestamp.
453
+ const EntityValueBefore = {
454
+ ...booking.get({ plain: true }),
455
+ };
456
+ booking.CustomerId = CustomerId;
457
+ booking.CustomerType = 'Customer';
458
+ booking.UpdatedById = loginUser.ObjectId;
459
+ booking.UpdatedAt = DateTime.now().toJSDate();
460
+ await booking.save({
461
+ transaction: dbTransaction,
462
+ });
463
+ // Record Update Activity:
464
+ // Initialise EntityValueAfter variable and set it to the updated SDB booking record.
465
+ // Instantiate a new activity from the Activity class, and set:
466
+ // ActivityId: activity.createId()
467
+ // Action: ActionEnum.Update
468
+ // Description: "Update Lead to Customer"
469
+ // EntityType: "Booking"
470
+ // EntityId: <UpdatedBookingRecord>.BookingId
471
+ // EntityValueBefore: Stringified representation of the original SDB booking record.
472
+ // EntityValueAfter: EntityValueAfter (stringified representation of the updated booking record).
473
+ const activity = new Activity();
474
+ activity.ActivityId = activity.createId();
475
+ activity.Action = ActionEnum.UPDATE;
476
+ activity.Description = 'Update Lead to Customer';
477
+ activity.EntityType = 'Booking';
478
+ activity.EntityId = booking.BookingNo;
479
+ activity.EntityValueBefore = JSON.stringify(EntityValueBefore);
480
+ activity.EntityValueAfter = JSON.stringify(
481
+ booking.get({ plain: true }),
482
+ );
483
+ // Call the activity create() method by passing:
484
+ // dbTransaction
485
+ // userId: loginUser.UserId
486
+ await activity.create(loginUser.ObjectId, dbTransaction);
487
+ }
488
+ } catch (error) {
489
+ throw error;
490
+ }
491
+ }
492
+ }