Merge branch 'development' of ssh://git.eigen.co.id:2222/eigen/pos-be into staging
commit
46b92afb89
|
@ -36,3 +36,6 @@ docker-compose.yml
|
|||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
|
||||
# IGNORE UPLOAD FOLDER
|
||||
/uploads
|
|
@ -6,6 +6,8 @@ JWT_REFRESH_EXPIRES="7d"
|
|||
ENC_KEY="921c83f3b90c92dca4ba9b947f99b4c9"
|
||||
IV="a671a96159e97a4f"
|
||||
|
||||
COUCHDB_CONFIG="http://root:password@172.10.10.2:5970"
|
||||
|
||||
DEFAULT_DB_HOST="postgres"
|
||||
DEFAULT_DB_PORT="5432"
|
||||
DEFAULT_DB_USER="root"
|
||||
|
@ -15,3 +17,20 @@ DEFAULT_DB_NAME="pos"
|
|||
ELASTIC_APM_ACTIVATE=true
|
||||
ELASTIC_APM_SERVICE_NAME="Skyworld POS"
|
||||
ELASTIC_APM_SERVER_URL="http://172.10.10.10:8200"
|
||||
|
||||
CRON_MIDNIGHT="55 11 * * *"
|
||||
CRON_EVERY_MINUTE="55 11 * * *"
|
||||
CRON_EVERY_HOUR="0 * * * *"
|
||||
|
||||
EMAIL_HOST="sandbox.smtp.mailtrap.io"
|
||||
EMAIL_POST=465
|
||||
EMAIL_USER="developer@eigen.co.id"
|
||||
EMAIL_TOKEN="bitqkbkzjzfywxqx"
|
||||
|
||||
MIDTRANS_URL=https://app.sandbox.midtrans.com
|
||||
MIDTRANS_PRODUCTION=false
|
||||
MIDTRANS_SERVER_KEY=
|
||||
MIDTRANS_CLIENT_KEY=
|
||||
|
||||
EXPORT_LIMIT_PARTITION=200
|
||||
ASSETS="https://asset.sky.eigen.co.id/"
|
|
@ -0,0 +1,36 @@
|
|||
PORT="3346"
|
||||
|
||||
JWT_SECRET="ftyYM4t4kjuj/0ixvIrS18gpdvBJw42NnW71GrFrEhcn0alQkkH7TQIHU5MFFJ1e"
|
||||
JWT_EXPIRES="24h"
|
||||
JWT_REFRESH_EXPIRES="7d"
|
||||
ENC_KEY="921c83f3b90c92dca4ba9b947f99b4c9"
|
||||
IV="a671a96159e97a4f"
|
||||
|
||||
COUCHDB_CONFIG="http://root:password@172.10.10.2:5970"
|
||||
|
||||
DEFAULT_DB_HOST="postgres"
|
||||
DEFAULT_DB_PORT="5432"
|
||||
DEFAULT_DB_USER="root"
|
||||
DEFAULT_DB_PASS="password"
|
||||
DEFAULT_DB_NAME="pos"
|
||||
|
||||
ELASTIC_APM_ACTIVATE=true
|
||||
ELASTIC_APM_SERVICE_NAME="Skyworld POS"
|
||||
ELASTIC_APM_SERVER_URL="http://172.10.10.10:8200"
|
||||
|
||||
CRON_MIDNIGHT="55 11 * * *"
|
||||
CRON_EVERY_MINUTE="55 11 * * *"
|
||||
CRON_EVERY_HOUR="0 * * * *"
|
||||
|
||||
EMAIL_HOST="sandbox.smtp.mailtrap.io"
|
||||
EMAIL_POST=465
|
||||
EMAIL_USER=
|
||||
EMAIL_TOKEN=
|
||||
|
||||
MIDTRANS_URL=https://app.midtrans.com
|
||||
MIDTRANS_PRODUCTION=true
|
||||
MIDTRANS_SERVER_KEY=
|
||||
MIDTRANS_CLIENT_KEY=
|
||||
|
||||
EXPORT_LIMIT_PARTITION=200
|
||||
ASSETS="https://asset.sky.eigen.co.id/"
|
|
@ -0,0 +1,42 @@
|
|||
## Formula Calculation
|
||||
|
||||
### Instalation
|
||||
```
|
||||
yarn add mathjs algebra.js
|
||||
```
|
||||
|
||||
|
||||
### Example
|
||||
```ts
|
||||
import * as math from 'mathjs'
|
||||
import { Equation, parse } from 'algebra.js'
|
||||
|
||||
const formula = 'dpp - (dpp*ppn) - (dpp*retribusi) - (dpp*service) - (dpp*ppn3)'
|
||||
const total = '300000'
|
||||
|
||||
const variable = {
|
||||
ppn: 11,
|
||||
retribusi: 5000,
|
||||
service: 5,
|
||||
ppn3: 5000
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
const x1 = math.simplify(formula, variable).toString()
|
||||
console.log('Formula ', x1)
|
||||
const dppFormula = parse(x1)
|
||||
const totalFormula = parse(total)
|
||||
const equation = new Equation(totalFormula, dppFormula)
|
||||
|
||||
console.log(equation.toString())
|
||||
const result = equation.solveFor('dpp').toString()
|
||||
console.log(result)
|
||||
|
||||
const value = math.evaluate(result)
|
||||
console.log(value)
|
||||
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
```
|
10
package.json
10
package.json
|
@ -34,15 +34,25 @@
|
|||
"@nestjs/cqrs": "^10.2.7",
|
||||
"@nestjs/jwt": "^10.2.0",
|
||||
"@nestjs/platform-express": "^10.0.0",
|
||||
"@nestjs/schedule": "^4.1.0",
|
||||
"@nestjs/swagger": "^7.3.1",
|
||||
"@nestjs/typeorm": "^10.0.2",
|
||||
"@types/multer": "^1.4.11",
|
||||
"algebra.js": "^0.2.6",
|
||||
"bcrypt": "^5.1.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"elastic-apm-node": "^4.5.4",
|
||||
"exceljs": "^4.4.0",
|
||||
"fs-extra": "^11.2.0",
|
||||
"googleapis": "^140.0.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"mathjs": "^13.0.2",
|
||||
"midtrans-client": "^1.3.1",
|
||||
"moment": "^2.30.1",
|
||||
"nano": "^10.1.3",
|
||||
"nodemailer": "^6.9.14",
|
||||
"pg": "^8.11.5",
|
||||
"plop": "^4.0.1",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
|
|
|
@ -29,7 +29,7 @@ module.exports = function (plop) {
|
|||
name: 'location',
|
||||
message: 'Location: ',
|
||||
choices: function () {
|
||||
return ['item related', 'user related', 'season related', 'transaction'];
|
||||
return ['item related', 'user related', 'season related', 'transaction', 'web information'];
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
@ -42,6 +42,33 @@ import { SeasonPeriodModel } from './modules/season-related/season-period/data/m
|
|||
import { ItemRateModule } from './modules/item-related/item-rate/item-rate.module';
|
||||
import { ItemRateModel } from './modules/item-related/item-rate/data/models/item-rate.model';
|
||||
import { GoogleCalendarModule } from './modules/configuration/google-calendar/google-calendar.module';
|
||||
import { TransactionModule } from './modules/transaction/transaction/transaction.module';
|
||||
import { TransactionModel } from './modules/transaction/transaction/data/models/transaction.model';
|
||||
import { TransactionItemModel } 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';
|
||||
import { ReportModule } from './modules/reports/report/report.module';
|
||||
import { ReportBookmarkModule } from './modules/reports/report-bookmark/report-bookmark.module';
|
||||
import { ReportExportModule } from './modules/reports/report-export/report-export.module';
|
||||
import { ReportBookmarkModel } from './modules/reports/shared/models/report-bookmark.model';
|
||||
import { ExportReportHistoryModel } from './modules/reports/shared/models/export-report-history.model';
|
||||
import { CronModule } from './modules/configuration/cron/cron.module';
|
||||
import { MidtransModule } from './modules/configuration/midtrans/midtrans.module';
|
||||
import { RefundModule } from './modules/transaction/refund/refund.module';
|
||||
import { RefundModel } from './modules/transaction/refund/data/models/refund.model';
|
||||
import { RefundItemModel } from './modules/transaction/refund/data/models/refund-item.model';
|
||||
import { GateModule } from './modules/web-information/gate/gate.module';
|
||||
import { GateModel } from './modules/web-information/gate/data/models/gate.model';
|
||||
import { TermConditionModule } from './modules/web-information/term-condition/term-condition.module';
|
||||
import { TermConditionModel } from './modules/web-information/term-condition/data/models/term-condition.model';
|
||||
import { FaqModel } from './modules/web-information/faq/data/models/faq.model';
|
||||
import { FaqModule } from './modules/web-information/faq/faq.module';
|
||||
import { UploadModule } from './modules/configuration/upload/upload.module';
|
||||
import { NewsModule } from './modules/web-information/news/news.module';
|
||||
import { NewsModel } from './modules/web-information/news/data/models/news.model';
|
||||
import { BannerModule } from './modules/web-information/banner/banner.module';
|
||||
import { BannerModel } from './modules/web-information/banner/data/models/banner.model';
|
||||
import { MailModule } from './modules/configuration/mail/mail.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -59,19 +86,33 @@ import { GoogleCalendarModule } from './modules/configuration/google-calendar/go
|
|||
database: process.env.DEFAULT_DB_NAME,
|
||||
entities: [
|
||||
...UserPrivilegeModels,
|
||||
BannerModel,
|
||||
ErrorLogModel,
|
||||
FaqModel,
|
||||
GateModel,
|
||||
ItemModel,
|
||||
ItemCategoryModel,
|
||||
ItemRateModel,
|
||||
LogModel,
|
||||
NewsModel,
|
||||
PaymentMethodModel,
|
||||
RefundModel,
|
||||
RefundItemModel,
|
||||
SalesPriceFormulaModel,
|
||||
SeasonPeriodModel,
|
||||
SeasonTypeModel,
|
||||
TaxModel,
|
||||
TermConditionModel,
|
||||
TransactionModel,
|
||||
TransactionItemModel,
|
||||
TransactionTaxModel,
|
||||
UserModel,
|
||||
VipCategoryModel,
|
||||
VipCodeModel,
|
||||
|
||||
// report
|
||||
ReportBookmarkModel,
|
||||
ExportReportHistoryModel,
|
||||
],
|
||||
synchronize: false,
|
||||
}),
|
||||
|
@ -79,9 +120,13 @@ import { GoogleCalendarModule } from './modules/configuration/google-calendar/go
|
|||
ConstantModule,
|
||||
CqrsModule,
|
||||
CouchModule,
|
||||
CronModule,
|
||||
GoogleCalendarModule,
|
||||
LogModule,
|
||||
MailModule,
|
||||
MidtransModule,
|
||||
SessionModule,
|
||||
UploadModule,
|
||||
|
||||
// user
|
||||
TenantModule,
|
||||
|
@ -96,14 +141,29 @@ import { GoogleCalendarModule } from './modules/configuration/google-calendar/go
|
|||
// transaction
|
||||
PaymentMethodModule,
|
||||
ProfitShareFormulaModule,
|
||||
ReconciliationModule,
|
||||
RefundModule,
|
||||
SalesPriceFormulaModule,
|
||||
TaxModule,
|
||||
TransactionModule,
|
||||
VipCategoryModule,
|
||||
VipCodeModule,
|
||||
|
||||
// session
|
||||
SeasonTypeModule,
|
||||
SeasonPeriodModule,
|
||||
|
||||
// web information
|
||||
BannerModule,
|
||||
FaqModule,
|
||||
GateModule,
|
||||
NewsModule,
|
||||
TermConditionModule,
|
||||
|
||||
// report
|
||||
ReportModule,
|
||||
ReportBookmarkModule,
|
||||
ReportExportModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [
|
||||
|
|
|
@ -61,7 +61,7 @@ export class PrivilegeService {
|
|||
}
|
||||
|
||||
async privilegeConfiguration(): Promise<UserPrivilegeConfigurationEntity> {
|
||||
const { module, menu, sub_menu, section } = this.moduleKey();
|
||||
const { module, menu } = this.moduleKey();
|
||||
return await this.repository.findOne({
|
||||
select: ['id', 'view', 'create', 'edit', 'delete', 'cancel', 'confirm'],
|
||||
where: {
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
import * as fs from 'fs-extra';
|
||||
import * as path from 'path';
|
||||
|
||||
export async function MoveFilePathHelper(data) {
|
||||
const imagePath = data['qr_image'] ?? data['image_url'];
|
||||
const sourcePath = path.join(__dirname, '../../../../uploads/', imagePath);
|
||||
const movePath =
|
||||
'data/' +
|
||||
imagePath
|
||||
.split('/')
|
||||
.filter((item) => !['uploads', 'tmp'].includes(item))
|
||||
.join('/');
|
||||
const destinationPath = path.join(
|
||||
__dirname,
|
||||
'../../../../uploads/',
|
||||
movePath,
|
||||
);
|
||||
|
||||
try {
|
||||
await fs.move(sourcePath, destinationPath);
|
||||
|
||||
Object.assign(data, {
|
||||
image_url: movePath,
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(`Failed! Error move file data`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
import { extname } from 'path';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { HttpException, HttpStatus } from '@nestjs/common';
|
||||
import * as fs from 'fs';
|
||||
import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
|
||||
import { diskStorage } from 'multer';
|
||||
|
||||
const MB = 1024 * 1024;
|
||||
|
||||
const fileFilter = (req, file, callback) => {
|
||||
if (file.mimetype.match(/\/(jpg|jpeg|png)$/)) {
|
||||
callback(null, true);
|
||||
} else {
|
||||
callback(
|
||||
new HttpException(
|
||||
`Unsupported file type ${extname(file.originalname)}`,
|
||||
HttpStatus.BAD_REQUEST,
|
||||
),
|
||||
false,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
const editFileName = (req, file, callback) => {
|
||||
const fileExtName = extname(file.originalname);
|
||||
const randomName = uuidv4();
|
||||
callback(null, `${randomName}${fileExtName}`);
|
||||
};
|
||||
|
||||
const destinationPath = (req, file, cb) => {
|
||||
let modulePath = req.body.module;
|
||||
if (req.body.sub_module) modulePath = `${modulePath}/${req.body.sub_module}`;
|
||||
|
||||
fs.mkdirSync(`./uploads/tmp/${modulePath}`, { recursive: true });
|
||||
cb(null, `./uploads/tmp/${modulePath}`);
|
||||
};
|
||||
|
||||
export const StoreFileConfig: MulterOptions = {
|
||||
storage: diskStorage({
|
||||
destination: destinationPath,
|
||||
filename: editFileName,
|
||||
}),
|
||||
fileFilter: fileFilter,
|
||||
};
|
|
@ -10,39 +10,14 @@ export function setQueryFilterDefault(
|
|||
baseFilter: BaseFilterEntity,
|
||||
tableName: TABLE_NAME,
|
||||
): SelectQueryBuilder<any> {
|
||||
// filter berdasarkan statuses
|
||||
if (!!baseFilter.statuses) {
|
||||
queryBuilder.andWhere(
|
||||
new Brackets((qb) => {
|
||||
baseFilter.statuses.map((status) => {
|
||||
// trim search
|
||||
const statusData = status.includes("'")
|
||||
? status.trim().replace(/'/g, "''").replace(/\s+/g, ' ')
|
||||
: status.trim().replace(/\s+/g, ' ');
|
||||
|
||||
// jika searching status terdapat dalam enum, maka dia mencari specific data
|
||||
// ? karena jika tidak, ketika dia search "active" maka "inactive" juga ikut
|
||||
if (STATUS[statusData.toUpperCase()])
|
||||
qb.orWhere(`${tableName}.status = :statusData`, {
|
||||
statusData: statusData,
|
||||
});
|
||||
else
|
||||
qb['orWhere'](
|
||||
`${tableName}.status::text ILIKE '%${[statusData]}%'`,
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// filter berdasarkan id pembuat
|
||||
if (!!baseFilter.created_ids)
|
||||
new WhereInQueryHelper(
|
||||
queryBuilder,
|
||||
tableName,
|
||||
'created_id',
|
||||
'creator_id',
|
||||
baseFilter.created_ids,
|
||||
'created_ids',
|
||||
'creator_ids',
|
||||
).getQuery();
|
||||
|
||||
// filter berdasarkan tanggal terakhir dibuat
|
||||
|
|
|
@ -63,7 +63,7 @@ export class ValidateRelationHelper<Entity> {
|
|||
)
|
||||
)
|
||||
throw new UnprocessableEntityException(message);
|
||||
} else if (data[`total_${relation.relation} `])
|
||||
} else if (data[`total_${relation.relation}`] > 0)
|
||||
throw new UnprocessableEntityException(message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,15 @@ export abstract class BaseDataService<Entity> {
|
|||
return await queryRunner.manager.save(newEntity);
|
||||
}
|
||||
|
||||
async createMany(
|
||||
queryRunner: QueryRunner,
|
||||
entityTarget: EntityTarget<Entity>,
|
||||
entity: Entity[],
|
||||
): Promise<Entity[]> {
|
||||
const newEntity = queryRunner.manager.create(entityTarget, entity);
|
||||
return await queryRunner.manager.save(newEntity);
|
||||
}
|
||||
|
||||
async createBatch(
|
||||
queryRunner: QueryRunner,
|
||||
entityTarget: EntityTarget<Entity>,
|
||||
|
@ -68,4 +77,8 @@ export abstract class BaseDataService<Entity> {
|
|||
async getOneByOptions(findOneOptions): Promise<Entity> {
|
||||
return await this.repository.findOne(findOneOptions);
|
||||
}
|
||||
|
||||
async getManyByOptions(findOneOptions): Promise<Entity[]> {
|
||||
return await this.repository.find(findOneOptions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,11 @@ export abstract class BaseReadService<Entity> {
|
|||
queryBuilder: SelectQueryBuilder<Entity>,
|
||||
params: BaseFilterEntity,
|
||||
): Promise<PaginationResponse<Entity>> {
|
||||
const limit = params.limit ?? 10;
|
||||
const page = params.page ?? 1;
|
||||
const [data, total] = await queryBuilder
|
||||
.take(+params.limit)
|
||||
.skip(+params.limit * +params.page - +params.limit)
|
||||
.take(+limit)
|
||||
.skip(+limit * +page - +limit)
|
||||
.getManyAndCount();
|
||||
|
||||
return {
|
||||
|
|
|
@ -25,7 +25,7 @@ export abstract class BaseBatchDeleteManager<Entity> extends BaseManager {
|
|||
async process(): Promise<void> {
|
||||
let totalFailed = 0;
|
||||
let totalSuccess = 0;
|
||||
let messages = [];
|
||||
const messages = [];
|
||||
|
||||
for (const id of this.dataIds) {
|
||||
try {
|
||||
|
|
|
@ -4,11 +4,14 @@ import { HttpStatus, NotFoundException } from '@nestjs/common';
|
|||
import { OPERATION, STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { ValidateRelationHelper } from 'src/core/helpers/validation/validate-relation.helper';
|
||||
import { RecordLog } from 'src/modules/configuration/log/domain/entities/log.event';
|
||||
import * as _ from 'lodash';
|
||||
|
||||
export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
|
||||
protected dataIds: string[];
|
||||
protected relations: string[] = [];
|
||||
protected result: BatchResult;
|
||||
protected dataStatus: STATUS;
|
||||
protected oldData: Entity;
|
||||
abstract get entityTarget(): any;
|
||||
|
||||
setData(ids: string[], status: STATUS): void {
|
||||
|
@ -27,7 +30,7 @@ export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
|
|||
async process(): Promise<void> {
|
||||
let totalFailed = 0;
|
||||
let totalSuccess = 0;
|
||||
let messages = [];
|
||||
const messages = [];
|
||||
|
||||
for (const id of this.dataIds) {
|
||||
try {
|
||||
|
@ -35,6 +38,7 @@ export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
|
|||
where: {
|
||||
id: id,
|
||||
},
|
||||
relations: this.relations,
|
||||
});
|
||||
|
||||
if (!entity) {
|
||||
|
@ -44,6 +48,13 @@ export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
|
|||
error: 'Entity Not Found',
|
||||
});
|
||||
}
|
||||
this.oldData = _.cloneDeep(entity);
|
||||
Object.assign(entity, {
|
||||
status: this.dataStatus,
|
||||
editor_id: this.user.id,
|
||||
editor_name: this.user.name,
|
||||
updated_at: new Date().getTime(),
|
||||
});
|
||||
|
||||
await this.validateData(entity);
|
||||
await new ValidateRelationHelper(
|
||||
|
@ -57,15 +68,10 @@ export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
|
|||
this.queryRunner,
|
||||
this.entityTarget,
|
||||
{ id: id },
|
||||
{
|
||||
status: this.dataStatus,
|
||||
editor_id: this.user.id,
|
||||
editor_name: this.user.name,
|
||||
updated_at: new Date().getTime(),
|
||||
},
|
||||
entity,
|
||||
);
|
||||
|
||||
this.publishEvents(entity, result);
|
||||
this.publishEvents(this.oldData, result);
|
||||
|
||||
totalSuccess = totalSuccess + 1;
|
||||
} catch (error) {
|
||||
|
@ -101,7 +107,7 @@ export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
|
|||
if (!this.eventTopics.length) return;
|
||||
for (const topic of this.eventTopics) {
|
||||
let data;
|
||||
if (!topic.relations) {
|
||||
if (topic.relations?.length) {
|
||||
data = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: dataNew.id,
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
import { BaseManager } from '../base.manager';
|
||||
import {
|
||||
EventTopics,
|
||||
columnUniques,
|
||||
validateRelations,
|
||||
} from 'src/core/strings/constants/interface.constants';
|
||||
import { HttpStatus, UnprocessableEntityException } from '@nestjs/common';
|
||||
import { SelectQueryBuilder } from 'typeorm';
|
||||
|
||||
export abstract class BaseChangePosition<Entity> extends BaseManager {
|
||||
protected result: Entity;
|
||||
protected duplicateColumn: string[];
|
||||
protected startData: Entity;
|
||||
protected endData: Entity;
|
||||
protected columnSort: string;
|
||||
|
||||
protected firstDataId: number;
|
||||
protected lastSort: number;
|
||||
protected sortTo: number;
|
||||
|
||||
abstract get entityTarget(): any;
|
||||
|
||||
setData(entity: Entity, columnSort: string): void {
|
||||
this.data = entity;
|
||||
this.columnSort = columnSort;
|
||||
}
|
||||
|
||||
async beforeProcess(): Promise<void> {
|
||||
if (!this.data?.end || this.data.start == this.data?.end) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: 'Please drag to another position',
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
}
|
||||
|
||||
this.startData = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: this.data.start,
|
||||
},
|
||||
});
|
||||
|
||||
if (!this.startData) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Entity with id : ${this.data.start} not found`,
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
}
|
||||
|
||||
this.endData = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: this.data.end,
|
||||
},
|
||||
});
|
||||
|
||||
if (!this.endData) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Entity with id : ${this.data.end} not found`,
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
}
|
||||
|
||||
if (this.endData[this.columnSort] > this.startData[this.columnSort]) {
|
||||
// drag from up
|
||||
this.firstDataId = this.startData[this.columnSort];
|
||||
this.lastSort = this.endData[this.columnSort];
|
||||
this.sortTo = this.lastSort;
|
||||
} else if (
|
||||
this.endData[this.columnSort] < this.startData[this.columnSort]
|
||||
) {
|
||||
// drag from bottom
|
||||
this.firstDataId = this.endData[this.columnSort];
|
||||
this.lastSort = this.startData[this.columnSort];
|
||||
this.sortTo = this.firstDataId;
|
||||
}
|
||||
}
|
||||
|
||||
async prepareData(): Promise<void> {
|
||||
Object.assign(this.data, {
|
||||
creator_id: this.user.id,
|
||||
creator_name: this.user.name,
|
||||
created_at: new Date().getTime(),
|
||||
updated_at: new Date().getTime(),
|
||||
});
|
||||
}
|
||||
|
||||
async validateProcess(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
async process(): Promise<void> {
|
||||
let dataArrange: Entity[];
|
||||
|
||||
const queryBuilder = this.dataService
|
||||
.getRepository()
|
||||
.createQueryBuilder(this.tableName)
|
||||
.where(`${this.tableName}.${this.columnSort} between :data1 and :data2`, {
|
||||
data1: this.firstDataId,
|
||||
data2: this.lastSort,
|
||||
});
|
||||
|
||||
const datas = await queryBuilder
|
||||
.orderBy(`${this.tableName}.${this.columnSort}`, 'ASC')
|
||||
.getManyAndCount();
|
||||
|
||||
if (datas[0].length) {
|
||||
let dataFirst = datas[0][0][this.columnSort];
|
||||
const data = datas[0];
|
||||
const length = datas[1];
|
||||
|
||||
if (this.endData[this.columnSort] > this.startData[this.columnSort]) {
|
||||
// drag from above
|
||||
const dataDragged = data[0];
|
||||
const arraySlice = data.slice(1, length);
|
||||
dataArrange = arraySlice.concat([dataDragged]);
|
||||
} else if (
|
||||
this.endData[this.columnSort] < this.startData[this.columnSort]
|
||||
) {
|
||||
// drag from bottom
|
||||
const dataDragged = data[length - 1];
|
||||
const arraySlice = data.slice(0, length - 1);
|
||||
|
||||
dataArrange = [dataDragged].concat(arraySlice);
|
||||
}
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
dataArrange[i][this.columnSort] = dataFirst;
|
||||
dataFirst++;
|
||||
}
|
||||
|
||||
await this.dataService.createMany(
|
||||
this.queryRunner,
|
||||
this.entityTarget,
|
||||
dataArrange,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
get validateRelations(): validateRelations[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
get eventTopics(): EventTopics[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
getResult(): string {
|
||||
return `Success! Data ${this.startData['name']} successfully moved to ${this.sortTo}`;
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ import {
|
|||
columnUniques,
|
||||
validateRelations,
|
||||
} from 'src/core/strings/constants/interface.constants';
|
||||
import { MoveFilePathHelper } from 'src/core/helpers/path/move-file-path.helper';
|
||||
|
||||
export abstract class BaseCreateManager<Entity> extends BaseManager {
|
||||
protected result: Entity;
|
||||
|
@ -43,6 +44,15 @@ export abstract class BaseCreateManager<Entity> extends BaseManager {
|
|||
}
|
||||
|
||||
async process(): Promise<void> {
|
||||
const keys = Object.keys(this.data);
|
||||
if (
|
||||
(keys.includes('qr_image') || keys.includes('image_url')) &&
|
||||
(this.data['image_url']?.includes('tmp') ||
|
||||
this.data['qr_image']?.includes('tmp'))
|
||||
) {
|
||||
await MoveFilePathHelper(this.data);
|
||||
}
|
||||
|
||||
this.result = await this.dataService.create(
|
||||
this.queryRunner,
|
||||
this.entityTarget,
|
||||
|
@ -75,11 +85,21 @@ export abstract class BaseCreateManager<Entity> extends BaseManager {
|
|||
|
||||
if (!this.eventTopics.length) return;
|
||||
for (const topic of this.eventTopics) {
|
||||
let data;
|
||||
if (!topic.data) {
|
||||
data = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: this.result['id'],
|
||||
},
|
||||
relations: topic.relations,
|
||||
});
|
||||
}
|
||||
|
||||
this.eventBus.publishAll([
|
||||
new topic.topic({
|
||||
id: this.result['id'],
|
||||
id: data?.['id'] ?? topic?.data?.['id'],
|
||||
old: null,
|
||||
data: topic.data,
|
||||
data: data ?? topic.data,
|
||||
user: this.user,
|
||||
description: '',
|
||||
module: this.tableName,
|
||||
|
|
|
@ -5,7 +5,7 @@ export abstract class BaseCustomManager<Entity> extends BaseManager {
|
|||
protected result: any;
|
||||
abstract get entityTarget(): any;
|
||||
|
||||
setData(entity: Entity): void {
|
||||
setData(entity: any): void {
|
||||
this.data = entity;
|
||||
}
|
||||
|
||||
|
|
|
@ -67,7 +67,7 @@ export abstract class BaseDeleteManager<Entity> extends BaseManager {
|
|||
this.eventBus.publishAll([
|
||||
new topic.topic({
|
||||
id: topic.data['id'],
|
||||
old: null,
|
||||
old: this.data,
|
||||
data: topic.data,
|
||||
user: this.user,
|
||||
description: '',
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from 'src/core/helpers/query/default-filter.helper';
|
||||
import { Param } from '../../entities/base-filter.entity';
|
||||
import { joinRelationHelper } from 'src/core/helpers/query/join-relations.helper';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
|
||||
export abstract class BaseIndexManager<Entity> extends BaseReadManager {
|
||||
protected result: PaginationResponse<Entity>;
|
||||
|
@ -19,6 +20,7 @@ export abstract class BaseIndexManager<Entity> extends BaseReadManager {
|
|||
}
|
||||
|
||||
async process(): Promise<void> {
|
||||
const specificFilter = this.specificFilter;
|
||||
const { joinRelations, selectRelations, countRelations } = this.relations;
|
||||
|
||||
if (joinRelations?.length)
|
||||
|
@ -40,10 +42,26 @@ export abstract class BaseIndexManager<Entity> extends BaseReadManager {
|
|||
|
||||
if (this.selects?.length) this.queryBuilder.select(this.selects);
|
||||
|
||||
if (this.filterParam.statuses?.length > 0) {
|
||||
const data = this.filterParam.statuses.map((status) => {
|
||||
const statusData = status.includes("'")
|
||||
? status.trim().replace(/'/g, "''").replace(/\s+/g, ' ')
|
||||
: status.trim().replace(/\s+/g, ' ');
|
||||
|
||||
// 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()] ?? statusData;
|
||||
});
|
||||
specificFilter.push({
|
||||
cols: `${this.tableName}.status::text`,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
new SpecificSearchFilter<Entity>(
|
||||
this.queryBuilder,
|
||||
this.tableName,
|
||||
this.specificFilter,
|
||||
specificFilter,
|
||||
).getFilter();
|
||||
|
||||
getOrderBy(this.filterParam, this.queryBuilder, this.tableName);
|
||||
|
|
|
@ -9,6 +9,7 @@ export abstract class BaseUpdateStatusManager<Entity> extends BaseManager {
|
|||
protected result: Entity;
|
||||
protected oldData: Entity;
|
||||
protected dataStatus: STATUS;
|
||||
protected relations = [];
|
||||
protected duplicateColumn: string[];
|
||||
abstract get entityTarget(): any;
|
||||
|
||||
|
@ -22,6 +23,7 @@ export abstract class BaseUpdateStatusManager<Entity> extends BaseManager {
|
|||
where: {
|
||||
id: this.dataId,
|
||||
},
|
||||
relations: this.relations,
|
||||
});
|
||||
this.oldData = _.cloneDeep(this.data);
|
||||
|
||||
|
|
|
@ -20,6 +20,18 @@ export abstract class BaseUpdateManager<Entity> extends BaseManager {
|
|||
}
|
||||
|
||||
async prepareData(): Promise<void> {
|
||||
this.oldData = await this.dataService.getOneByOptions({
|
||||
where: { id: this.dataId },
|
||||
});
|
||||
|
||||
if (!this.oldData) {
|
||||
throw new NotFoundException({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: `Failed! Entity with id ${this.dataId} not found`,
|
||||
error: 'Entity Not Found',
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(this.data, {
|
||||
editor_id: this.user.id,
|
||||
editor_name: this.user.name,
|
||||
|
@ -38,18 +50,6 @@ export abstract class BaseUpdateManager<Entity> extends BaseManager {
|
|||
}
|
||||
|
||||
async process(): Promise<void> {
|
||||
this.oldData = await this.dataService.getOneByOptions({
|
||||
where: { id: this.dataId },
|
||||
});
|
||||
|
||||
if (!this.oldData) {
|
||||
throw new NotFoundException({
|
||||
statusCode: HttpStatus.NOT_FOUND,
|
||||
message: `Failed! Entity with id ${this.dataId} not found`,
|
||||
error: 'Entity Not Found',
|
||||
});
|
||||
}
|
||||
|
||||
await new ValidateRelationHelper(
|
||||
this.dataId,
|
||||
this.dataService,
|
||||
|
|
|
@ -4,9 +4,9 @@ import { BaseDataOrchestrator } from './base-data.orchestrator';
|
|||
export abstract class BaseDataTransactionOrchestrator<
|
||||
Entity,
|
||||
> extends BaseDataOrchestrator<Entity> {
|
||||
abstract active(dataId: string): Promise<String>;
|
||||
abstract confirm(dataId: string): Promise<String>;
|
||||
abstract inactive(dataId: string): Promise<String>;
|
||||
abstract active(dataId: string): Promise<string>;
|
||||
abstract confirm(dataId: string): Promise<string>;
|
||||
abstract inactive(dataId: string): Promise<string>;
|
||||
abstract batchConfirm(dataIds: string[]): Promise<BatchResult>;
|
||||
abstract batchActive(dataIds: string[]): Promise<BatchResult>;
|
||||
abstract batchInactive(dataIds: string[]): Promise<BatchResult>;
|
||||
|
|
|
@ -3,6 +3,6 @@ import { BatchResult } from 'src/core/response/domain/ok-response.interface';
|
|||
export abstract class BaseDataOrchestrator<Entity> {
|
||||
abstract create(data: Entity): Promise<Entity>;
|
||||
abstract update(dataId: string, data: Entity): Promise<Entity>;
|
||||
abstract delete(dataId: string): Promise<String>;
|
||||
abstract delete(dataId: string): Promise<string>;
|
||||
abstract batchDelete(dataIds: string[]): Promise<BatchResult>;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
export class ChangePositionDto {
|
||||
start: string;
|
||||
end: string;
|
||||
}
|
|
@ -6,7 +6,9 @@ export enum STATUS {
|
|||
DRAFT = 'draft',
|
||||
EXPIRED = 'expired',
|
||||
INACTIVE = 'inactive',
|
||||
PARTIAL_REFUND = 'partial refund',
|
||||
PENDING = 'pending',
|
||||
PROCESS_REFUND = 'proses refund',
|
||||
REFUNDED = 'refunded',
|
||||
REJECTED = 'rejected',
|
||||
SETTLED = 'settled',
|
||||
|
@ -46,3 +48,5 @@ export const BLANK_USER = {
|
|||
role: null,
|
||||
user_privilege_id: null,
|
||||
};
|
||||
|
||||
export const EMPTY_UUID = '00000000-0000-0000-0000-000000000000';
|
||||
|
|
|
@ -24,7 +24,7 @@ export interface validateRelations {
|
|||
|
||||
export interface columnUniques {
|
||||
column: string;
|
||||
query?: Object;
|
||||
query?: any;
|
||||
}
|
||||
|
||||
export interface IEvent<Entity = any> {
|
||||
|
|
|
@ -1,15 +1,27 @@
|
|||
export enum MODULE_NAME {
|
||||
BANNER = 'banners',
|
||||
FAQ = 'faqs',
|
||||
GATE = 'gates',
|
||||
ITEM = 'items',
|
||||
ITEM_CATEGORY = 'item-categories',
|
||||
ITEM_RATE = 'item-rates',
|
||||
NEWS = 'news',
|
||||
PAYMENT_METHOD = 'payment-methods',
|
||||
RECONCILIATION = 'reconciliations',
|
||||
REFUND = 'refunds',
|
||||
SEASON_TYPE = 'season-types',
|
||||
SEASON_PERIOD = 'season-periods',
|
||||
TAX = 'taxes',
|
||||
TERM_CONDITION = 'term_conditions',
|
||||
TENANT = 'tenants',
|
||||
TRANSACTION = 'transactions',
|
||||
USER = 'users',
|
||||
USER_PRIVILEGE = 'user-privileges',
|
||||
USER_PRIVILEGE_CONFIGURATION = 'user-privilege-configurations',
|
||||
VIP_CATEGORY = 'vip-categories',
|
||||
VIP_CODE = 'vip-codes',
|
||||
|
||||
REPORT = 'report',
|
||||
REPORT_BOOKMARK = 'report-bookmark',
|
||||
REPORT_EXPORT = 'report-export',
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ export const PrivilegeAdminConstant = [
|
|||
index: 4,
|
||||
},
|
||||
{
|
||||
menu: 'REKONSILIASI',
|
||||
menu: 'RECONCILIATION',
|
||||
menu_label: 'Rekonsiliasi',
|
||||
actions: [
|
||||
PrivilegeAction.VIEW,
|
||||
|
@ -126,13 +126,13 @@ export const PrivilegeAdminConstant = [
|
|||
index: 11,
|
||||
},
|
||||
{
|
||||
menu: 'LAPORAN',
|
||||
menu: 'REPORT',
|
||||
menu_label: 'Laporan',
|
||||
actions: [PrivilegeAction.VIEW],
|
||||
index: 12,
|
||||
},
|
||||
{
|
||||
menu: 'DISKON_CODE',
|
||||
menu: 'DISCOUNT_CODE',
|
||||
menu_label: 'Generate Diskon Kode',
|
||||
actions: [PrivilegeAction.CREATE],
|
||||
index: 13,
|
||||
|
@ -170,7 +170,7 @@ export const PrivilegePOSConstant = [
|
|||
index: 17,
|
||||
},
|
||||
{
|
||||
menu: 'POS_DISKON_CODE',
|
||||
menu: 'POS_DISCOUNT_CODE',
|
||||
menu_label: 'Generate Diskon Kode',
|
||||
actions: [PrivilegeAction.CREATE],
|
||||
index: 18,
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
export enum TABLE_NAME {
|
||||
BANNER = 'banners',
|
||||
ERROR_LOG = 'log_errors',
|
||||
FAQ = 'faqs',
|
||||
ITEM = 'items',
|
||||
ITEM_CATEGORY = 'item_categories',
|
||||
ITEM_RATE = 'item_rates',
|
||||
GATE = 'gates',
|
||||
LOG = 'logs',
|
||||
NEWS = 'news',
|
||||
PAYMENT_METHOD = 'payment_methods',
|
||||
PRICE_FORMULA = 'price_formulas',
|
||||
REFUND = 'refunds',
|
||||
REFUND_ITEM = 'refund_items',
|
||||
SEASON_TYPE = 'season_types',
|
||||
SEASON_PERIOD = 'season_periods',
|
||||
TAX = 'taxes',
|
||||
TERM_CONDITION = 'term_conditions',
|
||||
TENANT = 'tenants',
|
||||
TRANSACTION = 'transactions',
|
||||
TRANSACTION_ITEM = 'transaction_items',
|
||||
TRANSACTION_TAX = 'transaction_taxes',
|
||||
USER = 'users',
|
||||
USER_PRIVILEGE = 'user_privileges',
|
||||
USER_PRIVILEGE_CONFIGURATION = 'user_privilege_configurations',
|
||||
|
|
|
@ -9,7 +9,7 @@ import { MODULE_NAME } from 'src/core/strings/constants/module.constants';
|
|||
import { Public } from 'src/core/guards';
|
||||
|
||||
@ApiTags(`${MODULE_NAME.{{constantCase name}}.split('-').join(' ')} - read`)
|
||||
@Controller(`v1/${MODULE_NAME.{{constantCase name}}}`)
|
||||
@Controller(`v1/${MODULE_NAME.{{constantCase name}} }`)
|
||||
@Public(false)
|
||||
@ApiBearerAuth('JWT')
|
||||
export class {{pascalCase name}}ReadController {
|
||||
|
|
|
@ -17,7 +17,7 @@ import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto'
|
|||
import { Public } from 'src/core/guards';
|
||||
|
||||
@ApiTags(`${MODULE_NAME.{{constantCase name}}.split('-').join(' ')} - data`)
|
||||
@Controller(`v1/${MODULE_NAME.{{constantCase name}}}`)
|
||||
@Controller(`v1/${MODULE_NAME.{{constantCase name}} }`)
|
||||
@Public(false)
|
||||
@ApiBearerAuth('JWT')
|
||||
export class {{pascalCase name}}DataController {
|
||||
|
@ -36,7 +36,7 @@ export class {{pascalCase name}}DataController {
|
|||
}
|
||||
|
||||
@Patch(':id/active')
|
||||
async active(@Param('id') dataId: string): Promise<String> {
|
||||
async active(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.active(dataId);
|
||||
}
|
||||
|
||||
|
@ -46,7 +46,7 @@ export class {{pascalCase name}}DataController {
|
|||
}
|
||||
|
||||
@Patch(':id/confirm')
|
||||
async confirm(@Param('id') dataId: string): Promise<String> {
|
||||
async confirm(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.confirm(dataId);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ export class {{pascalCase name}}DataController {
|
|||
}
|
||||
|
||||
@Patch(':id/inactive')
|
||||
async inactive(@Param('id') dataId: string): Promise<String> {
|
||||
async inactive(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.inactive(dataId);
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ export class {{pascalCase name}}DataController {
|
|||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') dataId: string): Promise<String> {
|
||||
async delete(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.delete(dataId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import {
|
|||
import { Public } from 'src/core/guards';
|
||||
|
||||
@ApiTags(`${MODULE_NAME.{{constantCase name}}.split('-').join(' ')} - data`)
|
||||
@Controller(`v1/${MODULE_NAME.{{constantCase name}}}`)
|
||||
@Controller(`v1/${MODULE_NAME.{{constantCase name}} }`)
|
||||
@Public(false)
|
||||
@ApiBearerAuth('JWT')
|
||||
export class {{pascalCase name}}DataController {
|
||||
|
@ -43,7 +43,7 @@ import {
|
|||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') dataId: string): Promise<String> {
|
||||
async delete(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.delete(dataId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export class {{pascalCase name}}DataOrchestrator extends Base{{pascalCase orches
|
|||
return this.updateManager.getResult();
|
||||
}
|
||||
|
||||
async delete(dataId): Promise<String> {
|
||||
async delete(dataId): Promise<string> {
|
||||
this.deleteManager.setData(dataId);
|
||||
this.deleteManager.setService(this.serviceData, TABLE_NAME.{{constantCase name}});
|
||||
await this.deleteManager.execute();
|
||||
|
@ -65,7 +65,7 @@ export class {{pascalCase name}}DataOrchestrator extends Base{{pascalCase orches
|
|||
return this.batchDeleteManager.getResult();
|
||||
}
|
||||
|
||||
async active(dataId): Promise<String> {
|
||||
async active(dataId): Promise<string> {
|
||||
this.activeManager.setData(dataId, STATUS.ACTIVE);
|
||||
this.activeManager.setService(this.serviceData, TABLE_NAME.{{constantCase name}});
|
||||
await this.activeManager.execute();
|
||||
|
@ -82,7 +82,7 @@ export class {{pascalCase name}}DataOrchestrator extends Base{{pascalCase orches
|
|||
return this.batchActiveManager.getResult();
|
||||
}
|
||||
|
||||
async confirm(dataId): Promise<String> {
|
||||
async confirm(dataId): Promise<string> {
|
||||
this.confirmManager.setData(dataId, STATUS.ACTIVE);
|
||||
this.confirmManager.setService(this.serviceData, TABLE_NAME.{{constantCase name}});
|
||||
await this.confirmManager.execute();
|
||||
|
@ -99,7 +99,7 @@ export class {{pascalCase name}}DataOrchestrator extends Base{{pascalCase orches
|
|||
return this.batchConfirmManager.getResult();
|
||||
}
|
||||
|
||||
async inactive(dataId): Promise<String> {
|
||||
async inactive(dataId): Promise<string> {
|
||||
this.inactiveManager.setData(dataId, STATUS.INACTIVE);
|
||||
this.inactiveManager.setService(
|
||||
this.serviceData,
|
||||
|
|
|
@ -36,7 +36,7 @@ export class {{pascalCase name}}DataOrchestrator extends Base{{pascalCase orches
|
|||
return this.updateManager.getResult();
|
||||
}
|
||||
|
||||
async delete(dataId): Promise<String> {
|
||||
async delete(dataId): Promise<string> {
|
||||
this.deleteManager.setData(dataId);
|
||||
this.deleteManager.setService(this.serviceData, TABLE_NAME.{{constantCase name}});
|
||||
await this.deleteManager.execute();
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class Transaction1719572714752 implements MigrationInterface {
|
||||
name = 'Transaction1719572714752';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "transaction_items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "item_id" character varying, "item_name" character varying, "item_type" character varying, "item_price" bigint, "item_tenant_id" character varying, "item_tenant_name" character varying, "item_tenant_share_margin" numeric, "total_price" numeric, "total_hpp" numeric, "total_profit" numeric, "total_share_tenant" numeric, "qty" integer, "transaction_id" uuid, CONSTRAINT "PK_ff5a487ad820dccafd53bebf578" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "transaction_taxes" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "tax_id" character varying, "tax_name" character varying, "taxt_value" character varying, "transaction_id" uuid, CONSTRAINT "PK_208b2abdb10640e1991972fc754" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_type_enum" AS ENUM('counter', 'admin', 'online')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_customer_type_enum" AS ENUM('group', 'vip')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_payment_type_enum" AS ENUM('midtrans', 'bank transfer', 'qris', 'counter', 'cash', 'credit card', 'debit', 'e-money')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "transactions" ("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"."transactions_status_enum" NOT NULL DEFAULT 'draft', "type" "public"."transactions_type_enum" NOT NULL DEFAULT 'admin', "invoice_code" character varying, "creator_counter_no" integer, "season_period_id" character varying, "season_period_name" character varying, "season_period_type_id" character varying, "season_period_type_name" character varying, "customer_type" "public"."transactions_customer_type_enum", "customer_category_id" character varying, "customer_category_name" character varying, "customer_name" character varying, "customer_phone" character varying, "customer_email" character varying, "customer_description" character varying, "no_of_group" character varying, "booking_date" date, "settlement_date" date, "invoice_date" date, "discount_code_id" character varying, "discount_code" character varying, "discount_percentage" integer, "discount_value" numeric, "payment_type" "public"."transactions_payment_type_enum" NOT NULL DEFAULT 'bank transfer', "payment_type_method_id" character varying, "payment_type_method_name" character varying, "payment_type_method_qr" character varying, "payment_card_information" character varying, "payment_code_reference" character varying, "payment_date" date, "payment_sub_total" numeric, "payment_discount_total" numeric, "payment_total" numeric, "payment_total_pay" numeric, "payment_change" numeric, "payment_total_share" numeric, "payment_total_tax" numeric, "payment_total_profit" numeric, "profit_share_formula" character varying, "sales_price_formula" character varying, CONSTRAINT "PK_a219afd8dd77ed80f5a862f1db9" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD CONSTRAINT "FK_5926425896b30c0d681fe879af0" FOREIGN KEY ("transaction_id") REFERENCES "transactions"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" ADD CONSTRAINT "FK_d21db1756c6656efc7c082fbaa6" FOREIGN KEY ("transaction_id") REFERENCES "transactions"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" DROP CONSTRAINT "FK_d21db1756c6656efc7c082fbaa6"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP CONSTRAINT "FK_5926425896b30c0d681fe879af0"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "transactions"`);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_payment_type_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_customer_type_enum"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."transactions_type_enum"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."transactions_status_enum"`);
|
||||
await queryRunner.query(`DROP TABLE "transaction_taxes"`);
|
||||
await queryRunner.query(`DROP TABLE "transaction_items"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddReconciliationToTransaction1719925690145
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddReconciliationToTransaction1719925690145';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "is_recap_transaction" boolean NOT NULL DEFAULT true`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "payment_type_method_number" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "reconciliation_mdr" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_reconciliation_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "reconciliation_status" "public"."transactions_reconciliation_status_enum" NOT NULL DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "reconciliation_confirm_date" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "reconciliation_confirm_by" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "payment_total_net_profit" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "payment_total_net_profit"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "reconciliation_confirm_by"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "reconciliation_confirm_date"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "reconciliation_status"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_reconciliation_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "reconciliation_mdr"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "payment_type_method_number"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "is_recap_transaction"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateTableTransaction1719934464407 implements MigrationInterface {
|
||||
name = 'UpdateTableTransaction1719934464407';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "item_hpp" bigint`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "item_category_id" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "item_category_name" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "item_bundlings" json`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_sending_invoice_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "sending_invoice_status" "public"."transactions_sending_invoice_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "sending_invoice_at" bigint`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_sending_qr_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "sending_qr_status" "public"."transactions_sending_qr_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "sending_qr_at" bigint`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "sending_qr_at"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "sending_qr_status"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_sending_qr_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "sending_invoice_at"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "sending_invoice_status"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_sending_invoice_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "item_bundlings"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "item_category_name"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "item_category_id"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "item_hpp"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddColumnTypeReportBookmark1719982860855
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'AddColumnTypeReportBookmark1719982860855';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."report_bookmark_type_enum" AS ENUM('TABLE_CONFIG', 'FILTER_TABLE')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "report_bookmark" ADD "type" "public"."report_bookmark_type_enum" NOT NULL DEFAULT 'TABLE_CONFIG'`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "report_bookmark" DROP COLUMN "type"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."report_bookmark_type_enum"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateDefaultColumnTransaction1720077765890
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'UpdateDefaultColumnTransaction1720077765890';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "is_recap_transaction" SET DEFAULT false`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "is_recap_transaction" SET DEFAULT true`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateTableTransaction1720436852936 implements MigrationInterface {
|
||||
name = 'UpdateTableTransaction1720436852936';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" ADD "tax_total_value" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" DROP COLUMN "taxt_value"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" ADD "taxt_value" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" DROP COLUMN "taxt_value"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" ADD "taxt_value" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_taxes" DROP COLUMN "tax_total_value"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateTableTransaction1720767689625 implements MigrationInterface {
|
||||
name = 'UpdateTableTransaction1720767689625';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "qty_remaining" integer`,
|
||||
);
|
||||
await queryRunner.query(`ALTER TABLE "transaction_items" ADD "taxes" json`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "payment_total_dpp" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "payment_total_dpp"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "taxes"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "qty_remaining"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class Refund1720768975877 implements MigrationInterface {
|
||||
name = 'Refund1720768975877';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."refunds_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."refunds_type_enum" AS ENUM('pengembalian booking', 'pengembalian wahana')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "refunds" ("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"."refunds_status_enum" NOT NULL DEFAULT 'draft', "type" "public"."refunds_type_enum" NOT NULL DEFAULT 'pengembalian booking', "code" character varying, "request_date" date, "refund_date" date, "refund_total" numeric, "bank_name" character varying, "bank_account_name" character varying, "bank_account_number" character varying, "transaction_id" uuid, CONSTRAINT "REL_8bb3b7579f49990d2e77684acd" UNIQUE ("transaction_id"), CONSTRAINT "PK_5106efb01eeda7e49a78b869738" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "refund_items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "qty_refund" numeric, "refund_total" numeric, "refund_item_id" uuid, "transaction_item_id" uuid, CONSTRAINT "REL_07b481a163c219f5de8fb1c90b" UNIQUE ("transaction_item_id"), CONSTRAINT "PK_ef892918375a6101948b90f1140" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ADD CONSTRAINT "FK_8bb3b7579f49990d2e77684acd4" FOREIGN KEY ("transaction_id") REFERENCES "transactions"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refund_items" ADD CONSTRAINT "FK_2a4bd60fb8a9c37f902f4f3da67" FOREIGN KEY ("refund_item_id") REFERENCES "refunds"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refund_items" ADD CONSTRAINT "FK_07b481a163c219f5de8fb1c90b3" FOREIGN KEY ("transaction_item_id") REFERENCES "transaction_items"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refund_items" DROP CONSTRAINT "FK_07b481a163c219f5de8fb1c90b3"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refund_items" DROP CONSTRAINT "FK_2a4bd60fb8a9c37f902f4f3da67"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" DROP CONSTRAINT "FK_8bb3b7579f49990d2e77684acd4"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "refund_items"`);
|
||||
await queryRunner.query(`DROP TABLE "refunds"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."refunds_type_enum"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."refunds_status_enum"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,461 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateEnumStatus1720774145470 implements MigrationInterface {
|
||||
name = 'UpdateEnumStatus1720774145470';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."item_categories_status_enum" RENAME TO "item_categories_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."item_categories_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "item_categories" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "item_categories" ALTER COLUMN "status" TYPE "public"."item_categories_status_enum" USING "status"::"text"::"public"."item_categories_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "item_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."item_categories_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."season_types_status_enum" RENAME TO "season_types_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."season_types_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_types" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_types" ALTER COLUMN "status" TYPE "public"."season_types_status_enum" USING "status"::"text"::"public"."season_types_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_types" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."season_types_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."season_periods_status_enum" RENAME TO "season_periods_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."season_periods_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_periods" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_periods" ALTER COLUMN "status" TYPE "public"."season_periods_status_enum" USING "status"::"text"::"public"."season_periods_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_periods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."season_periods_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."items_status_enum" RENAME TO "items_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."items_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" ALTER COLUMN "status" TYPE "public"."items_status_enum" USING "status"::"text"::"public"."items_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."items_status_enum_old"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."users_status_enum" RENAME TO "users_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."users_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "users" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "users" ALTER COLUMN "status" TYPE "public"."users_status_enum" USING "status"::"text"::"public"."users_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "users" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."users_status_enum_old"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."user_privileges_status_enum" RENAME TO "user_privileges_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."user_privileges_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_privileges" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_privileges" ALTER COLUMN "status" TYPE "public"."user_privileges_status_enum" USING "status"::"text"::"public"."user_privileges_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_privileges" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."user_privileges_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."refunds_status_enum" RENAME TO "refunds_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."refunds_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ALTER COLUMN "status" TYPE "public"."refunds_status_enum" USING "status"::"text"::"public"."refunds_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."refunds_status_enum_old"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_status_enum" RENAME TO "transactions_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "status" TYPE "public"."transactions_status_enum" USING "status"::"text"::"public"."transactions_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_reconciliation_status_enum" RENAME TO "transactions_reconciliation_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_reconciliation_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" TYPE "public"."transactions_reconciliation_status_enum" USING "reconciliation_status"::"text"::"public"."transactions_reconciliation_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_reconciliation_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_sending_invoice_status_enum" RENAME TO "transactions_sending_invoice_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_sending_invoice_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "sending_invoice_status" TYPE "public"."transactions_sending_invoice_status_enum" USING "sending_invoice_status"::"text"::"public"."transactions_sending_invoice_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_sending_invoice_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_sending_qr_status_enum" RENAME TO "transactions_sending_qr_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_sending_qr_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "sending_qr_status" TYPE "public"."transactions_sending_qr_status_enum" USING "sending_qr_status"::"text"::"public"."transactions_sending_qr_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_sending_qr_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."vip_categories_status_enum" RENAME TO "vip_categories_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."vip_categories_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "vip_categories" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "vip_categories" ALTER COLUMN "status" TYPE "public"."vip_categories_status_enum" USING "status"::"text"::"public"."vip_categories_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "vip_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."vip_categories_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."taxes_status_enum" RENAME TO "taxes_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."taxes_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "taxes" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "taxes" ALTER COLUMN "status" TYPE "public"."taxes_status_enum" USING "status"::"text"::"public"."taxes_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "taxes" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."taxes_status_enum_old"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."payment_methods_status_enum" RENAME TO "payment_methods_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."payment_methods_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "payment_methods" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "payment_methods" ALTER COLUMN "status" TYPE "public"."payment_methods_status_enum" USING "status"::"text"::"public"."payment_methods_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "payment_methods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."payment_methods_status_enum_old"`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."payment_methods_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "payment_methods" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "payment_methods" ALTER COLUMN "status" TYPE "public"."payment_methods_status_enum_old" USING "status"::"text"::"public"."payment_methods_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "payment_methods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."payment_methods_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."payment_methods_status_enum_old" RENAME TO "payment_methods_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."taxes_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "taxes" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "taxes" ALTER COLUMN "status" TYPE "public"."taxes_status_enum_old" USING "status"::"text"::"public"."taxes_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "taxes" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."taxes_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."taxes_status_enum_old" RENAME TO "taxes_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."vip_categories_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "vip_categories" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "vip_categories" ALTER COLUMN "status" TYPE "public"."vip_categories_status_enum_old" USING "status"::"text"::"public"."vip_categories_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "vip_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."vip_categories_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."vip_categories_status_enum_old" RENAME TO "vip_categories_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_sending_qr_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "sending_qr_status" TYPE "public"."transactions_sending_qr_status_enum_old" USING "sending_qr_status"::"text"::"public"."transactions_sending_qr_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_sending_qr_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_sending_qr_status_enum_old" RENAME TO "transactions_sending_qr_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_sending_invoice_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "sending_invoice_status" TYPE "public"."transactions_sending_invoice_status_enum_old" USING "sending_invoice_status"::"text"::"public"."transactions_sending_invoice_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_sending_invoice_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_sending_invoice_status_enum_old" RENAME TO "transactions_sending_invoice_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_reconciliation_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" TYPE "public"."transactions_reconciliation_status_enum_old" USING "reconciliation_status"::"text"::"public"."transactions_reconciliation_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP TYPE "public"."transactions_reconciliation_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_reconciliation_status_enum_old" RENAME TO "transactions_reconciliation_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."transactions_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "status" TYPE "public"."transactions_status_enum_old" USING "status"::"text"::"public"."transactions_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."transactions_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."transactions_status_enum_old" RENAME TO "transactions_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."refunds_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ALTER COLUMN "status" TYPE "public"."refunds_status_enum_old" USING "status"::"text"::"public"."refunds_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."refunds_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."refunds_status_enum_old" RENAME TO "refunds_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."user_privileges_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_privileges" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_privileges" ALTER COLUMN "status" TYPE "public"."user_privileges_status_enum_old" USING "status"::"text"::"public"."user_privileges_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "user_privileges" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."user_privileges_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."user_privileges_status_enum_old" RENAME TO "user_privileges_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."users_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "users" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "users" ALTER COLUMN "status" TYPE "public"."users_status_enum_old" USING "status"::"text"::"public"."users_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "users" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."users_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."users_status_enum_old" RENAME TO "users_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."items_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" ALTER COLUMN "status" TYPE "public"."items_status_enum_old" USING "status"::"text"::"public"."items_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."items_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."items_status_enum_old" RENAME TO "items_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."season_periods_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_periods" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_periods" ALTER COLUMN "status" TYPE "public"."season_periods_status_enum_old" USING "status"::"text"::"public"."season_periods_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_periods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."season_periods_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."season_periods_status_enum_old" RENAME TO "season_periods_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."season_types_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_types" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_types" ALTER COLUMN "status" TYPE "public"."season_types_status_enum_old" USING "status"::"text"::"public"."season_types_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "season_types" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."season_types_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."season_types_status_enum_old" RENAME TO "season_types_status_enum"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."item_categories_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "item_categories" ALTER COLUMN "status" DROP DEFAULT`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "item_categories" ALTER COLUMN "status" TYPE "public"."item_categories_status_enum_old" USING "status"::"text"::"public"."item_categories_status_enum_old"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "item_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
|
||||
);
|
||||
await queryRunner.query(`DROP TYPE "public"."item_categories_status_enum"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TYPE "public"."item_categories_status_enum_old" RENAME TO "item_categories_status_enum"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class Gate1721024987609 implements MigrationInterface {
|
||||
name = 'Gate1721024987609';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."gates_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"."gates_type_enum" AS ENUM('gate masuk', 'gate keluar')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "gates" ("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"."gates_status_enum" NOT NULL DEFAULT 'draft', "type" "public"."gates_type_enum" NOT NULL DEFAULT 'gate masuk', "code" character varying, "note" text, "item_id" uuid, CONSTRAINT "PK_2dd58a77462dd2c5695ec4a7975" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gates" ADD CONSTRAINT "FK_29f020cd153bb079722bcbee830" FOREIGN KEY ("item_id") REFERENCES "items"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "gates" DROP CONSTRAINT "FK_29f020cd153bb079722bcbee830"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "gates"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."gates_type_enum"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."gates_status_enum"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class TermCondition1721029248635 implements MigrationInterface {
|
||||
name = 'TermCondition1721029248635';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."term_conditions_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "term_conditions" ("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"."term_conditions_status_enum" NOT NULL DEFAULT 'draft', "title" character varying, "description" text, CONSTRAINT "PK_fc92769e487820f24ed68337feb" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE "term_conditions"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."term_conditions_status_enum"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class FrequentlyAskQuestion1721029454627 implements MigrationInterface {
|
||||
name = 'FrequentlyAskQuestion1721029454627';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."faqs_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "faqs" ("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"."faqs_status_enum" NOT NULL DEFAULT 'draft', "title" character varying, "description" text, CONSTRAINT "PK_2ddf4f2c910f8e8fa2663a67bf0" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE "faqs"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."faqs_status_enum"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateRefund1721031712642 implements MigrationInterface {
|
||||
name = 'UpdateRefund1721031712642';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" ADD "refund_sub_total" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "refunds" DROP COLUMN "refund_sub_total"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class News1721109817371 implements MigrationInterface {
|
||||
name = 'News1721109817371';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."news_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "news" ("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"."news_status_enum" NOT NULL DEFAULT 'draft', "image_url" character varying, "title" character varying, "teaser" character varying, "description" character varying, CONSTRAINT "PK_39a43dfcb6007180f04aff2357e" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE "news"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."news_status_enum"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class Banner1721111093665 implements MigrationInterface {
|
||||
name = 'Banner1721111093665';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TYPE "public"."banners_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "banners" ("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"."banners_status_enum" NOT NULL DEFAULT 'draft', "image_url" character varying, "title" character varying, "link" character varying, CONSTRAINT "PK_e9b186b959296fcb940790d31c3" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP TABLE "banners"`);
|
||||
await queryRunner.query(`DROP TYPE "public"."banners_status_enum"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateTableTransactions1721216510569
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'UpdateTableTransactions1721216510569';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "payment_midtrans_token" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "payment_midtrans_url" character varying`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "payment_midtrans_url"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "payment_midtrans_token"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateSortColumn1721284172572 implements MigrationInterface {
|
||||
name = 'UpdateSortColumn1721284172572';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "term_conditions" ADD "sort_order" integer NOT NULL DEFAULT '0'`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "faqs" ADD "sort_order" integer NOT NULL DEFAULT '0'`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "faqs" DROP COLUMN "sort_order"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "term_conditions" DROP COLUMN "sort_order"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateColumnTransaction1721385120750
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'UpdateColumnTransaction1721385120750';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "discount_percentage"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "discount_percentage" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "discount_percentage"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "discount_percentage" integer`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateImageColumnItem1721647955446 implements MigrationInterface {
|
||||
name = 'UpdateImageColumnItem1721647955446';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" RENAME COLUMN "image" TO "image_url"`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "items" RENAME COLUMN "image_url" TO "image"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -29,8 +29,6 @@ export class SeedDefaultFormula implements Seeder {
|
|||
.into(SalesPriceFormulaModel)
|
||||
.values([sales_formula, profit_formula])
|
||||
.execute();
|
||||
} catch (error) {
|
||||
console.log(error, 'er');
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,13 +5,13 @@ import { STATUS } from 'src/core/strings/constants/base.constants';
|
|||
import { ItemType } from 'src/modules/item-related/item-category/constants';
|
||||
import { LimitType } from 'src/modules/item-related/item/constants';
|
||||
import { PaymentMethodType } from 'src/modules/transaction/payment-method/constants';
|
||||
import { RefundType } from 'src/modules/transaction/refund/constants';
|
||||
import { GateType } from 'src/modules/web-information/gate/constants';
|
||||
|
||||
@ApiTags('configuration - constant')
|
||||
@Controller('v1/constant')
|
||||
@Public(true)
|
||||
export class ConstantController {
|
||||
constructor() {}
|
||||
|
||||
@Get('master-data-status')
|
||||
async masterDataStatus(): Promise<any> {
|
||||
return [STATUS.ACTIVE, STATUS.DRAFT, STATUS.INACTIVE];
|
||||
|
@ -31,4 +31,29 @@ export class ConstantController {
|
|||
async paymentMethodType(): Promise<any> {
|
||||
return Object.values(PaymentMethodType);
|
||||
}
|
||||
|
||||
@Get('transaction-user-type')
|
||||
async userType(): Promise<any> {
|
||||
return ['group', 'vip'];
|
||||
}
|
||||
|
||||
@Get('transaction-payment-type')
|
||||
async transactionPaymentType(): Promise<any> {
|
||||
return ['midtrans', 'bank transfer', 'qris', 'counter'];
|
||||
}
|
||||
|
||||
@Get('transaction-type')
|
||||
async transactionType(): Promise<any> {
|
||||
return ['counter', 'admin', 'online'];
|
||||
}
|
||||
|
||||
@Get('refund-type')
|
||||
async refundType(): Promise<any> {
|
||||
return Object.values(RefundType);
|
||||
}
|
||||
|
||||
@Get('gate-type')
|
||||
async gateType(): Promise<any> {
|
||||
return Object.values(GateType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
export const DatabaseListen = ['transaction'];
|
||||
export const DatabaseListen = ['transaction', 'vip_code'];
|
||||
|
|
|
@ -15,18 +15,64 @@ import {
|
|||
SeasonPeriodDeletedHandler,
|
||||
SeasonPeriodUpdatedHandler,
|
||||
} from './domain/managers/season-period.handler';
|
||||
import {
|
||||
ItemDeletedHandler,
|
||||
ItemUpdatedHandler,
|
||||
} from './domain/managers/item.handler';
|
||||
import {
|
||||
UserDeletedHandler,
|
||||
UserUpdatedHandler,
|
||||
} from './domain/managers/user.handler';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||
import { ItemModel } from 'src/modules/item-related/item/data/models/item.model';
|
||||
import { UserModel } from 'src/modules/user-related/user/data/models/user.model';
|
||||
import { UserDataService } from 'src/modules/user-related/user/data/services/user-data.service';
|
||||
import { ItemDataService } from 'src/modules/item-related/item/data/services/item-data.service';
|
||||
import {
|
||||
BookingDeletedEvent,
|
||||
BookingHandler,
|
||||
} from './domain/managers/booking.handler';
|
||||
import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service';
|
||||
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
|
||||
import { TransactionTaxModel } from 'src/modules/transaction/transaction/data/models/transaction-tax.model';
|
||||
import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model';
|
||||
import { VipCodeCreatedHandler } from './domain/managers/vip-code.handler';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule.forRoot(), CqrsModule],
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
TypeOrmModule.forFeature(
|
||||
[
|
||||
ItemModel,
|
||||
UserModel,
|
||||
TransactionModel,
|
||||
TransactionTaxModel,
|
||||
TransactionItemModel,
|
||||
],
|
||||
CONNECTION_NAME.DEFAULT,
|
||||
),
|
||||
CqrsModule,
|
||||
],
|
||||
controllers: [CouchDataController],
|
||||
providers: [
|
||||
BookingHandler,
|
||||
BookingDeletedEvent,
|
||||
PaymentMethodDeletedHandler,
|
||||
PaymentMethodUpdatedHandler,
|
||||
VipCodeCreatedHandler,
|
||||
VipCategoryDeletedHandler,
|
||||
VipCategoryUpdatedHandler,
|
||||
SeasonPeriodDeletedHandler,
|
||||
SeasonPeriodUpdatedHandler,
|
||||
ItemUpdatedHandler,
|
||||
ItemDeletedHandler,
|
||||
UserDeletedHandler,
|
||||
UserUpdatedHandler,
|
||||
|
||||
TransactionDataService,
|
||||
UserDataService,
|
||||
ItemDataService,
|
||||
CouchService,
|
||||
],
|
||||
})
|
||||
|
|
|
@ -1,23 +1,32 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, Logger } from '@nestjs/common';
|
||||
import { DatabaseListen } from '../../constants';
|
||||
import { EventBus } from '@nestjs/cqrs';
|
||||
import { ChangeDocEvent } from '../../domain/events/change-doc.event';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import * as Nano from 'nano';
|
||||
|
||||
@Injectable()
|
||||
export class CouchService {
|
||||
constructor(private eventBus: EventBus) {}
|
||||
constructor(
|
||||
private eventBus: EventBus,
|
||||
private configService: ConfigService,
|
||||
) {}
|
||||
|
||||
get nanoInstance() {
|
||||
const couchConfiguration = this.configService.get<string>('COUCHDB_CONFIG');
|
||||
return Nano(couchConfiguration);
|
||||
}
|
||||
|
||||
async onModuleInit() {
|
||||
const nano = require('nano')(
|
||||
'http://root:password@db_pos_couch_staging:5984',
|
||||
);
|
||||
const nano = this.nanoInstance;
|
||||
for (const database of DatabaseListen) {
|
||||
const db = nano.db.use(database);
|
||||
db.changesReader.start({ includeDocs: true }).on('change', (change) => {
|
||||
this.changeDoc(change, database);
|
||||
});
|
||||
|
||||
console.log(`start listen database ${database}`);
|
||||
Logger.log(`start listen database ${database}`, 'CouchService');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,9 +42,7 @@ export class CouchService {
|
|||
|
||||
public async createDoc(data, database) {
|
||||
try {
|
||||
const nano = require('nano')(
|
||||
'http://root:password@db_pos_couch_staging:5984',
|
||||
);
|
||||
const nano = this.nanoInstance;
|
||||
const db = nano.use(database);
|
||||
return await db.insert(data);
|
||||
} catch (error) {}
|
||||
|
@ -43,9 +50,7 @@ export class CouchService {
|
|||
|
||||
public async deleteDoc(data, database) {
|
||||
try {
|
||||
const nano = require('nano')(
|
||||
'http://root:password@db_pos_couch_staging:5984',
|
||||
);
|
||||
const nano = this.nanoInstance;
|
||||
const db = nano.use(database);
|
||||
const result = await db.get(data.id);
|
||||
await db.destroy(data.id, result._rev);
|
||||
|
@ -54,9 +59,7 @@ export class CouchService {
|
|||
|
||||
public async updateDoc(data, database) {
|
||||
try {
|
||||
const nano = require('nano')(
|
||||
'http://root:password@db_pos_couch_staging:5984',
|
||||
);
|
||||
const nano = this.nanoInstance;
|
||||
const db = nano.use(database);
|
||||
const result = await db.get(data.id);
|
||||
console.log(result, 'dsa');
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
|
||||
import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service';
|
||||
import { TransactionChangeStatusEvent } from 'src/modules/transaction/transaction/domain/entities/event/transaction-change-status.event';
|
||||
import { CouchService } from '../../data/services/couch.service';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { TransactionPaymentType } from 'src/modules/transaction/transaction/constants';
|
||||
import { mappingTransaction } from 'src/modules/transaction/transaction/domain/usecases/managers/helpers/mapping-transaction.helper';
|
||||
import { TransactionDeletedEvent } from 'src/modules/transaction/transaction/domain/entities/event/transaction-deleted.event';
|
||||
import { TransactionUpdatedEvent } from 'src/modules/transaction/transaction/domain/entities/event/transaction-updated.event';
|
||||
|
||||
@EventsHandler(TransactionDeletedEvent)
|
||||
export class BookingDeletedEvent
|
||||
implements IEventHandler<TransactionDeletedEvent>
|
||||
{
|
||||
constructor(private couchService: CouchService) {}
|
||||
|
||||
async handle(event: TransactionDeletedEvent) {
|
||||
await this.couchService.deleteDoc(
|
||||
{
|
||||
_id: event.data.id,
|
||||
...event.data.data,
|
||||
},
|
||||
'item',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@EventsHandler(TransactionChangeStatusEvent, TransactionUpdatedEvent)
|
||||
export class BookingHandler
|
||||
implements IEventHandler<TransactionChangeStatusEvent>
|
||||
{
|
||||
constructor(
|
||||
private bookingService: TransactionDataService,
|
||||
private couchService: CouchService,
|
||||
) {}
|
||||
|
||||
async handle(event: TransactionChangeStatusEvent) {
|
||||
const old_data = event.data.old;
|
||||
const data = event.data.data;
|
||||
|
||||
if (data.payment_type != TransactionPaymentType.COUNTER) return;
|
||||
|
||||
const booking = await this.bookingService.getOneByOptions({
|
||||
where: {
|
||||
id: data.id,
|
||||
},
|
||||
relations: ['items'],
|
||||
});
|
||||
|
||||
mappingTransaction(booking);
|
||||
|
||||
if (
|
||||
old_data?.status != data.status &&
|
||||
[STATUS.PENDING, STATUS.ACTIVE].includes(data.status)
|
||||
) {
|
||||
await this.couchService.createDoc(
|
||||
{
|
||||
_id: booking.id,
|
||||
...booking,
|
||||
},
|
||||
'booking',
|
||||
);
|
||||
} else {
|
||||
await this.couchService.updateDoc(
|
||||
{
|
||||
_id: booking.id,
|
||||
...booking,
|
||||
},
|
||||
'booking',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
|
||||
import { CouchService } from '../../data/services/couch.service';
|
||||
import { VipCodeCreatedEvent } from 'src/modules/transaction/vip-code/domain/entities/event/vip-code-created.event';
|
||||
|
||||
@EventsHandler(VipCodeCreatedEvent)
|
||||
export class VipCodeCreatedHandler
|
||||
implements IEventHandler<VipCodeCreatedEvent>
|
||||
{
|
||||
constructor(private couchService: CouchService) {}
|
||||
|
||||
async handle(event: VipCodeCreatedEvent) {
|
||||
const data = event.data.data;
|
||||
|
||||
await this.couchService.createDoc(
|
||||
{
|
||||
_id: data.id,
|
||||
...data,
|
||||
},
|
||||
'vip_code',
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,38 +1,44 @@
|
|||
import { Body, Controller, Get, Post } from '@nestjs/common';
|
||||
import { Body, Controller, Get, Injectable, Post } from '@nestjs/common';
|
||||
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Unprotected } from 'src/core/guards';
|
||||
import { Public } from 'src/core/guards';
|
||||
import * as Nano from 'nano';
|
||||
import { CreateUserPrivilegeDto } from 'src/modules/user-related/user-privilege/infrastructure/dto/create-user-privilege.dto';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@ApiTags(`couch`)
|
||||
@Controller('v1/couch')
|
||||
@Unprotected()
|
||||
@Public()
|
||||
@Injectable()
|
||||
export class CouchDataController {
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
get nanoInstance() {
|
||||
const couchConfiguration = this.configService.get<string>('COUCHDB_CONFIG');
|
||||
return Nano(couchConfiguration);
|
||||
}
|
||||
|
||||
@Post()
|
||||
async createDoc(@Body() entity: CreateUserPrivilegeDto) {
|
||||
try {
|
||||
let n = Nano('http://admin:secret@127.0.0.1:5984');
|
||||
let db = await n.db.create(entity.name);
|
||||
} catch (error) {
|
||||
console.log(error, 'dsa');
|
||||
}
|
||||
const n = this.nanoInstance;
|
||||
await n.db.create(entity.name);
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
@Post('doc')
|
||||
async createDocs(@Body() entity: CreateUserPrivilegeDto) {
|
||||
try {
|
||||
const nano = require('nano')('http://admin:secret@127.0.0.1:5984');
|
||||
const nano = this.nanoInstance;
|
||||
const people = nano.db.use('string');
|
||||
console.log(await people.info());
|
||||
const data = {
|
||||
id: '1212',
|
||||
name: 'dsadas',
|
||||
};
|
||||
// const data = {
|
||||
// id: '1212',
|
||||
// name: 'dsadas',
|
||||
// };
|
||||
// await people.insert(data)
|
||||
|
||||
people.changesReader
|
||||
.start()
|
||||
.start({})
|
||||
.on('change', (change) => {
|
||||
console.log(change);
|
||||
})
|
||||
|
@ -45,20 +51,17 @@ export class CouchDataController {
|
|||
.on('error', (e) => {
|
||||
console.error('error', e);
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error, 'dsa');
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
|
||||
@Get()
|
||||
async getDoc() {
|
||||
try {
|
||||
let n = Nano('http://admin:secret@127.0.0.1:5984');
|
||||
const people = n.use('string');
|
||||
const n = this.nanoInstance;
|
||||
const people = n.db.get('user');
|
||||
|
||||
return people;
|
||||
// return people.get();
|
||||
} catch (error) {
|
||||
console.log(error, 'dsa');
|
||||
}
|
||||
} catch (error) {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import { ConfigModule } from '@nestjs/config';
|
||||
import { MidnightCronManager } from './domain/managers/midnight-cron.manager';
|
||||
import { Module } from '@nestjs/common';
|
||||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
|
||||
@Module({
|
||||
imports: [ConfigModule.forRoot(), CqrsModule],
|
||||
controllers: [MidnightCronManager],
|
||||
providers: [],
|
||||
})
|
||||
export class CronModule {}
|
|
@ -0,0 +1,3 @@
|
|||
export class CronMidnightEvent {
|
||||
// constructor(public readonly data: {}) {}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
import { Controller } from '@nestjs/common';
|
||||
import { EventBus } from '@nestjs/cqrs';
|
||||
import { Cron } from '@nestjs/schedule';
|
||||
import { CronMidnightEvent } from '../entities/cron-midnight.event';
|
||||
|
||||
@Controller()
|
||||
export class MidnightCronManager {
|
||||
constructor(public eventBus: EventBus) {}
|
||||
|
||||
@Cron(`${process.env.CRON_MIDNIGHT}`)
|
||||
async scheduler(): Promise<void> {
|
||||
const local_time = new Date().toLocaleDateString('en-US', {
|
||||
timeZone: 'Asia/Jakarta',
|
||||
});
|
||||
const now = new Date(local_time).getTime();
|
||||
|
||||
console.log('Cron Event executed every 00:00 minutes.', now);
|
||||
|
||||
this.eventBus.publishAll([new CronMidnightEvent()]);
|
||||
}
|
||||
}
|
|
@ -6,5 +6,7 @@ import { ErrorLogService } from '../../data/services/error-log.service';
|
|||
export class RecordErrorLogHandler implements IEventHandler<RecordErrorLog> {
|
||||
constructor(private dataservice: ErrorLogService) {}
|
||||
|
||||
async handle(event: RecordErrorLog) {}
|
||||
async handle(event: RecordErrorLog) {
|
||||
// TODO: Implement logic here
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,5 +3,7 @@ import { RecordLog } from '../entities/log.event';
|
|||
|
||||
@EventsHandler(RecordLog)
|
||||
export class RecordLogHandler implements IEventHandler<RecordLog> {
|
||||
async handle(event: RecordLog) {}
|
||||
async handle(event: RecordLog) {
|
||||
// TODO: Implement logic here
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,413 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Email Ibunda</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink
|
||||
down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px;
|
||||
}
|
||||
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px;
|
||||
}
|
||||
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn>tbody>tr>td {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.btn table {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.btn-primary table td {
|
||||
background-color: #3498db;
|
||||
}
|
||||
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.mt0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mb0 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.powered-by a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
table[class=body] .content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important;
|
||||
}
|
||||
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important;
|
||||
}
|
||||
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
padding: 0 0 0 1em;
|
||||
}
|
||||
|
||||
ol li {
|
||||
margin: 1em 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
<table class="main">
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Halo {{customer_name}}</p>
|
||||
<p>Pemesanan tiket telah berhasil dengan nomor invoice {{invoice_code}}, Silahkan lakukan pembayaran</p>
|
||||
<br>
|
||||
<p>
|
||||
<b>PEMBAYARAN DAPAT MELALUI</b>
|
||||
<ul>
|
||||
{{#each payment_methods}}
|
||||
<li>
|
||||
<p>
|
||||
<b>{{issuer_name}}</b><br>
|
||||
<span>Name: <b>{{account_name}}</b></span><br>
|
||||
<span>Number: <b>{{account_number}}</b></span>
|
||||
</p>
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td class="content-block powered-by">
|
||||
Powered by Skyworld
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,404 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Email Ibunda</title>
|
||||
<style>
|
||||
/* -------------------------------------
|
||||
GLOBAL RESETS
|
||||
------------------------------------- */
|
||||
img {
|
||||
border: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: #f6f6f6;
|
||||
font-family: sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: 14px;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: separate;
|
||||
mso-table-lspace: 0pt;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table td {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
BODY & CONTAINER
|
||||
------------------------------------- */
|
||||
|
||||
.body {
|
||||
background-color: #f6f6f6;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Set a max-width, and make it display as block so it will automatically stretch to that width, but will also shrink
|
||||
down on a phone or something */
|
||||
.container {
|
||||
display: block;
|
||||
Margin: 0 auto !important;
|
||||
/* makes it centered */
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
width: 580px;
|
||||
}
|
||||
|
||||
/* This should also be a block element, so that it will fill 100% of the .container */
|
||||
.content {
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
Margin: 0 auto;
|
||||
max-width: 580px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
HEADER, FOOTER, MAIN
|
||||
------------------------------------- */
|
||||
.main {
|
||||
background: #ffffff;
|
||||
border-radius: 3px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
box-sizing: border-box;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.content-block {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.footer {
|
||||
clear: both;
|
||||
Margin-top: 10px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.footer td,
|
||||
.footer p,
|
||||
.footer span,
|
||||
.footer a {
|
||||
color: #999999;
|
||||
font-size: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
TYPOGRAPHY
|
||||
------------------------------------- */
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4 {
|
||||
color: #000000;
|
||||
font-family: sans-serif;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
margin: 0;
|
||||
Margin-bottom: 30px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 35px;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
p,
|
||||
ul,
|
||||
ol {
|
||||
font-family: sans-serif;
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
Margin-bottom: 15px;
|
||||
}
|
||||
|
||||
p li,
|
||||
ul li,
|
||||
ol li {
|
||||
list-style-position: inside;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #3498db;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
BUTTONS
|
||||
------------------------------------- */
|
||||
.btn {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn>tbody>tr>td {
|
||||
padding-bottom: 15px;
|
||||
}
|
||||
|
||||
.btn table {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.btn table td {
|
||||
background-color: #ffffff;
|
||||
border-radius: 5px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn a {
|
||||
background-color: #ffffff;
|
||||
border: solid 1px #3498db;
|
||||
border-radius: 5px;
|
||||
box-sizing: border-box;
|
||||
color: #3498db;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 12px 25px;
|
||||
text-decoration: none;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
.btn-primary table td {
|
||||
background-color: #3498db;
|
||||
}
|
||||
|
||||
.btn-primary a {
|
||||
background-color: #3498db;
|
||||
border-color: #3498db;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
OTHER STYLES THAT MIGHT BE USEFUL
|
||||
------------------------------------- */
|
||||
.last {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.mt0 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.mb0 {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.preheader {
|
||||
color: transparent;
|
||||
display: none;
|
||||
height: 0;
|
||||
max-height: 0;
|
||||
max-width: 0;
|
||||
opacity: 0;
|
||||
overflow: hidden;
|
||||
visibility: hidden;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.powered-by a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
Margin: 20px 0;
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
RESPONSIVE AND MOBILE FRIENDLY STYLES
|
||||
------------------------------------- */
|
||||
@media only screen and (max-width: 620px) {
|
||||
table[class=body] h1 {
|
||||
font-size: 28px !important;
|
||||
margin-bottom: 10px !important;
|
||||
}
|
||||
|
||||
table[class=body] p,
|
||||
table[class=body] ul,
|
||||
table[class=body] ol,
|
||||
table[class=body] td,
|
||||
table[class=body] span,
|
||||
table[class=body] a {
|
||||
font-size: 16px !important;
|
||||
}
|
||||
|
||||
table[class=body] .wrapper,
|
||||
table[class=body] .article {
|
||||
padding: 10px !important;
|
||||
}
|
||||
|
||||
table[class=body] .content {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
table[class=body] .container {
|
||||
padding: 0 !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class=body] .main {
|
||||
border-left-width: 0 !important;
|
||||
border-radius: 0 !important;
|
||||
border-right-width: 0 !important;
|
||||
}
|
||||
|
||||
table[class=body] .btn table {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class=body] .btn a {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
table[class=body] .img-responsive {
|
||||
height: auto !important;
|
||||
max-width: 100% !important;
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------
|
||||
PRESERVE THESE STYLES IN THE HEAD
|
||||
------------------------------------- */
|
||||
@media all {
|
||||
.ExternalClass {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ExternalClass,
|
||||
.ExternalClass p,
|
||||
.ExternalClass span,
|
||||
.ExternalClass font,
|
||||
.ExternalClass td,
|
||||
.ExternalClass div {
|
||||
line-height: 100%;
|
||||
}
|
||||
|
||||
.apple-link a {
|
||||
color: inherit !important;
|
||||
font-family: inherit !important;
|
||||
font-size: inherit !important;
|
||||
font-weight: inherit !important;
|
||||
line-height: inherit !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.btn-primary table td:hover {
|
||||
background-color: #34495e !important;
|
||||
}
|
||||
|
||||
.btn-primary a:hover {
|
||||
background-color: #34495e !important;
|
||||
border-color: #34495e !important;
|
||||
}
|
||||
}
|
||||
|
||||
ol {
|
||||
padding: 0 0 0 1em;
|
||||
}
|
||||
|
||||
ol li {
|
||||
margin: 1em 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="">
|
||||
<table border="0" cellpadding="0" cellspacing="0" class="body">
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td class="container">
|
||||
<div class="content">
|
||||
<table class="main">
|
||||
<!-- START MAIN CONTENT AREA -->
|
||||
<tr>
|
||||
<td class="wrapper">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<p>Halo {{customer_name}}</p>
|
||||
<p>Pemesanan tiket telah berhasil dengan nomor invoice {{invoice_code}}, Silahkan lakukan pembayaran dengan klik button dibawah ini</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"
|
||||
style="font-family: 'Lato', sans-serif; font-size:22px; color:#e5eaf5; line-height:24px; font-weight: 600;">
|
||||
<a href="{{payment_midtrans_url}}">Lanjutkan Pembayaran</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- END MAIN CONTENT AREA -->
|
||||
</table>
|
||||
|
||||
<!-- START FOOTER -->
|
||||
<div class="footer">
|
||||
<table border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td class="content-block powered-by">
|
||||
Powered by Skyworld
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!-- END FOOTER -->
|
||||
|
||||
<!-- END CENTERED WHITE CONTAINER -->
|
||||
</div>
|
||||
</td>
|
||||
<td> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,61 @@
|
|||
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { PaymentMethodDataService } from 'src/modules/transaction/payment-method/data/services/payment-method-data.service';
|
||||
import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service';
|
||||
import { TransactionChangeStatusEvent } from 'src/modules/transaction/transaction/domain/entities/event/transaction-change-status.event';
|
||||
import { sendEmail } from '../helpers/send-email.helper';
|
||||
import { TransactionPaymentType } from 'src/modules/transaction/transaction/constants';
|
||||
|
||||
@EventsHandler(TransactionChangeStatusEvent)
|
||||
export class PaymentTransactionHandler
|
||||
implements IEventHandler<TransactionChangeStatusEvent>
|
||||
{
|
||||
constructor(
|
||||
private dataService: TransactionDataService,
|
||||
private paymentService: PaymentMethodDataService,
|
||||
) {}
|
||||
|
||||
async handle(event: TransactionChangeStatusEvent) {
|
||||
const data_id = event.data.id;
|
||||
const old_data = event.data.old;
|
||||
const current_data = event.data.data;
|
||||
let payments = [];
|
||||
|
||||
if (
|
||||
old_data.status == STATUS.DRAFT &&
|
||||
current_data.status == STATUS.PENDING &&
|
||||
current_data.payment_type != TransactionPaymentType.COUNTER &&
|
||||
!!current_data.customer_email
|
||||
) {
|
||||
if (current_data.payment_type != TransactionPaymentType.MIDTRANS) {
|
||||
payments = await this.paymentService.getManyByOptions({
|
||||
where: {
|
||||
status: STATUS.ACTIVE,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const transaction = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: data_id,
|
||||
},
|
||||
relations: ['items'],
|
||||
});
|
||||
|
||||
try {
|
||||
sendEmail(
|
||||
[
|
||||
{
|
||||
...transaction,
|
||||
email: transaction.customer_email,
|
||||
payment_methods: payments,
|
||||
},
|
||||
],
|
||||
'Payment Confirmation',
|
||||
);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
import * as nodemailer from 'nodemailer';
|
||||
import * as handlebars from 'handlebars';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import { TransactionPaymentType } from 'src/modules/transaction/transaction/constants';
|
||||
|
||||
export async function sendEmail(receivers, subject) {
|
||||
const smtpTransport = nodemailer.createTransport({
|
||||
host: process.env.EMAIL_HOST,
|
||||
port: process.env.EMAIL_POST,
|
||||
auth: {
|
||||
user: process.env.EMAIL_USER,
|
||||
pass: process.env.EMAIL_TOKEN,
|
||||
},
|
||||
});
|
||||
let templateName = 'payment-confirmation-bank';
|
||||
|
||||
for (const receiver of receivers) {
|
||||
if (receiver.payment_type == TransactionPaymentType.MIDTRANS)
|
||||
templateName = 'payment-confirmation-midtrans';
|
||||
|
||||
let templatePath = path.resolve(
|
||||
__dirname,
|
||||
`../email-template/${templateName}.html`,
|
||||
);
|
||||
templatePath = templatePath.replace(/dist/g, 'src');
|
||||
const templateSource = fs.readFileSync(templatePath, 'utf8');
|
||||
|
||||
const template = handlebars.compile(templateSource);
|
||||
const htmlToSend = template(receiver);
|
||||
|
||||
const emailContext = {
|
||||
from: 'no-reply@eigen.co.id',
|
||||
to: receiver.email,
|
||||
subject: subject,
|
||||
html: htmlToSend,
|
||||
attachDataUrls: true,
|
||||
};
|
||||
|
||||
await new Promise((f) => setTimeout(f, 2000));
|
||||
|
||||
smtpTransport.sendMail(emailContext, function (err, data) {
|
||||
if (err) {
|
||||
console.log(`Error occurs on send to ${receiver.email}`);
|
||||
} else {
|
||||
console.log(`Email sent to ${receiver.email}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
import { PaymentMethodModel } from 'src/modules/transaction/payment-method/data/models/payment-method.model';
|
||||
import { PaymentMethodDataService } from 'src/modules/transaction/payment-method/data/services/payment-method-data.service';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
|
||||
import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service';
|
||||
import { PaymentTransactionHandler } from './domain/handlers/payment-transaction.handler';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
TypeOrmModule.forFeature(
|
||||
[PaymentMethodModel, TransactionModel],
|
||||
CONNECTION_NAME.DEFAULT,
|
||||
),
|
||||
CqrsModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [
|
||||
PaymentTransactionHandler,
|
||||
PaymentMethodDataService,
|
||||
TransactionDataService,
|
||||
],
|
||||
})
|
||||
export class MailModule {}
|
|
@ -0,0 +1,26 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { EventBus } from '@nestjs/cqrs';
|
||||
import { mappingMidtransTransaction } from '../../domain/usecases/helpers/mapping-transaction.helper';
|
||||
const midtransClient = require('midtrans-client');
|
||||
|
||||
@Injectable()
|
||||
export class MidtransService {
|
||||
constructor(private eventBus: EventBus) {}
|
||||
|
||||
get midtransInstance() {
|
||||
return new midtransClient.Snap({
|
||||
isProduction: false,
|
||||
serverKey: process.env.MIDTRANS_SERVER_KEY,
|
||||
clientKey: process.env.MIDTRANS_CLIENT_KEY,
|
||||
});
|
||||
}
|
||||
|
||||
async getStatus(orderId: string): Promise<any> {
|
||||
return await this.midtransInstance.transaction.status(orderId);
|
||||
}
|
||||
|
||||
async create(body): Promise<any> {
|
||||
const data = mappingMidtransTransaction(body);
|
||||
return await this.midtransInstance.createTransaction(data);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
export class MidtransCallbackEvent {
|
||||
constructor(public readonly data: IEventMidtrans) {}
|
||||
}
|
||||
|
||||
export interface IEventMidtrans {
|
||||
id: string;
|
||||
data: any;
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
export function mappingMidtransTransaction(transaction) {
|
||||
const item_details = transaction.items?.map((item) => {
|
||||
return {
|
||||
quantity: Number(item.qty),
|
||||
price: Number(item.total_price),
|
||||
name: item.item_name,
|
||||
category: item.item_category_name,
|
||||
};
|
||||
});
|
||||
|
||||
if (transaction.payment_discount_total) {
|
||||
item_details.push({
|
||||
quantity: 1,
|
||||
price: -Number(transaction.payment_discount_total),
|
||||
name: 'discount',
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
transaction_details: {
|
||||
order_id: transaction.id,
|
||||
gross_amount: Number(transaction.payment_total),
|
||||
},
|
||||
item_details: item_details,
|
||||
customer_details: {
|
||||
first_name: transaction.customer_name,
|
||||
email: transaction.customer_email,
|
||||
phone: transaction.customer_phone,
|
||||
},
|
||||
};
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class MidtransDto {
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
example: '123',
|
||||
})
|
||||
order_id: string;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
import { Body, Controller, Get, Injectable, Param, Post } from '@nestjs/common';
|
||||
import { ApiTags } from '@nestjs/swagger';
|
||||
import { Public } from 'src/core/guards';
|
||||
import { MidtransService } from '../data/services/midtrans.service';
|
||||
import { EventBus } from '@nestjs/cqrs';
|
||||
import { MidtransCallbackEvent } from '../domain/entities/midtrans-callback.event';
|
||||
import { MidtransDto } from './dto/midtrans.dto';
|
||||
|
||||
@ApiTags(`midtrans`)
|
||||
@Controller('v1/midtrans')
|
||||
@Public()
|
||||
@Injectable()
|
||||
export class MidtransController {
|
||||
constructor(
|
||||
private dataService: MidtransService,
|
||||
private eventBus: EventBus,
|
||||
) {}
|
||||
|
||||
@Get(':id/status')
|
||||
async getStatus(@Param('id') id: string) {
|
||||
return await this.dataService.getStatus(id);
|
||||
}
|
||||
|
||||
@Post('callback')
|
||||
async callback(@Body() callback: MidtransDto) {
|
||||
const data = await this.dataService.getStatus(callback?.order_id);
|
||||
this.eventBus.publishAll([
|
||||
new MidtransCallbackEvent({
|
||||
id: data.order_id,
|
||||
data: data,
|
||||
}),
|
||||
]);
|
||||
console.log(`midtrans callback for order ${data.order_id}`);
|
||||
return 'success listen callback';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
import { ConfigModule } from '@nestjs/config';
|
||||
import { CqrsModule } from '@nestjs/cqrs';
|
||||
import { MidtransController } from './infrastructure/midtrans.controller';
|
||||
import { MidtransService } from './data/services/midtrans.service';
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [ConfigModule.forRoot(), CqrsModule],
|
||||
controllers: [MidtransController],
|
||||
providers: [MidtransService],
|
||||
exports: [MidtransService],
|
||||
})
|
||||
export class MidtransModule {}
|
|
@ -0,0 +1,5 @@
|
|||
export interface UploadEntity {
|
||||
module: string;
|
||||
sub_module: string;
|
||||
file: any;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { UploadEntity } from '../../domain/entities/upload.entity';
|
||||
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
||||
|
||||
export class UploadDto implements UploadEntity {
|
||||
@ApiProperty({
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: TABLE_NAME.ITEM,
|
||||
})
|
||||
module: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: 'string',
|
||||
required: false,
|
||||
default: TABLE_NAME.ITEM,
|
||||
})
|
||||
sub_module: string;
|
||||
|
||||
@ApiProperty({ type: 'string', format: 'binary', required: true })
|
||||
file: any;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
import {
|
||||
Controller,
|
||||
Post,
|
||||
UseInterceptors,
|
||||
Body,
|
||||
UploadedFile,
|
||||
} from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { ApiBody, ApiConsumes, ApiTags } from '@nestjs/swagger';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { UploadDto } from './dto/upload.dto';
|
||||
import { Public } from 'src/core/guards';
|
||||
import { StoreFileConfig } from 'src/core/helpers/path/upload-store-path.helper';
|
||||
|
||||
@ApiTags('uploads')
|
||||
@Controller('uploads')
|
||||
@Public(true)
|
||||
export class UploadController {
|
||||
constructor(private moduleRef: ModuleRef) {}
|
||||
|
||||
@Post()
|
||||
@ApiConsumes('multipart/form-data')
|
||||
@ApiBody({ type: UploadDto })
|
||||
@UseInterceptors(FileInterceptor('file', StoreFileConfig))
|
||||
async storeFile(
|
||||
@UploadedFile() file: Express.Multer.File,
|
||||
@Body() body: UploadDto,
|
||||
): Promise<any> {
|
||||
return {
|
||||
path: file.path,
|
||||
full_path: `${process.env.ASSETS}${file.path}`,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { MulterModule } from '@nestjs/platform-express';
|
||||
import { UploadController } from './infrastructure/upload.controller';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
MulterModule.register({
|
||||
dest: './uploads',
|
||||
}),
|
||||
],
|
||||
controllers: [UploadController],
|
||||
providers: [],
|
||||
})
|
||||
export class UploadModule {}
|
|
@ -49,7 +49,7 @@ export class ItemCategoryDataOrchestrator extends BaseDataTransactionOrchestrato
|
|||
return this.updateManager.getResult();
|
||||
}
|
||||
|
||||
async delete(dataId): Promise<String> {
|
||||
async delete(dataId): Promise<string> {
|
||||
this.deleteManager.setData(dataId);
|
||||
this.deleteManager.setService(this.serviceData, TABLE_NAME.ITEM_CATEGORY);
|
||||
await this.deleteManager.execute();
|
||||
|
@ -66,7 +66,7 @@ export class ItemCategoryDataOrchestrator extends BaseDataTransactionOrchestrato
|
|||
return this.batchDeleteManager.getResult();
|
||||
}
|
||||
|
||||
async active(dataId): Promise<String> {
|
||||
async active(dataId): Promise<string> {
|
||||
this.activeManager.setData(dataId, STATUS.ACTIVE);
|
||||
this.activeManager.setService(this.serviceData, TABLE_NAME.ITEM_CATEGORY);
|
||||
await this.activeManager.execute();
|
||||
|
@ -83,7 +83,7 @@ export class ItemCategoryDataOrchestrator extends BaseDataTransactionOrchestrato
|
|||
return this.batchActiveManager.getResult();
|
||||
}
|
||||
|
||||
async confirm(dataId): Promise<String> {
|
||||
async confirm(dataId): Promise<string> {
|
||||
this.confirmManager.setData(dataId, STATUS.ACTIVE);
|
||||
this.confirmManager.setService(this.serviceData, TABLE_NAME.ITEM_CATEGORY);
|
||||
await this.confirmManager.execute();
|
||||
|
@ -100,7 +100,7 @@ export class ItemCategoryDataOrchestrator extends BaseDataTransactionOrchestrato
|
|||
return this.batchConfirmManager.getResult();
|
||||
}
|
||||
|
||||
async inactive(dataId): Promise<String> {
|
||||
async inactive(dataId): Promise<string> {
|
||||
this.inactiveManager.setData(dataId, STATUS.INACTIVE);
|
||||
this.inactiveManager.setService(this.serviceData, TABLE_NAME.ITEM_CATEGORY);
|
||||
await this.inactiveManager.execute();
|
||||
|
|
|
@ -22,7 +22,9 @@ export class CreateItemCategoryManager extends BaseCreateManager<ItemCategoryEnt
|
|||
return;
|
||||
}
|
||||
|
||||
async generateConfig(): Promise<void> {}
|
||||
async generateConfig(): Promise<void> {
|
||||
// TODO: Implement logic here
|
||||
}
|
||||
|
||||
get validateRelations(): validateRelations[] {
|
||||
return [];
|
||||
|
|
|
@ -34,7 +34,7 @@ export class ItemCategoryDataController {
|
|||
}
|
||||
|
||||
@Patch(':id/active')
|
||||
async active(@Param('id') dataId: string): Promise<String> {
|
||||
async active(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.active(dataId);
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ export class ItemCategoryDataController {
|
|||
}
|
||||
|
||||
@Patch(':id/confirm')
|
||||
async confirm(@Param('id') dataId: string): Promise<String> {
|
||||
async confirm(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.confirm(dataId);
|
||||
}
|
||||
|
||||
|
@ -54,7 +54,7 @@ export class ItemCategoryDataController {
|
|||
}
|
||||
|
||||
@Patch(':id/inactive')
|
||||
async inactive(@Param('id') dataId: string): Promise<String> {
|
||||
async inactive(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.inactive(dataId);
|
||||
}
|
||||
|
||||
|
@ -72,7 +72,7 @@ export class ItemCategoryDataController {
|
|||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') dataId: string): Promise<String> {
|
||||
async delete(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.delete(dataId);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ export class ItemRateDataOrchestrator extends BaseDataOrchestrator<ItemRateEntit
|
|||
return this.updateManager.getResult();
|
||||
}
|
||||
|
||||
async delete(dataId): Promise<String> {
|
||||
async delete(dataId): Promise<string> {
|
||||
this.deleteManager.setData(dataId);
|
||||
this.deleteManager.setService(this.serviceData, TABLE_NAME.ITEM_RATE);
|
||||
await this.deleteManager.execute();
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
RelationParam,
|
||||
} from 'src/core/modules/domain/entities/base-filter.entity';
|
||||
import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
|
||||
@Injectable()
|
||||
export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
||||
|
@ -19,7 +20,7 @@ export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
|||
|
||||
async afterProcess(): Promise<void> {
|
||||
this.result.data?.map((item) => {
|
||||
let prices = [];
|
||||
const prices = [];
|
||||
for (
|
||||
let d = new Date(this.filterParam.start_date);
|
||||
d <= new Date(this.filterParam.end_date);
|
||||
|
@ -27,6 +28,7 @@ export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
|||
) {
|
||||
const rate = item['item_rates']?.find(
|
||||
(rate) =>
|
||||
rate.season_period?.status == STATUS.ACTIVE &&
|
||||
d >= new Date(rate.season_period.start_date) &&
|
||||
d <= new Date(rate.season_period.end_date),
|
||||
);
|
||||
|
@ -70,6 +72,7 @@ export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
|||
get selects(): string[] {
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.status`,
|
||||
`${this.tableName}.created_at`,
|
||||
`${this.tableName}.name`,
|
||||
`${this.tableName}.base_price`,
|
||||
|
@ -84,6 +87,7 @@ export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
|||
'item_rates.price',
|
||||
|
||||
'season_period.id',
|
||||
'season_period.status',
|
||||
'season_period.holiday_name',
|
||||
'season_period.start_date',
|
||||
'season_period.end_date',
|
||||
|
@ -121,10 +125,11 @@ export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
|||
queryBuilder.andWhere(`${this.tableName}.tenant_id In (:...tenantIds)`, {
|
||||
tenantIds: this.filterParam.tenant_ids,
|
||||
});
|
||||
} else if (!this.filterParam.all_item) {
|
||||
queryBuilder.andWhere(`${this.tableName}.tenant_id Is Null`);
|
||||
}
|
||||
|
||||
queryBuilder.andWhere(`${this.tableName}.status In (:...statuses)`, {
|
||||
statuses: [STATUS.ACTIVE],
|
||||
});
|
||||
return queryBuilder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export class ItemRateDataController {
|
|||
}
|
||||
|
||||
// @Delete(':id')
|
||||
// async delete(@Param('id') dataId: string): Promise<String> {
|
||||
// async delete(@Param('id') dataId: string): Promise<string> {
|
||||
// return await this.orchestrator.delete(dataId);
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import { LimitType } from '../../constants';
|
|||
import { ItemCategoryModel } from 'src/modules/item-related/item-category/data/models/item-category.model';
|
||||
import { UserModel } from 'src/modules/user-related/user/data/models/user.model';
|
||||
import { ItemRateModel } from 'src/modules/item-related/item-rate/data/models/item-rate.model';
|
||||
import { GateModel } from 'src/modules/web-information/gate/data/models/gate.model';
|
||||
|
||||
@Entity(TABLE_NAME.ITEM)
|
||||
export class ItemModel
|
||||
|
@ -24,8 +25,8 @@ export class ItemModel
|
|||
@Column('varchar', { name: 'name' })
|
||||
name: string;
|
||||
|
||||
@Column('varchar', { name: 'image', nullable: true })
|
||||
image: string;
|
||||
@Column('varchar', { name: 'image_url', nullable: true })
|
||||
image_url: string;
|
||||
|
||||
@Column('enum', {
|
||||
name: 'item_type',
|
||||
|
@ -108,4 +109,11 @@ export class ItemModel
|
|||
onUpdate: 'CASCADE',
|
||||
})
|
||||
item_rates: ItemRateModel[];
|
||||
|
||||
// relasi ke gate
|
||||
@OneToMany(() => GateModel, (model) => model.item, {
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
gates: GateModel[];
|
||||
}
|
||||
|
|
|
@ -6,4 +6,5 @@ export interface FilterItemEntity extends BaseFilterEntity {
|
|||
limit_types: string[];
|
||||
tenant_ids: string[];
|
||||
all_item: boolean;
|
||||
season_period_ids: string[];
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import { LimitType } from '../../constants';
|
|||
export interface ItemEntity extends BaseStatusEntity {
|
||||
name: string;
|
||||
item_type: ItemType;
|
||||
image: string;
|
||||
image_url: string;
|
||||
|
||||
hpp: number;
|
||||
sales_margin: number;
|
||||
|
|
|
@ -15,6 +15,8 @@ import { BatchInactiveItemManager } from './managers/batch-inactive-item.manager
|
|||
import { BatchActiveItemManager } from './managers/batch-active-item.manager';
|
||||
import { BatchDeleteItemManager } from './managers/batch-delete-item.manager';
|
||||
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
||||
import { UpdateItemRatePriceManager } from './managers/update-item-rate-price.manager';
|
||||
import { ItemRateReadService } from 'src/modules/item-related/item-rate/data/services/item-rate-read.service';
|
||||
|
||||
@Injectable()
|
||||
export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator<ItemEntity> {
|
||||
|
@ -28,8 +30,10 @@ export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator<ItemEn
|
|||
private batchDeleteManager: BatchDeleteItemManager,
|
||||
private batchActiveManager: BatchActiveItemManager,
|
||||
private batchConfirmManager: BatchConfirmItemManager,
|
||||
private updatePriceManager: UpdateItemRatePriceManager,
|
||||
private batchInactiveManager: BatchInactiveItemManager,
|
||||
private serviceData: ItemDataService,
|
||||
private serviceRateData: ItemRateReadService,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -60,7 +64,14 @@ export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator<ItemEn
|
|||
return this.updateManager.getResult();
|
||||
}
|
||||
|
||||
async delete(dataId, tenantId?: string): Promise<String> {
|
||||
async updatePrice(data): Promise<any[]> {
|
||||
this.updatePriceManager.setData(data);
|
||||
this.updatePriceManager.setService(this.serviceRateData, TABLE_NAME.ITEM);
|
||||
await this.updatePriceManager.execute();
|
||||
return this.updatePriceManager.getResult();
|
||||
}
|
||||
|
||||
async delete(dataId, tenantId?: string): Promise<string> {
|
||||
this.deleteManager.setData(dataId);
|
||||
this.deleteManager.setService(this.serviceData, TABLE_NAME.ITEM);
|
||||
await this.deleteManager.execute();
|
||||
|
@ -77,7 +88,7 @@ export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator<ItemEn
|
|||
return this.batchDeleteManager.getResult();
|
||||
}
|
||||
|
||||
async active(dataId, tenantId?: string): Promise<String> {
|
||||
async active(dataId, tenantId?: string): Promise<string> {
|
||||
this.activeManager.setData(dataId, STATUS.ACTIVE);
|
||||
this.activeManager.setService(this.serviceData, TABLE_NAME.ITEM);
|
||||
await this.activeManager.execute();
|
||||
|
@ -94,7 +105,7 @@ export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator<ItemEn
|
|||
return this.batchActiveManager.getResult();
|
||||
}
|
||||
|
||||
async confirm(dataId, tenantId?: string): Promise<String> {
|
||||
async confirm(dataId, tenantId?: string): Promise<string> {
|
||||
this.confirmManager.setData(dataId, STATUS.ACTIVE);
|
||||
this.confirmManager.setService(this.serviceData, TABLE_NAME.ITEM);
|
||||
await this.confirmManager.execute();
|
||||
|
@ -111,7 +122,7 @@ export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator<ItemEn
|
|||
return this.batchConfirmManager.getResult();
|
||||
}
|
||||
|
||||
async inactive(dataId, tenantId?: string): Promise<String> {
|
||||
async inactive(dataId, tenantId?: string): Promise<string> {
|
||||
this.inactiveManager.setData(dataId, STATUS.INACTIVE);
|
||||
this.inactiveManager.setService(this.serviceData, TABLE_NAME.ITEM);
|
||||
await this.inactiveManager.execute();
|
||||
|
|
|
@ -33,6 +33,7 @@ export class DetailItemManager extends BaseDetailManager<ItemEntity> {
|
|||
get selects(): string[] {
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.image_url`,
|
||||
`${this.tableName}.created_at`,
|
||||
`${this.tableName}.status`,
|
||||
`${this.tableName}.item_type`,
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ItemRateEntity } from 'src/modules/item-related/item-rate/domain/entiti
|
|||
@Injectable()
|
||||
export class IndexItemRatesManager extends BaseIndexManager<ItemRateEntity> {
|
||||
async prepareData(): Promise<void> {
|
||||
this.filterParam.order_by = `${this.tableName}.id`;
|
||||
this.filterParam.order_by = `season_period.id`;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -38,9 +38,11 @@ export class IndexItemRatesManager extends BaseIndexManager<ItemRateEntity> {
|
|||
get selects(): string[] {
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.item_id`,
|
||||
`${this.tableName}.price`,
|
||||
|
||||
`season_period.id`,
|
||||
`season_period.priority`,
|
||||
`season_period.created_at`,
|
||||
`season_period.creator_name`,
|
||||
`season_period.editor_name`,
|
||||
|
@ -68,6 +70,25 @@ export class IndexItemRatesManager extends BaseIndexManager<ItemRateEntity> {
|
|||
itemIds: this.filterParam.item_ids,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.filterParam.season_period_ids) {
|
||||
queryBuilder.andWhere(`season_period.id In (:...seasonIdss)`, {
|
||||
seasonIdss: this.filterParam.season_period_ids,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.filterParam.start_date) {
|
||||
queryBuilder.andWhere(`season_period.start_date <= :inputStartDate`, {
|
||||
inputStartDate: this.filterParam.end_date,
|
||||
});
|
||||
|
||||
queryBuilder.andWhere(`season_period.end_date >= :inputEndDate`, {
|
||||
inputEndDate: this.filterParam.start_date,
|
||||
});
|
||||
}
|
||||
|
||||
queryBuilder.addOrderBy('season_period.priority', 'ASC');
|
||||
|
||||
return queryBuilder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager';
|
||||
import { ItemEntity } from '../../entities/item.entity';
|
||||
import { EventTopics } from 'src/core/strings/constants/interface.constants';
|
||||
import { ItemModel } from '../../../data/models/item.model';
|
||||
import { In, LessThanOrEqual, MoreThanOrEqual } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateItemRatePriceManager extends BaseCustomManager<ItemEntity> {
|
||||
protected rates = [];
|
||||
get entityTarget(): any {
|
||||
return ItemModel;
|
||||
}
|
||||
|
||||
get eventTopics(): EventTopics[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
async validateProcess(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
async beforeProcess(): Promise<void> {
|
||||
let query;
|
||||
const item_ids = this.data.items.map((item) => {
|
||||
return item.item.id;
|
||||
});
|
||||
|
||||
if (this.data.season_period_id) {
|
||||
query = {
|
||||
item_id: In(item_ids),
|
||||
season_period: {
|
||||
id: this.data.season_period_id,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
query = {
|
||||
item_id: In(item_ids),
|
||||
season_period: {
|
||||
start_date: MoreThanOrEqual(this.data.booking_date),
|
||||
end_date: LessThanOrEqual(this.data.booking_date),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
this.rates = await this.dataService.getManyByOptions({
|
||||
where: query,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
async process(): Promise<void> {
|
||||
this.data.items.map((item) => {
|
||||
const current_price = this.rates.find(
|
||||
(rate) => rate.item_id == item.item.id,
|
||||
);
|
||||
|
||||
Object.assign(item, {
|
||||
total_price: current_price?.price ?? item.item.base_price,
|
||||
});
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
async afterProcess(): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
async getResult() {
|
||||
return this.data.items;
|
||||
}
|
||||
}
|
|
@ -10,6 +10,12 @@ export class FilterItemDto extends BaseFilterDto implements FilterItemEntity {
|
|||
})
|
||||
item_categories: string[];
|
||||
|
||||
@ApiProperty({ type: ['string'], required: false })
|
||||
@Transform((body) => {
|
||||
return Array.isArray(body.value) ? body.value : [body.value];
|
||||
})
|
||||
season_period_ids: string[];
|
||||
|
||||
@ApiProperty({ type: ['string'], required: false })
|
||||
@Transform((body) => {
|
||||
return Array.isArray(body.value) ? body.value : [body.value];
|
||||
|
|
|
@ -29,7 +29,7 @@ export class ItemDto extends BaseStatusDto implements ItemEntity {
|
|||
})
|
||||
@IsString()
|
||||
@ValidateIf((body) => body.image)
|
||||
image: string;
|
||||
image_url: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: 'string',
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class UpdateItemPriceDto {
|
||||
@ApiProperty({
|
||||
type: [Object],
|
||||
required: true,
|
||||
example: [
|
||||
{
|
||||
item: {
|
||||
id: 'bee5c493-fb35-4ceb-b7a1-7bc3edb3c63b',
|
||||
name: 'TEnant 2 wahana air',
|
||||
item_type: 'wahana',
|
||||
base_price: '100000',
|
||||
hpp: '0',
|
||||
tenant: {
|
||||
id: 'e19a4637-d4db-48cc-89ce-501913d07cdd',
|
||||
name: 'e19a4637-d4db-48cc-89ce-501913d07cdd',
|
||||
share_margin: null,
|
||||
},
|
||||
item_category: {
|
||||
id: '88633772-ec34-4645-bc04-6cfdce6af0cf',
|
||||
name: 'Wahana Air',
|
||||
},
|
||||
},
|
||||
qty: 1,
|
||||
total_price: '100000',
|
||||
},
|
||||
],
|
||||
})
|
||||
items: Object[];
|
||||
|
||||
@ApiProperty({
|
||||
type: String,
|
||||
example: 'uuid',
|
||||
})
|
||||
season_period_id: string;
|
||||
|
||||
@ApiProperty({
|
||||
type: Date,
|
||||
example: '2024-08-17',
|
||||
})
|
||||
booking_date: Date;
|
||||
}
|
|
@ -15,6 +15,7 @@ import { ItemEntity } from '../domain/entities/item.entity';
|
|||
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
|
||||
import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto';
|
||||
import { Public } from 'src/core/guards';
|
||||
import { UpdateItemPriceDto } from './dto/update-item-price.dto';
|
||||
|
||||
@ApiTags(`${MODULE_NAME.ITEM.split('-').join(' ')} - data`)
|
||||
@Controller(`v1/${MODULE_NAME.ITEM}`)
|
||||
|
@ -28,13 +29,18 @@ export class ItemDataController {
|
|||
return await this.orchestrator.create(data);
|
||||
}
|
||||
|
||||
@Post('update-price')
|
||||
async updatePrice(@Body() body: UpdateItemPriceDto): Promise<any> {
|
||||
return await this.orchestrator.updatePrice(body);
|
||||
}
|
||||
|
||||
@Put('/batch-delete')
|
||||
async batchDeleted(@Body() body: BatchIdsDto): Promise<BatchResult> {
|
||||
return await this.orchestrator.batchDelete(body.ids);
|
||||
}
|
||||
|
||||
@Patch(':id/active')
|
||||
async active(@Param('id') dataId: string): Promise<String> {
|
||||
async active(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.active(dataId);
|
||||
}
|
||||
|
||||
|
@ -44,7 +50,7 @@ export class ItemDataController {
|
|||
}
|
||||
|
||||
@Patch(':id/confirm')
|
||||
async confirm(@Param('id') dataId: string): Promise<String> {
|
||||
async confirm(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.confirm(dataId);
|
||||
}
|
||||
|
||||
|
@ -54,7 +60,7 @@ export class ItemDataController {
|
|||
}
|
||||
|
||||
@Patch(':id/inactive')
|
||||
async inactive(@Param('id') dataId: string): Promise<String> {
|
||||
async inactive(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.inactive(dataId);
|
||||
}
|
||||
|
||||
|
@ -72,7 +78,7 @@ export class ItemDataController {
|
|||
}
|
||||
|
||||
@Delete(':id')
|
||||
async delete(@Param('id') dataId: string): Promise<String> {
|
||||
async delete(@Param('id') dataId: string): Promise<string> {
|
||||
return await this.orchestrator.delete(dataId);
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue