wip: calculate share item
parent
a6b1e5f49f
commit
e1f2cdfa4d
|
@ -0,0 +1,49 @@
|
||||||
|
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 "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<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 "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"`);
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ export enum TABLE_NAME {
|
||||||
TRANSACTION_ITEM = 'transaction_items',
|
TRANSACTION_ITEM = 'transaction_items',
|
||||||
TRANSACTION_ITEM_BREAKDOWN = 'transaction_item_breakdowns',
|
TRANSACTION_ITEM_BREAKDOWN = 'transaction_item_breakdowns',
|
||||||
TRANSACTION_TAX = 'transaction_taxes',
|
TRANSACTION_TAX = 'transaction_taxes',
|
||||||
|
TRANSACTION_ITEM_TAX = 'transaction_item_taxes',
|
||||||
|
TRANSACTION_ITEM_BREAKDOWN_TAX = 't_breakdown_item_taxes',
|
||||||
TRANSACTION_DEMOGRAPHY = 'transaction_demographies',
|
TRANSACTION_DEMOGRAPHY = 'transaction_demographies',
|
||||||
USER = 'users',
|
USER = 'users',
|
||||||
USER_PRIVILEGE = 'user_privileges',
|
USER_PRIVILEGE = 'user_privileges',
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { SalesPriceFormulaModel } from '../models/sales-price-formula.model';
|
import { SalesPriceFormulaModel } from '../models/sales-price-formula.model';
|
||||||
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
|
||||||
import { Repository } from 'typeorm';
|
import { Repository } from 'typeorm';
|
||||||
|
import { FormulaType } from '../../constants';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SalesPriceFormulaDataService extends BaseDataService<SalesPriceFormulaEntity> {
|
export class SalesPriceFormulaDataService extends BaseDataService<SalesPriceFormulaEntity> {
|
||||||
|
@ -14,4 +15,12 @@ export class SalesPriceFormulaDataService extends BaseDataService<SalesPriceForm
|
||||||
) {
|
) {
|
||||||
super(repo);
|
super(repo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
profitShareFormula() {
|
||||||
|
return this.repo.find({
|
||||||
|
where: {
|
||||||
|
type: FormulaType.PROFIT_SHARE,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,37 @@
|
||||||
import * as math from 'mathjs';
|
import * as math from 'mathjs';
|
||||||
import { Equation, parse } from 'algebra.js';
|
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,
|
||||||
|
) {
|
||||||
|
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('dpp').toString();
|
||||||
|
console.log(result, 'formula');
|
||||||
|
|
||||||
|
const value = math.evaluate(result);
|
||||||
|
console.log(value, 'value');
|
||||||
|
|
||||||
|
return value;
|
||||||
|
} catch (e) {
|
||||||
|
apm.captureError(e);
|
||||||
|
throw new BadRequestException('Wrong value');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function calculateSalesFormula(
|
export function calculateSalesFormula(
|
||||||
formula: string,
|
formula: string,
|
||||||
|
|
|
@ -3,7 +3,10 @@ import { BaseDataService } from 'src/core/modules/data/service/base-data.service
|
||||||
import { TaxEntity } from '../../domain/entities/tax.entity';
|
import { TaxEntity } from '../../domain/entities/tax.entity';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { TaxModel } from '../models/tax.model';
|
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';
|
import { Repository } from 'typeorm';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -14,4 +17,21 @@ export class TaxDataService extends BaseDataService<TaxEntity> {
|
||||||
) {
|
) {
|
||||||
super(repo);
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
return keyVal;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
} from '../../domain/entities/transaction-item.entity';
|
} from '../../domain/entities/transaction-item.entity';
|
||||||
import { TransactionModel } from './transaction.model';
|
import { TransactionModel } from './transaction.model';
|
||||||
import { RefundItemModel } from 'src/modules/transaction/refund/data/models/refund-item.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)
|
@Entity(TABLE_NAME.TRANSACTION_ITEM)
|
||||||
export class TransactionItemModel
|
export class TransactionItemModel
|
||||||
|
@ -64,6 +65,9 @@ export class TransactionItemModel
|
||||||
@Column('decimal', { name: 'total_share_tenant', nullable: true })
|
@Column('decimal', { name: 'total_share_tenant', nullable: true })
|
||||||
total_share_tenant: number;
|
total_share_tenant: number;
|
||||||
|
|
||||||
|
@Column('decimal', { name: 'total_profit_share', nullable: true })
|
||||||
|
total_profit_share: number;
|
||||||
|
|
||||||
@Column('int', { name: 'qty', nullable: true })
|
@Column('int', { name: 'qty', nullable: true })
|
||||||
qty: number;
|
qty: number;
|
||||||
|
|
||||||
|
@ -122,6 +126,9 @@ export class TransactionItemBreakdownModel extends BaseCoreModel<TransactionBund
|
||||||
@Column('bigint', { nullable: true })
|
@Column('bigint', { nullable: true })
|
||||||
item_rates: number;
|
item_rates: number;
|
||||||
|
|
||||||
|
@Column('decimal', { nullable: true })
|
||||||
|
total_profit_share: number;
|
||||||
|
|
||||||
@ManyToOne(() => TransactionItemModel, (model) => model.bundling_items, {
|
@ManyToOne(() => TransactionItemModel, (model) => model.bundling_items, {
|
||||||
onDelete: 'CASCADE',
|
onDelete: 'CASCADE',
|
||||||
onUpdate: 'CASCADE',
|
onUpdate: 'CASCADE',
|
||||||
|
@ -129,3 +136,57 @@ export class TransactionItemBreakdownModel extends BaseCoreModel<TransactionBund
|
||||||
@JoinColumn({ name: 'transaction_item_id' })
|
@JoinColumn({ name: 'transaction_item_id' })
|
||||||
transaction_item: TransactionItemModel;
|
transaction_item: TransactionItemModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 { 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 { CreateEventCalendarHelper } from 'src/modules/configuration/google-calendar/domain/usecases/managers/helpers/create-event-calanedar.helper';
|
||||||
import { TransactionUpdatedEvent } from '../../entities/event/transaction-updated.event';
|
import { TransactionUpdatedEvent } from '../../entities/event/transaction-updated.event';
|
||||||
|
import { PriceCalculator } from '../calculator/price.calculator';
|
||||||
|
|
||||||
@EventsHandler(TransactionChangeStatusEvent, TransactionUpdatedEvent)
|
@EventsHandler(TransactionChangeStatusEvent, TransactionUpdatedEvent)
|
||||||
export class SettledTransactionHandler
|
export class SettledTransactionHandler
|
||||||
|
@ -18,6 +19,7 @@ export class SettledTransactionHandler
|
||||||
private formulaService: SalesPriceFormulaDataService,
|
private formulaService: SalesPriceFormulaDataService,
|
||||||
private taxService: TaxDataService,
|
private taxService: TaxDataService,
|
||||||
private dataService: TransactionDataService,
|
private dataService: TransactionDataService,
|
||||||
|
private calculator: PriceCalculator,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async handle(event: TransactionChangeStatusEvent) {
|
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 profit_share_value = this.calculateFormula(profit_formula.formula_string, taxes, data.payment_total_net_profit ?? 0);
|
||||||
const { dpp_value, tax_datas } = calculateSalesFormula(
|
const { dpp_value, tax_datas } = await this.calculator.calculate(data);
|
||||||
sales_price.formula_string,
|
|
||||||
taxes,
|
// calculateSalesFormula(
|
||||||
data.payment_total_net_profit ?? 0,
|
// sales_price.formula_string,
|
||||||
);
|
// taxes,
|
||||||
|
// data.payment_total_net_profit ?? 0,
|
||||||
|
// );
|
||||||
|
|
||||||
// console.log(data, 'dsa');
|
// console.log(data, 'dsa');
|
||||||
const google_calendar = await CreateEventCalendarHelper(data);
|
const google_calendar = await CreateEventCalendarHelper(data);
|
||||||
|
|
Loading…
Reference in New Issue