import { ApplicationConfig, ComponentConfig } from '@tomei/config'; import { IOrderQuery } from '../../interfaces/order-query.interface'; import { OrderRepository } from './order.repository'; import { LoginUser } from '@tomei/sso'; import { Op } from 'sequelize'; import { OrderCustomerModel } from '../../models/order-customer.entity'; import { OrderCustomer } from '../order-customer/order-customer'; import { IOrderAttr } from '../../interfaces/order-attr.interface'; import { DeliveryMethodEnum } from '../../enums/delivery-method.enum'; import { ClassError, ObjectBase } from '@tomei/general'; import { ActionEnum, Activity } from '@tomei/activity-history'; import { OrderStatusEnum } from '../../enums/order-status.enum'; import { OrderShippingAddress } from '../order-shipping-address/order-shipping-address'; export class Order extends ObjectBase { ObjectId: string; ObjectName: string; ObjectType = 'Order'; TableName: string; CustomerId: string; OrderType: string; Platform: string; PlatformRefNo: string; Date: Date; Amount: number; Currency: string; DeliveryMethod: DeliveryMethodEnum; Status: OrderStatusEnum; AllowDepositYN: string; DepositAmount: number; CancellationType: string; CancellationReasonCode: string; CancellationById: string; CancellationByName: string; CancellationDate: Date; DiscountPercentage: number; CreatedById: string; CreatedAt: Date; UpdatedById: string; UpdatedAt: Date; Customer: OrderCustomer; protected static _OrderRepo = new OrderRepository(); get OrderNo(): string { return this.ObjectId; } set OrderNo(value: string) { this.ObjectId = value; } protected constructor(orderData?: IOrderAttr) { super(); if (orderData) { this.OrderNo = orderData.OrderNo; this.CustomerId = orderData.CustomerId; this.OrderType = orderData.OrderType; this.Platform = orderData.Platform; this.PlatformRefNo = orderData.PlatformRefNo; this.Date = orderData.Date; this.Amount = orderData.Amount; this.Currency = orderData.Currency; this.DeliveryMethod = orderData.DeliveryMethod; this.Status = orderData.Status; this.AllowDepositYN = orderData.AllowDepositYN; this.DepositAmount = orderData.DepositAmount; this.CancellationType = orderData.CancellationType; this.CancellationReasonCode = orderData.CancellationReasonCode; this.CancellationById = orderData.CancellationById; this.CancellationByName = orderData.CancellationByName; this.CancellationDate = orderData.CancellationDate; this.DiscountPercentage = orderData.DiscountPercentage; this.CreatedById = orderData.CreatedById; this.CreatedAt = orderData.CreatedAt; this.UpdatedById = orderData.UpdatedById; this.UpdatedAt = orderData.UpdatedAt; } } static async init(OrderNo?: string, dbTransaction?: any) { try { if (OrderNo) { const orderData = await Order._OrderRepo.findByPk(OrderNo, { transaction: dbTransaction, }); if (orderData) { return new Order(orderData?.get({ plain: true })); } else { throw Error('Order not found.'); } } return new Order(); } catch (error) { throw error; } } public static async getOrders( loginUser: LoginUser, dbTransaction: any, search: any, ) { try { // Privilege checking const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'Order - List', ); if (!isPrivileged) { throw new Error('You do not have permission to list Order.'); } const { Page, Row } = search; let options: any = { transaction: dbTransaction, }; if (search.Search) { const queryObj: any = {}; Object.entries(search.Search).forEach(([key, value]) => { const propIsString = ['OrderNo', 'CustomerId', 'OrderType', 'Status']; if (propIsString.includes(key)) { if (typeof value === 'string') { queryObj[key] = { [Op.substring]: value, }; } } }); if (search.Search.StartDate) { queryObj.Date = { [Op.between]: [search.Search.StartDate, search.Search.EndDate], }; } options = { ...options, where: queryObj, }; } if (Page && Row) { const offset = Row * (Page - 1); options = { ...options, offset: +offset, limit: +Row, order: [['createdAt', 'DESC']], transaction: dbTransaction, }; } const orders = await Order._OrderRepo.findAndCountAll({ ...options, include: [{ model: OrderCustomerModel }], }); return orders; } catch (error) { throw error; } } public async updateDeliveryMethod( loginUser: LoginUser, deliveryMethod: DeliveryMethodEnum, dbTransaction: any, ) { try { //Part 1: Check Privileges const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'Order - Edit Delivery Method', ); if (!isPrivileged) { throw new Error( 'You do not have permission to Update Order Delivery Method.', ); } //Part 2: Update Order.DeliveryMethod with repo await Order._OrderRepo.update( { DeliveryMethod: deliveryMethod, }, { where: { OrderNo: this.OrderNo, }, transaction: dbTransaction, }, ); } catch (error) { throw error; } } public async update(loginUser: LoginUser, dbTransaction: any) { try { //Part 1: Check Privileges const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'ORDER_UPDATE', ); if (!isPrivileged) { throw new Error('You do not have permission to Update Order.'); } //Part 2: Update Order //Retrieve old order const oldOrder = await Order._OrderRepo.findByPk(this.OrderNo, { transaction: dbTransaction, }); //Set EntityValueBefore with the old order properties const entityValueBefore = oldOrder.get({ plain: true }); this.UpdatedById = loginUser.ObjectId; this.UpdatedAt = new Date(); //Set payload const payload = { CustomerId: this.CustomerId, OrderType: this.OrderType, Platform: this.Platform, PlatformRefNo: this.PlatformRefNo, Date: this.Date, Amount: this.Amount, Currency: this.Currency, DeliveryMethod: this.DeliveryMethod, Status: this.Status, AllowDepositYN: this.AllowDepositYN, DepositAmount: this.DepositAmount, CancellationType: this.CancellationType, CancellationReasonCode: this.CancellationReasonCode, CancellationById: this.CancellationById, CancellationByName: this.CancellationByName, CancellationDate: this.CancellationDate, DiscountPercentage: this.DiscountPercentage, CreatedById: this.CreatedById, CreatedAt: this.CreatedAt, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; //Update order await Order._OrderRepo.update(payload, { where: { OrderNo: this.OrderNo, }, transaction: dbTransaction, }); //Set EntityValueAfter with the new order properties const entityValueAfter = { OrderNo: this.OrderNo, ...payload, }; //Part 3: Log the changes const activity = new Activity(); activity.ActivityId = activity.createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Update Order ${this.OrderNo}`; activity.EntityId = this.OrderNo; activity.EntityType = 'Order'; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); } catch (error) { throw error; } } async setPendingCollection(loginUser: LoginUser, dbTransaction: any) { try { //Part 1: Check Privileges const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'ORDER_UPDATE', ); if (!isPrivileged) { throw new Error('You do not have permission to Update Order.'); } //Part 2: Update Order //Retrieve old order const oldOrder = await Order._OrderRepo.findByPk(this.OrderNo, { transaction: dbTransaction, }); //Set EntityValueBefore with the old order properties const entityValueBefore = oldOrder.get({ plain: true }); this.UpdatedById = loginUser.ObjectId; this.UpdatedAt = new Date(); this.Status = OrderStatusEnum.PENDING_COLLECTION; //Set payload const payload = { Status: this.Status, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; //Update order await Order._OrderRepo.update(payload, { where: { OrderNo: this.OrderNo, }, transaction: dbTransaction, }); //Set EntityValueAfter with the new order properties const entityValueAfter = { OrderNo: this.OrderNo, CustomerId: this.CustomerId, OrderType: this.OrderType, Platform: this.Platform, PlatformRefNo: this.PlatformRefNo, Date: this.Date, Amount: this.Amount, Currency: this.Currency, DeliveryMethod: this.DeliveryMethod, Status: this.Status, AllowDepositYN: this.AllowDepositYN, DepositAmount: this.DepositAmount, CancellationType: this.CancellationType, CancellationReasonCode: this.CancellationReasonCode, CancellationById: this.CancellationById, CancellationByName: this.CancellationByName, CancellationDate: this.CancellationDate, DiscountPercentage: this.DiscountPercentage, CreatedById: this.CreatedById, CreatedAt: this.CreatedAt, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, ...payload, }; //Part 3: Log the changes const activity = new Activity(); activity.ActivityId = activity.createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Set the order ${this.OrderNo} status to Pending Collection`; activity.EntityId = this.OrderNo; activity.EntityType = 'Order'; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); } catch (error) { throw error; } } async setDelivered(loginUser: LoginUser, dbTransaction: any) { try { //Part 1: Check Privileges const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'ORDER_UPDATE', ); if (!isPrivileged) { throw new Error('You do not have permission to Update Order.'); } //Check if the order status is Processing if ( this.Status !== OrderStatusEnum.PENDING_COLLECTION && this.Status !== OrderStatusEnum.SHIPPED_DISPATCHED ) { throw new ClassError( 'Order', 'OrderErrMsg0X', 'Order status must be either pending collection or shipped to set as Delivered.', 'setDelivered', 400, ); } //Part 2: Update Order //Retrieve old order const oldOrder = await Order._OrderRepo.findByPk(this.OrderNo, { transaction: dbTransaction, }); //Set EntityValueBefore with the old order properties const entityValueBefore = oldOrder.get({ plain: true }); this.UpdatedById = loginUser.ObjectId; this.UpdatedAt = new Date(); this.Status = OrderStatusEnum.DELIVERED; //Set payload const payload = { Status: this.Status, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; //Update order await Order._OrderRepo.update(payload, { where: { OrderNo: this.OrderNo, }, transaction: dbTransaction, }); //Set EntityValueAfter with the new order properties const entityValueAfter = { OrderNo: this.OrderNo, CustomerId: this.CustomerId, OrderType: this.OrderType, Platform: this.Platform, PlatformRefNo: this.PlatformRefNo, Date: this.Date, Amount: this.Amount, Currency: this.Currency, DeliveryMethod: this.DeliveryMethod, Status: this.Status, AllowDepositYN: this.AllowDepositYN, DepositAmount: this.DepositAmount, CancellationType: this.CancellationType, CancellationReasonCode: this.CancellationReasonCode, CancellationById: this.CancellationById, CancellationByName: this.CancellationByName, CancellationDate: this.CancellationDate, DiscountPercentage: this.DiscountPercentage, CreatedById: this.CreatedById, CreatedAt: this.CreatedAt, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, ...payload, }; //Part 3: Log the changes const activity = new Activity(); activity.ActivityId = activity.createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Set the order ${this.OrderNo} status to Delivered`; activity.EntityId = this.OrderNo; activity.EntityType = 'Order'; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); } catch (error) { throw error; } } async setProcessing(loginUser: LoginUser, dbTransaction: any) { try { //Part 1: Check Privileges const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'ORDER_UPDATE', ); if (!isPrivileged) { throw new Error('You do not have permission to Update Order.'); } //Part 2: Update Order //Retrieve old order const oldOrder = await Order._OrderRepo.findByPk(this.OrderNo, { transaction: dbTransaction, }); //Set EntityValueBefore with the old order properties const entityValueBefore = oldOrder.get({ plain: true }); this.UpdatedById = loginUser.ObjectId; this.UpdatedAt = new Date(); this.Status = OrderStatusEnum.PROCESSING; //Set payload const payload = { Status: this.Status, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; //Update order await Order._OrderRepo.update(payload, { where: { OrderNo: this.OrderNo, }, transaction: dbTransaction, }); //Set EntityValueAfter with the new order properties const entityValueAfter = { OrderNo: this.OrderNo, CustomerId: this.CustomerId, OrderType: this.OrderType, Platform: this.Platform, PlatformRefNo: this.PlatformRefNo, Date: this.Date, Amount: this.Amount, Currency: this.Currency, DeliveryMethod: this.DeliveryMethod, Status: this.Status, AllowDepositYN: this.AllowDepositYN, DepositAmount: this.DepositAmount, CancellationType: this.CancellationType, CancellationReasonCode: this.CancellationReasonCode, CancellationById: this.CancellationById, CancellationByName: this.CancellationByName, CancellationDate: this.CancellationDate, DiscountPercentage: this.DiscountPercentage, CreatedById: this.CreatedById, CreatedAt: this.CreatedAt, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, ...payload, }; //Part 3: Log the changes const activity = new Activity(); activity.ActivityId = activity.createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Set the order ${this.OrderNo} status to Processing`; activity.EntityId = this.OrderNo; activity.EntityType = 'Order'; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); } catch (error) { throw error; } } async setShipped(loginUser: LoginUser, dbTransaction: any) { try { const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'ORDER_UPDATE', ); if (!isPrivileged) { throw new Error('You do not have permission to Update Order.'); } //Check if the order status is Pending Collection if (this.Status !== OrderStatusEnum.PENDING_COLLECTION) { throw new ClassError( 'Order', 'OrderErrMsg0X', 'Order status must be Pending Collection to set as Shipped.', 'setShipped', 400, ); } //Check if the order already have address try { await OrderShippingAddress.init(this.OrderNo, dbTransaction); } catch (error) { if (error?.message == 'orderShippingAddress not found.') { throw new ClassError( 'Order', 'OrderErrMsg0X', 'Order must have shipping address to set as Shipped.', 'setShipped', 400, ); } else { throw error; } } //Part 2: Update Order //Retrieve old order const oldOrder = await Order._OrderRepo.findByPk(this.OrderNo, { transaction: dbTransaction, }); //Set EntityValueBefore with the old order properties const entityValueBefore = oldOrder.get({ plain: true }); this.UpdatedById = loginUser.ObjectId; this.UpdatedAt = new Date(); this.Status = OrderStatusEnum.SHIPPED_DISPATCHED; //Set payload const payload = { Status: this.Status, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; //Update order await Order._OrderRepo.update(payload, { where: { OrderNo: this.OrderNo, }, transaction: dbTransaction, }); //Set EntityValueAfter with the new order properties const entityValueAfter = { OrderNo: this.OrderNo, CustomerId: this.CustomerId, OrderType: this.OrderType, Platform: this.Platform, PlatformRefNo: this.PlatformRefNo, Date: this.Date, Amount: this.Amount, Currency: this.Currency, DeliveryMethod: this.DeliveryMethod, Status: this.Status, AllowDepositYN: this.AllowDepositYN, DepositAmount: this.DepositAmount, CancellationType: this.CancellationType, CancellationReasonCode: this.CancellationReasonCode, CancellationById: this.CancellationById, CancellationByName: this.CancellationByName, CancellationDate: this.CancellationDate, DiscountPercentage: this.DiscountPercentage, CreatedById: this.CreatedById, CreatedAt: this.CreatedAt, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, ...payload, }; //Part 3: Log the changes const activity = new Activity(); activity.ActivityId = activity.createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Set the order ${this.OrderNo} status to Shipped/Dispatched`; activity.EntityId = this.OrderNo; activity.EntityType = 'Order'; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); } catch (error) { throw error; } } public async delete(loginUser: LoginUser, dbTransaction: any) { try { //Part 1: Check Privileges const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'ORDER_DELETE', ); if (!isPrivileged) { throw new Error('You do not have permission to Delete Order.'); } //Part 2: Delete Order const order = await Order._OrderRepo.findByPk(this.OrderNo, { transaction: dbTransaction, }); let allowedStatus = [ OrderStatusEnum.PENDING, OrderStatusEnum.PENDING_COLLECTION, OrderStatusEnum.PROCESSING, ]; if (!allowedStatus.includes(order.Status)) { throw new Error( 'Cannot delete the order, order already confirmed, processed, or cancelled.', ); } //Set EntityValueBefore with the old order properties const entityValueBefore = order.get({ plain: true }); await Order._OrderRepo.delete(this.OrderNo, dbTransaction); //Part 3: Log the changes const activity = new Activity(); activity.ActivityId = activity.createId(); activity.Action = ActionEnum.DELETE; activity.Description = `Delete Order`; activity.EntityId = this.OrderNo; activity.EntityType = this.ObjectType; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify({}); await activity.create(loginUser.ObjectId, dbTransaction); } catch (error) { throw error; } } public async cancel( loginUser: LoginUser, dbTransaction: any, cancellationType: string, cancellationReasonCode: string, ) { try { //Part 1: Check Privileges const systemCode = ApplicationConfig.getComponentConfigValue('system-code'); const isPrivileged = await loginUser.checkPrivileges( systemCode, 'ORDER_CANCEL', ); if (!isPrivileged) { throw new Error('You do not have permission to Cancel Order.'); } //Part 2: Delete Order const order = await Order._OrderRepo.findByPk(this.OrderNo, { transaction: dbTransaction, }); let allowedStatus = [ OrderStatusEnum.PENDING, OrderStatusEnum.CONFIRMED, OrderStatusEnum.PROCESSING, ]; if (!allowedStatus.includes(this.Status)) { throw new Error( 'Cannot cancel the order, order must be on confirmed, processed, or cancelled stage.', ); } const cancellationTypes = ComponentConfig.getComponentConfigValue( '@tomei/order', 'cancellationTypes', ); const cancellationReasonCodes = ComponentConfig.getComponentConfigValue( '@tomei/order', 'cancellationReasonCodes', ); if (!cancellationTypes.includes(cancellationType)) { throw new Error( 'Cannot cancel the order, Cancellation Type not supported.', ); } if (!cancellationReasonCodes.includes(cancellationReasonCode)) { throw new Error( 'Cannot cancel the order, Cancellation Reason Code not supported.', ); } //Set EntityValueBefore with the old order properties const entityValueBefore = order.get({ plain: true }); this.UpdatedById = loginUser.ObjectId; this.UpdatedAt = new Date(); this.CancellationById = loginUser.ObjectId; this.CancellationByName = loginUser.FullName; this.CancellationDate = new Date(); this.CancellationReasonCode = cancellationReasonCode; this.CancellationType = cancellationType; this.Status = OrderStatusEnum.CANCELLED; //Set payload const payload = { CustomerId: this.CustomerId, OrderType: this.OrderType, Platform: this.Platform, PlatformRefNo: this.PlatformRefNo, Date: this.Date, Amount: this.Amount, Currency: this.Currency, DeliveryMethod: this.DeliveryMethod, Status: this.Status, AllowDepositYN: this.AllowDepositYN, DepositAmount: this.DepositAmount, CancellationType: this.CancellationType, CancellationReasonCode: this.CancellationReasonCode, CancellationById: this.CancellationById, CancellationByName: this.CancellationByName, CancellationDate: this.CancellationDate, DiscountPercentage: this.DiscountPercentage, CreatedById: this.CreatedById, CreatedAt: this.CreatedAt, UpdatedById: this.UpdatedById, UpdatedAt: this.UpdatedAt, }; //Update order await Order._OrderRepo.update(payload, { where: { OrderNo: this.OrderNo, }, transaction: dbTransaction, }); //Set EntityValueAfter with the new order properties const entityValueAfter = { OrderNo: this.OrderNo, ...payload, }; //Part 3: Log the changes const activity = new Activity(); activity.ActivityId = activity.createId(); activity.Action = ActionEnum.UPDATE; activity.Description = `Cancel Order`; activity.EntityId = this.OrderNo; activity.EntityType = this.ObjectType; activity.EntityValueBefore = JSON.stringify(entityValueBefore); activity.EntityValueAfter = JSON.stringify(entityValueAfter); await activity.create(loginUser.ObjectId, dbTransaction); return entityValueAfter; } catch (error) { throw error; } } }