Compare commits

...

11 Commits

Author SHA1 Message Date
Firman Ramdhani 07e7b86cd4 feat: update query report income and merge with development branch
continuous-integration/drone/push Build is passing Details
2024-09-04 18:21:08 +07:00
Firman Ramdhani db57b1973e Merge branch 'development' of ssh://git.eigen.co.id:2222/eigen/pos-be into feat/fix-couch-transaction 2024-09-04 18:20:13 +07:00
firmanr b716c75a2c Merge pull request 'feat/fix-couch-transaction' (#78) from feat/fix-couch-transaction into development
continuous-integration/drone/push Build is passing Details
Reviewed-on: #78
2024-09-04 10:26:33 +00:00
shancheas 1a2a37d185 chore: add label to APM 2024-09-03 18:44:04 +07:00
shancheas 665eacd39f feat: add reject QR gate
continuous-integration/drone/tag Build is passing Details
2024-09-03 10:23:02 +07:00
shancheas 9d98003a2d feat: add detail formula api getter
continuous-integration/drone/tag Build is passing Details
2024-09-03 09:45:23 +07:00
shancheas 45c4bde838 feat: add gate scanner module
continuous-integration/drone/tag Build is passing Details
2024-09-02 13:59:19 +07:00
shancheas b9927da0c4 Merge branch 'development' of ssh://git.eigen.co.id:2222/eigen/pos-be into development
continuous-integration/drone/tag Build is passing Details
2024-09-02 13:07:38 +07:00
Firman Ramdhani d523009acd feat: add report income per item master
continuous-integration/drone/tag Build is failing Details
2024-08-30 19:32:03 +07:00
Firman Ramdhani 457ce30cc1 feat: update email template
continuous-integration/drone/tag Build is passing Details
2024-08-30 15:27:24 +07:00
shancheas e7a7ffb2bc feat: add variable config to formula 2024-08-29 17:19:24 +07:00
29 changed files with 16511 additions and 7 deletions

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

BIN
assets/image/we.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

View File

@ -76,6 +76,7 @@ import { PosLogModel } from './modules/configuration/log/data/models/pos-log.mod
import { ExportModule } from './modules/configuration/export/export.module';
import { TransactionDemographyModel } from './modules/transaction/transaction/data/models/transaction-demography.model';
import { SupersetModule } from './modules/configuration/superset/superset.module';
import { GateScanModule } from './modules/gates/gate.module';
@Module({
imports: [
@ -178,6 +179,8 @@ import { SupersetModule } from './modules/configuration/superset/superset.module
// superset
SupersetModule,
GateScanModule,
],
controllers: [],
providers: [

View File

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

View File

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

View File

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

View File

@ -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"`,
);
}
}

View File

@ -32,7 +32,6 @@ export class PaymentTransactionHandler
const current_data = event.data.data;
const data_id = current_data.transaction_id ?? event.data.id;
const from_refund = event.data.module == TABLE_NAME.REFUND;
console.log('payment handlet', { data_id });
const payments = await this.paymentService.getManyByOptions({
where: {
@ -106,6 +105,15 @@ export class PaymentTransactionHandler
`;
})}
</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,
};
}),
});
}

View File

@ -17,7 +17,7 @@ export async function sendEmail(receivers, invoiceType, attachment?) {
for (const receiver of receivers) {
try {
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 template = handlebars.compile(templateSource);

View File

@ -0,0 +1,5 @@
export interface GateScanEntity {
gate_id: string;
type: string;
uuid: string;
}

View File

@ -0,0 +1,8 @@
export interface GateResponseEntity {
code: number;
message: string;
}
export interface GateMasterEntity {
codes: string[];
}

View File

@ -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 {}

View File

@ -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;
}
}

View File

@ -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: `CASE WHEN main.type != 'counter' THEN main.invoice_code ELSE null END`,
label: 'Kode Booking',
type: DATA_TYPE.DIMENSION,
format: DATA_FORMAT.TEXT,
},
{
column: 'main__payment_code',
query: `CASE WHEN main.type = 'counter' THEN main.invoice_code ELSE main.payment_code END`,
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,
},
],
};

View File

@ -2,6 +2,7 @@ import { ReportConfigEntity } from '../../entities/report-config.entity';
import IncomeReport from './configs/income';
import IncomeReportPerItem from './configs/income-per-item';
import IncomeReportPerItemMaster from './configs/income-per-item-master';
import GivingDiscount from './configs/giving-discounts';
import VisitorsPerRideReport from './configs/visitors-per-ride';
import TimePerRideReport from './configs/time-per-ride';
@ -14,6 +15,7 @@ import ReconciliationReport from './configs/reconciliation';
export const TransactionReportConfig: ReportConfigEntity[] = [
IncomeReport,
IncomeReportPerItem,
IncomeReportPerItemMaster,
GivingDiscount,
// VisitorsPerRideReport,
// TimePerRideReport,

View File

@ -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 [];
}
}

View File

@ -11,6 +11,7 @@ import { SalesPriceFormulaEntity } from 'src/modules/transaction/sales-price-for
import { In } from 'typeorm';
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 { FormulaType } from 'src/modules/transaction/sales-price-formula/constants';
@Injectable()
export class UpdateProfitShareFormulaManager extends BaseUpdateManager<SalesPriceFormulaEntity> {
@ -30,6 +31,35 @@ export class UpdateProfitShareFormulaManager extends BaseUpdateManager<SalesPric
}
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;
}

View File

@ -3,14 +3,24 @@ import { DetailProfitShareFormulaManager } from './managers/detail-profit-share-
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 { SalesPriceFormulaEntity } from 'src/modules/transaction/sales-price-formula/domain/entities/sales-price-formula.entity';
import { IndexProfitShareFormulaManager } from './managers/index-profit-share-formula.manager';
@Injectable()
export class ProfitShareFormulaReadOrchestrator {
constructor(
private detailManager: DetailProfitShareFormulaManager,
private indexManager: IndexProfitShareFormulaManager,
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> {
this.detailManager.setData('');
this.detailManager.setService(this.serviceData, TABLE_NAME.PRICE_FORMULA);

View File

@ -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 { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { Public } from 'src/core/guards';
@ -15,4 +15,9 @@ export class ProfitShareFormulaReadController {
async detail(): Promise<SalesPriceFormulaEntity> {
return await this.orchestrator.detail();
}
@Get('detail')
async breakdown(): Promise<SalesPriceFormulaEntity[]> {
return await this.orchestrator.index();
}
}

View File

@ -12,6 +12,7 @@ import { DetailProfitShareFormulaManager } from './domain/usecases/managers/deta
import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales-price-formula.model';
import { TaxDataService } from '../tax/data/services/tax-data.service';
import { TaxModel } from '../tax/data/models/tax.model';
import { IndexProfitShareFormulaManager } from './domain/usecases/managers/index-profit-share-formula.manager';
@Module({
imports: [
@ -29,6 +30,7 @@ import { TaxModel } from '../tax/data/models/tax.model';
providers: [
DetailProfitShareFormulaManager,
UpdateProfitShareFormulaManager,
IndexProfitShareFormulaManager,
ProfitShareFormulaDataOrchestrator,
ProfitShareFormulaReadOrchestrator,

View File

@ -22,6 +22,9 @@ export class SalesPriceFormulaModel
@Column('varchar', { name: 'formula_string', nullable: true })
formula_string: string;
@Column('varchar', { default: 'dpp' })
value_for: string;
@Column('varchar', { name: 'example_formula', nullable: true })
example_formula: string;

View File

@ -6,5 +6,13 @@ export interface SalesPriceFormulaEntity extends BaseEntity {
formula_string: string; // digunakan untuk menyimpan string dari formula
example_formula: string;
example_result: number;
value_for: string;
type: FormulaType;
additional?: AdditionalFormula[];
}
export interface AdditionalFormula {
formula_render: any;
formula_string: string;
value_for: string;
}

View File

@ -1,11 +1,37 @@
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 { ValidateIf } from 'class-validator';
import { ValidateIf, ValidateNested } from 'class-validator';
import { Exclude } from 'class-transformer';
import { Any } from 'typeorm';
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
extends BaseDto
implements SalesPriceFormulaEntity
@ -30,6 +56,19 @@ export class SalesPriceFormulaDto
@Exclude()
example_result: number;
@Exclude()
value_for: string;
@Exclude()
type: FormulaType;
@ApiProperty({
type: [AdditionalFormulaDto],
default: AdditionalFormulaDto,
})
@ValidateIf(({ additional }) => {
return additional != null;
})
@ValidateNested({ each: true })
additional: AdditionalFormulaDto[];
}