Merge pull request 'fix/price-calculator' (#93) from fix/price-calculator into development
Reviewed-on: #93pull/94/head^2 20.1.67-alpha.1
commit
93c822f34e
|
@ -45,8 +45,10 @@ import { GoogleCalendarModule } from './modules/configuration/google-calendar/go
|
|||
import { TransactionModule } from './modules/transaction/transaction/transaction.module';
|
||||
import { TransactionModel } from './modules/transaction/transaction/data/models/transaction.model';
|
||||
import {
|
||||
TransactionBreakdownTaxModel,
|
||||
TransactionItemBreakdownModel,
|
||||
TransactionItemModel,
|
||||
TransactionItemTaxModel,
|
||||
} from './modules/transaction/transaction/data/models/transaction-item.model';
|
||||
import { TransactionTaxModel } from './modules/transaction/transaction/data/models/transaction-tax.model';
|
||||
import { ReconciliationModule } from './modules/transaction/reconciliation/reconciliation.module';
|
||||
|
@ -121,6 +123,8 @@ import { AuthService } from './core/guards/domain/services/auth.service';
|
|||
TransactionTaxModel,
|
||||
TransactionDemographyModel,
|
||||
TransactionItemBreakdownModel,
|
||||
TransactionItemTaxModel,
|
||||
TransactionBreakdownTaxModel,
|
||||
UserModel,
|
||||
UserLoginModel,
|
||||
|
||||
|
|
|
@ -22,6 +22,8 @@ export enum TABLE_NAME {
|
|||
TRANSACTION_ITEM = 'transaction_items',
|
||||
TRANSACTION_ITEM_BREAKDOWN = 'transaction_item_breakdowns',
|
||||
TRANSACTION_TAX = 'transaction_taxes',
|
||||
TRANSACTION_ITEM_TAX = 'transaction_item_taxes',
|
||||
TRANSACTION_ITEM_BREAKDOWN_TAX = 't_breakdown_item_taxes',
|
||||
TRANSACTION_DEMOGRAPHY = 'transaction_demographies',
|
||||
USER = 'users',
|
||||
USER_LOGIN = 'users_login',
|
||||
|
|
|
@ -0,0 +1,73 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddTaxItemTransaction1726045820711 implements MigrationInterface {
|
||||
name = 'AddTaxItemTransaction1726045820711';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "transaction_item_taxes" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "tax_id" character varying, "tax_name" character varying, "taxt_value" numeric, "tax_total_value" numeric, "transaction_id" uuid, CONSTRAINT "PK_fc5f6da61b24eb5bfdd503b0a0d" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "t_breakdown_item_taxes" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "tax_id" character varying, "tax_name" character varying, "taxt_value" numeric, "tax_total_value" numeric, "transaction_id" uuid, CONSTRAINT "PK_a1ef08d2c68169a50102aa70eca" PRIMARY KEY ("id"))`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "total_profit_share" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" ADD "total_profit_share" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_taxes" ADD CONSTRAINT "FK_f5c4966a381d903899cafb4b5ba" FOREIGN KEY ("transaction_id") REFERENCES "transaction_items"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "t_breakdown_item_taxes" ADD CONSTRAINT "FK_74bedce7e94f6707ddf26ef0c0f" FOREIGN KEY ("transaction_id") REFERENCES "transaction_item_breakdowns"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "payment_total_dpp" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" ADD "payment_total_dpp" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" ADD "payment_total_tax" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" ADD "payment_total_tax" numeric`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" ADD "total_share_tenant" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "t_breakdown_item_taxes" DROP CONSTRAINT "FK_74bedce7e94f6707ddf26ef0c0f"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_taxes" DROP CONSTRAINT "FK_f5c4966a381d903899cafb4b5ba"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "total_profit_share"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "total_profit_share"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE "t_breakdown_item_taxes"`);
|
||||
await queryRunner.query(`DROP TABLE "transaction_item_taxes"`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "payment_total_dpp"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "payment_total_dpp"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "payment_total_tax"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_items" DROP COLUMN "payment_total_tax"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transaction_item_breakdowns" DROP COLUMN "total_share_tenant"`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class AddFormulaToTax1726365023179 implements MigrationInterface {
|
||||
name = 'AddFormulaToTax1726365023179';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "taxes" ADD "formula_render" json`);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "taxes" ADD "formula_string" character varying`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "formula_string"`);
|
||||
await queryRunner.query(`ALTER TABLE "taxes" DROP COLUMN "formula_render"`);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,6 @@ 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';
|
||||
|
@ -17,12 +16,7 @@ export class IndexProfitShareFormulaManager extends BaseIndexManager<SalesPriceF
|
|||
}
|
||||
|
||||
get specificFilter(): Param[] {
|
||||
return [
|
||||
{
|
||||
cols: `${this.tableName}.type::text`,
|
||||
data: [FormulaType.PROFIT_SHARE],
|
||||
},
|
||||
];
|
||||
return [];
|
||||
}
|
||||
|
||||
async prepareData(): Promise<void> {
|
||||
|
@ -51,6 +45,12 @@ export class IndexProfitShareFormulaManager extends BaseIndexManager<SalesPriceF
|
|||
}
|
||||
|
||||
get selects(): string[] {
|
||||
return [];
|
||||
// return [];
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.formula_render`,
|
||||
`${this.tableName}.formula_string`,
|
||||
`${this.tableName}.value_for`,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import {
|
||||
Param,
|
||||
RelationParam,
|
||||
} from 'src/core/modules/domain/entities/base-filter.entity';
|
||||
import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager';
|
||||
import { SelectQueryBuilder } from 'typeorm';
|
||||
import { TaxEntity } from 'src/modules/transaction/tax/domain/entities/tax.entity';
|
||||
|
||||
@Injectable()
|
||||
export class IndexTaxFormulaManager extends BaseIndexManager<TaxEntity> {
|
||||
setQueryFilter(
|
||||
queryBuilder: SelectQueryBuilder<TaxEntity>,
|
||||
): SelectQueryBuilder<TaxEntity> {
|
||||
return queryBuilder;
|
||||
}
|
||||
|
||||
get specificFilter(): Param[] {
|
||||
return [
|
||||
{
|
||||
cols: `${this.tableName}.status::text`,
|
||||
isStatus: true,
|
||||
data: [`'active'`],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
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 [];
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.formula_render`,
|
||||
`${this.tableName}.formula_string`,
|
||||
`${this.tableName}.name`,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -8,21 +8,21 @@ import {
|
|||
import { SalesPriceFormulaModel } from 'src/modules/transaction/sales-price-formula/data/models/sales-price-formula.model';
|
||||
import { ProfitShareFormulaUpdatedEvent } from '../../entities/event/profit-share-formula-updated.event';
|
||||
import { SalesPriceFormulaEntity } from 'src/modules/transaction/sales-price-formula/domain/entities/sales-price-formula.entity';
|
||||
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';
|
||||
// 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';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateProfitShareFormulaManager extends BaseUpdateManager<SalesPriceFormulaEntity> {
|
||||
async validateProcess(): Promise<void> {
|
||||
const taxes = await this.dataServiceFirstOpt.getManyByOptions({
|
||||
where: {
|
||||
status: In([STATUS.ACTIVE]),
|
||||
},
|
||||
});
|
||||
// const taxes = await this.dataServiceFirstOpt.getManyByOptions({
|
||||
// where: {
|
||||
// status: In([STATUS.ACTIVE]),
|
||||
// },
|
||||
// });
|
||||
|
||||
calculateProfitFormula(this.data.formula_string, taxes, 10000, 50, true);
|
||||
// TODO: Save Validation
|
||||
// calculateProfitFormula(this.data.formula_string, taxes, 10000, 50, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -32,34 +32,9 @@ 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);
|
||||
}
|
||||
const taxFormula = this.data.taxes;
|
||||
this.dataService.create(null, null, additionalFormula);
|
||||
this.dataServiceFirstOpt.create(null, null, taxFormula);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ export class ProfitShareFormulaDataOrchestrator {
|
|||
async update(data): Promise<SalesPriceFormulaEntity> {
|
||||
const formula = await this.serviceData.getOneByOptions({
|
||||
where: {
|
||||
type: FormulaType.PROFIT_SHARE,
|
||||
type: FormulaType.SALES_PRICE,
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
@ -4,13 +4,17 @@ 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';
|
||||
import { IndexTaxFormulaManager } from './managers/tax-formula.manager';
|
||||
import { TaxReadService } from 'src/modules/transaction/tax/data/services/tax-read.service';
|
||||
|
||||
@Injectable()
|
||||
export class ProfitShareFormulaReadOrchestrator {
|
||||
constructor(
|
||||
private detailManager: DetailProfitShareFormulaManager,
|
||||
private indexManager: IndexProfitShareFormulaManager,
|
||||
private taxManager: IndexTaxFormulaManager,
|
||||
private serviceData: SalesPriceFormulaReadService,
|
||||
private taxServiceData: TaxReadService,
|
||||
) {}
|
||||
|
||||
async index(): Promise<SalesPriceFormulaEntity[]> {
|
||||
|
@ -21,6 +25,19 @@ export class ProfitShareFormulaReadOrchestrator {
|
|||
return data;
|
||||
}
|
||||
|
||||
async tax(): Promise<Partial<SalesPriceFormulaEntity>[]> {
|
||||
this.taxManager.setFilterParam({});
|
||||
this.taxManager.setService(this.taxServiceData, TABLE_NAME.TAX);
|
||||
await this.taxManager.execute();
|
||||
const { data } = this.taxManager.getResult();
|
||||
return data.map((tax) => {
|
||||
return {
|
||||
...tax,
|
||||
value_for: tax.name,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
async detail(): Promise<SalesPriceFormulaEntity> {
|
||||
this.detailManager.setData('');
|
||||
this.detailManager.setService(this.serviceData, TABLE_NAME.PRICE_FORMULA);
|
||||
|
|
|
@ -20,4 +20,9 @@ export class ProfitShareFormulaReadController {
|
|||
async breakdown(): Promise<SalesPriceFormulaEntity[]> {
|
||||
return await this.orchestrator.index();
|
||||
}
|
||||
|
||||
@Get('tax')
|
||||
async tax(): Promise<Partial<SalesPriceFormulaEntity>[]> {
|
||||
return await this.orchestrator.tax();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales
|
|||
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';
|
||||
import { IndexTaxFormulaManager } from './domain/usecases/managers/tax-formula.manager';
|
||||
import { TaxReadService } from '../tax/data/services/tax-read.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -31,11 +33,13 @@ import { IndexProfitShareFormulaManager } from './domain/usecases/managers/index
|
|||
DetailProfitShareFormulaManager,
|
||||
UpdateProfitShareFormulaManager,
|
||||
IndexProfitShareFormulaManager,
|
||||
IndexTaxFormulaManager,
|
||||
|
||||
ProfitShareFormulaDataOrchestrator,
|
||||
ProfitShareFormulaReadOrchestrator,
|
||||
|
||||
TaxDataService,
|
||||
TaxReadService,
|
||||
],
|
||||
})
|
||||
export class ProfitShareFormulaModule {}
|
||||
|
|
|
@ -1,17 +1,91 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { Injectable, UnprocessableEntityException } from '@nestjs/common';
|
||||
import { BaseDataService } from 'src/core/modules/data/service/base-data.service';
|
||||
import { SalesPriceFormulaEntity } from '../../domain/entities/sales-price-formula.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { SalesPriceFormulaModel } from '../models/sales-price-formula.model';
|
||||
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||
import { Repository } from 'typeorm';
|
||||
import { FormulaType } from '../../constants';
|
||||
import { TaxModel } from 'src/modules/transaction/tax/data/models/tax.model';
|
||||
import { ItemModel } from 'src/modules/item-related/item/data/models/item.model';
|
||||
|
||||
@Injectable()
|
||||
export class SalesPriceFormulaDataService extends BaseDataService<SalesPriceFormulaEntity> {
|
||||
constructor(
|
||||
@InjectRepository(SalesPriceFormulaModel, CONNECTION_NAME.DEFAULT)
|
||||
private repo: Repository<SalesPriceFormulaModel>,
|
||||
@InjectRepository(TaxModel, CONNECTION_NAME.DEFAULT)
|
||||
private tax: Repository<TaxModel>,
|
||||
@InjectRepository(ItemModel, CONNECTION_NAME.DEFAULT)
|
||||
private item: Repository<ItemModel>,
|
||||
) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
profitShareFormula() {
|
||||
return this.repo.find({
|
||||
where: {
|
||||
type: FormulaType.PROFIT_SHARE,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async itemTax(id: string) {
|
||||
const item = await this.item.findOne({
|
||||
relations: ['tenant'],
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
});
|
||||
|
||||
const profitShare = (item?.share_profit ?? 0) / 100;
|
||||
const tenantShare = (item?.tenant?.share_margin ?? 0) / 100;
|
||||
|
||||
return { profitShare, tenantShare };
|
||||
}
|
||||
|
||||
async salesPriceFormula() {
|
||||
const salesFormula = await this.repo.findOne({
|
||||
where: {
|
||||
type: FormulaType.SALES_PRICE,
|
||||
},
|
||||
});
|
||||
|
||||
const taxes = await this.tax.find();
|
||||
|
||||
for (const tax of taxes) {
|
||||
salesFormula.formula_string = salesFormula.formula_string.replace(
|
||||
tax.name,
|
||||
`(${tax.formula_string})`,
|
||||
);
|
||||
}
|
||||
|
||||
const counter = {};
|
||||
do {
|
||||
let isInfinite = false;
|
||||
for (const tax of taxes) {
|
||||
salesFormula.formula_string = salesFormula.formula_string.replace(
|
||||
`${tax.name}_value`,
|
||||
`(${tax.formula_string})`,
|
||||
);
|
||||
|
||||
if (salesFormula.formula_string.includes(`${tax.name}_value`))
|
||||
counter[tax.name] = counter[tax.name] ? counter[tax.name] + 1 : 1;
|
||||
|
||||
for (const count of Object.keys(counter)) {
|
||||
if (!isInfinite && counter[count] > 50) isInfinite = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (isInfinite) {
|
||||
throw new UnprocessableEntityException({
|
||||
message: 'Infinity Loop Formula, please fix formula',
|
||||
error: 'Infinity Loop Formula',
|
||||
meta: counter,
|
||||
});
|
||||
}
|
||||
} while (salesFormula.formula_string.includes('_value'));
|
||||
|
||||
return salesFormula;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,46 @@
|
|||
import * as math from 'mathjs';
|
||||
import { Equation, parse } from 'algebra.js';
|
||||
import { HttpStatus, UnprocessableEntityException } from '@nestjs/common';
|
||||
import {
|
||||
BadRequestException,
|
||||
HttpStatus,
|
||||
UnprocessableEntityException,
|
||||
} from '@nestjs/common';
|
||||
import { apm } from 'src/core/apm';
|
||||
|
||||
export function calculateFormula(
|
||||
formula: string,
|
||||
variable: object,
|
||||
total: number,
|
||||
solveFor = 'dpp',
|
||||
) {
|
||||
try {
|
||||
const x1 = math.simplify(formula, variable).toString();
|
||||
// console.log('Formula ', x1);
|
||||
const dppFormula = parse(x1);
|
||||
const totalFormula = parse(total.toString());
|
||||
const equation = new Equation(totalFormula, dppFormula);
|
||||
|
||||
// console.log(equation.toString());
|
||||
const result = equation.solveFor(solveFor).toString();
|
||||
// console.log(result, 'formula');
|
||||
|
||||
const value = math.evaluate(result);
|
||||
// console.log(value, 'value');
|
||||
|
||||
return value;
|
||||
} catch (e) {
|
||||
apm.captureError(e);
|
||||
throw new BadRequestException({
|
||||
message: 'Wrong value',
|
||||
meta: {
|
||||
formula,
|
||||
variable,
|
||||
total,
|
||||
solveFor,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateSalesFormula(
|
||||
formula: string,
|
||||
|
|
|
@ -14,13 +14,14 @@ import { DetailSalesPriceFormulaManager } from './domain/usecases/managers/detai
|
|||
import { SalesPriceFormulaModel } from './data/models/sales-price-formula.model';
|
||||
import { TaxDataService } from '../tax/data/services/tax-data.service';
|
||||
import { TaxModel } from '../tax/data/models/tax.model';
|
||||
import { ItemModel } from 'src/modules/item-related/item/data/models/item.model';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
TypeOrmModule.forFeature(
|
||||
[SalesPriceFormulaModel, TaxModel],
|
||||
[SalesPriceFormulaModel, TaxModel, ItemModel],
|
||||
CONNECTION_NAME.DEFAULT,
|
||||
),
|
||||
CqrsModule,
|
||||
|
|
|
@ -10,4 +10,10 @@ export class TaxModel extends BaseStatusModel<TaxEntity> implements TaxEntity {
|
|||
|
||||
@Column('float', { name: 'value', default: 0 })
|
||||
value: number;
|
||||
|
||||
@Column('json', { name: 'formula_render', nullable: true })
|
||||
formula_render: any;
|
||||
|
||||
@Column('varchar', { name: 'formula_string', nullable: true })
|
||||
formula_string: string;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,10 @@ import { BaseDataService } from 'src/core/modules/data/service/base-data.service
|
|||
import { TaxEntity } from '../../domain/entities/tax.entity';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { TaxModel } from '../models/tax.model';
|
||||
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||
import {
|
||||
CONNECTION_NAME,
|
||||
STATUS,
|
||||
} from 'src/core/strings/constants/base.constants';
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
|
@ -14,4 +17,21 @@ export class TaxDataService extends BaseDataService<TaxEntity> {
|
|||
) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
async taxKeyValue(): Promise<any> {
|
||||
const taxes = await this.getManyByOptions({
|
||||
where: {
|
||||
status: STATUS.ACTIVE,
|
||||
},
|
||||
});
|
||||
|
||||
const keyVal = {};
|
||||
|
||||
for (const tax of taxes) {
|
||||
const { name, value } = tax;
|
||||
keyVal[name] = value / 100;
|
||||
}
|
||||
|
||||
return keyVal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,5 @@ import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.e
|
|||
export interface TaxEntity extends BaseStatusEntity {
|
||||
name: string;
|
||||
value: number;
|
||||
formula_string?: string;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { IndexTaxManager } from './index-tax.manager';
|
||||
import { Param } from 'src/core/modules/domain/entities/base-filter.entity';
|
||||
|
||||
@Injectable()
|
||||
export class IndexTaxFormulaManager extends IndexTaxManager {
|
||||
get selects(): string[] {
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.name`,
|
||||
`${this.tableName}.value`,
|
||||
];
|
||||
}
|
||||
|
||||
get specificFilter(): Param[] {
|
||||
return [
|
||||
{
|
||||
cols: `${this.tableName}.status::text`,
|
||||
data: [`'active'`],
|
||||
isStatus: true,
|
||||
},
|
||||
...super.specificFilter,
|
||||
];
|
||||
}
|
||||
}
|
|
@ -6,11 +6,13 @@ import { PaginationResponse } from 'src/core/response/domain/ok-response.interfa
|
|||
import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator';
|
||||
import { DetailTaxManager } from './managers/detail-tax.manager';
|
||||
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
||||
import { IndexTaxFormulaManager } from './managers/index-tax-formula.manager';
|
||||
|
||||
@Injectable()
|
||||
export class TaxReadOrchestrator extends BaseReadOrchestrator<TaxEntity> {
|
||||
constructor(
|
||||
private indexManager: IndexTaxManager,
|
||||
private formulaManager: IndexTaxFormulaManager,
|
||||
private detailManager: DetailTaxManager,
|
||||
private serviceData: TaxReadService,
|
||||
) {
|
||||
|
@ -24,6 +26,13 @@ export class TaxReadOrchestrator extends BaseReadOrchestrator<TaxEntity> {
|
|||
return this.indexManager.getResult();
|
||||
}
|
||||
|
||||
async formula(params): Promise<PaginationResponse<TaxEntity>> {
|
||||
this.formulaManager.setFilterParam(params);
|
||||
this.formulaManager.setService(this.serviceData, TABLE_NAME.TAX);
|
||||
await this.formulaManager.execute();
|
||||
return this.formulaManager.getResult();
|
||||
}
|
||||
|
||||
async detail(dataId: string): Promise<TaxEntity> {
|
||||
this.detailManager.setData(dataId);
|
||||
this.detailManager.setService(this.serviceData, TABLE_NAME.TAX);
|
||||
|
|
|
@ -23,6 +23,14 @@ export class TaxReadController {
|
|||
return await this.orchestrator.index(params);
|
||||
}
|
||||
|
||||
@Get('formula')
|
||||
@Pagination()
|
||||
async formula(
|
||||
@Query() params: FilterTaxDto,
|
||||
): Promise<PaginationResponse<TaxEntity>> {
|
||||
return await this.orchestrator.formula(params);
|
||||
}
|
||||
|
||||
@Get(':id')
|
||||
async detail(@Param('id') id: string): Promise<TaxEntity> {
|
||||
return await this.orchestrator.detail(id);
|
||||
|
|
|
@ -24,6 +24,7 @@ import { BatchInactiveTaxManager } from './domain/usecases/managers/batch-inacti
|
|||
import { TaxModel } from './data/models/tax.model';
|
||||
import { SalesPriceFormulaReadService } from '../sales-price-formula/data/services/sales-price-formula-read.service';
|
||||
import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales-price-formula.model';
|
||||
import { IndexTaxFormulaManager } from './domain/usecases/managers/index-tax-formula.manager';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -37,6 +38,7 @@ import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales
|
|||
controllers: [TaxDataController, TaxReadController],
|
||||
providers: [
|
||||
IndexTaxManager,
|
||||
IndexTaxFormulaManager,
|
||||
DetailTaxManager,
|
||||
CreateTaxManager,
|
||||
DeleteTaxManager,
|
||||
|
|
|
@ -7,6 +7,7 @@ import {
|
|||
} from '../../domain/entities/transaction-item.entity';
|
||||
import { TransactionModel } from './transaction.model';
|
||||
import { RefundItemModel } from 'src/modules/transaction/refund/data/models/refund-item.model';
|
||||
import { TransactionTaxEntity } from '../../domain/entities/transaction-tax.entity';
|
||||
|
||||
@Entity(TABLE_NAME.TRANSACTION_ITEM)
|
||||
export class TransactionItemModel
|
||||
|
@ -64,6 +65,15 @@ export class TransactionItemModel
|
|||
@Column('decimal', { name: 'total_share_tenant', nullable: true })
|
||||
total_share_tenant: number;
|
||||
|
||||
@Column('decimal', { name: 'total_profit_share', nullable: true })
|
||||
total_profit_share: number;
|
||||
|
||||
@Column('decimal', { nullable: true })
|
||||
payment_total_dpp: number;
|
||||
|
||||
@Column('decimal', { nullable: true })
|
||||
payment_total_tax: number;
|
||||
|
||||
@Column('int', { name: 'qty', nullable: true })
|
||||
qty: number;
|
||||
|
||||
|
@ -103,6 +113,13 @@ export class TransactionItemModel
|
|||
},
|
||||
)
|
||||
bundling_items: TransactionItemBreakdownModel[];
|
||||
|
||||
@OneToMany(() => TransactionItemTaxModel, (model) => model.transaction, {
|
||||
cascade: true,
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
item_taxes: TransactionItemTaxModel[];
|
||||
}
|
||||
|
||||
@Entity(TABLE_NAME.TRANSACTION_ITEM_BREAKDOWN)
|
||||
|
@ -122,10 +139,83 @@ export class TransactionItemBreakdownModel extends BaseCoreModel<TransactionBund
|
|||
@Column('bigint', { nullable: true })
|
||||
item_rates: number;
|
||||
|
||||
@Column('decimal', { nullable: true })
|
||||
total_profit_share: number;
|
||||
|
||||
@Column('decimal', { nullable: true })
|
||||
total_share_tenant: number;
|
||||
|
||||
@Column('decimal', { nullable: true })
|
||||
payment_total_dpp: number;
|
||||
|
||||
@Column('decimal', { nullable: true })
|
||||
payment_total_tax: number;
|
||||
|
||||
@ManyToOne(() => TransactionItemModel, (model) => model.bundling_items, {
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'transaction_item_id' })
|
||||
transaction_item: TransactionItemModel;
|
||||
|
||||
@OneToMany(() => TransactionBreakdownTaxModel, (model) => model.transaction, {
|
||||
cascade: true,
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
item_taxes: TransactionBreakdownTaxModel[];
|
||||
}
|
||||
|
||||
@Entity(TABLE_NAME.TRANSACTION_ITEM_TAX)
|
||||
export class TransactionItemTaxModel
|
||||
extends BaseCoreModel<TransactionTaxEntity>
|
||||
implements TransactionTaxEntity
|
||||
{
|
||||
@Column('varchar', { name: 'tax_id', nullable: true })
|
||||
tax_id: string;
|
||||
|
||||
@Column('varchar', { name: 'tax_name', nullable: true })
|
||||
tax_name: string;
|
||||
|
||||
@Column('decimal', { name: 'taxt_value', nullable: true })
|
||||
taxt_value: number;
|
||||
|
||||
@Column('decimal', { name: 'tax_total_value', nullable: true })
|
||||
tax_total_value: number;
|
||||
|
||||
@Column('varchar', { name: 'transaction_id', nullable: true })
|
||||
transaction_id: string;
|
||||
@ManyToOne(() => TransactionItemModel, (model) => model.taxes, {
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'transaction_id' })
|
||||
transaction: TransactionItemModel;
|
||||
}
|
||||
|
||||
@Entity(TABLE_NAME.TRANSACTION_ITEM_BREAKDOWN_TAX)
|
||||
export class TransactionBreakdownTaxModel
|
||||
extends BaseCoreModel<TransactionTaxEntity>
|
||||
implements TransactionTaxEntity
|
||||
{
|
||||
@Column('varchar', { name: 'tax_id', nullable: true })
|
||||
tax_id: string;
|
||||
|
||||
@Column('varchar', { name: 'tax_name', nullable: true })
|
||||
tax_name: string;
|
||||
|
||||
@Column('decimal', { name: 'taxt_value', nullable: true })
|
||||
taxt_value: number;
|
||||
|
||||
@Column('decimal', { name: 'tax_total_value', nullable: true })
|
||||
tax_total_value: number;
|
||||
|
||||
@Column('varchar', { name: 'transaction_id', nullable: true })
|
||||
transaction_id: string;
|
||||
@ManyToOne(() => TransactionItemBreakdownModel, {
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'transaction_id' })
|
||||
transaction: TransactionItemBreakdownModel;
|
||||
}
|
||||
|
|
|
@ -34,4 +34,5 @@ export interface TransactionBundlingItemEntity extends BaseCoreEntity {
|
|||
hpp: number;
|
||||
base_price: number;
|
||||
item_rates: number;
|
||||
total_price?: number;
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
} from '../../constants';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { TransactionItemEntity } from './transaction-item.entity';
|
||||
import { TransactionTaxEntity } from './transaction-tax.entity';
|
||||
|
||||
export interface TransactionEntity extends BaseStatusEntity {
|
||||
// general info
|
||||
|
@ -89,4 +90,5 @@ export interface TransactionEntity extends BaseStatusEntity {
|
|||
calendar_link?: string;
|
||||
|
||||
items: TransactionItemEntity[];
|
||||
taxes?: TransactionTaxEntity[];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,272 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { SalesPriceFormulaDataService } from 'src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service';
|
||||
import { TaxDataService } from 'src/modules/transaction/tax/data/services/tax-data.service';
|
||||
import { TransactionEntity } from '../../entities/transaction.entity';
|
||||
import { calculateFormula } from 'src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper';
|
||||
import * as math from 'mathjs';
|
||||
import {
|
||||
TransactionBundlingItemEntity,
|
||||
TransactionItemEntity,
|
||||
} from '../../entities/transaction-item.entity';
|
||||
|
||||
@Injectable()
|
||||
export class PriceCalculator {
|
||||
constructor(
|
||||
private formulaService: SalesPriceFormulaDataService,
|
||||
private taxService: TaxDataService,
|
||||
) {}
|
||||
|
||||
private initialValue(formulas: any[]) {
|
||||
const tax = {};
|
||||
for (const formula of formulas) {
|
||||
tax[`${formula.name}_value`] = null;
|
||||
}
|
||||
|
||||
return tax;
|
||||
}
|
||||
|
||||
async calculate(transaction: TransactionEntity) {
|
||||
const prices = [];
|
||||
const transaction_taxes = [];
|
||||
|
||||
for (let i = 0; i < transaction.items.length; i++) {
|
||||
const item = transaction.items[i];
|
||||
const price = await this.calculateItem(item);
|
||||
const priceValues = this.calculatePrice(price);
|
||||
prices.push(priceValues);
|
||||
transaction_taxes.push(price.taxesValue);
|
||||
|
||||
if (item.bundling_items) {
|
||||
for (let b = 0; b < item.bundling_items.length; b++) {
|
||||
const bundling = item.bundling_items[b];
|
||||
bundling.total_price = bundling.item_rates ?? bundling.base_price;
|
||||
|
||||
const bundlingPrice = await this.calculateItem(bundling);
|
||||
const bundlingValues = this.calculatePrice(bundlingPrice);
|
||||
|
||||
const item_taxes = { item_taxes: bundlingPrice.taxesValue };
|
||||
Object.assign(bundling, bundlingValues, item_taxes);
|
||||
item.bundling_items[b] = bundling;
|
||||
}
|
||||
}
|
||||
|
||||
const item_taxes = { item_taxes: price.taxesValue };
|
||||
Object.assign(item, priceValues, item_taxes);
|
||||
transaction.items[i] = item;
|
||||
}
|
||||
|
||||
const tatal_taxes = this.mergeAndSumArrays(transaction_taxes);
|
||||
transaction.taxes = tatal_taxes;
|
||||
const { payment_total_dpp, ...otherValue } =
|
||||
this.sumArrayBasedOnObjectKey(prices);
|
||||
|
||||
return {
|
||||
dpp_value: payment_total_dpp,
|
||||
other: otherValue,
|
||||
tax_datas: tatal_taxes,
|
||||
};
|
||||
}
|
||||
|
||||
async calculateItem(
|
||||
transaction: TransactionItemEntity | TransactionBundlingItemEntity,
|
||||
) {
|
||||
// const itemShare = 20 / 100;
|
||||
// const profitShare = 10 / 100;
|
||||
|
||||
const { profitShare, tenantShare } = await this.formulaService.itemTax(
|
||||
transaction.item_id,
|
||||
);
|
||||
|
||||
const tax = await this.taxService.taxKeyValue();
|
||||
/* Update constant from
|
||||
- profit_share -> tenant_share
|
||||
- item_share -> profit_share
|
||||
*/
|
||||
const taxes = {
|
||||
...tax,
|
||||
tenant_share: tenantShare,
|
||||
profit_share: profitShare,
|
||||
};
|
||||
const dpp = await this.formulaService.salesPriceFormula();
|
||||
const taxFormula = await this.taxService.getManyByOptions({});
|
||||
const shareFormulas = await this.formulaService.profitShareFormula();
|
||||
const taxShareFormulas = shareFormulas.map((formula) => {
|
||||
return {
|
||||
...formula,
|
||||
name: formula.value_for,
|
||||
};
|
||||
});
|
||||
const formulas = [...taxFormula, ...taxShareFormulas];
|
||||
const values = {
|
||||
total: transaction.total_price,
|
||||
...this.initialValue(formulas),
|
||||
...taxes,
|
||||
};
|
||||
|
||||
// const dpp = formulas.find((formula) => formula.value_for == 'dpp');
|
||||
|
||||
const dppValue = calculateFormula(
|
||||
dpp.formula_string,
|
||||
taxes,
|
||||
transaction.total_price,
|
||||
);
|
||||
|
||||
values['dpp'] = dppValue;
|
||||
values['dpp_value'] = dppValue;
|
||||
|
||||
let calledVariable = [];
|
||||
const taxesValue = [];
|
||||
do {
|
||||
const valueFor = this.withNullValue(values, calledVariable);
|
||||
|
||||
const formula = formulas.find((formula) => {
|
||||
const name = formula['name'] ?? formula['value_for'];
|
||||
return `${name}_value` == valueFor;
|
||||
});
|
||||
let result = null;
|
||||
|
||||
try {
|
||||
result = math.evaluate(formula.formula_string, values);
|
||||
calledVariable = [];
|
||||
} catch (error) {
|
||||
calledVariable.push(valueFor);
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
values[valueFor] = result;
|
||||
if (result != null) {
|
||||
const tax = taxFormula.find(({ id }) => id == formula.id);
|
||||
if (tax != null) {
|
||||
taxesValue.push({
|
||||
tax_id: tax?.id,
|
||||
tax_name: tax?.name,
|
||||
taxt_value: result,
|
||||
tax_total_value: result,
|
||||
transaction_id: transaction.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
// values[`${valueFor}_value`] = result;
|
||||
} while (this.containsNullValue(values));
|
||||
|
||||
// const itemShareFormula = shareFormulas.find(
|
||||
// (f) => f.value_for == 'tenant_share',
|
||||
// );
|
||||
// values['tenant_share_value'] = math.evaluate(
|
||||
// itemShareFormula.formula_string,
|
||||
// values,
|
||||
// );
|
||||
|
||||
// const profitShareFormula = shareFormulas.find(
|
||||
// (f) => f.value_for == 'profit_share',
|
||||
// );
|
||||
// values['profit_share_value'] = math.evaluate(
|
||||
// profitShareFormula.formula_string,
|
||||
// values,
|
||||
// );
|
||||
|
||||
return { dpp_value: dppValue, tax_datas: values, taxesValue };
|
||||
}
|
||||
|
||||
calculatePrice(prices: any) {
|
||||
const data = prices.tax_datas;
|
||||
const filteredObject: any = Object.keys(data)
|
||||
.filter((key) => key.endsWith('_value'))
|
||||
.reduce((acc, key) => ({ ...acc, [key]: data[key] }), {});
|
||||
|
||||
const { dpp_value, tenant_share_value, profit_share_value, ...tax } =
|
||||
filteredObject;
|
||||
const taxes = this.sumPriceObject(tax);
|
||||
return {
|
||||
total_profit_share: profit_share_value,
|
||||
total_share_tenant: tenant_share_value,
|
||||
payment_total_tax: taxes,
|
||||
payment_total_dpp: dpp_value,
|
||||
tax,
|
||||
};
|
||||
}
|
||||
|
||||
sumArrayBasedOnObjectKey(arr) {
|
||||
return arr.reduce((acc, cur) => {
|
||||
Object.keys(cur).forEach((key) => {
|
||||
if (!acc[key]) {
|
||||
acc[key] = 0;
|
||||
}
|
||||
acc[key] += cur[key];
|
||||
});
|
||||
return acc;
|
||||
}, {});
|
||||
}
|
||||
|
||||
sumPriceObject(taxes: any): number {
|
||||
let total = 0;
|
||||
for (const tax in taxes) {
|
||||
total += taxes[tax] ?? 0;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
containsNullValue(obj) {
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return obj === null; // Return true if the value itself is null
|
||||
}
|
||||
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (this.containsNullValue(obj[key])) {
|
||||
return true; // If any nested value is null, return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false; // If no null values are found, return false
|
||||
}
|
||||
|
||||
mergeAndSumArrays(arrays): any {
|
||||
const mergedData = {};
|
||||
|
||||
arrays.forEach((arr) => {
|
||||
arr.forEach((item) => {
|
||||
if (!mergedData[item.tax_id]) {
|
||||
mergedData[item.tax_id] = {
|
||||
tax_name: item.tax_name,
|
||||
taxt_value: 0,
|
||||
tax_total_value: 0,
|
||||
transaction_id: item.transaction_id,
|
||||
};
|
||||
}
|
||||
|
||||
mergedData[item.tax_id].taxt_value += item.taxt_value;
|
||||
mergedData[item.tax_id].tax_total_value += item.tax_total_value;
|
||||
});
|
||||
});
|
||||
|
||||
return Object.values(mergedData);
|
||||
}
|
||||
|
||||
withNullValue(mainObj, called = []) {
|
||||
const obj = { ...mainObj };
|
||||
for (const variable of called) {
|
||||
delete obj[variable];
|
||||
}
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return null; // Return null for non-object values
|
||||
}
|
||||
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
if (obj[key] === null) {
|
||||
return key; // Found a null value, return the key
|
||||
} else if (typeof obj[key] === 'object') {
|
||||
const nestedKey = this.withNullValue(obj[key]);
|
||||
if (nestedKey !== null) {
|
||||
return `${key}.${nestedKey}`; // Found null in nested object, return nested key path
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null; // No null values found, return null
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import { TransactionModel } from '../../../data/models/transaction.model';
|
|||
import { calculateSalesFormula } from 'src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper';
|
||||
import { CreateEventCalendarHelper } from 'src/modules/configuration/google-calendar/domain/usecases/managers/helpers/create-event-calanedar.helper';
|
||||
import { TransactionUpdatedEvent } from '../../entities/event/transaction-updated.event';
|
||||
import { PriceCalculator } from '../calculator/price.calculator';
|
||||
|
||||
@EventsHandler(TransactionChangeStatusEvent, TransactionUpdatedEvent)
|
||||
export class SettledTransactionHandler
|
||||
|
@ -18,6 +19,7 @@ export class SettledTransactionHandler
|
|||
private formulaService: SalesPriceFormulaDataService,
|
||||
private taxService: TaxDataService,
|
||||
private dataService: TransactionDataService,
|
||||
private calculator: PriceCalculator,
|
||||
) {}
|
||||
|
||||
async handle(event: TransactionChangeStatusEvent) {
|
||||
|
@ -62,12 +64,16 @@ export class SettledTransactionHandler
|
|||
});
|
||||
|
||||
// const profit_share_value = this.calculateFormula(profit_formula.formula_string, taxes, data.payment_total_net_profit ?? 0);
|
||||
const { dpp_value, tax_datas } = calculateSalesFormula(
|
||||
sales_price.formula_string,
|
||||
taxes,
|
||||
data.payment_total_net_profit ?? 0,
|
||||
const { dpp_value, tax_datas, other } = await this.calculator.calculate(
|
||||
data,
|
||||
);
|
||||
|
||||
// calculateSalesFormula(
|
||||
// sales_price.formula_string,
|
||||
// taxes,
|
||||
// data.payment_total_net_profit ?? 0,
|
||||
// );
|
||||
|
||||
// console.log(data, 'dsa');
|
||||
const google_calendar = await CreateEventCalendarHelper(data);
|
||||
|
||||
|
@ -78,6 +84,8 @@ export class SettledTransactionHandler
|
|||
taxes: tax_datas,
|
||||
calendar_id: google_calendar?.id,
|
||||
calendar_link: google_calendar?.htmlLink,
|
||||
payment_total_share: other.total_profit_share,
|
||||
payment_total_tax: other.payment_total_tax,
|
||||
});
|
||||
} else if (oldSettled) {
|
||||
// console.log(data, 'data oldSettled');
|
||||
|
|
|
@ -6,6 +6,7 @@ import { PaginationResponse } from 'src/core/response/domain/ok-response.interfa
|
|||
import { BaseReadOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-read.orchestrator';
|
||||
import { DetailTransactionManager } from './managers/detail-transaction.manager';
|
||||
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
||||
import { PriceCalculator } from './calculator/price.calculator';
|
||||
|
||||
@Injectable()
|
||||
export class TransactionReadOrchestrator extends BaseReadOrchestrator<TransactionEntity> {
|
||||
|
@ -13,6 +14,7 @@ export class TransactionReadOrchestrator extends BaseReadOrchestrator<Transactio
|
|||
private indexManager: IndexTransactionManager,
|
||||
private detailManager: DetailTransactionManager,
|
||||
private serviceData: TransactionReadService,
|
||||
private calculator: PriceCalculator,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
@ -30,4 +32,53 @@ export class TransactionReadOrchestrator extends BaseReadOrchestrator<Transactio
|
|||
await this.detailManager.execute();
|
||||
return this.detailManager.getResult();
|
||||
}
|
||||
|
||||
async dummyCalculate(id: string): Promise<void> {
|
||||
const transaction = await this.serviceData.getOneByOptions({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
relations: ['items', 'items.bundling_items'],
|
||||
});
|
||||
|
||||
const price = await this.calculator.calculate(transaction);
|
||||
transaction.payment_total_profit =
|
||||
transaction.payment_total -
|
||||
price.other.total_profit_share -
|
||||
price.other.payment_total_tax -
|
||||
price.other.total_share_tenant;
|
||||
transaction.payment_total_dpp = price.dpp_value;
|
||||
transaction.payment_total_share = price.other.total_profit_share;
|
||||
transaction.payment_total_tax = price.other.payment_total_tax;
|
||||
console.log({ price }, transaction.payment_total);
|
||||
}
|
||||
|
||||
async calculatePrice(): Promise<void> {
|
||||
const transactions = await this.serviceData.getManyByOptions({
|
||||
where: {
|
||||
is_recap_transaction: false,
|
||||
},
|
||||
relations: ['items', 'items.bundling_items'],
|
||||
});
|
||||
|
||||
for (const transaction of transactions) {
|
||||
try {
|
||||
const price = await this.calculator.calculate(transaction);
|
||||
transaction.payment_total_profit =
|
||||
transaction.payment_total -
|
||||
price.other.total_profit_share -
|
||||
price.other.payment_total_tax -
|
||||
price.other.total_share_tenant;
|
||||
transaction.payment_total_dpp = price.dpp_value;
|
||||
transaction.payment_total_share = price.other.total_profit_share;
|
||||
transaction.payment_total_tax = price.other.payment_total_tax;
|
||||
console.log(transaction.id);
|
||||
await this.serviceData.getRepository().save(transaction);
|
||||
|
||||
// break;
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,4 +27,18 @@ export class TransactionReadController {
|
|||
async detail(@Param('id') id: string): Promise<TransactionEntity> {
|
||||
return await this.orchestrator.detail(id);
|
||||
}
|
||||
|
||||
@Public(true)
|
||||
@Get('dummy/:id')
|
||||
async calculate(@Param('id') id: string): Promise<string> {
|
||||
this.orchestrator.dummyCalculate(id);
|
||||
return 'OK';
|
||||
}
|
||||
|
||||
@Public(true)
|
||||
@Get('dummy2/calculate')
|
||||
async calculateAll(): Promise<string> {
|
||||
this.orchestrator.calculatePrice();
|
||||
return 'OK';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@ import { BatchDeleteTransactionManager } from './domain/usecases/managers/batch-
|
|||
import { BatchConfirmTransactionManager } from './domain/usecases/managers/batch-confirm-transaction.manager';
|
||||
import { TransactionModel } from './data/models/transaction.model';
|
||||
import {
|
||||
TransactionBreakdownTaxModel,
|
||||
TransactionItemBreakdownModel,
|
||||
TransactionItemModel,
|
||||
TransactionItemTaxModel,
|
||||
} from './data/models/transaction-item.model';
|
||||
import { TransactionTaxModel } from './data/models/transaction-tax.model';
|
||||
import { CancelTransactionManager } from './domain/usecases/managers/cancel-transaction.manager';
|
||||
|
@ -39,6 +41,8 @@ import { PdfMakeManager } from 'src/modules/configuration/export/domain/managers
|
|||
import { PaymentMethodDataService } from '../payment-method/data/services/payment-method-data.service';
|
||||
import { PaymentMethodModel } from '../payment-method/data/models/payment-method.model';
|
||||
import { TransactionDemographyModel } from './data/models/transaction-demography.model';
|
||||
import { PriceCalculator } from './domain/usecases/calculator/price.calculator';
|
||||
import { ItemModel } from 'src/modules/item-related/item/data/models/item.model';
|
||||
|
||||
@Module({
|
||||
exports: [TransactionReadService],
|
||||
|
@ -51,9 +55,12 @@ import { TransactionDemographyModel } from './data/models/transaction-demography
|
|||
TransactionDemographyModel,
|
||||
TransactionItemBreakdownModel,
|
||||
TransactionTaxModel,
|
||||
TransactionItemTaxModel,
|
||||
TransactionBreakdownTaxModel,
|
||||
TaxModel,
|
||||
SalesPriceFormulaModel,
|
||||
PaymentMethodModel,
|
||||
ItemModel,
|
||||
],
|
||||
CONNECTION_NAME.DEFAULT,
|
||||
),
|
||||
|
@ -61,6 +68,7 @@ import { TransactionDemographyModel } from './data/models/transaction-demography
|
|||
],
|
||||
controllers: [TransactionDataController, TransactionReadController],
|
||||
providers: [
|
||||
PriceCalculator,
|
||||
RefundUpdatedHandler,
|
||||
PosTransactionHandler,
|
||||
MidtransCallbackHandler,
|
||||
|
|
Loading…
Reference in New Issue