Merge branch 'development' of ssh://git.eigen.co.id:2222/eigen/pos-be into feat/fix-couch-transaction
commit
db57b1973e
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
After Width: | Height: | Size: 504 KiB |
|
@ -76,6 +76,7 @@ import { PosLogModel } from './modules/configuration/log/data/models/pos-log.mod
|
||||||
import { ExportModule } from './modules/configuration/export/export.module';
|
import { ExportModule } from './modules/configuration/export/export.module';
|
||||||
import { TransactionDemographyModel } from './modules/transaction/transaction/data/models/transaction-demography.model';
|
import { TransactionDemographyModel } from './modules/transaction/transaction/data/models/transaction-demography.model';
|
||||||
import { SupersetModule } from './modules/configuration/superset/superset.module';
|
import { SupersetModule } from './modules/configuration/superset/superset.module';
|
||||||
|
import { GateScanModule } from './modules/gates/gate.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -178,6 +179,8 @@ import { SupersetModule } from './modules/configuration/superset/superset.module
|
||||||
|
|
||||||
// superset
|
// superset
|
||||||
SupersetModule,
|
SupersetModule,
|
||||||
|
|
||||||
|
GateScanModule,
|
||||||
],
|
],
|
||||||
controllers: [],
|
controllers: [],
|
||||||
providers: [
|
providers: [
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
export const PAGINATION_RESPONSE = 'PAGINATION_RESPONSE';
|
export const PAGINATION_RESPONSE = 'PAGINATION_RESPONSE';
|
||||||
|
export const GATE_RESPONSE = 'GATE_RESPONSE';
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { SetMetadata } from '@nestjs/common';
|
import { SetMetadata } from '@nestjs/common';
|
||||||
import { PAGINATION_RESPONSE } from '../../constants';
|
import { GATE_RESPONSE, PAGINATION_RESPONSE } from '../../constants';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This decorator will tell the response,
|
* This decorator will tell the response,
|
||||||
|
@ -7,3 +7,5 @@ import { PAGINATION_RESPONSE } from '../../constants';
|
||||||
*/
|
*/
|
||||||
export const Pagination = (isPagination = true) =>
|
export const Pagination = (isPagination = true) =>
|
||||||
SetMetadata(PAGINATION_RESPONSE, isPagination);
|
SetMetadata(PAGINATION_RESPONSE, isPagination);
|
||||||
|
|
||||||
|
export const Gate = () => SetMetadata(GATE_RESPONSE, true);
|
||||||
|
|
|
@ -8,13 +8,20 @@ import { Observable } from 'rxjs';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
import { Reflector } from '@nestjs/core';
|
import { Reflector } from '@nestjs/core';
|
||||||
import { PAGINATION_RESPONSE } from '../constants';
|
import { GATE_RESPONSE, PAGINATION_RESPONSE } from '../constants';
|
||||||
import { createPaginationResponse } from './utils/pagination-meta.helper';
|
import { createPaginationResponse } from './utils/pagination-meta.helper';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TransformInterceptor implements NestInterceptor {
|
export class TransformInterceptor implements NestInterceptor {
|
||||||
constructor(protected readonly reflector: Reflector) {}
|
constructor(protected readonly reflector: Reflector) {}
|
||||||
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||||
|
const isGate = this.reflector.getAllAndOverride<boolean>(GATE_RESPONSE, [
|
||||||
|
context.getHandler(),
|
||||||
|
context.getClass(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isGate) return next.handle();
|
||||||
|
|
||||||
const isPagination = this.reflector.getAllAndOverride<boolean>(
|
const isPagination = this.reflector.getAllAndOverride<boolean>(
|
||||||
PAGINATION_RESPONSE,
|
PAGINATION_RESPONSE,
|
||||||
[context.getHandler(), context.getClass()],
|
[context.getHandler(), context.getClass()],
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||||
|
|
||||||
|
export class AddValueVariableFormula1724926316235
|
||||||
|
implements MigrationInterface
|
||||||
|
{
|
||||||
|
name = 'AddValueVariableFormula1724926316235';
|
||||||
|
|
||||||
|
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "price_formulas" ADD "value_for" character varying NOT NULL DEFAULT 'dpp'`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||||
|
await queryRunner.query(
|
||||||
|
`ALTER TABLE "price_formulas" DROP COLUMN "value_for"`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -32,7 +32,6 @@ export class PaymentTransactionHandler
|
||||||
const current_data = event.data.data;
|
const current_data = event.data.data;
|
||||||
const data_id = current_data.transaction_id ?? event.data.id;
|
const data_id = current_data.transaction_id ?? event.data.id;
|
||||||
const from_refund = event.data.module == TABLE_NAME.REFUND;
|
const from_refund = event.data.module == TABLE_NAME.REFUND;
|
||||||
console.log('payment handlet', { data_id });
|
|
||||||
|
|
||||||
const payments = await this.paymentService.getManyByOptions({
|
const payments = await this.paymentService.getManyByOptions({
|
||||||
where: {
|
where: {
|
||||||
|
@ -106,6 +105,15 @@ export class PaymentTransactionHandler
|
||||||
`;
|
`;
|
||||||
})}
|
})}
|
||||||
</ul>`,
|
</ul>`,
|
||||||
|
|
||||||
|
refund_items_data: transaction?.['refund']?.refund_items
|
||||||
|
?.filter((item) => Number(item.qty_refund) > 0)
|
||||||
|
.map((item) => {
|
||||||
|
return {
|
||||||
|
qty_refund: item.qty_refund,
|
||||||
|
item_name: item.transaction_item.item_name,
|
||||||
|
};
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ export async function sendEmail(receivers, invoiceType, attachment?) {
|
||||||
for (const receiver of receivers) {
|
for (const receiver of receivers) {
|
||||||
try {
|
try {
|
||||||
const templateName = getTemplate(receiver.payment_type, invoiceType);
|
const templateName = getTemplate(receiver.payment_type, invoiceType);
|
||||||
const templatePath = `./assets/email-template/${templateName}.html`;
|
const templatePath = `./assets/email-template/redesign/${templateName}.html`;
|
||||||
const templateSource = fs.readFileSync(templatePath, 'utf8');
|
const templateSource = fs.readFileSync(templatePath, 'utf8');
|
||||||
|
|
||||||
const template = handlebars.compile(templateSource);
|
const template = handlebars.compile(templateSource);
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
export interface GateScanEntity {
|
||||||
|
gate_id: string;
|
||||||
|
type: string;
|
||||||
|
uuid: string;
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
export interface GateResponseEntity {
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GateMasterEntity {
|
||||||
|
codes: string[];
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { GateController } from './infrastructure/gate.controller';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot(),
|
||||||
|
// TypeOrmModule.forFeature(
|
||||||
|
// [],
|
||||||
|
// CONNECTION_NAME.DEFAULT,
|
||||||
|
// ),
|
||||||
|
],
|
||||||
|
controllers: [GateController],
|
||||||
|
providers: [],
|
||||||
|
})
|
||||||
|
export class GateScanModule {}
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { Body, Controller, Get, Param, Post, Res } from '@nestjs/common';
|
||||||
|
import { Response } from 'express';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { Public } from 'src/core/guards';
|
||||||
|
import { GateScanEntity } from '../domain/entity/gate-request.entity';
|
||||||
|
import {
|
||||||
|
GateMasterEntity,
|
||||||
|
GateResponseEntity,
|
||||||
|
} from '../domain/entity/gate-response.entity';
|
||||||
|
import { Gate } from 'src/core/response';
|
||||||
|
|
||||||
|
const masterGates = [
|
||||||
|
'319b6d3e-b661-4d19-8695-0dd6fb76465e',
|
||||||
|
'9afdb79d-7162-43e6-8ac6-f1941adea7ba',
|
||||||
|
'7e4c0281-8cf2-420e-aba1-c8ff834de450',
|
||||||
|
'19318ac8-caa0-47e4-8a41-2aac238d3665',
|
||||||
|
'495bc25f-42c4-4007-8e79-3747fa1054b6',
|
||||||
|
'b90fc9a9-efd9-4216-a8af-7ed120b141de',
|
||||||
|
'4399e93c-a839-4802-a49d-f933c72b1433',
|
||||||
|
'970673a7-6370-444a-931a-9784220dd35d',
|
||||||
|
'151ab50e-4e54-4252-b3ab-f5c0817b27a0',
|
||||||
|
'4c0e6924-baf5-47fb-a15b-fd1cd0958cc0',
|
||||||
|
];
|
||||||
|
|
||||||
|
const failedGates = [
|
||||||
|
'b3c3ae7b-daf5-4340-998b-ee35ed41323d',
|
||||||
|
'be157609-92b8-4989-920d-a81769bcb05a',
|
||||||
|
];
|
||||||
|
|
||||||
|
const gateResponses = [
|
||||||
|
{
|
||||||
|
statusCode: 200,
|
||||||
|
code: 1,
|
||||||
|
message: 'Berhasil Check In',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusCode: 403,
|
||||||
|
code: 2,
|
||||||
|
message: 'Gagal melakukan Check In. Karena tiket telah kadaluarsa',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
statusCode: 403,
|
||||||
|
code: 3,
|
||||||
|
message: 'Gagal melakukan Check In. Tiket tidak tersedia',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
@ApiTags(`Gate - read`)
|
||||||
|
@Controller(`v1/gate`)
|
||||||
|
@Public(true)
|
||||||
|
@Gate()
|
||||||
|
export class GateController {
|
||||||
|
@Post('scan')
|
||||||
|
async scan(
|
||||||
|
@Body() data: GateScanEntity,
|
||||||
|
@Res({ passthrough: true }) res: Response,
|
||||||
|
): Promise<GateResponseEntity> {
|
||||||
|
console.log(data);
|
||||||
|
if (masterGates.includes(data.uuid)) {
|
||||||
|
res.status(200);
|
||||||
|
return gateResponses[0];
|
||||||
|
}
|
||||||
|
if (failedGates.includes(data.uuid)) {
|
||||||
|
res.status(403);
|
||||||
|
return gateResponses[2];
|
||||||
|
}
|
||||||
|
|
||||||
|
const response = Math.floor(Math.random() * 3);
|
||||||
|
const responseValue = gateResponses[response];
|
||||||
|
|
||||||
|
res.status(responseValue.statusCode);
|
||||||
|
return responseValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get(':id/master')
|
||||||
|
async detail(@Param('id') id: string): Promise<GateMasterEntity> {
|
||||||
|
if (id == '1') return { codes: masterGates };
|
||||||
|
return {
|
||||||
|
codes: this.createRandomStringArray(masterGates),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
createRandomStringArray(inputArray: string[]): string[] {
|
||||||
|
const randomLength = Math.floor(Math.random() * 4) + 2; // Random length between 2 and 5
|
||||||
|
const outputArray: string[] = [];
|
||||||
|
|
||||||
|
while (outputArray.length < randomLength) {
|
||||||
|
const randomIndex = Math.floor(Math.random() * inputArray.length);
|
||||||
|
outputArray.push(inputArray[randomIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return outputArray;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,340 @@
|
||||||
|
import {
|
||||||
|
DATA_FORMAT,
|
||||||
|
DATA_TYPE,
|
||||||
|
FILTER_FIELD_TYPE,
|
||||||
|
FILTER_TYPE,
|
||||||
|
REPORT_GROUP,
|
||||||
|
} from '../../../constant';
|
||||||
|
import { ReportConfigEntity } from '../../../entities/report-config.entity';
|
||||||
|
import { TransactionType } from 'src/modules/transaction/transaction/constants';
|
||||||
|
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||||
|
|
||||||
|
export default <ReportConfigEntity>{
|
||||||
|
group_name: REPORT_GROUP.transaction_report,
|
||||||
|
unique_name: `${REPORT_GROUP.transaction_report}__income_per_item_master`,
|
||||||
|
label: 'Pendapatan Per Item Master',
|
||||||
|
table_schema: `transactions main
|
||||||
|
LEFT JOIN transaction_items tr_item ON tr_item.transaction_id::text = main.id::text
|
||||||
|
LEFT JOIN transaction_item_breakdowns tr_item_bundling ON tr_item_bundling.transaction_item_id::text = tr_item.id::text
|
||||||
|
LEFT JOIN refunds refund ON refund.transaction_id = main.id
|
||||||
|
LEFT JOIN items item ON item.id::text = tr_item.item_id::text
|
||||||
|
LEFT JOIN users tenant ON tenant.id::text = item.tenant_id::text
|
||||||
|
LEFT JOIN refund_items refund_item ON refund_item.refund_item_id::text = tr_item.item_id::text`,
|
||||||
|
main_table_alias: 'main',
|
||||||
|
whereDefaultConditions: [
|
||||||
|
{
|
||||||
|
column: 'main.status',
|
||||||
|
filter_type: FILTER_TYPE.TEXT_IN_MEMBER,
|
||||||
|
values: [STATUS.SETTLED, STATUS.REFUNDED, STATUS.PROCESS_REFUND],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
defaultOrderBy: [],
|
||||||
|
lowLevelOrderBy: [],
|
||||||
|
filter_period_config: {
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
column_configs: [
|
||||||
|
{
|
||||||
|
column: 'main__settlement_date',
|
||||||
|
query: 'main.settlement_date',
|
||||||
|
label: 'Tanggal Pendapatan',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.DATE_TIMESTAMP,
|
||||||
|
date_format: 'DD/MM/YYYY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'item_owner',
|
||||||
|
query: `CASE WHEN tenant.name is not null THEN tenant.name ELSE 'Company' END`,
|
||||||
|
label: 'Kepemilikan',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__type',
|
||||||
|
query: 'main.type',
|
||||||
|
label: 'Sumber',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__invoice_code',
|
||||||
|
query: 'main.invoice_code',
|
||||||
|
label: 'Kode Booking',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__payment_code',
|
||||||
|
query: 'main.payment_code',
|
||||||
|
label: 'Kode Pembayaran',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tr_item__item_category_name',
|
||||||
|
query: 'tr_item.item_category_name',
|
||||||
|
label: 'Kategori Item',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tr_item__item_name',
|
||||||
|
query: 'tr_item.item_name',
|
||||||
|
label: 'Nama Item',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tr_item__breakdown_bundling',
|
||||||
|
query: 'tr_item.breakdown_bundling',
|
||||||
|
label: 'Breakdown Bundling',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.BOOLEAN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tr_item_bundling__item_name',
|
||||||
|
query: 'tr_item_bundling.item_name',
|
||||||
|
label: 'Nama Item Bundling',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__customer_type',
|
||||||
|
query: 'main.customer_type',
|
||||||
|
label: 'Tipe Pelanggan',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__creator_counter_no',
|
||||||
|
query: 'main.creator_counter_no',
|
||||||
|
label: 'No.PoS',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tr_item__qty',
|
||||||
|
query: 'tr_item.qty',
|
||||||
|
label: 'Qty',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.NUMBER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tr_item__total_hpp',
|
||||||
|
query: 'tr_item.total_hpp',
|
||||||
|
label: 'Total HPP',
|
||||||
|
type: DATA_TYPE.MEASURE,
|
||||||
|
format: DATA_FORMAT.CURRENCY,
|
||||||
|
},
|
||||||
|
// TODO => tambahkan total dpp per item
|
||||||
|
// TODO => tambahkan total tax
|
||||||
|
{
|
||||||
|
column: 'tr_item__total_price',
|
||||||
|
query: 'tr_item.total_price',
|
||||||
|
label: 'Total Penjualan',
|
||||||
|
type: DATA_TYPE.MEASURE,
|
||||||
|
format: DATA_FORMAT.CURRENCY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'refund__refund_date',
|
||||||
|
query: 'refund.refund_date',
|
||||||
|
label: 'Tanggal Pengembalian',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.DATE_TIMESTAMP,
|
||||||
|
date_format: 'DD/MM/YYYY',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'refund__status',
|
||||||
|
query: 'refund.status',
|
||||||
|
label: 'Status Pengembalian',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'refund__code',
|
||||||
|
query: 'refund.code',
|
||||||
|
label: 'Kode Pengembalian',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'refund_item__qty_refund',
|
||||||
|
query: 'refund_item.qty_refund',
|
||||||
|
label: 'Qty Pengembalian',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.NUMBER,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'refund_item__refund_total',
|
||||||
|
query: '(refund_item.refund_total * -1)',
|
||||||
|
label: 'Total Pengembalian',
|
||||||
|
type: DATA_TYPE.MEASURE,
|
||||||
|
format: DATA_FORMAT.CURRENCY,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
column: 'transaction_balance',
|
||||||
|
query: `CASE WHEN refund.id is null THEN tr_item.total_price ELSE tr_item.total_price - refund_item.refund_total END`,
|
||||||
|
label: 'Balance',
|
||||||
|
type: DATA_TYPE.MEASURE,
|
||||||
|
format: DATA_FORMAT.CURRENCY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tr_item__item_tenant_share_margin',
|
||||||
|
query: 'tr_item.item_tenant_share_margin',
|
||||||
|
label: 'Profile Share (IDR)',
|
||||||
|
type: DATA_TYPE.MEASURE,
|
||||||
|
format: DATA_FORMAT.CURRENCY,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'tenant_income',
|
||||||
|
query: 'tr_item.total_price - tr_item.item_tenant_share_margin',
|
||||||
|
label: 'Pendapatan Tenant',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.CURRENCY,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
column: 'main__customer_name',
|
||||||
|
query: 'main.customer_name',
|
||||||
|
label: 'Nama Pelanggan',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__customer_description',
|
||||||
|
query: 'main.customer_description',
|
||||||
|
label: 'Deskripsi',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__customer_phone',
|
||||||
|
query: 'main.customer_phone',
|
||||||
|
label: 'Telepon',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
column: 'main__creator_name',
|
||||||
|
query: 'main.creator_name',
|
||||||
|
label: 'Dibuat Oleh',
|
||||||
|
type: DATA_TYPE.DIMENSION,
|
||||||
|
format: DATA_FORMAT.TEXT,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
whereCondition(filterModel) {
|
||||||
|
const queryFilter = [];
|
||||||
|
const breakdown = filterModel.tr_item__breakdown_bundling;
|
||||||
|
if (breakdown) {
|
||||||
|
const value = breakdown.filter.map((item) => {
|
||||||
|
return item === 'Yes' ? true : false;
|
||||||
|
});
|
||||||
|
|
||||||
|
queryFilter.push(`tr_item.breakdown_bundling in (${value.join()})`);
|
||||||
|
}
|
||||||
|
return queryFilter;
|
||||||
|
},
|
||||||
|
ignore_filter_keys: ['tr_item__breakdown_bundling'],
|
||||||
|
filter_configs: [
|
||||||
|
{
|
||||||
|
filed_label: 'Tanggal Pendapatan',
|
||||||
|
filter_column: 'main__settlement_date',
|
||||||
|
field_type: FILTER_FIELD_TYPE.date_range_picker,
|
||||||
|
filter_type: FILTER_TYPE.DATE_IN_RANGE_TIMESTAMP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Kepemilikan',
|
||||||
|
filter_column: 'item_owner',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Sumber',
|
||||||
|
filter_column: 'main__type',
|
||||||
|
field_type: FILTER_FIELD_TYPE.select,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_IN_MEMBER,
|
||||||
|
select_custom_options: [...Object.values(TransactionType)],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Kode Booking',
|
||||||
|
filter_column: 'main__invoice_code',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Kode Pembayaran',
|
||||||
|
filter_column: 'main__payment_code',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Kategori Item',
|
||||||
|
filter_column: 'tr_item__item_category_name',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Nama Item',
|
||||||
|
filter_column: 'tr_item__item_name',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Breakdown Item',
|
||||||
|
filter_column: 'tr_item__breakdown_bundling',
|
||||||
|
field_type: FILTER_FIELD_TYPE.select,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
select_custom_options: ['Yes', 'No'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Nama Item Bundling',
|
||||||
|
filter_column: 'tr_item_bundling__item_name',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Tipe Pelanggan',
|
||||||
|
filter_column: 'main__customer_type',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'No. PoS',
|
||||||
|
filter_column: 'main__creator_counter_no',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Tanggal Pengembalian',
|
||||||
|
filter_column: 'refund__refund_date',
|
||||||
|
field_type: FILTER_FIELD_TYPE.date_range_picker,
|
||||||
|
filter_type: FILTER_TYPE.DATE_IN_RANGE_TIMESTAMP,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Kode Pengembalian',
|
||||||
|
filter_column: 'refund__code',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Nama Pelanggan',
|
||||||
|
filter_column: 'main__customer_name',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Bank/Issuer',
|
||||||
|
filter_column: 'main__payment_type_method_name',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
filed_label: 'Dibuat Oleh',
|
||||||
|
filter_column: 'main__creator_name',
|
||||||
|
field_type: FILTER_FIELD_TYPE.input_tag,
|
||||||
|
filter_type: FILTER_TYPE.TEXT_MULTIPLE_CONTAINS,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
|
@ -2,6 +2,7 @@ import { ReportConfigEntity } from '../../entities/report-config.entity';
|
||||||
|
|
||||||
import IncomeReport from './configs/income';
|
import IncomeReport from './configs/income';
|
||||||
import IncomeReportPerItem from './configs/income-per-item';
|
import IncomeReportPerItem from './configs/income-per-item';
|
||||||
|
import IncomeReportPerItemMaster from './configs/income-per-item-master';
|
||||||
import GivingDiscount from './configs/giving-discounts';
|
import GivingDiscount from './configs/giving-discounts';
|
||||||
import VisitorsPerRideReport from './configs/visitors-per-ride';
|
import VisitorsPerRideReport from './configs/visitors-per-ride';
|
||||||
import TimePerRideReport from './configs/time-per-ride';
|
import TimePerRideReport from './configs/time-per-ride';
|
||||||
|
@ -14,6 +15,7 @@ import ReconciliationReport from './configs/reconciliation';
|
||||||
export const TransactionReportConfig: ReportConfigEntity[] = [
|
export const TransactionReportConfig: ReportConfigEntity[] = [
|
||||||
IncomeReport,
|
IncomeReport,
|
||||||
IncomeReportPerItem,
|
IncomeReportPerItem,
|
||||||
|
IncomeReportPerItemMaster,
|
||||||
GivingDiscount,
|
GivingDiscount,
|
||||||
// VisitorsPerRideReport,
|
// VisitorsPerRideReport,
|
||||||
// TimePerRideReport,
|
// TimePerRideReport,
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
Param,
|
||||||
|
RelationParam,
|
||||||
|
} from 'src/core/modules/domain/entities/base-filter.entity';
|
||||||
|
import { FormulaType } from 'src/modules/transaction/sales-price-formula/constants';
|
||||||
|
import { SalesPriceFormulaEntity } from 'src/modules/transaction/sales-price-formula/domain/entities/sales-price-formula.entity';
|
||||||
|
import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager';
|
||||||
|
import { SelectQueryBuilder } from 'typeorm';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class IndexProfitShareFormulaManager extends BaseIndexManager<SalesPriceFormulaEntity> {
|
||||||
|
setQueryFilter(
|
||||||
|
queryBuilder: SelectQueryBuilder<SalesPriceFormulaEntity>,
|
||||||
|
): SelectQueryBuilder<SalesPriceFormulaEntity> {
|
||||||
|
return queryBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
get specificFilter(): Param[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
cols: `${this.tableName}.type::text`,
|
||||||
|
data: [FormulaType.PROFIT_SHARE],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
async prepareData(): Promise<void> {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async beforeProcess(): Promise<void> {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
async afterProcess(): Promise<void> {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
get relations(): RelationParam {
|
||||||
|
return {
|
||||||
|
// relation only join (for query purpose)
|
||||||
|
joinRelations: [],
|
||||||
|
|
||||||
|
// relation join and select (relasi yang ingin ditampilkan),
|
||||||
|
selectRelations: [],
|
||||||
|
|
||||||
|
// relation yang hanya ingin dihitung (akan return number)
|
||||||
|
countRelations: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get selects(): string[] {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ import { SalesPriceFormulaEntity } from 'src/modules/transaction/sales-price-for
|
||||||
import { In } from 'typeorm';
|
import { In } from 'typeorm';
|
||||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||||
import { calculateProfitFormula } from 'src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper';
|
import { calculateProfitFormula } from 'src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper';
|
||||||
|
import { FormulaType } from 'src/modules/transaction/sales-price-formula/constants';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UpdateProfitShareFormulaManager extends BaseUpdateManager<SalesPriceFormulaEntity> {
|
export class UpdateProfitShareFormulaManager extends BaseUpdateManager<SalesPriceFormulaEntity> {
|
||||||
|
@ -30,6 +31,35 @@ export class UpdateProfitShareFormulaManager extends BaseUpdateManager<SalesPric
|
||||||
}
|
}
|
||||||
|
|
||||||
async afterProcess(): Promise<void> {
|
async afterProcess(): Promise<void> {
|
||||||
|
const additionalFormula = this.data.additional;
|
||||||
|
for (const additional of additionalFormula) {
|
||||||
|
/**
|
||||||
|
* Find formula for variable
|
||||||
|
* If the formula doesn't exist, then create data for save
|
||||||
|
*/
|
||||||
|
const formula = (await this.dataService.getOneByOptions({
|
||||||
|
where: {
|
||||||
|
value_for: additional.value_for,
|
||||||
|
},
|
||||||
|
})) ?? {
|
||||||
|
editor_id: this.user.id,
|
||||||
|
editor_name: this.user.name,
|
||||||
|
updated_at: new Date().getTime(),
|
||||||
|
created_at: new Date().getTime(),
|
||||||
|
type: FormulaType.PROFIT_SHARE,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Update formula value to exist formula or new formula
|
||||||
|
formula.formula_render = additional.formula_render;
|
||||||
|
formula.formula_string = additional.formula_string;
|
||||||
|
formula.value_for = additional.value_for;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function is create, but inside function is save
|
||||||
|
* So, if the id is provide, the data will be update instead create new data
|
||||||
|
*/
|
||||||
|
this.dataService.create(null, null, formula);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,14 +3,24 @@ import { DetailProfitShareFormulaManager } from './managers/detail-profit-share-
|
||||||
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
||||||
import { SalesPriceFormulaReadService } from 'src/modules/transaction/sales-price-formula/data/services/sales-price-formula-read.service';
|
import { SalesPriceFormulaReadService } from 'src/modules/transaction/sales-price-formula/data/services/sales-price-formula-read.service';
|
||||||
import { SalesPriceFormulaEntity } from 'src/modules/transaction/sales-price-formula/domain/entities/sales-price-formula.entity';
|
import { SalesPriceFormulaEntity } from 'src/modules/transaction/sales-price-formula/domain/entities/sales-price-formula.entity';
|
||||||
|
import { IndexProfitShareFormulaManager } from './managers/index-profit-share-formula.manager';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class ProfitShareFormulaReadOrchestrator {
|
export class ProfitShareFormulaReadOrchestrator {
|
||||||
constructor(
|
constructor(
|
||||||
private detailManager: DetailProfitShareFormulaManager,
|
private detailManager: DetailProfitShareFormulaManager,
|
||||||
|
private indexManager: IndexProfitShareFormulaManager,
|
||||||
private serviceData: SalesPriceFormulaReadService,
|
private serviceData: SalesPriceFormulaReadService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
async index(): Promise<SalesPriceFormulaEntity[]> {
|
||||||
|
this.indexManager.setFilterParam({});
|
||||||
|
this.indexManager.setService(this.serviceData, TABLE_NAME.PRICE_FORMULA);
|
||||||
|
await this.indexManager.execute();
|
||||||
|
const { data } = this.indexManager.getResult();
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
async detail(): Promise<SalesPriceFormulaEntity> {
|
async detail(): Promise<SalesPriceFormulaEntity> {
|
||||||
this.detailManager.setData('');
|
this.detailManager.setData('');
|
||||||
this.detailManager.setService(this.serviceData, TABLE_NAME.PRICE_FORMULA);
|
this.detailManager.setService(this.serviceData, TABLE_NAME.PRICE_FORMULA);
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Controller, Get, Param } from '@nestjs/common';
|
import { Controller, Get } from '@nestjs/common';
|
||||||
import { ProfitShareFormulaReadOrchestrator } from '../domain/usecases/profit-share-formula-read.orchestrator';
|
import { ProfitShareFormulaReadOrchestrator } from '../domain/usecases/profit-share-formula-read.orchestrator';
|
||||||
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
|
||||||
import { Public } from 'src/core/guards';
|
import { Public } from 'src/core/guards';
|
||||||
|
@ -15,4 +15,9 @@ export class ProfitShareFormulaReadController {
|
||||||
async detail(): Promise<SalesPriceFormulaEntity> {
|
async detail(): Promise<SalesPriceFormulaEntity> {
|
||||||
return await this.orchestrator.detail();
|
return await this.orchestrator.detail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('detail')
|
||||||
|
async breakdown(): Promise<SalesPriceFormulaEntity[]> {
|
||||||
|
return await this.orchestrator.index();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { DetailProfitShareFormulaManager } from './domain/usecases/managers/deta
|
||||||
import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales-price-formula.model';
|
import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales-price-formula.model';
|
||||||
import { TaxDataService } from '../tax/data/services/tax-data.service';
|
import { TaxDataService } from '../tax/data/services/tax-data.service';
|
||||||
import { TaxModel } from '../tax/data/models/tax.model';
|
import { TaxModel } from '../tax/data/models/tax.model';
|
||||||
|
import { IndexProfitShareFormulaManager } from './domain/usecases/managers/index-profit-share-formula.manager';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -29,6 +30,7 @@ import { TaxModel } from '../tax/data/models/tax.model';
|
||||||
providers: [
|
providers: [
|
||||||
DetailProfitShareFormulaManager,
|
DetailProfitShareFormulaManager,
|
||||||
UpdateProfitShareFormulaManager,
|
UpdateProfitShareFormulaManager,
|
||||||
|
IndexProfitShareFormulaManager,
|
||||||
|
|
||||||
ProfitShareFormulaDataOrchestrator,
|
ProfitShareFormulaDataOrchestrator,
|
||||||
ProfitShareFormulaReadOrchestrator,
|
ProfitShareFormulaReadOrchestrator,
|
||||||
|
|
|
@ -22,6 +22,9 @@ export class SalesPriceFormulaModel
|
||||||
@Column('varchar', { name: 'formula_string', nullable: true })
|
@Column('varchar', { name: 'formula_string', nullable: true })
|
||||||
formula_string: string;
|
formula_string: string;
|
||||||
|
|
||||||
|
@Column('varchar', { default: 'dpp' })
|
||||||
|
value_for: string;
|
||||||
|
|
||||||
@Column('varchar', { name: 'example_formula', nullable: true })
|
@Column('varchar', { name: 'example_formula', nullable: true })
|
||||||
example_formula: string;
|
example_formula: string;
|
||||||
|
|
||||||
|
|
|
@ -6,5 +6,13 @@ export interface SalesPriceFormulaEntity extends BaseEntity {
|
||||||
formula_string: string; // digunakan untuk menyimpan string dari formula
|
formula_string: string; // digunakan untuk menyimpan string dari formula
|
||||||
example_formula: string;
|
example_formula: string;
|
||||||
example_result: number;
|
example_result: number;
|
||||||
|
value_for: string;
|
||||||
type: FormulaType;
|
type: FormulaType;
|
||||||
|
additional?: AdditionalFormula[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AdditionalFormula {
|
||||||
|
formula_render: any;
|
||||||
|
formula_string: string;
|
||||||
|
value_for: string;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,37 @@
|
||||||
import { BaseDto } from 'src/core/modules/infrastructure/dto/base.dto';
|
import { BaseDto } from 'src/core/modules/infrastructure/dto/base.dto';
|
||||||
import { SalesPriceFormulaEntity } from '../../domain/entities/sales-price-formula.entity';
|
import {
|
||||||
|
AdditionalFormula,
|
||||||
|
SalesPriceFormulaEntity,
|
||||||
|
} from '../../domain/entities/sales-price-formula.entity';
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { ValidateIf } from 'class-validator';
|
import { ValidateIf, ValidateNested } from 'class-validator';
|
||||||
import { Exclude } from 'class-transformer';
|
import { Exclude } from 'class-transformer';
|
||||||
import { Any } from 'typeorm';
|
import { Any } from 'typeorm';
|
||||||
import { FormulaType } from '../../constants';
|
import { FormulaType } from '../../constants';
|
||||||
|
|
||||||
|
export class AdditionalFormulaDto implements AdditionalFormula {
|
||||||
|
@ApiProperty({
|
||||||
|
type: Any,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@ValidateIf((body) => body.formula_render)
|
||||||
|
formula_render: any;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@ValidateIf((body) => body.formula_string)
|
||||||
|
formula_string: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
required: false,
|
||||||
|
})
|
||||||
|
@ValidateIf((body) => body.value_for)
|
||||||
|
value_for: string;
|
||||||
|
}
|
||||||
|
|
||||||
export class SalesPriceFormulaDto
|
export class SalesPriceFormulaDto
|
||||||
extends BaseDto
|
extends BaseDto
|
||||||
implements SalesPriceFormulaEntity
|
implements SalesPriceFormulaEntity
|
||||||
|
@ -30,6 +56,19 @@ export class SalesPriceFormulaDto
|
||||||
@Exclude()
|
@Exclude()
|
||||||
example_result: number;
|
example_result: number;
|
||||||
|
|
||||||
|
@Exclude()
|
||||||
|
value_for: string;
|
||||||
|
|
||||||
@Exclude()
|
@Exclude()
|
||||||
type: FormulaType;
|
type: FormulaType;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: [AdditionalFormulaDto],
|
||||||
|
default: AdditionalFormulaDto,
|
||||||
|
})
|
||||||
|
@ValidateIf(({ additional }) => {
|
||||||
|
return additional != null;
|
||||||
|
})
|
||||||
|
@ValidateNested({ each: true })
|
||||||
|
additional: AdditionalFormulaDto[];
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue