Compare commits
30 Commits
1.4.0-prod
...
production
Author | SHA1 | Date |
---|---|---|
|
0b482be669 | |
|
064112e731 | |
|
05473fce3d | |
|
d6a238a224 | |
|
a8b3f67d1b | |
|
dc5e938f75 | |
|
eb4da7ccc4 | |
|
bef9f99f13 | |
|
46caaba6bd | |
|
8e0c8462b9 | |
|
7953c7dbbd | |
|
6911f6f0a2 | |
|
762340a72b | |
|
00e2b6e015 | |
|
61cbbf81ef | |
|
7b864d4163 | |
|
4ea53f7088 | |
|
00ec2c9342 | |
|
0548141e5f | |
|
440a589f8a | |
|
29e4dc5400 | |
|
f9d8f85179 | |
|
769e8174f4 | |
|
3ace59c450 | |
|
a51b6ea520 | |
|
187555a543 | |
|
750cee8853 | |
|
551bd12f5b | |
|
0a98bcfd59 | |
|
1692c8234a |
|
@ -49,6 +49,7 @@
|
|||
"exceljs": "^4.4.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"googleapis": "^140.0.0",
|
||||
"gtts": "^0.2.1",
|
||||
"handlebars": "^4.7.8",
|
||||
"mathjs": "^13.0.2",
|
||||
"midtrans-client": "^1.3.1",
|
||||
|
|
|
@ -5,7 +5,7 @@ export class AddEstimationToItem1723801180604 implements MigrationInterface {
|
|||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" ADD "play_estimation" integer`,
|
||||
`ALTER TABLE "items" ADD "play_estimation" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -73,13 +73,14 @@ export class LoginAdminQueueManager extends BaseCustomManager<UserEntity> {
|
|||
message: `Akun anda sudah login di item "${hasLoginAsQueue?.item_name}"`,
|
||||
error: 'Unauthorized',
|
||||
});
|
||||
} else if (itemLogin && itemLogin.user_id !== this.userLogin.id) {
|
||||
throw new UnauthorizedException({
|
||||
statusCode: HttpStatus.UNAUTHORIZED,
|
||||
message: `"${userLoginItem.name}" masih login sebagai admin antrian `,
|
||||
error: 'Unauthorized',
|
||||
});
|
||||
}
|
||||
// else if (itemLogin && itemLogin.user_id !== this.userLogin.id) {
|
||||
// throw new UnauthorizedException({
|
||||
// statusCode: HttpStatus.UNAUTHORIZED,
|
||||
// message: `"${userLoginItem.name}" masih login sebagai admin antrian `,
|
||||
// error: 'Unauthorized',
|
||||
// });
|
||||
// }
|
||||
|
||||
// * Disini untuk isi token
|
||||
const tokenData = {
|
||||
|
|
|
@ -19,6 +19,7 @@ export class CouchService {
|
|||
}
|
||||
|
||||
async onModuleInit() {
|
||||
// return;
|
||||
const nano = this.nanoInstance;
|
||||
for (const database of DatabaseListen) {
|
||||
const db = nano.db.use(database);
|
||||
|
@ -95,4 +96,41 @@ export class CouchService {
|
|||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
getUnixTimestampLast7Days() {
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 4);
|
||||
date.setHours(0, 0, 0, 0);
|
||||
return date.getTime();
|
||||
}
|
||||
|
||||
public async clearTransactions() {
|
||||
const nano = this.nanoInstance;
|
||||
const transaction = nano.use('transaction');
|
||||
|
||||
const expiredDate = this.getUnixTimestampLast7Days();
|
||||
|
||||
const selectorPayment = {
|
||||
created_at: {
|
||||
$lt: expiredDate,
|
||||
},
|
||||
};
|
||||
|
||||
const transactions = await transaction.find({
|
||||
selector: selectorPayment,
|
||||
fields: ['_id', '_rev'],
|
||||
limit: 100000,
|
||||
});
|
||||
|
||||
const { docs } = transactions;
|
||||
console.log(docs.length);
|
||||
const deletedDocs = {
|
||||
docs: docs.map((doc) => ({
|
||||
_id: doc._id,
|
||||
_rev: doc._rev,
|
||||
_deleted: true,
|
||||
})),
|
||||
};
|
||||
await transaction.bulk(deletedDocs);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,17 @@ import { Public } from 'src/core/guards';
|
|||
import * as Nano from 'nano';
|
||||
import { CreateUserPrivilegeDto } from 'src/modules/user-related/user-privilege/infrastructure/dto/create-user-privilege.dto';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { CouchService } from '../data/services/couch.service';
|
||||
|
||||
@ApiTags(`couch`)
|
||||
@Controller('v1/couch')
|
||||
@Public()
|
||||
@Injectable()
|
||||
export class CouchDataController {
|
||||
constructor(private configService: ConfigService) {}
|
||||
constructor(
|
||||
private configService: ConfigService,
|
||||
private couchService: CouchService,
|
||||
) {}
|
||||
|
||||
get nanoInstance() {
|
||||
const couchConfiguration = this.configService.get<string>('COUCHDB_CONFIG');
|
||||
|
@ -64,4 +68,11 @@ export class CouchDataController {
|
|||
// return people.get();
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
@Public(true)
|
||||
@Get('clear-transactions')
|
||||
async clearTransactions(): Promise<string> {
|
||||
await this.couchService.clearTransactions();
|
||||
return 'OK';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import { Controller, Get, Param, Query } from '@nestjs/common';
|
||||
import {
|
||||
Controller,
|
||||
Get,
|
||||
Param,
|
||||
Query,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { FilterItemQueueDto } from './dto/filter-item-queue.dto';
|
||||
import { Pagination } from 'src/core/response';
|
||||
import { PaginationResponse } from 'src/core/response/domain/ok-response.interface';
|
||||
|
@ -7,6 +13,7 @@ import { ItemQueueReadOrchestrator } from '../domain/usecases/item-queue-read.or
|
|||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
import { MODULE_NAME } from 'src/core/strings/constants/module.constants';
|
||||
import { Public } from 'src/core/guards';
|
||||
import { validate as isValidUUID } from 'uuid';
|
||||
|
||||
@ApiTags(`${MODULE_NAME.ITEM_QUEUE.split('-').join(' ')} - read`)
|
||||
@Controller(`v1/${MODULE_NAME.ITEM_QUEUE}`)
|
||||
|
@ -44,6 +51,7 @@ export class ItemQueueReadController {
|
|||
@Get('display/:id')
|
||||
@Public(true)
|
||||
async detailPublic(@Param('id') id: string): Promise<ItemQueueEntity> {
|
||||
if (!isValidUUID(id)) throw new UnauthorizedException('id is required');
|
||||
return await this.orchestrator.detail(id);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ export class ItemModel
|
|||
@Column('bigint', { name: 'base_price', nullable: true })
|
||||
base_price: number;
|
||||
|
||||
@Column('int', { name: 'play_estimation', nullable: true })
|
||||
@Column('decimal', { name: 'play_estimation', nullable: true })
|
||||
play_estimation: number;
|
||||
|
||||
@Column('boolean', { name: 'use_queue', default: false })
|
||||
|
|
|
@ -17,8 +17,7 @@ export function phoneNumberOnly(phone: string): string {
|
|||
export function timeIsBefore(
|
||||
currentTime: number,
|
||||
time: number,
|
||||
diff = 15,
|
||||
diffInMilliseconds: number,
|
||||
): boolean {
|
||||
const diffInMilliseconds = diff * 60 * 1000;
|
||||
return time < currentTime + diffInMilliseconds;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import {
|
|||
} from 'src/core/strings/constants/base.constants';
|
||||
import { QueueTimeFormula } from './usecases/formula/queue-time.formula';
|
||||
import * as moment from 'moment';
|
||||
import { timeIsBefore, toTime } from './helpers/time.helper';
|
||||
import { timeIsBefore } from './helpers/time.helper';
|
||||
import { WhatsappService } from 'src/services/whatsapp/whatsapp.service';
|
||||
import { WhatsappQueue } from 'src/services/whatsapp/entity/whatsapp-queue.entity';
|
||||
|
||||
|
@ -63,11 +63,12 @@ export class QueueAdminOrchestrator {
|
|||
for (const queueId in queueTimes) {
|
||||
const callTime = queueTimes[queueId];
|
||||
|
||||
const queueTicket = await this.service.queueTicket(queueId);
|
||||
if (
|
||||
queueItem.requiring_notification ||
|
||||
timeIsBefore(currentTime, callTime, queueItem.call_preparation)
|
||||
(timeIsBefore(currentTime, callTime, call_preparation) &&
|
||||
queueTicket.time > currentTime)
|
||||
) {
|
||||
const queueTicket = await this.service.queueTicket(queueId);
|
||||
const payload: WhatsappQueue = {
|
||||
id: queueId,
|
||||
phone: queueTicket.item.ticket.phone,
|
||||
|
@ -86,7 +87,10 @@ export class QueueAdminOrchestrator {
|
|||
// },
|
||||
// ]);
|
||||
|
||||
if (queueTicket.last_notification < currentTime - call_preparation) {
|
||||
if (
|
||||
queueTicket.item.ticket.phone != null &&
|
||||
queueTicket.last_notification < currentTime - call_preparation
|
||||
) {
|
||||
await notification.queueProcess(payload);
|
||||
this.service.updateLastNotification(queueId, currentTime);
|
||||
}
|
||||
|
|
|
@ -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<QueueModel[]> {
|
||||
return await this.generateQueueManager.generate(data);
|
||||
}
|
||||
|
||||
async loginCustomer(id: string): Promise<QueueOrder> {
|
||||
try {
|
||||
const order = await this.dataService.loginQueue(id);
|
||||
|
|
|
@ -31,7 +31,7 @@ export class QueueTimeFormula {
|
|||
|
||||
const times = itemQueue.items.map((item) => item.play_estimation ?? 0);
|
||||
const average = times.length > 0 ? math.mean(times) * 60 * 1000 : 0; // change average minute to milliseconds
|
||||
this.average = average;
|
||||
this.average = Math.floor(average);
|
||||
|
||||
const calledQueue = await this.queueDataService.lastQueue(item_queue_id);
|
||||
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
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';
|
||||
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[] = [];
|
||||
const vipCustomer =
|
||||
transaction.customer_category?.has_vip_pass ||
|
||||
transaction.customer_type === TransactionUserType.VIP;
|
||||
if (vipCustomer || transaction.creator_counter_no != null) {
|
||||
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 isVip = vipCustomer || transaction.creator_counter_no == null;
|
||||
const result = await this.create(payload as any, isVip);
|
||||
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<QueueItem>(result);
|
||||
}
|
||||
|
||||
async create(data: RegisterQueueDto, isVip = false): Promise<QueueModel> {
|
||||
const queue = await this.queueService.getTicketItems(
|
||||
data.ticket_id,
|
||||
data.item_id,
|
||||
);
|
||||
const queueRequest: any = {
|
||||
qty: data.qty,
|
||||
item_id: queue.id,
|
||||
vip: isVip,
|
||||
};
|
||||
const registerQueueManager = new RegisterQueueManager(
|
||||
this.bucketService,
|
||||
this.queueTimeFormula,
|
||||
);
|
||||
registerQueueManager.setData(queueRequest);
|
||||
registerQueueManager.setService(this.queueService, TABLE_NAME.QUEUE);
|
||||
await registerQueueManager.execute();
|
||||
|
||||
const result = await registerQueueManager.getResult();
|
||||
result.time = null;
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -38,7 +38,7 @@ export class RegisterQueueManager extends BaseCreateManager<QueueModel> {
|
|||
const first = queues[0];
|
||||
const last = queues[queues.length - 1] ?? moment().valueOf();
|
||||
const average = this.queueTimeFormula.average;
|
||||
return [first, last + average];
|
||||
return [Math.floor(first), Math.floor(last + average)];
|
||||
}
|
||||
|
||||
async beforeProcess(): Promise<void> {
|
||||
|
@ -91,9 +91,12 @@ export class RegisterQueueManager extends BaseCreateManager<QueueModel> {
|
|||
item_name: item.name,
|
||||
time: this.result.time,
|
||||
};
|
||||
notificationService.queueRegister(payload);
|
||||
const currentTime = moment().valueOf();
|
||||
this.dataService.updateLastNotification(this.result.id, currentTime);
|
||||
|
||||
if (payload.phone) {
|
||||
notificationService.queueRegister(payload);
|
||||
const currentTime = moment().valueOf();
|
||||
this.dataService.updateLastNotification(this.result.id, currentTime);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Body, Controller, Get, Param, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Param, Post, Query, Res } from '@nestjs/common';
|
||||
|
||||
import { MODULE_NAME } from 'src/core/strings/constants/module.constants';
|
||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||
|
@ -11,6 +11,12 @@ 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';
|
||||
|
||||
import * as Gtts from 'gtts';
|
||||
import { Response } from 'express';
|
||||
|
||||
@ApiTags(`Queue`)
|
||||
@Controller(`v1/${MODULE_NAME.QUEUE}`)
|
||||
|
@ -19,6 +25,12 @@ import { LoginReceiptDto } from './dto/login-receipt.dto';
|
|||
export class QueueController {
|
||||
constructor(private orchestrator: QueueOrchestrator) {}
|
||||
|
||||
@Post('generate')
|
||||
async generateQueue(@Body() data: any): Promise<QueueModel[]> {
|
||||
mappingRevertTransaction(data, TransactionType.COUNTER);
|
||||
return await this.orchestrator.generate(data);
|
||||
}
|
||||
|
||||
@Post('register')
|
||||
async registerQueue(@Body() data: RegisterQueueDto): Promise<Queue> {
|
||||
return await this.orchestrator.create(data);
|
||||
|
@ -96,6 +108,15 @@ export class QueueController {
|
|||
return await this.orchestrator.queueOrderItems(id);
|
||||
}
|
||||
|
||||
@Get('speech')
|
||||
async speech(
|
||||
@Query('text') text: string,
|
||||
@Res() response: Response,
|
||||
): Promise<void> {
|
||||
const gtts = new Gtts(text, 'id');
|
||||
gtts.stream().pipe(response);
|
||||
}
|
||||
|
||||
@Get(':id/items/:item_id')
|
||||
async queueItemTickets(
|
||||
@Param('id') id: string,
|
||||
|
|
|
@ -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) {
|
||||
|
@ -36,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: {
|
||||
|
@ -50,127 +36,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<QueueItem>(result);
|
||||
}
|
||||
|
||||
async create(data: RegisterQueueDto): Promise<void> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -116,7 +116,7 @@ export function mappingRevertTransaction(data, type) {
|
|||
}
|
||||
|
||||
Object.assign(data, {
|
||||
id: data.booking_id ?? data._id,
|
||||
id: data.booking_id ?? data._id ?? data.id,
|
||||
creator_counter_no: Number(data.pos_number),
|
||||
creator_counter_name: data.pos_name,
|
||||
settlement_date: new Date(data.created_at),
|
||||
|
|
|
@ -12,6 +12,7 @@ import {
|
|||
} from './whatsapp.constant';
|
||||
import axios from 'axios';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { apm } from 'src/core/apm';
|
||||
|
||||
export class WhatsappService {
|
||||
async sendMessage(data) {
|
||||
|
@ -25,8 +26,14 @@ export class WhatsappService {
|
|||
data: data,
|
||||
};
|
||||
|
||||
const response = await axios(config);
|
||||
return response.data;
|
||||
try {
|
||||
const response = await axios(config);
|
||||
return response.data;
|
||||
} catch (error) {
|
||||
Logger.error(error);
|
||||
apm?.captureError(error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async queueRegister(data: WhatsappQueue) {
|
||||
|
@ -91,8 +98,11 @@ export class WhatsappService {
|
|||
},
|
||||
};
|
||||
|
||||
await this.sendMessage(payload);
|
||||
Logger.log(`Notification register for ${data.code} send to ${data.phone}`);
|
||||
const response = await this.sendMessage(payload);
|
||||
if (response)
|
||||
Logger.log(
|
||||
`Notification register for ${data.code} send to ${data.phone}`,
|
||||
);
|
||||
}
|
||||
|
||||
async queueProcess(data: WhatsappQueue) {
|
||||
|
@ -152,7 +162,8 @@ export class WhatsappService {
|
|||
},
|
||||
};
|
||||
|
||||
await this.sendMessage(payload);
|
||||
Logger.log(`Notification process for ${data.code} send to ${data.phone}`);
|
||||
const response = await this.sendMessage(payload);
|
||||
if (response)
|
||||
Logger.log(`Notification process for ${data.code} send to ${data.phone}`);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue