From 3ace59c45020f96df17a90c90d9b8fcc4f46fb88 Mon Sep 17 00:00:00 2001 From: shancheas Date: Mon, 13 Jan 2025 12:42:57 +0700 Subject: [PATCH 1/4] feat: create queue using API --- .../queue/domain/queue.orchestrator.ts | 12 +- .../domain/usecases/generate-queue.manager.ts | 157 ++++++++++++++++++ .../controllers/queue.controller.ts | 9 + .../handlers/transaction.handler.ts | 142 +--------------- src/modules/queue/queue.module.ts | 2 + 5 files changed, 182 insertions(+), 140 deletions(-) create mode 100644 src/modules/queue/domain/usecases/generate-queue.manager.ts diff --git a/src/modules/queue/domain/queue.orchestrator.ts b/src/modules/queue/domain/queue.orchestrator.ts index 112af5b..7ce8c81 100644 --- a/src/modules/queue/domain/queue.orchestrator.ts +++ b/src/modules/queue/domain/queue.orchestrator.ts @@ -22,8 +22,13 @@ import * as moment from 'moment'; import { CustomerQueueTicketSummaryManager } from './usecases/queue/customer-queue-ticket.manager'; import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service'; import { CustomerQueuePosItemManager } from './usecases/queue/customer-queue-pos-item.manager'; -import { QueueOrderModel, QueueTicketModel } from '../data/models/queue.model'; +import { + QueueModel, + QueueOrderModel, + QueueTicketModel, +} from '../data/models/queue.model'; import { CustomerQueueRecommendManager } from './usecases/queue/customer-queue-recommend.manager'; +import { GenerateQueueManager } from './usecases/generate-queue.manager'; @Injectable() export class QueueOrchestrator { @@ -35,8 +40,13 @@ export class QueueOrchestrator { private readonly registerQueueManager: RegisterQueueManager, private readonly splitQueueManager: SplitQueueManager, private readonly queueDataService: QueueDataService, + private readonly generateQueueManager: GenerateQueueManager, ) {} + async generate(data: any): Promise { + return await this.generateQueueManager.generate(data); + } + async loginCustomer(id: string): Promise { try { const order = await this.dataService.loginQueue(id); diff --git a/src/modules/queue/domain/usecases/generate-queue.manager.ts b/src/modules/queue/domain/usecases/generate-queue.manager.ts new file mode 100644 index 0000000..208cbe6 --- /dev/null +++ b/src/modules/queue/domain/usecases/generate-queue.manager.ts @@ -0,0 +1,157 @@ +import { Injectable } from '@nestjs/common'; +import moment from 'moment'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { TransactionUserType } from 'src/modules/transaction/transaction/constants'; +import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model'; +import { QueueBucketReadService } from '../../data/services/queue-bucket'; +import { QueueService } from '../../data/services/queue.service'; +import { TicketDataService } from '../../data/services/ticket.service'; +import { RegisterQueueDto } from '../../infrastructure/controllers/dto/register-queue.dto'; +import { QueueOrder } from '../entities/order.entity'; +import { QueueItem } from '../entities/queue-item.entity'; +import { QueueTicket } from '../entities/ticket.entity'; +import { QueueTimeFormula } from './formula/queue-time.formula'; +import { RegisterQueueManager } from './register-queue.manager'; +import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model'; +import { QueueModel } from '../../data/models/queue.model'; + +@Injectable() +export class GenerateQueueManager { + constructor( + private readonly ticketService: TicketDataService, + private readonly queueService: QueueService, + private readonly bucketService: QueueBucketReadService, + private readonly queueTimeFormula: QueueTimeFormula, + ) {} + + async generate(transaction: TransactionModel) { + const date = transaction.booking_date ?? transaction.invoice_date; + const queue_date = moment(date, 'YYYY-MM-DD').unix(); + + const { id, customer_name, customer_phone, invoice_code } = transaction; + + const customerOrder = { + code: invoice_code, + customer: customer_name, + phone: customer_phone, + date: queue_date * 1000, + transaction_id: id, + }; + + const items = this.generateItems(transaction.items); + + const existTicket = await this.ticketService.ticketByUser( + customer_name, + customer_phone, + ); + + const insertTickets: QueueTicket[] = []; + if (customer_name && customer_phone && existTicket) { + existTicket.items.push(...items); + await this.ticketService.updateQueueTicket(existTicket); + + existTicket.items = items; + insertTickets.push(existTicket); + } else { + const ticket: QueueTicket = { ...customerOrder, items }; + const order: QueueOrder = { ...customerOrder, tickets: [ticket] }; + + const queueOrder = await this.ticketService.createQueueOrder(order); + insertTickets.push(...queueOrder.tickets); + } + + const results: QueueModel[] = []; + if ( + transaction.customer_category?.has_vip_pass || + transaction.customer_type === TransactionUserType.VIP + ) { + for (const ticket of insertTickets) { + const ticket_id = ticket.id; + const items = {}; + + ticket.items.forEach((item) => { + const item_id = item['item_queue_id']; + const currentItem = items[item_id]; + + if (currentItem) { + currentItem.qty += item.qty; + } + items[item_id] = currentItem + ? currentItem + : { + item_id, + ticket_id, + qty: item.qty, + }; + }); + + for (const payload of Object.values(items)) { + const result = await this.create(payload as any); + results.push(result); + } + } + } + return results; + } + + generateItems(items: TransactionItemModel[]): QueueItem[] { + const transactionItems = []; + + items.forEach((item) => { + if (item.item.use_queue) { + transactionItems.push({ + item_queue_id: item.item.item_queue_id, + item_id: item.item_id, + qty: item.qty, + }); + } + + if (item.item.bundling_items.length > 0) { + item.item.bundling_items.forEach((bundling_item) => { + if (bundling_item.use_queue) { + transactionItems.push({ + item_queue_id: bundling_item.item_queue_id, + item_id: bundling_item.id, + qty: item.qty, + }); + } + }); + } + }); + + return this.mergeQueueItems(transactionItems); + } + + mergeQueueItems(arr) { + const result = {}; + arr.forEach((item) => { + if (result[item.item_id]) { + result[item.item_id].qty += item.qty; + } else { + result[item.item_id] = { ...item }; + } + }); + return Object.values(result); + } + + async create(data: RegisterQueueDto): Promise { + const queue = await this.queueService.getTicketItems( + data.ticket_id, + data.item_id, + ); + const queueRequest: any = { + qty: data.qty, + item_id: queue.id, + vip: true, + }; + const registerQueueManager = new RegisterQueueManager( + this.bucketService, + this.queueTimeFormula, + ); + registerQueueManager.setData(queueRequest); + registerQueueManager.setService(this.queueService, TABLE_NAME.QUEUE); + await registerQueueManager.execute(); + + return registerQueueManager.getResult(); + } +} diff --git a/src/modules/queue/infrastructure/controllers/queue.controller.ts b/src/modules/queue/infrastructure/controllers/queue.controller.ts index e7a12b7..136cfea 100644 --- a/src/modules/queue/infrastructure/controllers/queue.controller.ts +++ b/src/modules/queue/infrastructure/controllers/queue.controller.ts @@ -11,6 +11,9 @@ import { RegisterQueueDto } from './dto/register-queue.dto'; import { SplitQueueDto } from './dto/split-queue.dto'; import { LoginQueueDto } from './dto/login-queue.dto'; import { LoginReceiptDto } from './dto/login-receipt.dto'; +import { mappingRevertTransaction } from 'src/modules/transaction/transaction/domain/usecases/managers/helpers/mapping-transaction.helper'; +import { TransactionType } from 'src/modules/transaction/transaction/constants'; +import { QueueModel } from '../../data/models/queue.model'; @ApiTags(`Queue`) @Controller(`v1/${MODULE_NAME.QUEUE}`) @@ -19,6 +22,12 @@ import { LoginReceiptDto } from './dto/login-receipt.dto'; export class QueueController { constructor(private orchestrator: QueueOrchestrator) {} + @Post('generate') + async generateQueue(@Body() data: any): Promise { + mappingRevertTransaction(data, TransactionType.COUNTER); + return await this.orchestrator.generate(data); + } + @Post('register') async registerQueue(@Body() data: RegisterQueueDto): Promise { return await this.orchestrator.create(data); diff --git a/src/modules/queue/infrastructure/handlers/transaction.handler.ts b/src/modules/queue/infrastructure/handlers/transaction.handler.ts index b3e5b42..5fc81c7 100644 --- a/src/modules/queue/infrastructure/handlers/transaction.handler.ts +++ b/src/modules/queue/infrastructure/handlers/transaction.handler.ts @@ -4,19 +4,7 @@ import { TransactionChangeStatusEvent, TransactionCreateQueueEvent, } from 'src/modules/transaction/transaction/domain/entities/event/transaction-change-status.event'; -import { TicketDataService } from '../../data/services/ticket.service'; -import { QueueOrder } from '../../domain/entities/order.entity'; -import { QueueTicket } from '../../domain/entities/ticket.entity'; -import { QueueItem } from '../../domain/entities/queue-item.entity'; -import * as moment from 'moment'; -import { TransactionUserType } from 'src/modules/transaction/transaction/constants'; -import { RegisterQueueManager } from '../../domain/usecases/register-queue.manager'; -import { RegisterQueueDto } from '../controllers/dto/register-queue.dto'; -import { QueueService } from '../../data/services/queue.service'; -import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; -import { QueueBucketReadService } from '../../data/services/queue-bucket'; -import { QueueTimeFormula } from '../../domain/usecases/formula/queue-time.formula'; -import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model'; +import { GenerateQueueManager } from '../../domain/usecases/generate-queue.manager'; @EventsHandler(TransactionChangeStatusEvent, TransactionCreateQueueEvent) export class QueueTransactionHandler @@ -24,10 +12,7 @@ export class QueueTransactionHandler { constructor( private readonly dataService: TransactionDataService, - private readonly ticketService: TicketDataService, - private readonly queueService: QueueService, - private readonly bucketService: QueueBucketReadService, - private readonly queueTimeFormula: QueueTimeFormula, + private readonly queueGenerateManager: GenerateQueueManager, ) {} async handle(event: TransactionChangeStatusEvent) { @@ -50,127 +35,6 @@ export class QueueTransactionHandler ], }); - const date = transaction.booking_date ?? transaction.invoice_date; - const queue_date = moment(date, 'YYYY-MM-DD').unix(); - - const { id, customer_name, customer_phone, invoice_code } = transaction; - - const customerOrder = { - code: invoice_code, - customer: customer_name, - phone: customer_phone, - date: queue_date * 1000, - transaction_id: id, - }; - - const items = this.generateItems(transaction.items); - - const existTicket = await this.ticketService.ticketByUser( - customer_name, - customer_phone, - ); - - const insertTickets: QueueTicket[] = []; - if (customer_name && customer_phone && existTicket) { - existTicket.items.push(...items); - await this.ticketService.updateQueueTicket(existTicket); - - existTicket.items = items; - insertTickets.push(existTicket); - } else { - const ticket: QueueTicket = { ...customerOrder, items }; - const order: QueueOrder = { ...customerOrder, tickets: [ticket] }; - - const queueOrder = await this.ticketService.createQueueOrder(order); - insertTickets.push(...queueOrder.tickets); - } - - if ( - transaction.customer_category?.has_vip_pass || - transaction.customer_type === TransactionUserType.VIP - ) { - insertTickets.forEach((ticket) => { - const ticket_id = ticket.id; - const items = {}; - ticket.items.forEach((item) => { - const item_id = item['item_queue_id']; - const currentItem = items[item_id]; - - if (currentItem) { - currentItem.qty += item.qty; - } - items[item_id] = currentItem - ? currentItem - : { - item_id, - ticket_id, - qty: item.qty, - }; - }); - - Object.values(items).forEach((payload: any) => { - this.create(payload); - }); - }); - } - } - - generateItems(items: TransactionItemModel[]): QueueItem[] { - const transactionItems = []; - - items.forEach((item) => { - if (item.item.use_queue) { - transactionItems.push({ - item_queue_id: item.item.item_queue_id, - item_id: item.item_id, - qty: item.qty, - }); - } - - if (item.item.bundling_items.length > 0) { - item.item.bundling_items.forEach((bundling_item) => { - if (bundling_item.use_queue) { - transactionItems.push({ - item_queue_id: bundling_item.item_queue_id, - item_id: bundling_item.id, - qty: item.qty, - }); - } - }); - } - }); - - return this.mergeQueueItems(transactionItems); - } - - mergeQueueItems(arr) { - const result = {}; - arr.forEach((item) => { - if (result[item.item_id]) { - result[item.item_id].qty += item.qty; - } else { - result[item.item_id] = { ...item }; - } - }); - return Object.values(result); - } - - async create(data: RegisterQueueDto): Promise { - const queue = await this.queueService.getTicketItems( - data.ticket_id, - data.item_id, - ); - const queueRequest: any = { - qty: data.qty, - item_id: queue.id, - vip: true, - }; - const registerQueueManager = new RegisterQueueManager( - this.bucketService, - this.queueTimeFormula, - ); - registerQueueManager.setData(queueRequest); - registerQueueManager.setService(this.queueService, TABLE_NAME.QUEUE); - await registerQueueManager.execute(); + await this.queueGenerateManager.generate(transaction); } } diff --git a/src/modules/queue/queue.module.ts b/src/modules/queue/queue.module.ts index e0c96d3..b974dd3 100644 --- a/src/modules/queue/queue.module.ts +++ b/src/modules/queue/queue.module.ts @@ -38,6 +38,7 @@ import { QueueTransactionCancelHandler } from './infrastructure/handlers/cancel- import { ItemQueueModel } from '../item-related/item-queue/data/models/item-queue.model'; import { QueueTimeFormula } from './domain/usecases/formula/queue-time.formula'; import { QueueJobController } from './infrastructure/controllers/queue-job.controller'; +import { GenerateQueueManager } from './domain/usecases/generate-queue.manager'; @Module({ imports: [ @@ -79,6 +80,7 @@ import { QueueJobController } from './infrastructure/controllers/queue-job.contr SplitQueueManager, QueueTimeFormula, + GenerateQueueManager, ], }) export class QueueModule {} -- 2.40.1 From 769e8174f477987d5f6e1d366f42d9e43a0764da Mon Sep 17 00:00:00 2001 From: shancheas Date: Mon, 13 Jan 2025 12:59:30 +0700 Subject: [PATCH 2/4] fix: don't create queue from POS --- .../queue/infrastructure/handlers/transaction.handler.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/queue/infrastructure/handlers/transaction.handler.ts b/src/modules/queue/infrastructure/handlers/transaction.handler.ts index 5fc81c7..8c4710d 100644 --- a/src/modules/queue/infrastructure/handlers/transaction.handler.ts +++ b/src/modules/queue/infrastructure/handlers/transaction.handler.ts @@ -21,7 +21,8 @@ export class QueueTransactionHandler /** * If data still in process (not settled) then don't create the queue order */ - if (process_data?.status != 'settled') return; + if (process_data?.status != 'settled' || process_data?.pos_number != null) + return; const transaction = await this.dataService.getOneByOptions({ where: { -- 2.40.1 From f9d8f85179b57cd5c3a66bf1f203e5adad64e4a6 Mon Sep 17 00:00:00 2001 From: shancheas Date: Mon, 13 Jan 2025 13:31:19 +0700 Subject: [PATCH 3/4] fix: moment js when generate --- src/modules/queue/domain/usecases/generate-queue.manager.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/modules/queue/domain/usecases/generate-queue.manager.ts b/src/modules/queue/domain/usecases/generate-queue.manager.ts index 208cbe6..7a2805c 100644 --- a/src/modules/queue/domain/usecases/generate-queue.manager.ts +++ b/src/modules/queue/domain/usecases/generate-queue.manager.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import moment from 'moment'; +import * as moment from 'moment'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { TransactionUserType } from 'src/modules/transaction/transaction/constants'; import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model'; -- 2.40.1 From 29e4dc54006a360790bd4f61d4aae2b697784a73 Mon Sep 17 00:00:00 2001 From: shancheas Date: Mon, 13 Jan 2025 14:10:32 +0700 Subject: [PATCH 4/4] fix: code not generate because vip pass --- .../queue/domain/usecases/generate-queue.manager.ts | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/modules/queue/domain/usecases/generate-queue.manager.ts b/src/modules/queue/domain/usecases/generate-queue.manager.ts index 7a2805c..97e416c 100644 --- a/src/modules/queue/domain/usecases/generate-queue.manager.ts +++ b/src/modules/queue/domain/usecases/generate-queue.manager.ts @@ -61,10 +61,10 @@ export class GenerateQueueManager { } const results: QueueModel[] = []; - if ( + const vipCustomer = transaction.customer_category?.has_vip_pass || - transaction.customer_type === TransactionUserType.VIP - ) { + transaction.customer_type === TransactionUserType.VIP; + if (vipCustomer || transaction.creator_counter_no != null) { for (const ticket of insertTickets) { const ticket_id = ticket.id; const items = {}; @@ -86,7 +86,8 @@ export class GenerateQueueManager { }); for (const payload of Object.values(items)) { - const result = await this.create(payload as any); + const isVip = vipCustomer || transaction.creator_counter_no == null; + const result = await this.create(payload as any, isVip); results.push(result); } } @@ -134,7 +135,7 @@ export class GenerateQueueManager { return Object.values(result); } - async create(data: RegisterQueueDto): Promise { + async create(data: RegisterQueueDto, isVip = false): Promise { const queue = await this.queueService.getTicketItems( data.ticket_id, data.item_id, @@ -142,7 +143,7 @@ export class GenerateQueueManager { const queueRequest: any = { qty: data.qty, item_id: queue.id, - vip: true, + vip: isVip, }; const registerQueueManager = new RegisterQueueManager( this.bucketService, -- 2.40.1