Compare commits

..

2 Commits

Author SHA1 Message Date
Firman Ramdhani 7c0df75670 Merge branch 'development' of ssh://git.eigen.co.id:2222/eigen/pos-be into fix/bug-firman 2024-08-21 09:37:39 +07:00
Firman Ramdhani 50aca761f2 feat: remove consol on booking handler 2024-08-19 16:36:00 +07:00
395 changed files with 574 additions and 31243 deletions

View File

@ -2,42 +2,18 @@ kind: pipeline
type: docker
name: server
steps:
# - name: build
# image: appleboy/drone-ssh
# settings:
# host:
# - 172.10.10.10
# username: eigen
# key:
# from_secret: DEVOPS_SSH_PRIVATE_OPEN
# port: 22
# script:
# - cd /home/eigen/PROJECT/POS/POS.DEV/BE
# - sh build.sh
# when:
# ref:
# - refs/tags/devel_*
# - refs/tags/*-alpha.*
- name: build-testing
image: plugins/docker
- name: build
image: appleboy/drone-ssh
settings:
registry: registry.eigen.co.id
repo: registry.eigen.co.id/eigen/${DRONE_REPO_NAME}
tags: ${DRONE_TAG}
custom_dns: 172.10.10.16
when:
ref:
- refs/tags/*-alpha.*
- name: build-production
image: plugins/docker
settings:
registry: registry.eigen.co.id
repo: registry.eigen.co.id/eigen/${DRONE_REPO_NAME}
tags: ${DRONE_TAG}
custom_dns: 172.10.10.16
when:
ref:
- refs/tags/*-production.*
host:
- 172.10.10.10
username: eigen
key:
from_secret: DEVOPS_SSH_PRIVATE_OPEN
port: 22
script:
- cd /home/eigen/PROJECT/POS/POS.DEV/BE
- sh build.sh
- name: send-message
image: plugins/webhook
settings:
@ -51,59 +27,9 @@ steps:
"message": "Build {{repo.name}} sudah selesai"
}
trigger:
ref:
- refs/tags/devel_*
- refs/tags/*-alpha.*
event:
exclude:
- promote
---
kind: pipeline
type: docker
name: kustomize
clone:
disable: true
steps:
- name: kustomize-testing
image: registry.k8s.io/kustomize/kustomize:v5.0.0
environment:
DEVOPS_SSH_PRIVATE:
from_secret: DEVOPS_SSH_PRIVATE
DEVOPS_SSH_PUBLIC:
from_secret: DEVOPS_SSH_PUBLIC
INFRASTRUCTURE_REPO: "k8s-kustomize-external"
DIRECTORY_NAME: "weplay-pos-testing"
commands:
- mkdir -p ~/.ssh &&
- echo $DEVOPS_SSH_PRIVATE | base64 -d > ~/.ssh/id_rsa &&
- echo $DEVOPS_SSH_PUBLIC | base64 -d > ~/.ssh/id_rsa.pub &&
- ssh-keyscan -H -p 2222 git.eigen.co.id >> ~/.ssh/known_hosts &&
- chmod 700 ~/.ssh/ &&
- chmod 600 ~/.ssh/id_rsa &&
- git clone ssh://git@git.eigen.co.id:2222/eigen/$INFRASTRUCTURE_REPO.git &&
- cd $INFRASTRUCTURE_REPO/$DIRECTORY_NAME
- kustomize edit set image registry.eigen.co.id/eigen/$DRONE_REPO_NAME=registry.eigen.co.id/eigen/$DRONE_REPO_NAME:$DRONE_TAG &&
- git add . &&
- |-
git commit -m "feat: update $DRONE_REPO_NAME testing to $DRONE_TAG" &&
- git push origin master
- name: send-message
image: harbor.eigen.co.id/docker.com/plugins/webhook
settings:
urls: https://mattermost.eigen.co.id/api/v4/posts
content_type: application/json
headers:
- Authorization=Bearer 5zubexudb38uuradfa36qy98ca
template: |
{
"channel_id": "s1ekqde1c3du5p35g6budnuotc",
"message": "ALERT: {{ repo.name }} gagal update dengan tag ${DRONE_TAG}"
}
when:
status:
- failure
trigger:
ref:
include:
- refs/tags/*-alpha.*
depends_on:
- server

View File

@ -5,11 +5,11 @@ COPY . .
RUN yarn install
RUN yarn build
FROM node:18.17-alpine
# ARG env_target
ARG env_target
WORKDIR /app
# RUN echo ${env_target}
# COPY env/$env_target /app/.env
# COPY --from=builder /app/env/$env_target .env
RUN echo ${env_target}
COPY env/$env_target /app/.env
COPY --from=builder /app/env/$env_target .env
COPY --from=builder /app/node_modules ./node_modules
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/assets ./assets

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 KiB

7
env/env.development vendored
View File

@ -40,10 +40,3 @@ ASSETS="https://asset.sky.eigen.co.id/"
GOOGLE_CALENDAR_KEY="AIzaSyCSg4P3uC9Z7kD1P4f3rf1BbBaz4Q-M55o"
GOOGLE_CALENDAR_ID="326464ac296874c7121825f5ef2e2799baa90b51da240f0045aae22beec10bd5@group.calendar.google.com"
SUPERSET_URL=https://dashboard.weplayground.eigen.co.id
SUPERSET_ADMIN_USERNAME=admin
SUPERSET_ADMIN_PASSWORD=admin
WHATSAPP_BUSINESS_ACCOUNT_NUMBER_ID=604883366037548
WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV

7
env/env.production vendored
View File

@ -37,10 +37,3 @@ ASSETS="https://asset.sky.eigen.co.id/"
GOOGLE_CALENDAR_KEY="AIzaSyCSg4P3uC9Z7kD1P4f3rf1BbBaz4Q-M55o"
GOOGLE_CALENDAR_ID="326464ac296874c7121825f5ef2e2799baa90b51da240f0045aae22beec10bd5@group.calendar.google.com"
SUPERSET_URL=https://dashboard.weplayground.eigen.co.id
SUPERSET_ADMIN_USERNAME=admin
SUPERSET_ADMIN_PASSWORD=admin
WHATSAPP_BUSINESS_ACCOUNT_NUMBER_ID=604883366037548
WHATSAPP_BUSINESS_ACCESS_TOKEN=EAAINOvRRiEEBO9yQsYDnYtjHZB7q1nZCwbBpRcxIGMDWajKZBtmWxNRKvPYkS95KQZBsZBOvSFyjiEg5CcCZBZBtaSZApxyV8fiA3cEyVwf7iVZBQP2YCTPRQZArMFeeXbO0uq5TGygmjsIz3M4YxcUHxPzKO4pKxIyxnzcoUZCqCSo1NqQSLVf3a0JyZAwgDXGL55dV

View File

@ -28,7 +28,6 @@
},
"dependencies": {
"@faker-js/faker": "^8.4.1",
"@nestjs/axios": "^3.0.3",
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.2",
"@nestjs/core": "^10.0.0",
@ -40,7 +39,6 @@
"@nestjs/typeorm": "^10.0.2",
"@types/multer": "^1.4.11",
"algebra.js": "^0.2.6",
"axios": "^1.7.5",
"bcrypt": "^5.1.1",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
@ -49,7 +47,6 @@
"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",
@ -59,7 +56,6 @@
"pdfmake": "^0.2.10",
"pg": "^8.11.5",
"plop": "^4.0.1",
"qrcode": "^1.5.4",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.5.0",
"typeorm": "^0.3.20",
@ -72,7 +68,6 @@
"@types/express": "^4.17.13",
"@types/jest": "29.5.12",
"@types/node": "^20.12.13",
"@types/qrcode": "^1.5.5",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",

View File

@ -33,10 +33,7 @@ import { SeasonTypeModel } from './modules/season-related/season-type/data/model
import { TaxModule } from './modules/transaction/tax/tax.module';
import { TaxModel } from './modules/transaction/tax/data/models/tax.model';
import { SalesPriceFormulaModule } from './modules/transaction/sales-price-formula/sales-price-formula.module';
import {
SalesPriceFormulaModel,
TransactionSettingModel,
} from './modules/transaction/sales-price-formula/data/models/sales-price-formula.model';
import { SalesPriceFormulaModel } from './modules/transaction/sales-price-formula/data/models/sales-price-formula.model';
import { ProfitShareFormulaModule } from './modules/transaction/profit-share-formula/profit-share-formula.module';
import { PaymentMethodModule } from './modules/transaction/payment-method/payment-method.module';
import { PaymentMethodModel } from './modules/transaction/payment-method/data/models/payment-method.model';
@ -48,10 +45,8 @@ import { GoogleCalendarModule } from './modules/configuration/google-calendar/go
import { TransactionModule } from './modules/transaction/transaction/transaction.module';
import { TransactionModel } from './modules/transaction/transaction/data/models/transaction.model';
import {
TransactionBreakdownTaxModel,
TransactionItemBreakdownModel,
TransactionItemModel,
TransactionItemTaxModel,
} from './modules/transaction/transaction/data/models/transaction-item.model';
import { TransactionTaxModel } from './modules/transaction/transaction/data/models/transaction-tax.model';
import { ReconciliationModule } from './modules/transaction/reconciliation/reconciliation.module';
@ -80,33 +75,6 @@ import { MailModule } from './modules/configuration/mail/mail.module';
import { PosLogModel } from './modules/configuration/log/data/models/pos-log.model';
import { ExportModule } from './modules/configuration/export/export.module';
import { TransactionDemographyModel } from './modules/transaction/transaction/data/models/transaction-demography.model';
import { SupersetModule } from './modules/configuration/superset/superset.module';
import { GateScanModule } from './modules/gates/gate.module';
import { UserLoginModel } from './modules/user-related/user/data/models/user-login.model';
import { LogUserLoginModel } from './modules/configuration/log/data/models/log-user-login.model';
import { AuthService } from './core/guards/domain/services/auth.service';
import { ReportSummaryModule } from './modules/reports/report-summary/report-summary.module';
import { QueueModule } from './modules/queue/queue.module';
import {
QueueOrderModel,
QueueTicketModel,
QueueItemModel,
QueueModel,
} from './modules/queue/data/models/queue.model';
import { ItemQueueModule } from './modules/item-related/item-queue/item-queue.module';
import { ItemQueueModel } from './modules/item-related/item-queue/data/models/item-queue.model';
import { QueueBucketModel } from './modules/queue/data/models/queue-bucket.model';
import { VerificationModel } from './modules/booking-online/authentication/data/models/verification.model';
import { BookingOnlineAuthModule } from './modules/booking-online/authentication/auth.module';
import { BookingOrderModule } from './modules/booking-online/order/order.module';
import { TimeGroupModule } from './modules/item-related/time-group/time-group.module';
import { TimeGroupModel } from './modules/item-related/time-group/data/models/time-group.model';
import { OtpVerificationModule } from './modules/configuration/otp-verification/otp-verification.module';
import { OtpVerificationModel } from './modules/configuration/otp-verification/data/models/otp-verification.model';
import { OtpVerifierModel } from './modules/configuration/otp-verification/data/models/otp-verifier.model';
import { RescheduleVerificationModel } from './modules/booking-online/order/data/models/reschedule-verification.model';
import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard';
@Module({
imports: [
@ -131,10 +99,7 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard';
ItemModel,
ItemCategoryModel,
ItemRateModel,
ItemQueueModel,
TimeGroupModel,
LogModel,
LogUserLoginModel,
NewsModel,
PaymentMethodModel,
PosLogModel,
@ -150,32 +115,13 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard';
TransactionTaxModel,
TransactionDemographyModel,
TransactionItemBreakdownModel,
TransactionItemTaxModel,
TransactionBreakdownTaxModel,
TransactionSettingModel,
UserModel,
UserLoginModel,
VipCategoryModel,
VipCodeModel,
// report
ReportBookmarkModel,
ExportReportHistoryModel,
// Queue
QueueOrderModel,
QueueTicketModel,
QueueItemModel,
QueueModel,
QueueBucketModel,
// Booking Online
VerificationModel,
RescheduleVerificationModel,
OtpVerificationModel,
OtpVerifierModel,
],
synchronize: false,
}),
@ -201,8 +147,6 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard';
ItemCategoryModule,
ItemModule,
ItemRateModule,
ItemQueueModule,
TimeGroupModule,
// transaction
PaymentMethodModule,
@ -230,25 +174,10 @@ import { OtpCheckerGuard } from './core/guards/domain/otp-checker.guard';
ReportModule,
ReportBookmarkModule,
ReportExportModule,
ReportSummaryModule,
// superset
SupersetModule,
GateScanModule,
QueueModule,
BookingOnlineAuthModule,
BookingOrderModule,
OtpVerificationModule,
],
controllers: [],
providers: [
AuthService,
PrivilegeService,
OtpCheckerGuard,
/**
* By default all request from client will protect by JWT
* if there is some endpoint/function that does'nt require authentication

View File

@ -7,10 +7,10 @@ import {
UnauthorizedException,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { SessionService, UsersSession } from 'src/core/sessions';
import { UNPROTECTED_URL } from '../constants';
import { PrivilegeService } from './services/privilege.service';
import { AuthService } from './services/auth.service';
@Injectable({ scope: Scope.REQUEST })
export class JWTGuard implements CanActivate {
@ -18,13 +18,14 @@ export class JWTGuard implements CanActivate {
protected readonly session: SessionService,
protected readonly reflector: Reflector,
protected readonly privilege: PrivilegeService,
protected readonly authService: AuthService,
) {}
protected isPublic = false;
protected userSession: UsersSession;
async canActivate(context: ExecutionContext) {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
/**
* Check if access url is protected or not
* By default `isUnprotected` equals `false`
@ -60,29 +61,9 @@ export class JWTGuard implements CanActivate {
*/
try {
this.userSession = this.session.verifyToken(token);
await this.authService.verifyRegisteredLoginToken(token);
Logger.log(`Access from ${this.userSession.name}`, 'AuthGuard');
return true;
} catch (error) {
const expiredError = error.message;
if (expiredError === 'jwt expired') {
const [, body] = token.split('.');
const bodyToken = JSON.parse(atob(body));
const user = {
role: bodyToken.role,
user_id: bodyToken.id,
username: bodyToken.username,
user_privilege_id: bodyToken.user_privilege_id,
item_id: bodyToken.item_id,
item_name: bodyToken.item_name,
source: bodyToken.source,
};
this.authService.logoutUser(user, token);
}
throw new UnauthorizedException({
code: 10001,
message:

View File

@ -1,57 +0,0 @@
import {
CanActivate,
ExecutionContext,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { OtpVerificationModel } from 'src/modules/configuration/otp-verification/data/models/otp-verification.model';
import { OtpVerificationEntity } from 'src/modules/configuration/otp-verification/domain/entities/otp-verification.entity';
import { DataSource } from 'typeorm';
@Injectable()
export class OtpCheckerGuard implements CanActivate {
constructor(
@InjectDataSource(CONNECTION_NAME.DEFAULT)
protected readonly dataSource: DataSource,
) {}
get otpRepository() {
return this.dataSource.getRepository(OtpVerificationModel);
}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const verificationCode = request.headers['x-verification-code'];
console.log({ verificationCode });
if (verificationCode) {
const decoded = Buffer.from(verificationCode, 'base64').toString('ascii');
const [dataIdentity, otpCode] = decoded.split('|');
let otpData: OtpVerificationEntity;
otpData = await this.otpRepository.findOne({
where: {
otp_code: otpCode,
target_id: dataIdentity,
},
});
if (!otpData) {
otpData = await this.otpRepository.findOne({
where: {
otp_code: otpCode,
reference: dataIdentity,
},
});
}
// console.log({ dataIdentity, otpCode, otpData });
if (otpData && otpData?.verified_at) return true;
}
throw new UnprocessableEntityException('OTP not verified.');
}
}

View File

@ -9,7 +9,7 @@ import { MAIN_MENU } from '../constants';
@Injectable()
export class RolesGuard extends JWTGuard {
async canActivate(context: ExecutionContext): Promise<boolean> {
await super.canActivate(context);
super.canActivate(context);
// jika endpoint tersebut bukan public, maka lakukan check lanjutan
if (!this.isPublic) {

View File

@ -1,78 +0,0 @@
import {
HttpStatus,
Injectable,
Scope,
UnauthorizedException,
} from '@nestjs/common';
import { InjectDataSource } from '@nestjs/typeorm';
import {
CONNECTION_NAME,
OPERATION,
} from 'src/core/strings/constants/base.constants';
import { DataSource } from 'typeorm';
import { UserRole } from 'src/modules/user-related/user/constants';
import { UserModel } from 'src/modules/user-related/user/data/models/user.model';
import { AppSource, LogUserType } from 'src/core/helpers/constant';
import { EventBus } from '@nestjs/cqrs';
import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event';
import { UserLoginModel } from 'src/modules/user-related/user/data/models/user-login.model';
interface UserEntity {
user_id: string;
username: string;
role: UserRole;
user_privilege_id: string;
item_id: string;
item_name: string;
source: AppSource;
}
@Injectable({ scope: Scope.REQUEST })
export class AuthService {
constructor(
@InjectDataSource(CONNECTION_NAME.DEFAULT)
protected readonly dataSource: DataSource,
private eventBus: EventBus,
) {}
get repository() {
return this.dataSource.getRepository(UserLoginModel);
}
async logoutUser(user: UserEntity, token: string) {
await this.repository.delete({ login_token: token });
const userLogout = {
type: LogUserType.logout,
created_at: new Date().getTime(),
name: user.username,
user_privilege_id: user.user_privilege_id,
...user,
};
this.eventBus.publish(
new LogUserLoginEvent({
id: user.user_id,
old: null,
data: userLogout,
user: userLogout as any,
description: 'Logout',
module: UserModel.name,
op: OPERATION.UPDATE,
}),
);
}
async verifyRegisteredLoginToken(token: string) {
const data = await this.repository.findOneBy({ login_token: token });
if (!data) {
throw new UnauthorizedException({
statusCode: HttpStatus.UNAUTHORIZED,
message: `Invalid token`,
error: 'Unauthorized',
});
}
}
}

View File

@ -1,11 +0,0 @@
export enum LogUserType {
login = 'login',
logout = 'logout',
}
export enum AppSource {
POS_ADMIN = 'POS_ADMIN',
POS_COUNTER = 'POS_COUNTER',
QUEUE_ADMIN = 'QUEUE_ADMIN',
QUEUE_CUSTOMER = 'QUEUE_CUSTOMER',
}

View File

@ -1,66 +0,0 @@
interface OtpServiceEntity {
length?: number;
}
export class OtpService {
private readonly otpLength: number;
constructor({ length = 4 }: OtpServiceEntity) {
this.otpLength = Math.max(length, 4); // Minimum of 4 digits
}
private hasSequentialDigits(str: string): boolean {
for (let i = 0; i < str.length - 1; i++) {
const current = parseInt(str[i], 10);
const next = parseInt(str[i + 1], 10);
if (next === current + 1 || next === current - 1) {
return true;
}
}
return false;
}
private hasRepeatedDigits(str: string): boolean {
return str.split('').every((char) => char === str[0]);
}
private isPalindrome(str: string): boolean {
return str === str.split('').reverse().join('');
}
private hasPartiallyRepeatedDigits(str: string): boolean {
const counts: Record<string, number> = {};
for (const char of str) {
counts[char] = (counts[char] || 0) + 1;
}
// Reject if any digit appears more than twice
return Object.values(counts).some((count) => count > 2);
}
private hasNoMatchLength(str: string) {
return str.length !== this.otpLength;
}
private hasStartWithZero(str: string) {
return str.split('')[0] === '0';
}
public generateSecureOTP(): string {
let otp: string;
do {
otp = Array.from({ length: this.otpLength }, () =>
Math.floor(Math.random() * 10).toString(),
).join('');
} while (
this.hasNoMatchLength(otp) ||
this.hasSequentialDigits(otp) ||
this.hasRepeatedDigits(otp) ||
this.isPalindrome(otp) ||
this.hasPartiallyRepeatedDigits(otp) ||
this.hasStartWithZero(otp)
);
return otp;
}
}

View File

@ -8,9 +8,7 @@ import { diskStorage } from 'multer';
const MB = 1024 * 1024;
const fileFilter = (req, file, callback) => {
if (
file.mimetype.match(/\/(jpg|jpeg|png|flv|mp4|m3u8|ts|3gp|mov|avi|wmv)$/)
) {
if (file.mimetype.match(/\/(jpg|jpeg|png)$/)) {
callback(null, true);
} else {
callback(

View File

@ -55,7 +55,7 @@ export class ValidateRelationHelper<Entity> {
const relationColumn =
data[relation.relation]?.[`${relation.singleQuery[0]}`];
if (
// !!relationColumn &&
!!relationColumn &&
this.mappingValidator(
relationColumn,
relation.singleQuery[1],

View File

@ -32,7 +32,7 @@ export abstract class BaseManager {
setUser() {
try {
this.user = this.userProvider?.user ?? BLANK_USER;
this.user = this.userProvider?.user;
} catch (error) {
this.user = BLANK_USER;
}

View File

@ -71,7 +71,7 @@ export abstract class BaseCreateManager<Entity> extends BaseManager {
}
async publishEvents() {
this.eventBus?.publish(
this.eventBus.publish(
new RecordLog({
id: this.result['id'],
old: null,

View File

@ -50,7 +50,7 @@ export abstract class BaseIndexManager<Entity> extends BaseReadManager {
// jika searching status terdapat dalam enum, maka dia mencari specific data
// ? karena jika tidak, ketika dia search "active" maka "inactive" juga ikut
return `'${STATUS[statusData.toUpperCase()]}'`;
return `'${STATUS[statusData.toUpperCase()]}'` ?? `'%${statusData}%'`;
});
const exist = specificFilter.find((item) => item.isStatus);

View File

@ -1,10 +1,6 @@
import { ValidateRelationHelper } from 'src/core/helpers/validation/validate-relation.helper';
import { BaseManager } from '../base.manager';
import {
OPERATION,
QUEUE_STATUS,
STATUS,
} from 'src/core/strings/constants/base.constants';
import { OPERATION, STATUS } from 'src/core/strings/constants/base.constants';
import * as _ from 'lodash';
import { RecordLog } from 'src/modules/configuration/log/domain/entities/log.event';
@ -12,12 +8,12 @@ export abstract class BaseUpdateStatusManager<Entity> extends BaseManager {
protected dataId: string;
protected result: Entity;
protected oldData: Entity;
protected dataStatus: STATUS | QUEUE_STATUS;
protected dataStatus: STATUS;
protected relations = [];
protected duplicateColumn: string[];
abstract get entityTarget(): any;
setData(id: string, status: STATUS | QUEUE_STATUS): void {
setData(id: string, status: STATUS): void {
/**
* // TODO: Handle case confirm multiple tabs;
* Pola id yang dikirim dirubah menjadi data_id___updated_at

View File

@ -23,7 +23,6 @@ export class BaseFilterDto implements BaseFilterEntity {
@IsNumber()
limit = 10;
@ApiProperty({ type: String, required: false })
q: string;
@ApiProperty({ type: ['string'], required: false })

View File

@ -1,2 +1 @@
export const PAGINATION_RESPONSE = 'PAGINATION_RESPONSE';
export const GATE_RESPONSE = 'GATE_RESPONSE';

View File

@ -1,5 +1,5 @@
import { SetMetadata } from '@nestjs/common';
import { GATE_RESPONSE, PAGINATION_RESPONSE } from '../../constants';
import { PAGINATION_RESPONSE } from '../../constants';
/**
* This decorator will tell the response,
@ -7,5 +7,3 @@ import { GATE_RESPONSE, PAGINATION_RESPONSE } from '../../constants';
*/
export const Pagination = (isPagination = true) =>
SetMetadata(PAGINATION_RESPONSE, isPagination);
export const Gate = () => SetMetadata(GATE_RESPONSE, true);

View File

@ -8,20 +8,13 @@ import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Request } from 'express';
import { Reflector } from '@nestjs/core';
import { GATE_RESPONSE, PAGINATION_RESPONSE } from '../constants';
import { PAGINATION_RESPONSE } from '../constants';
import { createPaginationResponse } from './utils/pagination-meta.helper';
@Injectable()
export class TransformInterceptor implements NestInterceptor {
constructor(protected readonly reflector: Reflector) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const isGate = this.reflector.getAllAndOverride<boolean>(GATE_RESPONSE, [
context.getHandler(),
context.getClass(),
]);
if (isGate) return next.handle();
const isPagination = this.reflector.getAllAndOverride<boolean>(
PAGINATION_RESPONSE,
[context.getHandler(), context.getClass()],

View File

@ -1,11 +1,8 @@
import { AppSource } from 'src/core/helpers/constant';
import { UserRole } from 'src/modules/user-related/user/constants';
export interface UsersSession {
id: number;
name: string;
role: UserRole;
source?: AppSource;
item_id?: string;
user_privilege_id: string;
}

View File

@ -23,9 +23,4 @@ export class UserProvider {
const [, token] = this.request.headers['authorization'].split(' ');
return this.session.verifyToken(token);
}
get token(): string {
const [, token] = this.request.headers['authorization'].split(' ');
return token;
}
}

View File

@ -15,11 +15,6 @@ export enum STATUS {
WAITING = 'waiting',
}
export enum QUEUE_STATUS {
DONE = 'done',
CALLED = 'called',
}
export enum ORDER_TYPE {
ASC = 'ASC',
DESC = 'DESC',

View File

@ -4,7 +4,6 @@ export enum MODULE_NAME {
GATE = 'gates',
ITEM = 'items',
ITEM_CATEGORY = 'item-categories',
ITEM_QUEUE = 'item-queues',
ITEM_RATE = 'item-rates',
NEWS = 'news',
PAYMENT_METHOD = 'payment-methods',
@ -25,12 +24,4 @@ export enum MODULE_NAME {
REPORT = 'report',
REPORT_BOOKMARK = 'report-bookmark',
REPORT_EXPORT = 'report-export',
REPORT_SUMMARY = 'report-summary',
QUEUE = 'queue',
TIME_GROUPS = 'time-groups',
OTP_VERIFICATIONS = 'otp-verification',
OTP_VERIFIER = 'otp-verifier',
}

View File

@ -4,7 +4,6 @@ export enum TABLE_NAME {
FAQ = 'faqs',
ITEM = 'items',
ITEM_CATEGORY = 'item_categories',
ITEM_QUEUE = 'item_queues',
ITEM_RATE = 'item_rates',
GATE = 'gates',
LOG = 'logs',
@ -12,7 +11,6 @@ export enum TABLE_NAME {
NEWS = 'news',
PAYMENT_METHOD = 'payment_methods',
PRICE_FORMULA = 'price_formulas',
TRANSACTION_SETTING = 'transaction_settings',
REFUND = 'refunds',
REFUND_ITEM = 'refund_items',
SEASON_TYPE = 'season_types',
@ -24,12 +22,8 @@ export enum TABLE_NAME {
TRANSACTION_ITEM = 'transaction_items',
TRANSACTION_ITEM_BREAKDOWN = 'transaction_item_breakdowns',
TRANSACTION_TAX = 'transaction_taxes',
TRANSACTION_ITEM_TAX = 'transaction_item_taxes',
TRANSACTION_ITEM_BREAKDOWN_TAX = 't_breakdown_item_taxes',
TRANSACTION_DEMOGRAPHY = 'transaction_demographies',
USER = 'users',
USER_LOGIN = 'users_login',
LOG_USER_LOGIN = 'log_users_login',
USER_PRIVILEGE = 'user_privileges',
USER_PRIVILEGE_CONFIGURATION = 'user_privilege_configurations',
VIP_CATEGORY = 'vip_categories',
@ -37,14 +31,4 @@ export enum TABLE_NAME {
REPORT_BOOKMARK = 'report_bookmark',
EXPORT_REPORT_HISTORY = 'export_report_history',
QUEUE = 'queues',
QUEUE_ORDER = 'queue_orders',
QUEUE_TICKET = 'queue_tickets',
QUEUE_ITEM = 'queue_items',
QUEUE_BUCKET = 'queue_bucket',
TIME_GROUPS = 'time_groups',
OTP_VERIFICATIONS = 'otp_verifications',
OTP_VERIFIER = 'otp_verifier',
}

View File

@ -21,6 +21,9 @@ export class FixDemographyNationality1723716561482
await queryRunner.query(
`ALTER TABLE "transaction_demographies" DROP CONSTRAINT "FK_a2b705884bca06c148e3b35ab04"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ADD CONSTRAINT "UQ_a2b705884bca06c148e3b35ab04" UNIQUE ("transaction_id")`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ADD CONSTRAINT "FK_a2b705884bca06c148e3b35ab04" FOREIGN KEY ("transaction_id") REFERENCES "transactions"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
@ -30,6 +33,9 @@ export class FixDemographyNationality1723716561482
await queryRunner.query(
`ALTER TABLE "transaction_demographies" DROP CONSTRAINT "FK_a2b705884bca06c148e3b35ab04"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" DROP CONSTRAINT "UQ_a2b705884bca06c148e3b35ab04"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ADD CONSTRAINT "FK_a2b705884bca06c148e3b35ab04" FOREIGN KEY ("transaction_id") REFERENCES "transactions"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);

View File

@ -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" numeric`,
`ALTER TABLE "items" ADD "play_estimation" integer`,
);
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddBreakdownToTransactionItem1724233193743
implements MigrationInterface
{
name = 'AddBreakdownToTransactionItem1724233193743';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "breakdown_bundling" boolean NOT NULL DEFAULT false`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "breakdown_bundling"`,
);
}
}

View File

@ -1,47 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddValueToDemography1724240624025 implements MigrationInterface {
name = 'AddValueToDemography1724240624025';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TYPE "public"."transaction_demographies_nationality_enum" RENAME TO "transaction_demographies_nationality_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transaction_demographies_nationality_enum" AS ENUM('local', 'foreign', 'mix', 'foreigner')`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ALTER COLUMN "nationality" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ALTER COLUMN "nationality" TYPE "public"."transaction_demographies_nationality_enum" USING "nationality"::"text"::"public"."transaction_demographies_nationality_enum"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ALTER COLUMN "nationality" SET DEFAULT 'local'`,
);
await queryRunner.query(
`DROP TYPE "public"."transaction_demographies_nationality_enum_old"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."transaction_demographies_nationality_enum_old" AS ENUM('local', 'foreign', 'mix')`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ALTER COLUMN "nationality" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ALTER COLUMN "nationality" TYPE "public"."transaction_demographies_nationality_enum_old" USING "nationality"::"text"::"public"."transaction_demographies_nationality_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_demographies" ALTER COLUMN "nationality" SET DEFAULT 'local'`,
);
await queryRunner.query(
`DROP TYPE "public"."transaction_demographies_nationality_enum"`,
);
await queryRunner.query(
`ALTER TYPE "public"."transaction_demographies_nationality_enum_old" RENAME TO "transaction_demographies_nationality_enum"`,
);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddValueVariableFormula1724926316235
implements MigrationInterface
{
name = 'AddValueVariableFormula1724926316235';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "price_formulas" ADD "value_for" character varying NOT NULL DEFAULT 'dpp'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "price_formulas" DROP COLUMN "value_for"`,
);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddPaymentDateBankColumnAtTransaction1725962197762
implements MigrationInterface
{
name = 'AddPaymentDateBankColumnAtTransaction1725962197762';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" ADD "payment_date_bank" date`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "payment_date_bank"`,
);
}
}

View File

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddPosNameColumn1726033041774 implements MigrationInterface {
name = 'AddPosNameColumn1726033041774';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" ADD "creator_counter_name" character varying`,
);
await queryRunner.query(
`ALTER TABLE "logs_pos" ADD "pos_name" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "logs_pos" DROP COLUMN "pos_name"`);
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "creator_counter_name"`,
);
}
}

View File

@ -1,43 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddFlagRoleQueue1726041175749 implements MigrationInterface {
name = 'AddFlagRoleQueue1726041175749';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TYPE "public"."users_role_enum" RENAME TO "users_role_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."users_role_enum" AS ENUM('superadmin', 'staff', 'tenant', 'queue_admin')`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "role" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "role" TYPE "public"."users_role_enum" USING "role"::"text"::"public"."users_role_enum"`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "role" SET DEFAULT 'staff'`,
);
await queryRunner.query(`DROP TYPE "public"."users_role_enum_old"`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."users_role_enum_old" AS ENUM('superadmin', 'staff', 'tenant')`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "role" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "role" TYPE "public"."users_role_enum_old" USING "role"::"text"::"public"."users_role_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "role" SET DEFAULT 'staff'`,
);
await queryRunner.query(`DROP TYPE "public"."users_role_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."users_role_enum_old" RENAME TO "users_role_enum"`,
);
}
}

View File

@ -1,73 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTaxItemTransaction1726045820711 implements MigrationInterface {
name = 'AddTaxItemTransaction1726045820711';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "transaction_item_taxes" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "tax_id" character varying, "tax_name" character varying, "taxt_value" numeric, "tax_total_value" numeric, "transaction_id" uuid, CONSTRAINT "PK_fc5f6da61b24eb5bfdd503b0a0d" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "t_breakdown_item_taxes" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "tax_id" character varying, "tax_name" character varying, "taxt_value" numeric, "tax_total_value" numeric, "transaction_id" uuid, CONSTRAINT "PK_a1ef08d2c68169a50102aa70eca" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "total_profit_share" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" ADD "total_profit_share" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_taxes" ADD CONSTRAINT "FK_f5c4966a381d903899cafb4b5ba" FOREIGN KEY ("transaction_id") REFERENCES "transaction_items"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "t_breakdown_item_taxes" ADD CONSTRAINT "FK_74bedce7e94f6707ddf26ef0c0f" FOREIGN KEY ("transaction_id") REFERENCES "transaction_item_breakdowns"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "payment_total_dpp" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" ADD "payment_total_dpp" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "payment_total_tax" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" ADD "payment_total_tax" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" ADD "total_share_tenant" numeric`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "t_breakdown_item_taxes" DROP CONSTRAINT "FK_74bedce7e94f6707ddf26ef0c0f"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_taxes" DROP CONSTRAINT "FK_f5c4966a381d903899cafb4b5ba"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "total_profit_share"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "total_profit_share"`,
);
await queryRunner.query(`DROP TABLE "t_breakdown_item_taxes"`);
await queryRunner.query(`DROP TABLE "transaction_item_taxes"`);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "payment_total_dpp"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "payment_total_dpp"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "payment_total_tax"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "payment_total_tax"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "total_share_tenant"`,
);
}
}

View File

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTableUserLogin1726115025759 implements MigrationInterface {
name = 'AddTableUserLogin1726115025759';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "users_login" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "login_date" bigint NOT NULL, "login_token" character varying, "user_id" uuid, CONSTRAINT "REL_2a80a213b51423ce5b8211f058" UNIQUE ("user_id"), CONSTRAINT "PK_e564194a9a22f8c623354284f75" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`,
);
await queryRunner.query(`DROP TABLE "users_login"`);
}
}

View File

@ -1,23 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateTableUserLogin1726122619596 implements MigrationInterface {
name = 'UpdateTableUserLogin1726122619596';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
}

View File

@ -1,23 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTableLogUserLogin1726123955427 implements MigrationInterface {
name = 'AddTableLogUserLogin1726123955427';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."log_users_login_type_enum" AS ENUM('login', 'logout')`,
);
await queryRunner.query(
`CREATE TYPE "public"."log_users_login_role_enum" AS ENUM('superadmin', 'staff', 'tenant', 'queue_admin')`,
);
await queryRunner.query(
`CREATE TABLE "log_users_login" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" "public"."log_users_login_type_enum", "role" "public"."log_users_login_role_enum", "user_id" uuid, "username" character varying, "created_at" bigint, CONSTRAINT "PK_75141588aa6ee560504f7d3adce" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "log_users_login"`);
await queryRunner.query(`DROP TYPE "public"."log_users_login_role_enum"`);
await queryRunner.query(`DROP TYPE "public"."log_users_login_type_enum"`);
}
}

View File

@ -1,17 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumnItemId1726139426994 implements MigrationInterface {
name = 'AddColumnItemId1726139426994';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users_login" ADD "item_id" uuid`);
await queryRunner.query(`ALTER TABLE "log_users_login" ADD "item_id" uuid`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "log_users_login" DROP COLUMN "item_id"`,
);
await queryRunner.query(`ALTER TABLE "users_login" DROP COLUMN "item_id"`);
}
}

View File

@ -1,23 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumnItemName1726141393404 implements MigrationInterface {
name = 'AddColumnItemName1726141393404';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "users_login" ADD "item_name" character varying`,
);
await queryRunner.query(
`ALTER TABLE "log_users_login" ADD "item_name" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "log_users_login" DROP COLUMN "item_name"`,
);
await queryRunner.query(
`ALTER TABLE "users_login" DROP COLUMN "item_name"`,
);
}
}

View File

@ -1,17 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddFormulaToTax1726365023179 implements MigrationInterface {
name = 'AddFormulaToTax1726365023179';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "taxes" ADD "formula_render" json`);
await queryRunner.query(
`ALTER TABLE "taxes" ADD "formula_string" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "formula_string"`);
await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "formula_render"`);
}
}

View File

@ -1,35 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class ChangeUserLoginRelation1726642119207
implements MigrationInterface
{
name = 'ChangeUserLoginRelation1726642119207';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users" DROP COLUMN "refresh_token"`);
await queryRunner.query(
`ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`,
);
await queryRunner.query(
`ALTER TABLE "users_login" DROP CONSTRAINT "REL_2a80a213b51423ce5b8211f058"`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD CONSTRAINT "REL_2a80a213b51423ce5b8211f058" UNIQUE ("user_id")`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "users" ADD "refresh_token" character varying`,
);
}
}

View File

@ -1,29 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumnSourceAtUserLogin1726642499135
implements MigrationInterface
{
name = 'AddColumnSourceAtUserLogin1726642499135';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."users_login_role_enum" AS ENUM('superadmin', 'staff', 'tenant', 'queue_admin')`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD "role" "public"."users_login_role_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."users_login_source_enum" AS ENUM('POS_ADMIN', 'POS_COUNTER', 'QUEUE_ADMIN', 'QUEUE_CUSTOMER')`,
);
await queryRunner.query(
`ALTER TABLE "users_login" ADD "source" "public"."users_login_source_enum"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "users_login" DROP COLUMN "source"`);
await queryRunner.query(`DROP TYPE "public"."users_login_source_enum"`);
await queryRunner.query(`ALTER TABLE "users_login" DROP COLUMN "role"`);
await queryRunner.query(`DROP TYPE "public"."users_login_role_enum"`);
}
}

View File

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddSourceOnLogLogin1726647442006 implements MigrationInterface {
name = 'AddSourceOnLogLogin1726647442006';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."log_users_login_source_enum" AS ENUM('POS_ADMIN', 'POS_COUNTER', 'QUEUE_ADMIN', 'QUEUE_CUSTOMER')`,
);
await queryRunner.query(
`ALTER TABLE "log_users_login" ADD "source" "public"."log_users_login_source_enum"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "log_users_login" DROP COLUMN "source"`,
);
await queryRunner.query(`DROP TYPE "public"."log_users_login_source_enum"`);
}
}

View File

@ -1,43 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddDiscountForItemTransaction1726824289989
implements MigrationInterface
{
name = 'AddDiscountForItemTransaction1726824289989';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "subtotal" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "discount_value" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" ADD "subtotal" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" ADD "discount_value" numeric`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" ADD "total_price" numeric`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "total_price"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "discount_value"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "subtotal"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "discount_value"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "subtotal"`,
);
}
}

View File

@ -1,23 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class ChangeColumnName1726830293878 implements MigrationInterface {
name = 'ChangeColumnName1726830293878';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_items" RENAME COLUMN "subtotal" TO "total_net_price"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" RENAME COLUMN "subtotal" TO "total_net_price"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_item_breakdowns" RENAME COLUMN "total_net_price" TO "subtotal"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" RENAME COLUMN "total_net_price" TO "subtotal"`,
);
}
}

View File

@ -1,25 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddDiscountValueToVoucher1728377112337
implements MigrationInterface
{
name = 'AddDiscountValueToVoucher1728377112337';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "vip_codes" ADD "discount_value" numeric`,
);
await queryRunner.query(
`ALTER TABLE "vip_codes" ALTER COLUMN "discount" DROP NOT NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "vip_codes" ALTER COLUMN "discount" SET NOT NULL`,
);
await queryRunner.query(
`ALTER TABLE "vip_codes" DROP COLUMN "discount_value"`,
);
}
}

View File

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class ItemVideoAndVipPass1729072422409 implements MigrationInterface {
name = 'ItemVideoAndVipPass1729072422409';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" ADD "video_url" character varying`,
);
await queryRunner.query(
`ALTER TABLE "vip_categories" ADD "has_vip_pass" boolean NOT NULL DEFAULT false`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "vip_categories" DROP COLUMN "has_vip_pass"`,
);
await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "video_url"`);
}
}

View File

@ -1,43 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class QueueTable1729151429165 implements MigrationInterface {
name = 'QueueTable1729151429165';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "queue_orders" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "code" character varying NOT NULL, "customer" character varying, "phone" character varying, "date" bigint NOT NULL, CONSTRAINT "PK_b139e4cc9ca3e709c152f820d2e" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "queue_tickets" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "code" character varying NOT NULL, "customer" character varying, "phone" character varying, "date" bigint NOT NULL, "order_id" uuid, CONSTRAINT "PK_1b903aa90bcc04136caa6540c55" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "queue_items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "qty" integer NOT NULL, "ticket_id" uuid, "item_id" uuid, CONSTRAINT "PK_2245e11ac3517494bacfe932773" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "queue_tickets" ADD CONSTRAINT "FK_0e9823b8b7ca9523b3be73878e5" FOREIGN KEY ("order_id") REFERENCES "queue_orders"("id") ON DELETE SET NULL ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "queue_items" ADD CONSTRAINT "FK_25352739034765f6917757df74b" FOREIGN KEY ("ticket_id") REFERENCES "queue_tickets"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "queue_items" ADD CONSTRAINT "FK_ab15c053aeb4f739ebf533b61cd" FOREIGN KEY ("item_id") REFERENCES "items"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queue_items" DROP CONSTRAINT "FK_ab15c053aeb4f739ebf533b61cd"`,
);
await queryRunner.query(
`ALTER TABLE "queue_items" DROP CONSTRAINT "FK_25352739034765f6917757df74b"`,
);
await queryRunner.query(
`ALTER TABLE "queue_tickets" DROP CONSTRAINT "FK_0e9823b8b7ca9523b3be73878e5"`,
);
await queryRunner.query(`DROP TABLE "queue_items"`);
await queryRunner.query(`DROP TABLE "queue_tickets"`);
await queryRunner.query(`DROP TABLE "queue_orders"`);
}
}

View File

@ -1,18 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class VideoUrlToJson1729248576381 implements MigrationInterface {
name = 'VideoUrlToJson1729248576381';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "video_url"`);
await queryRunner.query(`ALTER TABLE "items" ADD "video_url" json`);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "video_url"`);
await queryRunner.query(
`ALTER TABLE "items" ADD "video_url" character varying`,
);
await queryRunner.query(`DROP TABLE "item_queues"`);
}
}

View File

@ -1,33 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class ItemQueues1729570177597 implements MigrationInterface {
name = 'ItemQueues1729570177597';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."item_queues_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`CREATE TYPE "public"."item_queues_item_type_enum" AS ENUM('tiket masuk', 'wahana', 'bundling', 'free gift', 'other')`,
);
await queryRunner.query(
`CREATE TABLE "item_queues" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."item_queues_status_enum" NOT NULL DEFAULT 'draft', "name" character varying NOT NULL, "item_type" "public"."item_queues_item_type_enum" NOT NULL DEFAULT 'tiket masuk', CONSTRAINT "PK_e19adb0b99d995e8f10c189985f" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`ALTER TABLE "items" ADD "item_queue_id" uuid`);
await queryRunner.query(
`ALTER TABLE "items" ADD CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c" FOREIGN KEY ("item_queue_id") REFERENCES "item_queues"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" DROP CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c"`,
);
await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "item_queue_id"`);
await queryRunner.query(`DROP TABLE "item_queues"`);
await queryRunner.query(`DROP TYPE "public"."item_queues_item_type_enum"`);
await queryRunner.query(`DROP TYPE "public"."item_queues_status_enum"`);
}
}

View File

@ -1,25 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class ItemQueueOnDelete1729582398827 implements MigrationInterface {
name = 'ItemQueueOnDelete1729582398827';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" DROP CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c"`,
);
await queryRunner.query(
`ALTER TABLE "items" ADD CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c" FOREIGN KEY ("item_queue_id") REFERENCES "item_queues"("id") ON DELETE SET NULL ON UPDATE CASCADE`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" DROP CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c"`,
);
await queryRunner.query(
`ALTER TABLE "items" ADD CONSTRAINT "FK_2cbbeb03e176addcf60d65f7c9c" FOREIGN KEY ("item_queue_id") REFERENCES "item_queues"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTransactionIdToQueueOrder1729653046392
implements MigrationInterface
{
name = 'AddTransactionIdToQueueOrder1729653046392';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queue_orders" ADD "transaction_id" character varying NOT NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queue_orders" DROP COLUMN "transaction_id"`,
);
}
}

View File

@ -1,22 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddQueueTable1729756969674 implements MigrationInterface {
name = 'AddQueueTable1729756969674';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "queues" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "code" character varying NOT NULL, "status" character varying NOT NULL, "time" bigint NOT NULL, "call_time" bigint NOT NULL, "vip" boolean NOT NULL, "item_id" uuid NOT NULL, "qty" integer NOT NULL, CONSTRAINT "PK_d966f9eb39a9396658387071bb3" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "queues" ADD CONSTRAINT "FK_435954e9a0d9967f17e043d54b4" FOREIGN KEY ("item_id") REFERENCES "queue_items"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queues" DROP CONSTRAINT "FK_435954e9a0d9967f17e043d54b4"`,
);
await queryRunner.query(`DROP TABLE "queues"`);
}
}

View File

@ -1,39 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddQueueBaseModel1729838994129 implements MigrationInterface {
name = 'AddQueueBaseModel1729838994129';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queues" ADD "creator_id" character varying(36)`,
);
await queryRunner.query(
`ALTER TABLE "queues" ADD "creator_name" character varying(125)`,
);
await queryRunner.query(
`ALTER TABLE "queues" ADD "editor_id" character varying(36)`,
);
await queryRunner.query(
`ALTER TABLE "queues" ADD "editor_name" character varying(125)`,
);
await queryRunner.query(
`ALTER TABLE "queues" ADD "created_at" bigint NOT NULL`,
);
await queryRunner.query(
`ALTER TABLE "queues" ADD "updated_at" bigint NOT NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queues" ALTER COLUMN "call_time" DROP NOT NULL`,
);
await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "updated_at"`);
await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "created_at"`);
await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "editor_name"`);
await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "editor_id"`);
await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "creator_name"`);
await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "creator_id"`);
}
}

View File

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddQueueBucket1730859187883 implements MigrationInterface {
name = 'AddQueueBucket1730859187883';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "queue_bucket" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "queue_item_id" character varying NOT NULL, "date" bigint NOT NULL, "regular" integer NOT NULL, "vip" integer NOT NULL, CONSTRAINT "PK_cdd58b0d9e93e4be922da9d8bd6" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "queue_bucket"`);
}
}

View File

@ -1,50 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTransactionAndItemRelation1731383726542
implements MigrationInterface
{
name = 'AddTransactionAndItemRelation1731383726542';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "item_id"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "item_id" uuid`,
);
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "customer_category_id"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ADD "customer_category_id" uuid`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD CONSTRAINT "FK_edb934ab033f847e3f7ed4fc0fc" FOREIGN KEY ("item_id") REFERENCES "items"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ADD CONSTRAINT "FK_08dc8138714894a66e94820766d" FOREIGN KEY ("customer_category_id") REFERENCES "vip_categories"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" DROP CONSTRAINT "FK_08dc8138714894a66e94820766d"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP CONSTRAINT "FK_edb934ab033f847e3f7ed4fc0fc"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "customer_category_id"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ADD "customer_category_id" character varying`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "item_id"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "item_id" character varying`,
);
}
}

View File

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddItemQueueToQueue1731498661938 implements MigrationInterface {
name = 'AddItemQueueToQueue1731498661938';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queues" ADD "item_queue_id" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "queues" DROP COLUMN "item_queue_id"`);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddInformationToItemQueue1731570311609
implements MigrationInterface
{
name = 'AddInformationToItemQueue1731570311609';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "item_queues" ADD "information" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "item_queues" DROP COLUMN "information"`,
);
}
}

View File

@ -1,25 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class ItemQueueAddTimeAndPeakLevel1733199330134
implements MigrationInterface
{
name = 'ItemQueueAddTimeAndPeakLevel1733199330134';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "item_queues" ADD "max_peak_level" integer NOT NULL DEFAULT '100'`,
);
await queryRunner.query(
`ALTER TABLE "item_queues" ADD "call_preparation" integer NOT NULL DEFAULT '5'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "item_queues" DROP COLUMN "call_preparation"`,
);
await queryRunner.query(
`ALTER TABLE "item_queues" DROP COLUMN "max_peak_level"`,
);
}
}

View File

@ -1,25 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddNotificationConfigToItemQueue1734717058658
implements MigrationInterface
{
name = 'AddNotificationConfigToItemQueue1734717058658';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "item_queues" ADD "use_notification" boolean NOT NULL DEFAULT true`,
);
await queryRunner.query(
`ALTER TABLE "item_queues" ADD "requiring_notification" boolean NOT NULL DEFAULT false`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "item_queues" DROP COLUMN "requiring_notification"`,
);
await queryRunner.query(
`ALTER TABLE "item_queues" DROP COLUMN "use_notification"`,
);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddLastNotificationToQueue1734718462106
implements MigrationInterface
{
name = 'AddLastNotificationToQueue1734718462106';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queues" ADD "last_notification" bigint NULL`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "queues" DROP COLUMN "last_notification"`,
);
}
}

View File

@ -1,17 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class TransactionSettingModel1745991391299
implements MigrationInterface
{
name = 'TransactionSettingModel1745991391299';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "transaction_settings" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "value" numeric NOT NULL DEFAULT '100', CONSTRAINT "PK_db7fb38a439358b499ebdee4761" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "transaction_settings"`);
}
}

View File

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class BookingAuthentication1748313715598 implements MigrationInterface {
name = 'BookingAuthentication1748313715598';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "booking_verification" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "phone_number" character varying NOT NULL, "code" character varying, "tried" integer NOT NULL DEFAULT '0', "created_at" bigint NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()) * 1000, "updated_at" bigint NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()) * 1000, CONSTRAINT "PK_046e9288c7dd05c7259275d9fc0" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "booking_verification"`);
}
}

View File

@ -1,27 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateTimeGroupTable1748409891706 implements MigrationInterface {
name = 'CreateTimeGroupTable1748409891706';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."time_groups_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`CREATE TABLE "time_groups" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."time_groups_status_enum" NOT NULL DEFAULT 'draft', "name" character varying NOT NULL, "start_time" TIME NOT NULL, "end_time" TIME NOT NULL, "max_usage_time" TIME NOT NULL, CONSTRAINT "PK_083d02988db7bedfe3b7c869b50" PRIMARY KEY ("id"))`,
);
await queryRunner.query(`ALTER TABLE "items" ADD "time_group_id" uuid`);
await queryRunner.query(
`ALTER TABLE "items" ADD CONSTRAINT "FK_f44f222e1808448dca1b6cc4557" FOREIGN KEY ("time_group_id") REFERENCES "time_groups"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" DROP CONSTRAINT "FK_f44f222e1808448dca1b6cc4557"`,
);
await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "time_group_id"`);
await queryRunner.query(`DROP TABLE "time_groups"`);
await queryRunner.query(`DROP TYPE "public"."time_groups_status_enum"`);
}
}

View File

@ -1,21 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumnOtpCode1748935417155 implements MigrationInterface {
name = 'AddColumnOtpCode1748935417155';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "vip_codes" ADD "otp_code" character varying`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ADD "otp_code" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "otp_code"`,
);
await queryRunner.query(`ALTER TABLE "vip_codes" DROP COLUMN "otp_code"`);
}
}

View File

@ -1,29 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class CreateTableOtpCerifications1749028279580
implements MigrationInterface
{
name = 'CreateTableOtpCerifications1749028279580';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."otp_verifications_action_type_enum" AS ENUM('CREATE_DISCOUNT', 'CANCEL_TRANSACTION')`,
);
await queryRunner.query(
`CREATE TYPE "public"."otp_verifications_source_enum" AS ENUM('POS', 'WEB')`,
);
await queryRunner.query(
`CREATE TABLE "otp_verifications" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "otp_code" character varying NOT NULL, "action_type" "public"."otp_verifications_action_type_enum" NOT NULL, "target_id" character varying, "reference" character varying, "source" "public"."otp_verifications_source_enum" NOT NULL, "is_used" boolean NOT NULL DEFAULT false, "expired_at" bigint NOT NULL, "verified_at" bigint, CONSTRAINT "PK_91d17e75ac3182dba6701869b39" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "otp_verifications"`);
await queryRunner.query(
`DROP TYPE "public"."otp_verifications_source_enum"`,
);
await queryRunner.query(
`DROP TYPE "public"."otp_verifications_action_type_enum"`,
);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddColumnIsReplacedOtpVerification1749030419440
implements MigrationInterface
{
name = 'AddColumnIsReplacedOtpVerification1749030419440';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "otp_verifications" ADD "is_replaced" boolean NOT NULL DEFAULT false`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "otp_verifications" DROP COLUMN "is_replaced"`,
);
}
}

View File

@ -1,15 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddTableOtpVerifier1749043616622 implements MigrationInterface {
name = 'AddTableOtpVerifier1749043616622';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "otp_verifier" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "name" character varying, "phone_number" character varying NOT NULL, CONSTRAINT "PK_884e2d0873fc589a1bdc477b2ea" PRIMARY KEY ("id"))`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "otp_verifier"`);
}
}

View File

@ -1,37 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateEnumOtpActionType1749046285398
implements MigrationInterface
{
name = 'UpdateEnumOtpActionType1749046285398';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TYPE "public"."otp_verifications_action_type_enum" RENAME TO "otp_verifications_action_type_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."otp_verifications_action_type_enum" AS ENUM('CREATE_DISCOUNT', 'CANCEL_TRANSACTION', 'REJECT_RECONCILIATION')`,
);
await queryRunner.query(
`ALTER TABLE "otp_verifications" ALTER COLUMN "action_type" TYPE "public"."otp_verifications_action_type_enum" USING "action_type"::"text"::"public"."otp_verifications_action_type_enum"`,
);
await queryRunner.query(
`DROP TYPE "public"."otp_verifications_action_type_enum_old"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."otp_verifications_action_type_enum_old" AS ENUM('CREATE_DISCOUNT', 'CANCEL_TRANSACTION')`,
);
await queryRunner.query(
`ALTER TABLE "otp_verifications" ALTER COLUMN "action_type" TYPE "public"."otp_verifications_action_type_enum_old" USING "action_type"::"text"::"public"."otp_verifications_action_type_enum_old"`,
);
await queryRunner.query(
`DROP TYPE "public"."otp_verifications_action_type_enum"`,
);
await queryRunner.query(
`ALTER TYPE "public"."otp_verifications_action_type_enum_old" RENAME TO "otp_verifications_action_type_enum"`,
);
}
}

View File

@ -1,28 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class RescheduleOtp1749524993295 implements MigrationInterface {
name = 'RescheduleOtp1749524993295';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TABLE "reschedule_verification" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "name" character varying NOT NULL, "phone_number" character varying NOT NULL, "booking_id" character varying NOT NULL, "reschedule_date" character varying NOT NULL, "code" integer NOT NULL, "tried" integer NOT NULL DEFAULT '0', "created_at" bigint NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()) * 1000, "updated_at" bigint NOT NULL DEFAULT EXTRACT(EPOCH FROM NOW()) * 1000, CONSTRAINT "PK_d4df453337ca12771eb223323d8" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "booking_verification" ALTER COLUMN "created_at" SET DEFAULT EXTRACT(EPOCH FROM NOW()) * 1000`,
);
await queryRunner.query(
`ALTER TABLE "booking_verification" ALTER COLUMN "updated_at" SET DEFAULT EXTRACT(EPOCH FROM NOW()) * 1000`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "booking_verification" ALTER COLUMN "updated_at" SET DEFAULT (EXTRACT(epoch FROM now()) * (1000))`,
);
await queryRunner.query(
`ALTER TABLE "booking_verification" ALTER COLUMN "created_at" SET DEFAULT (EXTRACT(epoch FROM now()) * (1000))`,
);
await queryRunner.query(`DROP TABLE "reschedule_verification"`);
}
}

View File

@ -1,19 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddBookingDescriptionToItem1749537252986
implements MigrationInterface
{
name = 'AddBookingDescriptionToItem1749537252986';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" ADD "booking_description" text`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" DROP COLUMN "booking_description"`,
);
}
}

View File

@ -1,24 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddBookingParentToTransaction1749604239749
implements MigrationInterface
{
name = 'AddBookingParentToTransaction1749604239749';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "transactions" ADD "parent_id" uuid`);
await queryRunner.query(
`ALTER TABLE "transactions" ADD CONSTRAINT "FK_413e95171729ba18cabce1c31e3" FOREIGN KEY ("parent_id") REFERENCES "transactions"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" DROP CONSTRAINT "FK_413e95171729ba18cabce1c31e3"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "parent_id"`,
);
}
}

View File

@ -1,55 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddEnumOtpActionTypeAndUpdateColumnOtpVerifier1750045520332
implements MigrationInterface
{
name = 'AddEnumOtpActionTypeAndUpdateColumnOtpVerifier1750045520332';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "otp_verifier" ADD "is_all_action" boolean NOT NULL DEFAULT false`,
);
await queryRunner.query(
`CREATE TYPE "public"."otp_verifier_action_types_enum" AS ENUM('CREATE_DISCOUNT', 'CANCEL_TRANSACTION', 'REJECT_RECONCILIATION', 'ACTIVATE_ITEM', 'ACTIVATE_USER', 'UPDATE_ITEM_PRICE', 'UPDATE_ITEM_DETAILS', 'CONFIRM_TRANSACTION')`,
);
await queryRunner.query(
`ALTER TABLE "otp_verifier" ADD "action_types" "public"."otp_verifier_action_types_enum" array`,
);
await queryRunner.query(
`ALTER TYPE "public"."otp_verifications_action_type_enum" RENAME TO "otp_verifications_action_type_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."otp_verifications_action_type_enum" AS ENUM('CREATE_DISCOUNT', 'CANCEL_TRANSACTION', 'REJECT_RECONCILIATION', 'ACTIVATE_ITEM', 'ACTIVATE_USER', 'UPDATE_ITEM_PRICE', 'UPDATE_ITEM_DETAILS', 'CONFIRM_TRANSACTION')`,
);
await queryRunner.query(
`ALTER TABLE "otp_verifications" ALTER COLUMN "action_type" TYPE "public"."otp_verifications_action_type_enum" USING "action_type"::"text"::"public"."otp_verifications_action_type_enum"`,
);
await queryRunner.query(
`DROP TYPE "public"."otp_verifications_action_type_enum_old"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."otp_verifications_action_type_enum_old" AS ENUM('CREATE_DISCOUNT', 'CANCEL_TRANSACTION', 'REJECT_RECONCILIATION')`,
);
await queryRunner.query(
`ALTER TABLE "otp_verifications" ALTER COLUMN "action_type" TYPE "public"."otp_verifications_action_type_enum_old" USING "action_type"::"text"::"public"."otp_verifications_action_type_enum_old"`,
);
await queryRunner.query(
`DROP TYPE "public"."otp_verifications_action_type_enum"`,
);
await queryRunner.query(
`ALTER TYPE "public"."otp_verifications_action_type_enum_old" RENAME TO "otp_verifications_action_type_enum"`,
);
await queryRunner.query(
`ALTER TABLE "otp_verifier" DROP COLUMN "action_types"`,
);
await queryRunner.query(
`DROP TYPE "public"."otp_verifier_action_types_enum"`,
);
await queryRunner.query(
`ALTER TABLE "otp_verifier" DROP COLUMN "is_all_action"`,
);
}
}

View File

@ -1,29 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AddUsageTypeToItem1750319148269 implements MigrationInterface {
name = 'AddUsageTypeToItem1750319148269';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."item_queues_usage_type_enum" AS ENUM('one_time', 'multiple')`,
);
await queryRunner.query(
`ALTER TABLE "item_queues" ADD "usage_type" "public"."item_queues_usage_type_enum" NOT NULL DEFAULT 'one_time'`,
);
await queryRunner.query(
`CREATE TYPE "public"."items_usage_type_enum" AS ENUM('one_time', 'multiple')`,
);
await queryRunner.query(
`ALTER TABLE "items" ADD "usage_type" "public"."items_usage_type_enum" NOT NULL DEFAULT 'one_time'`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "usage_type"`);
await queryRunner.query(`DROP TYPE "public"."items_usage_type_enum"`);
await queryRunner.query(
`ALTER TABLE "item_queues" DROP COLUMN "usage_type"`,
);
await queryRunner.query(`DROP TYPE "public"."item_queues_usage_type_enum"`);
}
}

View File

@ -1,17 +0,0 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class RemoveItemNameUnique1750834308368 implements MigrationInterface {
name = 'RemoveItemNameUnique1750834308368';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" DROP CONSTRAINT "UQ_213736582899b3599acaade2cd1"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" ADD CONSTRAINT "UQ_213736582899b3599acaade2cd1" UNIQUE ("name")`,
);
}
}

View File

@ -1,25 +0,0 @@
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { ConfigModule } from '@nestjs/config';
import { Module } from '@nestjs/common';
import { VerificationModel } from './data/models/verification.model';
import { TypeOrmModule } from '@nestjs/typeorm';
import { BookingAuthenticationController } from './infrastructure/controllers/booking-authentication.controller';
import { VerificationService } from './data/services/verification.service';
import { JwtModule } from '@nestjs/jwt';
import { JWT_EXPIRED } from 'src/core/sessions/constants';
import { JWT_SECRET } from 'src/core/sessions/constants';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forFeature([VerificationModel], CONNECTION_NAME.DEFAULT),
JwtModule.register({
secret: JWT_SECRET,
signOptions: { expiresIn: JWT_EXPIRED },
}),
],
controllers: [BookingAuthenticationController],
providers: [VerificationService],
})
export class BookingOnlineAuthModule {}

View File

@ -1,29 +0,0 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { BookingVerification } from '../../domain/entities/booking-verification.entity';
@Entity('booking_verification')
export class VerificationModel implements BookingVerification {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column()
phone_number: string;
@Column({ nullable: true })
code?: string;
@Column({ default: 0 })
tried: number;
@Column({ type: 'bigint', default: () => 'EXTRACT(EPOCH FROM NOW()) * 1000' })
created_at: number;
@Column({
type: 'bigint',
default: () => 'EXTRACT(EPOCH FROM NOW()) * 1000',
onUpdate: 'EXTRACT(EPOCH FROM NOW()) * 1000',
})
updated_at: number;
}

View File

@ -1,110 +0,0 @@
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { VerificationModel } from '../models/verification.model';
import { BookingVerification } from '../../domain/entities/booking-verification.entity';
import { UnprocessableEntityException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { WhatsappService } from 'src/services/whatsapp/whatsapp.service';
export class VerificationService {
constructor(
@InjectRepository(VerificationModel)
private readonly verificationRepository: Repository<VerificationModel>,
private readonly jwtService: JwtService,
) {}
maxAttempts = 3;
expiredTime = 5 * 60 * 1000;
expiredTimeRegister = 1 * 60 * 1000;
async generateToken(payload: BookingVerification) {
return this.jwtService.sign({
phone_number: payload.phone_number,
name: payload.name,
created_at: payload.created_at,
});
}
async register(data: BookingVerification) {
const isProduction = process.env.NODE_ENV === 'true';
const currentTime = Math.floor(Date.now()); // current time in seconds
// Generate a 4 digit OTP code
const otpCode = Math.floor(1000 + Math.random() * 9000).toString();
let verification = await this.verificationRepository.findOne({
where: { phone_number: data.phone_number },
});
if (
isProduction &&
verification.updated_at &&
currentTime - verification.updated_at < this.expiredTimeRegister
) {
throw new UnprocessableEntityException('Please try again in 1 minute');
}
data.code = otpCode;
data.tried = 0;
data.updated_at = currentTime;
if (verification) {
// Update existing record
verification = this.verificationRepository.merge(verification, data);
} else {
// Create new record
verification = this.verificationRepository.create(data);
}
const payload = await this.verificationRepository.save(verification);
const notificationService = new WhatsappService();
notificationService.sendOtpNotification({
phone: data.phone_number,
code: otpCode,
});
return payload;
}
async findByPhoneNumber(phoneNumber: string) {
return this.verificationRepository.findOne({
where: { phone_number: phoneNumber },
});
}
async verify(data: BookingVerification): Promise<BookingVerification> {
const verification = await this.findByPhoneNumber(data.phone_number);
if (!verification) {
throw new UnprocessableEntityException('Phone number not found');
}
if (verification.tried >= this.maxAttempts) {
throw new UnprocessableEntityException(
'Too many attempts, please resend OTP Code',
);
}
if (verification.code != data.code) {
verification.tried++;
await this.verificationRepository.save(verification);
throw new UnprocessableEntityException('Invalid verification code');
}
const currentTime = Math.floor(Date.now());
if (
verification.updated_at &&
currentTime - verification.updated_at > this.expiredTime
) {
throw new UnprocessableEntityException('Verification code expired');
}
return verification;
}
async update(id: string, data: BookingVerification) {
return this.verificationRepository.update(id, data);
}
async delete(id: string) {
return this.verificationRepository.delete(id);
}
}

View File

@ -1,9 +0,0 @@
export interface BookingVerification {
id: string;
name: string;
phone_number: string;
code?: string;
tried?: number;
created_at?: number;
updated_at?: number;
}

View File

@ -1,43 +0,0 @@
import { Controller, Post, Body, Get, Param } from '@nestjs/common';
import {
BookingVerificationDto,
VerificationCodeDto,
} from '../dto/booking-verification.dto';
import { VerificationService } from '../../data/services/verification.service';
import { Public } from 'src/core/guards/domain/decorators/unprotected.guard';
import { ApiTags } from '@nestjs/swagger';
@ApiTags('Booking Authentication')
@Public()
@Controller('v1/booking-authentication')
export class BookingAuthenticationController {
constructor(
private readonly bookingAuthenticationService: VerificationService,
) {}
@Post('verify')
async verify(@Body() body: VerificationCodeDto) {
const verification = await this.bookingAuthenticationService.verify(body);
const token = await this.bookingAuthenticationService.generateToken(
verification,
);
return {
message: `Verification successful for ${verification.phone_number}`,
token,
};
}
@Post('register')
async register(@Body() body: BookingVerificationDto) {
const verification = await this.bookingAuthenticationService.register(body);
return {
message: `Verification code sent to ${verification.phone_number}`,
};
}
@Get('get-by-phone/:phone_number')
async getByPhoneNumber(@Param('phone_number') phone_number: string) {
return this.bookingAuthenticationService.findByPhoneNumber(phone_number);
}
}

View File

@ -1,67 +0,0 @@
import { BookingVerification } from '../../domain/entities/booking-verification.entity';
import { IsString, IsNotEmpty, Matches } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger';
export class BookingVerificationDto implements BookingVerification {
id: string;
@ApiProperty({
type: String,
required: true,
example: 'John Doe',
description: 'Name of the person booking',
})
@IsString()
@IsNotEmpty()
name: string;
@ApiProperty({
type: String,
required: true,
example: '628123456789',
description: 'Phone number containing only numeric characters',
})
@IsString()
@IsNotEmpty()
@Matches(/^\d+$/, {
message: 'phone_number must contain only numeric characters',
})
phone_number: string;
}
export class VerificationCodeDto implements BookingVerification {
id: string;
@ApiProperty({
type: String,
required: true,
example: 'John Doe',
description: 'Name of the person booking',
})
@IsString()
@IsNotEmpty()
name: string;
@ApiProperty({
type: String,
required: true,
example: '628123456789',
description: 'Phone number containing only numeric characters',
})
@IsString()
@IsNotEmpty()
@Matches(/^\d+$/, {
message: 'phone_number must contain only numeric characters',
})
phone_number: string;
@ApiProperty({
type: String,
required: true,
example: '1234',
description: 'Verification code',
})
@IsString()
@IsNotEmpty()
code?: string;
}

View File

@ -1,9 +0,0 @@
export function generateOtp(digits = 4): number {
if (digits < 1) {
throw new Error('OTP digits must be at least 1');
}
const min = Math.pow(10, digits - 1);
const max = Math.pow(10, digits) - 1;
const otp = Math.floor(Math.random() * (max - min + 1)) + min;
return otp;
}

View File

@ -1,36 +0,0 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
import { RescheduleVerification } from '../../domain/entities/reschedule-verification.entity';
@Entity('reschedule_verification')
export class RescheduleVerificationModel implements RescheduleVerification {
@PrimaryGeneratedColumn('uuid')
id: string;
@Column()
name: string;
@Column()
phone_number: string;
@Column()
booking_id: string;
@Column()
reschedule_date: string;
@Column()
code: number;
@Column({ default: 0 })
tried: number;
@Column({ type: 'bigint', default: () => 'EXTRACT(EPOCH FROM NOW()) * 1000' })
created_at: number;
@Column({
type: 'bigint',
default: () => 'EXTRACT(EPOCH FROM NOW()) * 1000',
onUpdate: 'EXTRACT(EPOCH FROM NOW()) * 1000',
})
updated_at: number;
}

View File

@ -1,16 +0,0 @@
export interface RescheduleVerification {
id: string;
name: string;
phone_number: string;
booking_id: string;
reschedule_date: string;
code: number;
tried?: number;
created_at?: number;
updated_at?: number;
}
export interface RescheduleRequest {
booking_id: string;
reschedule_date: string;
}

View File

@ -1,67 +0,0 @@
import { Injectable } from '@nestjs/common';
import { RelationParam } from 'src/core/modules/domain/entities/base-filter.entity';
import { PaginationResponse } from 'src/core/response/domain/ok-response.interface';
import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity';
import { IndexItemManager } from 'src/modules/item-related/item/domain/usecases/managers/index-item.manager';
import { SelectQueryBuilder } from 'typeorm';
@Injectable()
export class BookingItemManager extends IndexItemManager {
get relations(): RelationParam {
return {
// relation only join (for query purpose)
joinRelations: [],
// relation join and select (relasi yang ingin ditampilkan),
selectRelations: [
'item_category',
'bundling_items',
'tenant',
'time_group',
'item_rates',
],
// relation yang hanya ingin dihitung (akan return number)
countRelations: [],
};
}
get selects(): string[] {
const parent = super.selects;
return [
...parent,
`${this.tableName}.image_url`,
'item_rates.id',
'item_rates.price',
'item_rates.season_period_id',
];
}
getResult(): PaginationResponse<ItemEntity> {
const result = super.getResult();
const { data, total } = result;
const hasRates = (this.filterParam.season_period_ids?.length ?? 0) > 0;
const items = data.map((item) => {
const currentRate = item.item_rates.find((rate) =>
this.filterParam.season_period_ids?.includes(rate.season_period_id),
);
const { item_rates, ...rest } = item;
const rate = currentRate?.['price'] ?? rest.base_price;
return {
...rest,
base_price: hasRates ? rate : rest.base_price,
};
});
return { total, data: items };
}
setQueryFilter(
queryBuilder: SelectQueryBuilder<ItemEntity>,
): SelectQueryBuilder<ItemEntity> {
const query = super.setQueryFilter(queryBuilder);
query.andWhere(`${this.tableName}.status = 'active'`);
return query;
}
}

View File

@ -1,64 +0,0 @@
import { HttpStatus } from '@nestjs/common';
import { UnprocessableEntityException } from '@nestjs/common';
import { STATUS } from 'src/core/strings/constants/base.constants';
import { TransactionType } from 'src/modules/transaction/transaction/constants';
import { CreateTransactionManager } from 'src/modules/transaction/transaction/domain/usecases/managers/create-transaction.manager';
import { generateInvoiceCodeHelper } from 'src/modules/transaction/transaction/domain/usecases/managers/helpers/generate-invoice-code.helper';
import { mappingRevertTransaction } from 'src/modules/transaction/transaction/domain/usecases/managers/helpers/mapping-transaction.helper';
import { WhatsappService } from 'src/services/whatsapp/whatsapp.service';
import { v4 as uuidv4 } from 'uuid';
export class CreateBookingManager extends CreateTransactionManager {
async beforeProcess(): Promise<void> {
mappingRevertTransaction(this.data, TransactionType.ONLINE);
const id = uuidv4();
const invoiceCode = await generateInvoiceCodeHelper(
this.dataService,
'BOOK',
);
try {
const { token, redirect_url } = await this.dataServiceFirstOpt.create({
...this.data,
id,
});
Object.assign(this.data, {
payment_midtrans_token: token,
payment_midtrans_url: redirect_url,
});
} catch (error) {
console.log({ error });
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Gagal! transaksi telah terbuat, silahkan periksa email untuk melanjutkan pembayaran`,
error: 'Unprocessable Entity',
});
}
Object.assign(this.data, {
id,
invoice_code: invoiceCode,
status: STATUS.PENDING,
invoice_date: new Date(),
});
return;
}
async afterProcess(): Promise<void> {
const whatsapp = new WhatsappService();
console.log(`/snap/v4/redirection/${this.data.payment_midtrans_token}`);
console.log(this.data.payment_midtrans_url);
await whatsapp.bookingRegister(
{
phone: this.data.customer_phone,
code: this.data.invoice_code,
name: this.data.customer_name,
time: this.data.booking_date,
id: this.data.id,
},
this.data.payment_total,
`snap/v4/redirection/${this.data.payment_midtrans_token}`,
);
}
}

