UNPKG

@tomei/rental

Version:
918 lines (818 loc) 30.4 kB
import { ClassError, ObjectBase } from '@tomei/general'; import { RentalHirerChangeRequestRepository } from './rental-hirer-change-request.repository'; import { ApplicationConfig, ComponentConfig } from '@tomei/config'; import { LoginUser } from '@tomei/sso'; import { ActionEnum, Activity } from '@tomei/activity-history'; import { HirerChangeRequestTypeEnum } from '../../enum/rental-hirer-change-request-type'; import { HirerChangeRequestStatusEnum } from '../../enum/rental-hirer-change-request-status'; import { IRentalHirerChangeRequestAttr, IRentalHirerChangeRequestUpdate, } from '../../interfaces'; import { Rental } from '../rental/rental'; import { HirerChangeRequestHirerRoleEnum, RentalAccountTypeEnum, } from '../../enum'; import { Op } from 'sequelize'; import { JointHirerRepository } from '../../components/joint-hirer/joint-hirer.repository'; import { HirerChangeRequestSignatureRepository } from '../hirer-change-request-signature/hirer-change-request-signature.repository'; import { HirerChangeRequestSignature } from '../hirer-change-request-signature/hirer-change-request-signature'; import { HirerChangeRequestNewHirer } from '../hirer-change-request-new-hirer/hirer-change-request-new-hirer'; import { HirerChangeRequestRemoveHirer } from '../hirer-change-request-remove-hirer/hirer-change-request-remove-hirer'; import { JointHirer } from '../joint-hirer/joint-hirer'; export class RentalHirerChangeRequest extends ObjectBase implements IRentalHirerChangeRequestAttr { ObjectId: string; ObjectName: string; ObjectType: string = 'RentalHirerChangeRequest'; TableName: string = 'rental_HirerChangeRequest'; RentalId: string; Type: HirerChangeRequestTypeEnum; Status: HirerChangeRequestStatusEnum; RequestedAt: Date; RequestedById: string; RequestingHirerId: string; RequestingHirerType: HirerChangeRequestHirerRoleEnum; CancelRemarks: string; UpdatedAt: Date; UpdatedById: string; Signatures: HirerChangeRequestSignature[] = []; Rental: Rental | null = null; get RequestId(): string { return this.ObjectId; } set RequestId(value: string) { this.ObjectId = value; } protected static _Repository = new RentalHirerChangeRequestRepository(); protected static _HirerSignatureRepository = new HirerChangeRequestSignatureRepository(); protected static _JointHirerRepository = new JointHirerRepository(); protected constructor( hirerChangeRequestAttr?: IRentalHirerChangeRequestAttr, ) { super(); if (hirerChangeRequestAttr) { this.RequestId = hirerChangeRequestAttr.RequestId; this.RentalId = hirerChangeRequestAttr.RentalId; this.Type = hirerChangeRequestAttr.Type; this.Status = hirerChangeRequestAttr.Status; this.RequestedAt = hirerChangeRequestAttr.RequestedAt; this.RequestedById = hirerChangeRequestAttr.RequestedById; this.RequestingHirerId = hirerChangeRequestAttr.RequestingHirerId; this.RequestingHirerType = hirerChangeRequestAttr.RequestingHirerType; this.CancelRemarks = hirerChangeRequestAttr.CancelRemarks; this.UpdatedAt = hirerChangeRequestAttr.UpdatedAt; this.UpdatedById = hirerChangeRequestAttr.UpdatedById; } } toJSON(): IRentalHirerChangeRequestAttr { return { RequestId: this.RequestId, RentalId: this.RentalId, Type: this.Type, Status: this.Status, RequestedAt: this.RequestedAt, RequestedById: this.RequestedById, RequestingHirerId: this.RequestingHirerId, RequestingHirerType: this.RequestingHirerType, CancelRemarks: this.CancelRemarks, UpdatedAt: this.UpdatedAt, UpdatedById: this.UpdatedById, }; } public static async init(requestId?: string, dbTransaction?: any) { try { if (requestId) { const hirerChangeReq = await RentalHirerChangeRequest._Repository.findByPk( requestId, dbTransaction, ); if (hirerChangeReq) { return new RentalHirerChangeRequest( hirerChangeReq.get({ plain: true }), ); } else { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg00', 'HirerChangeRequest not found', ); } } return new RentalHirerChangeRequest(); } catch (error) { console.error('Error initializing RentalHirerChangeRequest:', error); throw error; } } private async checkForDuplicate(dbTransaction: any) { try { if (!this.RentalId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg08', 'RentalId is required to check for duplicate requests.', ); } const isDuplicate = await RentalHirerChangeRequest._Repository.findAll({ where: { RentalId: this.RentalId, Type: this.Type, Status: { [Op.notIn]: [ HirerChangeRequestStatusEnum.COMPLETED, HirerChangeRequestStatusEnum.CANCELLED, ], }, }, transaction: dbTransaction, }); if (isDuplicate.length > 0) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg04', 'Duplicate record detected.', ); } } catch (error) { throw error; } } public async getSignatures(dbTransaction: any) { try { if (this.Signatures.length > 0) { return this.Signatures; } const hirers = await RentalHirerChangeRequest._HirerSignatureRepository.findAll({ where: { RequestId: this.RequestId, }, transaction: dbTransaction, }); this.Signatures = await Promise.all( hirers.map(async (hirer) => { return await HirerChangeRequestSignature.init( hirer.SignatureId, dbTransaction, ); }), ); return this.Signatures; } catch (error) { throw error; } } private async checkIsAllSigned(dbTransaction) { try { const listUpdatedHirer = await this.getSignatures(dbTransaction); if (listUpdatedHirer.length > 0) { return listUpdatedHirer.every((record) => record.SignedAt !== null); } else { return false; } } catch (error) { throw error; } } private async create(loginUser: LoginUser, dbTransaction?: any) { try { // Part 1: Check Privilege // 1.1 Make sure user got "HIRER_CHANGE_REQUEST" privilege const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'HIRER_CHANGE_REQUEST', ); if (!isPrivileged) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg01', "You do not have 'HIRER_CHANGE_REQUEST' privilege.", ); } // Part 2: Validation Rental // 2.1 Make sure this.RentalId and this.RequestingHirerId got value. if (!this.RentalId || !this.RequestingHirerId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg02', 'RentalId and RequestingHirerId are required.', ); } const rental = await Rental.init(dbTransaction, this.RentalId); // 2.2 Only allow for joint account if (rental.AccountType !== RentalAccountTypeEnum.JOINT) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg03', 'Only Rental with Joint Account Type Allowed.', ); } // 2.3 only allow for rental account where its joint hirer not maximum yet (at this step you've retrieve the number of joint hirer) const jointHirers = await rental.getJointHirers(dbTransaction); if (this.Type === HirerChangeRequestTypeEnum.ADD) { const maxJointHirer = ComponentConfig.getComponentConfigValue( '@tomei/rental', 'maxJointHirerLength', ); if (jointHirers?.length >= maxJointHirer) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg05', 'Joint Hirer exceed max joint hirer length.', ); } } // For Remove type, we don't need to check the max joint hirer length // but we need to ensure that there is at least one joint hirer left after removal if ( this.Type == HirerChangeRequestTypeEnum.REMOVE && jointHirers?.length <= 1 ) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg05', 'Cannot remove the last joint hirer.', ); } // 2.4 avoid duplicate request, call this.checkForDuplicate() private method, this method will ensure no record with same RentalId and same Type and Status is not "Completed" or "Cancelled" await this.checkForDuplicate(dbTransaction); // Part 3: Create Hirer Change Request and Hirer Change Request Hirer Records // 3.1 Set other attributes this.ObjectId = this.createId(); this.RequestedById = loginUser.ObjectId; this.RequestedAt = new Date(); this.UpdatedById = loginUser.ObjectId; this.UpdatedAt = new Date(); this.Status = HirerChangeRequestStatusEnum.PENDINGSIGNATURES; this.RequestingHirerType = rental.CustomerId === this.RequestingHirerId ? HirerChangeRequestHirerRoleEnum.MAIN : HirerChangeRequestHirerRoleEnum.JOINT; // 3.2 Set Entity value after const entityValueAfter: IRentalHirerChangeRequestAttr = { RequestId: this.RequestId, RentalId: this.RentalId, Type: this.Type, Status: this.Status, RequestedById: this.RequestedById, RequestedAt: this.RequestedAt, RequestingHirerId: this.RequestingHirerId, RequestingHirerType: this.RequestingHirerType, CancelRemarks: this.CancelRemarks, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; // 3.3 Call repo class create method by passing the class attributes and db transaction. await RentalHirerChangeRequest._Repository.create(entityValueAfter, { transaction: dbTransaction, }); // Generate signatures, call this.generateSignature by passing: await this.generateSignatures(loginUser, dbTransaction); //Part 4: Record Create Hirer Change Request Activity const activity = new Activity(); activity.ObjectId = this._createId(); activity.Action = ActionEnum.CREATE; activity.Description = this.Type === HirerChangeRequestTypeEnum.ADD ? 'Add New Hirer' : 'Remove Hirer'; activity.EntityId = this.ObjectId; activity.EntityType = this.ObjectType; activity.EntityValueBefore = JSON.stringify({}); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); return this; } catch (error) { throw error; } } public async update( loginUser: LoginUser, dbTransaction: any, params: IRentalHirerChangeRequestUpdate, ) { try { // Part 1: Validation // 1.1 Check if RequestId is not null if null return error if (!this.RequestId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg06', 'RequestId are required.', ); } // Part 2: Update Hirer Change Request Hirer Records // 2.1 Set entityValueBefore const entityValueBefore: IRentalHirerChangeRequestAttr = { RequestId: this.RequestId, RentalId: this.RentalId, Type: this.Type, Status: this.Status, RequestedById: this.RequestedById, RequestedAt: this.RequestedAt, RequestingHirerId: this.RequestingHirerId, RequestingHirerType: this.RequestingHirerType, CancelRemarks: this.CancelRemarks, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; // 2.2 Check if jointHirerId, customerId is null or not let { jointHirerId, customerId } = params; if (!customerId && !jointHirerId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg07', 'At least one of customerId or jointHirerId must be provided.', ); } // 2.3 Get list hirer related to request then find hirer with same CustomerId, if found update the hirer record (make it signed) const listHirer = await this.getSignatures(dbTransaction); if (listHirer?.length > 0) { let foundHirer = listHirer?.find((record) => { record.CustomerId === customerId; }); let hirer = await HirerChangeRequestSignature.init( foundHirer.SignatureId, dbTransaction, ); await hirer.update(loginUser, dbTransaction); } // 2.4 Check if all hirer in request is signed, if yes check request Type: // Type = "Add" = update Request status to “AwaitingNewHirer” // Type = "Remove" = flag the joint hirer record status to "Inactive" & update Request status to "Completed" const isSigned = await this.checkIsAllSigned(dbTransaction); if (isSigned) { if (this.Type === HirerChangeRequestTypeEnum.ADD) { this.Status = HirerChangeRequestStatusEnum.AWAITINGNEWHIRER; } else if (this.Type === HirerChangeRequestTypeEnum.REMOVE) { // TODO: Currently JointHirer dont have status props await RentalHirerChangeRequest._JointHirerRepository.update( { Status: 'Inactive' }, { where: { HirerId: this.RequestId, }, transaction: dbTransaction, }, ); this.Status = HirerChangeRequestStatusEnum.COMPLETED; } } // 2.5 Set entityValueAfter const entityValueAfter: IRentalHirerChangeRequestAttr = { RequestId: this.RequestId, RentalId: this.RentalId, Type: this.Type, Status: this.Status, RequestedById: this.RequestedById, RequestedAt: this.RequestedAt, RequestingHirerId: this.RequestingHirerId, RequestingHirerType: this.RequestingHirerType, CancelRemarks: this.CancelRemarks, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; // 2.6 Call repo class update method by passing the class attributes and db transaction. await RentalHirerChangeRequest._Repository.update(entityValueAfter, { where: { RequestId: this.RequestId, }, transaction: dbTransaction, }); //Part 3: Record Create Hirer Change Request Activity const activity = new Activity(); activity.ObjectId = this._createId(); activity.Action = ActionEnum.UPDATE; activity.Description = 'Update Hirer Change Request'; activity.EntityId = this.ObjectId; activity.EntityType = this.ObjectType; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); return this; } catch (error) { throw error; } } public async generateSignatures(loginUser: LoginUser, dbTransaction?: any) { try { if (!this.RentalId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg09', 'RentalId is required to create signatures.', ); } // 1. If this.Rental is null, set it to Rental instantiation using this.RentalId if (!this.Rental) { this.Rental = await Rental.init(dbTransaction, this.RentalId); } // 2. Prepare all signature data (main + joint) in one go const jointHirers = await this.Rental.getJointHirers(dbTransaction); // Main hirer signature const mainSignature = await HirerChangeRequestSignature.init(); mainSignature.RequestId = this.RequestId; mainSignature.CustomerId = this.Rental.CustomerId; mainSignature.HirerType = HirerChangeRequestHirerRoleEnum.MAIN; mainSignature.Method = 'Upload'; // Joint hirer signatures const jointSignaturePromises = jointHirers.map(async (jointHirer) => { const signature = await HirerChangeRequestSignature.init(); signature.RequestId = this.RequestId; signature.JointHirerId = jointHirer.HirerId; signature.HirerType = HirerChangeRequestHirerRoleEnum.JOINT; signature.Method = 'Upload'; return signature; }); const jointSignatures = await Promise.all(jointSignaturePromises); // 3. Create all signatures in parallel await Promise.all([ mainSignature.create(loginUser, dbTransaction), ...jointSignatures.map((sig) => sig.create(loginUser, dbTransaction)), ]); this.Signatures = [mainSignature, ...jointSignatures]; } catch (error) { console.error('Error creating signatures:', error); throw error; } } public async createAddRequest( newHirer: { FullName: string; IdNo: string; IdType: string; Email?: string; ContactNo: string; Relationship: string; Address: string; City: string; State: string; Postcode: string; Country: string; }, loginUser: LoginUser, dbTransaction?: any, ): Promise<{ hirerChangeRequest: RentalHirerChangeRequest; NewHirer: HirerChangeRequestNewHirer; }> { try { // Validations // 1. Make sure newHirer required fields cannot be null. if ( !newHirer.FullName || !newHirer.IdNo || !newHirer.IdType || !newHirer.ContactNo || !newHirer.Address || !newHirer.City || !newHirer.State || !newHirer.Postcode || !newHirer.Country ) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg10', 'FullName, IdNo, IdType, ContactNo, and Address are required.', ); } // 2. Set this.Type to "Add". this.Type = HirerChangeRequestTypeEnum.ADD; // 3. Call this.create by passing: // loginUser, dbTransaction await this.create(loginUser, dbTransaction); // 4. Insert into hirerChangeRequest_NewHirer const nh = await HirerChangeRequestNewHirer.init(); nh.RequestId = this.RequestId; nh.FullName = newHirer.FullName; nh.Email = newHirer.Email; nh.IdType = newHirer.IdType; nh.IdNo = newHirer.IdNo; nh.ContactNo = newHirer.ContactNo; nh.Relationship = newHirer.Relationship; nh.Address = newHirer.Address; nh.City = newHirer.City; nh.State = newHirer.State; nh.Postcode = newHirer.Postcode; nh.Country = newHirer.Country; await nh.create(loginUser, dbTransaction); // 5. Return HirerChangeRequest instance and NewHirer instance. return { hirerChangeRequest: this, NewHirer: nh, }; } catch (error) { console.error('Error creating add request:', error); throw error; } } public async createRemoveRequest( loginUser: LoginUser, dbTransaction: any, targetHirerId: string, ): Promise<{ hirerChangeRequest: RentalHirerChangeRequest; RemoveHirer: HirerChangeRequestRemoveHirer; }> { try { // 1. Make sure targetHirerId can be instantiated into JointHirer class (existing joint hirer) await JointHirer.init(targetHirerId, dbTransaction); // 2. Set this.Type to "Remove" this.Type = HirerChangeRequestTypeEnum.REMOVE; // 3. Call this.create() method by passing loginUser, dbTransaction await this.create(loginUser, dbTransaction); // 4. Insert into hirerChangeRequest_RemoveHirer const removeHirer = await HirerChangeRequestRemoveHirer.init(); removeHirer.RequestId = this.RequestId; removeHirer.TargetHirerId = targetHirerId; await removeHirer.create(loginUser, dbTransaction); // 5. Return the returned HirerChangeRequest instance and RemoveHirer instance. return { hirerChangeRequest: this, RemoveHirer: removeHirer, }; } catch (error) { console.error('Error creating remove request:', error); throw error; } } public static async findAll( loginUser: LoginUser, dbTransaction: any, rentalId: string, ): Promise<RentalHirerChangeRequest[]> { try { // 1. Make sure user got "HIRER_CHANGE_REQUEST" privilege. const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'HIRER_CHANGE_REQUEST', ); if (!isPrivileged) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg01', "You do not have 'HIRER_CHANGE_REQUEST' privilege.", ); } // 2. Make sure rentalId not null. if (!rentalId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg12', 'rentalId is required.', ); } // 3. Instantiate rental (to check rental id valid) await Rental.init(dbTransaction, rentalId); // 4. Call Repo.findAll({ where: { RentalId: rentalId }, transaction: dbTransaction }) const records = await RentalHirerChangeRequest._Repository.findAll({ where: { RentalId: rentalId }, transaction: dbTransaction, }); // 5. Instantiate the returned items const result = (records || []).map( (rec) => new RentalHirerChangeRequest(rec.get({ plain: true })), ); // 6. Return the instance array return result; } catch (error) { console.error('Error finding all RentalHirerChangeRequest:', error); throw error; } } public async getNewHirer( dbTransaction: any, ): Promise<HirerChangeRequestNewHirer> { try { // 1. Make sure this.RequestId got value. if (!this.RequestId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg13', 'RequestId is required to get new hirer.', ); } // 2. Call HirerChangeRequestNewHirer.findAll() const newHirerRecords = await HirerChangeRequestNewHirer.findAll( { RequestId: this.RequestId, }, dbTransaction, ); // 3. Return the newHirer instance return newHirerRecords[0] || null; } catch (error) { console.error('Error getting new hirer:', error); throw error; } } public async getRemoveHirer( dbTransaction: any, ): Promise<HirerChangeRequestRemoveHirer> { try { // 1. Make sure this.RequestId got value. if (!this.RequestId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg13', 'RequestId is required to get remove hirer.', ); } // 2. Call HirerChangeRequestRemoveHirer.findAll() const removeHirerRecords = await HirerChangeRequestRemoveHirer.findAll( { RequestId: this.RequestId, }, dbTransaction, ); // 3. Return the removeHirer instance return removeHirerRecords[0] || null; } catch (error) { console.error('Error getting remove hirer:', error); throw error; } } public async sign( loginUser: LoginUser, dbTransaction: any, signatureId: string, ): Promise<HirerChangeRequestSignature> { try { // 1. Make sure this.RequestId got value. if (!this.RequestId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg13', 'RequestId is required to sign signature.', ); } // Validate signatureId const hcrSignature = await HirerChangeRequestSignature.init( signatureId, dbTransaction, ); //Call hcrSignature.markSigned() method by passing loginUser and dbTransaction await hcrSignature.markSigned(loginUser, dbTransaction); // const isSigned = await this.checkIsAllSigned(dbTransaction); // if (isSigned && this.Type === HirerChangeRequestTypeEnum.ADD) { // this.Status = HirerChangeRequestStatusEnum.AWAITINGNEWHIRER; // this.UpdatedAt = new Date(); // this.UpdatedById = loginUser.ObjectId; // await RentalHirerChangeRequest._Repository.update(this.toJSON(), { // where: { RequestId: this.RequestId }, // transaction: dbTransaction, // }); // } return hcrSignature; } catch (error) { throw error; } } public async complete( loginUser: LoginUser, dbTransaction: any, customerId?: string, customerType?: string, ): Promise<RentalHirerChangeRequest> { // 1. Validations // a. Make sure user got "HIRER_CHANGE_REQUEST" privilege. const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'HIRER_CHANGE_REQUEST', ); if (!isPrivileged) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg01', "You do not have 'HIRER_CHANGE_REQUEST' privilege.", ); } // b. Make sure this.RequestId not null. if (!this.RequestId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg06', 'RequestId is required.', ); } // c. Make sure all signatures are signed. const signatures = await this.getSignatures(dbTransaction); if (!signatures.length || !signatures.every((sig) => sig.SignedAt)) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg14', 'All signatures must be signed before completing the request.', ); } // 2. Create/Remove Hirer if (this.Type === HirerChangeRequestTypeEnum.ADD) { //Make sure CustomerId is set, if not throw error if (!customerId || !customerType) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg15', 'CustomerId and CustomerType are required for new hirer.', ); } // c. Create joint hirer using JointHirer.create() const jointHirer = await JointHirer.init(); jointHirer.RentalId = this.RentalId; jointHirer.CustomerId = customerId; jointHirer.CustomerType = customerType; await jointHirer.create(loginUser, dbTransaction); } else if (this.Type === HirerChangeRequestTypeEnum.REMOVE) { // a. Get joint hirer from remove hirer request const removeHirer = await this.getRemoveHirer(dbTransaction); const jointHirer = await JointHirer.init( removeHirer.TargetHirerId, dbTransaction, ); //Call jointHirer.remove() method by passing loginUser and dbTransaction await jointHirer.remove(loginUser, dbTransaction); } // 3. Call _Repo update method const now = new Date(); const entityValueBefore: IRentalHirerChangeRequestAttr = this.toJSON(); this.Status = HirerChangeRequestStatusEnum.COMPLETED; this.UpdatedAt = new Date(); this.UpdatedById = loginUser.ObjectId; const entityValueAfter: IRentalHirerChangeRequestAttr = this.toJSON(); await RentalHirerChangeRequest._Repository.update(entityValueAfter, { where: { RequestId: this.RequestId }, transaction: dbTransaction, }); // 4. Record activity history const activity = new Activity(); activity.ObjectId = this._createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Mark request as completed.`; activity.EntityId = this.RequestId; activity.EntityType = this.ObjectType; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); // 5. Return updated instance return this; } public async cancel( loginUser: LoginUser, dbTransaction: any, cancelRemarks: string, ): Promise<RentalHirerChangeRequest> { // 1. Make sure user got "HIRER_CHANGE_REQUEST" privilege. const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'HIRER_CHANGE_REQUEST', ); if (!isPrivileged) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg01', "You do not have 'HIRER_CHANGE_REQUEST' privilege.", ); } // 2. Make sure this.RequestId not null. if (!this.RequestId) { throw new ClassError( 'HirerChangeRequest', 'HirerChangeRequestErrMsg06', 'RequestId is required.', ); } // 3. Call _Repo.update() method const entityValueBefore: IRentalHirerChangeRequestAttr = this.toJSON(); this.Status = HirerChangeRequestStatusEnum.CANCELLED; this.CancelRemarks = cancelRemarks; this.UpdatedAt = new Date(); this.UpdatedById = loginUser.ObjectId; const entityValueAfter: IRentalHirerChangeRequestAttr = this.toJSON(); await RentalHirerChangeRequest._Repository.update(entityValueAfter, { where: { RequestId: this.RequestId }, transaction: dbTransaction, }); // 4. Record activity history const activity = new Activity(); activity.ObjectId = this._createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Cancel hirer change request.`; activity.EntityId = this.RequestId; activity.EntityType = this.ObjectType; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); // 5. Return updated instance return this; } }