From e1f2cdfa4d17f38b6a78155c5b1982879cceac82 Mon Sep 17 00:00:00 2001 From: shancheas Date: Wed, 11 Sep 2024 16:12:22 +0700 Subject: [PATCH] wip: calculate share item --- 1726045820711-add-tax-item-transaction.ts | 49 +++++++++ src/core/strings/constants/table.constants.ts | 2 + .../sales-price-formula-data.service.ts | 9 ++ .../helpers/calculation-formula.helper.ts | 33 +++++- .../tax/data/services/tax-data.service.ts | 22 +++- .../data/models/transaction-item.model.ts | 61 +++++++++++ .../usecases/calculator/price.calculator.ts | 100 ++++++++++++++++++ .../handlers/settled-transaction.handler.ts | 14 ++- 8 files changed, 283 insertions(+), 7 deletions(-) create mode 100644 1726045820711-add-tax-item-transaction.ts create mode 100644 src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts diff --git a/1726045820711-add-tax-item-transaction.ts b/1726045820711-add-tax-item-transaction.ts new file mode 100644 index 0000000..93634fa --- /dev/null +++ b/1726045820711-add-tax-item-transaction.ts @@ -0,0 +1,49 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTaxItemTransaction1726045820711 implements MigrationInterface { + name = 'AddTaxItemTransaction1726045820711'; + + public async up(queryRunner: QueryRunner): Promise { + 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 "price_formulas" ADD "value_for" character varying NOT NULL DEFAULT 'dpp'`, + ); + 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`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + 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 "price_formulas" DROP COLUMN "value_for"`, + ); + 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"`); + } +} diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 9fa6af6..3594e97 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -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_PRIVILEGE = 'user_privileges', diff --git a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts index 5ed3752..5d71513 100644 --- a/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts +++ b/src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service.ts @@ -5,6 +5,7 @@ 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'; @Injectable() export class SalesPriceFormulaDataService extends BaseDataService { @@ -14,4 +15,12 @@ export class SalesPriceFormulaDataService extends BaseDataService { ) { super(repo); } + + async taxKeyValue(): Promise { + const taxes = await this.getManyByOptions({ + where: { + status: STATUS.ACTIVE, + }, + }); + + const keyVal = {}; + + for (const tax of taxes) { + const { name, value } = tax; + keyVal[name] = value; + } + + return keyVal; + } } diff --git a/src/modules/transaction/transaction/data/models/transaction-item.model.ts b/src/modules/transaction/transaction/data/models/transaction-item.model.ts index 01eab5a..7164925 100644 --- a/src/modules/transaction/transaction/data/models/transaction-item.model.ts +++ b/src/modules/transaction/transaction/data/models/transaction-item.model.ts @@ -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,9 @@ 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('int', { name: 'qty', nullable: true }) qty: number; @@ -122,6 +126,9 @@ export class TransactionItemBreakdownModel extends BaseCoreModel TransactionItemModel, (model) => model.bundling_items, { onDelete: 'CASCADE', onUpdate: 'CASCADE', @@ -129,3 +136,57 @@ export class TransactionItemBreakdownModel extends BaseCoreModel + 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 + 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; +} diff --git a/src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts b/src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts new file mode 100644 index 0000000..527a771 --- /dev/null +++ b/src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts @@ -0,0 +1,100 @@ +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'; + +@Injectable() +export class PriceCalculator { + constructor( + private formulaService: SalesPriceFormulaDataService, + private taxService: TaxDataService, + ) {} + + private initialValue(tax: any) { + if (typeof tax !== 'object' || tax === null) { + return null; // Return null for non-object values + } + + for (const key in tax) { + if (tax.hasOwnProperty(key)) { + tax[key] = null; + } + } + + tax['dpp'] = null; + + return tax; + } + + async calculate(transaction: TransactionEntity) { + const taxes = await this.taxService.taxKeyValue(); + const formulas = await this.formulaService.profitShareFormula(); + const values = this.initialValue(taxes); + + const dpp = formulas.find((formula) => formula.value_for == 'dpp'); + const dppValue = calculateFormula( + dpp.formula_string, + taxes, + transaction.payment_total_net_profit, + ); + + values['dpp'] = dppValue; + + do { + const valueFor = this.withNullValue(values); + const formula = formulas[valueFor]; + let result; + + try { + result = calculateFormula( + formula.formula_string, + values, + transaction.payment_total_net_profit, + ); + } catch (error) {} + + values[valueFor] = result; + values[`${valueFor}_value`] = result; + } while (this.containsNullValue(values)); + + return { dpp_value: dppValue, tax_datas: values }; + } + + 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 + } + + withNullValue(obj) { + 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 + } +} diff --git a/src/modules/transaction/transaction/domain/usecases/handlers/settled-transaction.handler.ts b/src/modules/transaction/transaction/domain/usecases/handlers/settled-transaction.handler.ts index 0bf4e4d..d6b8bc7 100644 --- a/src/modules/transaction/transaction/domain/usecases/handlers/settled-transaction.handler.ts +++ b/src/modules/transaction/transaction/domain/usecases/handlers/settled-transaction.handler.ts @@ -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,11 +64,13 @@ 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 } = 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);