View File

@ -1,115 +0,0 @@
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { RescheduleVerificationModel } from '../../../data/models/reschedule-verification.model';
import {
RescheduleRequest,
RescheduleVerification,
} from '../../entities/reschedule-verification.entity';
import { generateOtp } from 'src/modules/booking-online/helpers/generate-otp';
import { TransactionReadService } from 'src/modules/transaction/transaction/data/services/transaction-read.service';
import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity';
import { WhatsappService } from 'src/services/whatsapp/whatsapp.service';
@Injectable()
export class RescheduleVerificationManager {
constructor(
@InjectRepository(RescheduleVerificationModel)
private readonly rescheduleVerificationRepository: Repository<RescheduleVerificationModel>,
private readonly transactionService: TransactionReadService,
) {}
async saveVerification(
request: RescheduleRequest,
): Promise<RescheduleVerificationModel> {
try {
const otp = generateOtp();
const transaction = await this.findDetailByBookingId(request.booking_id);
if (!transaction) {
throw new Error('Transaction not found for the provided booking id');
}
const data: Partial<RescheduleVerification> = {
code: otp,
booking_id: transaction.id,
name: transaction.customer_name,
phone_number: transaction.customer_phone,
reschedule_date: request.reschedule_date,
};
const existTransaction =
await this.rescheduleVerificationRepository.findOne({
where: {
booking_id: transaction.id,
},
});
const verification =
existTransaction ?? this.rescheduleVerificationRepository.create(data);
const result = await this.rescheduleVerificationRepository.save({
...verification,
code: otp,
});
const whatsapp = new WhatsappService();
whatsapp.sendOtpNotification({
phone: transaction.customer_phone,
code: otp.toString(),
});
// whatsapp.bookingRescheduleOTP({
// phone: transaction.customer_phone,
// code: otp.toString(),
// name: transaction.customer_name,
// time: new Date(request.reschedule_date).getTime(),
// id: transaction.id,
// });
return result;
} catch (error) {
// You can customize the error handling as needed, e.g., throw HttpException for NestJS
throw new UnprocessableEntityException(
`Failed to save reschedule verification: ${error.message}`,
);
}
}
async verifyOtp(
booking_id: string,
code: number,
): Promise<RescheduleVerificationModel> {
const verification = await this.rescheduleVerificationRepository.findOne({
where: { booking_id, code },
order: { created_at: 'DESC' },
});
if (!verification) {
throw new UnprocessableEntityException({
success: false,
message: 'Verification code not match',
});
}
// Optionally, you can implement OTP expiration logic here
if (verification.code !== code) {
// Increment tried count
verification.tried = (verification.tried || 0) + 1;
await this.rescheduleVerificationRepository.save(verification);
throw new UnprocessableEntityException({
success: false,
message: 'Invalid verification code.',
});
}
// Optionally, you can mark the verification as used or verified here
return verification;
}
async findDetailByBookingId(bookingId: string): Promise<TransactionEntity> {
const transaction = await this.transactionService.getOneByOptions({
where: { id: bookingId },
});
return transaction;
}
}

View File

@ -1,90 +0,0 @@
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
import { STATUS } from 'src/core/strings/constants/base.constants';
import { v4 as uuidv4 } from 'uuid';
import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service';
import { generateInvoiceCodeHelper } from 'src/modules/transaction/transaction/domain/usecases/managers/helpers/generate-invoice-code.helper';
import * as moment from 'moment';
import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model';
import { RescheduleVerificationModel } from '../../../data/models/reschedule-verification.model';
import { WhatsappService } from 'src/services/whatsapp/whatsapp.service';
@Injectable()
export class RescheduleManager {
constructor(private serviceData: TransactionDataService) {}
async reschedule(data: RescheduleVerificationModel) {
const transaction = await this.serviceData.getTransactionWithReschedule(
data.booking_id,
);
const rescheduleDate = moment(data.reschedule_date, 'DD-MM-YYYY');
const id = uuidv4();
const invoiceCode = await generateInvoiceCodeHelper(
this.serviceData,
'BOOK',
);
const items = this.makeItemZeroPrice(transaction.items);
const transactionData = this.makeTransactionZeroPrice(transaction);
Object.assign(transactionData, {
parent_id: transaction.id,
id,
invoice_code: invoiceCode,
status: STATUS.SETTLED,
invoice_date: rescheduleDate.format('YYYY-MM-DD'),
booking_date: rescheduleDate.format('YYYY-MM-DD'),
created_at: moment().unix() * 1000,
updated_at: moment().unix() * 1000,
items,
});
await this.serviceData.getRepository().save(transactionData);
const whatsapp = new WhatsappService();
whatsapp.rescheduleCreated({
id: transactionData.id,
name: transactionData.customer_name,
phone: transactionData.customer_phone,
time: moment(transactionData.invoice_date).unix() * 1000,
code: transactionData.invoice_code,
});
return transactionData;
}
private makeItemZeroPrice(items: TransactionItemModel[]) {
return items.map((item) => {
return {
...item,
id: uuidv4(),
item_price: 0,
total_price: 0,
total_hpp: 0,
total_profit: 0,
total_profit_share: 0,
payment_total_dpp: 0,
payment_total_tax: 0,
total_net_price: 0,
};
});
}
private makeTransactionZeroPrice(transaction: TransactionModel) {
return {
...transaction,
payment_sub_total: 0,
payment_discount_total: 0,
payment_total: 0,
payment_total_pay: 0,
payment_total_share: 0,
payment_total_tax: 0,
payment_total_profit: 0,
payment_total_net_profit: 0,
payment_total_dpp: 0,
discount_percentage: 0,
};
}
}

View File

@ -1,121 +0,0 @@
import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto';
import { ApiProperty } from '@nestjs/swagger';
import {
IsArray,
IsNumber,
IsObject,
IsString,
ValidateIf,
} from 'class-validator';
import { SeasonPeriodEntity } from 'src/modules/season-related/season-period/domain/entities/season-period.entity';
import { TransactionItemEntity } from 'src/modules/transaction/transaction/domain/entities/transaction-item.entity';
import {
TransactionPaymentType,
TransactionUserType,
} from 'src/modules/transaction/transaction/constants';
export class TransactionDto extends BaseStatusDto {
@ApiProperty({
type: Object,
required: false,
example: {
id: 'uuid',
season_type: {
id: 'uuid',
name: 'high season',
},
},
})
@IsObject()
@ValidateIf((body) => body.season_period)
season_period: SeasonPeriodEntity;
@ApiProperty({
type: String,
required: true,
example: TransactionUserType.GROUP,
})
@IsString()
customer_type: TransactionUserType;
@ApiProperty({
type: String,
required: true,
example: 'Andika',
})
@IsString()
customer_name: string;
@ApiProperty({
type: String,
required: false,
example: '0823...',
})
@ValidateIf((body) => body.customer_phone)
customer_phone: string;
@ApiProperty({
type: Date,
required: true,
example: '2024-01-01',
})
booking_date: Date;
@ApiProperty({
type: String,
required: false,
example: TransactionPaymentType.MIDTRANS,
})
payment_type: TransactionPaymentType;
@ApiProperty({
type: Number,
required: true,
example: 7000000,
})
@IsNumber()
payment_sub_total: number;
@ApiProperty({
type: Number,
required: true,
example: 3500000,
})
@IsNumber()
payment_total: number;
@ApiProperty({
type: [Object],
required: true,
example: [
{
item: {
id: '68aa12f7-2cce-422b-9bae-185eb1343b94',
created_at: '1718876384378',
status: 'active',
name: 'tes',
item_type: 'bundling',
hpp: '100000',
base_price: '100000',
limit_type: 'no limit',
limit_value: 0,
item_category: {
id: 'ab15981a-a656-4efc-856c-b2abfbe30979',
name: 'Kategori Bundling 2',
},
bundling_items: [
{
id: 'bd5a7a38-df25-4203-a1cd-bf94867946b2',
name: 'Wahana 21 panjangggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggggg',
},
],
tenant: null,
},
qty: 40,
total_price: 4000000,
},
],
})
@IsArray()
items: TransactionItemEntity[];
}

View File

@ -1,47 +0,0 @@
import { IsString, Matches } from 'class-validator';
import { RescheduleRequest } from 'src/modules/booking-online/order/domain/entities/reschedule-verification.entity';
import { ApiProperty } from '@nestjs/swagger';
export class RescheduleRequestDTO implements RescheduleRequest {
@ApiProperty({
type: String,
required: true,
example: '123e4567-e89b-12d3-a456-426614174000',
description: 'The unique identifier of the booking',
})
@IsString()
booking_id: string;
@ApiProperty({
type: String,
required: true,
example: '25-12-2024',
description: 'The new date for rescheduling in the format DD-MM-YYYY',
})
@IsString()
@Matches(/^(0[1-9]|[12][0-9]|3[01])-(0[1-9]|1[0-2])-\d{4}$/, {
message: 'reschedule_date must be in the format DD-MM-YYYY',
})
reschedule_date: string;
}
export class RescheduleVerificationOTP {
@ApiProperty({
type: String,
required: true,
example: '123e4567-e89b-12d3-a456-426614174000',
description: 'The unique identifier of the booking',
})
@IsString()
booking_id: string;
@ApiProperty({
type: String,
required: true,
example: '123456',
description: 'The OTP code sent for verification',
})
@IsString()
code: string;
}

Some files were not shown because too many files have changed in this diff Show More