diff --git a/src/app.module.ts b/src/app.module.ts index c346f5a..e552023 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -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, 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 89a45ae..8e23965 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 @@ -1,4 +1,4 @@ -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'; @@ -7,6 +7,7 @@ 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 { @@ -15,6 +16,8 @@ export class SalesPriceFormulaDataService extends BaseDataService, @InjectRepository(TaxModel, CONNECTION_NAME.DEFAULT) private tax: Repository, + @InjectRepository(ItemModel, CONNECTION_NAME.DEFAULT) + private item: Repository, ) { super(repo); } @@ -27,6 +30,20 @@ export class SalesPriceFormulaDataService extends BaseDataService 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; } } diff --git a/src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper.ts b/src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper.ts index ac068b4..aff347c 100644 --- a/src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper.ts +++ b/src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper.ts @@ -30,7 +30,15 @@ export function calculateFormula( return value; } catch (e) { apm.captureError(e); - throw new BadRequestException('Wrong value'); + throw new BadRequestException({ + message: 'Wrong value', + meta: { + formula, + variable, + total, + solveFor, + }, + }); } } diff --git a/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts b/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts index accd83c..02be135 100644 --- a/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts +++ b/src/modules/transaction/sales-price-formula/sales-price-formula.module.ts @@ -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, 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 01b5019..e472982 100644 --- a/src/modules/transaction/transaction/data/models/transaction-item.model.ts +++ b/src/modules/transaction/transaction/data/models/transaction-item.model.ts @@ -113,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) @@ -150,6 +157,13 @@ export class TransactionItemBreakdownModel extends BaseCoreModel TransactionBreakdownTaxModel, (model) => model.transaction, { + cascade: true, + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + }) + item_taxes: TransactionBreakdownTaxModel[]; } @Entity(TABLE_NAME.TRANSACTION_ITEM_TAX) diff --git a/src/modules/transaction/transaction/domain/entities/transaction.entity.ts b/src/modules/transaction/transaction/domain/entities/transaction.entity.ts index 0f32849..8aa0b55 100644 --- a/src/modules/transaction/transaction/domain/entities/transaction.entity.ts +++ b/src/modules/transaction/transaction/domain/entities/transaction.entity.ts @@ -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[]; } diff --git a/src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts b/src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts index 2c109d2..88d8619 100644 --- a/src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts +++ b/src/modules/transaction/transaction/domain/usecases/calculator/price.calculator.ts @@ -27,18 +27,14 @@ export class PriceCalculator { async calculate(transaction: TransactionEntity) { const prices = []; - // for (const item of transaction.items) { - // const price = await this.calculateItem(item); - // const priceValues = this.calculatePrice(price); - - // prices.push(priceValues); - // } + 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++) { @@ -48,40 +44,66 @@ export class PriceCalculator { const bundlingPrice = await this.calculateItem(bundling); const bundlingValues = this.calculatePrice(bundlingPrice); - Object.assign(bundling, bundlingValues); + const item_taxes = { item_taxes: bundlingPrice.taxesValue }; + Object.assign(bundling, bundlingValues, item_taxes); item.bundling_items[b] = bundling; } } - Object.assign(item, priceValues); + 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: [] }; + 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 itemShare = 20 / 100; + // const profitShare = 10 / 100; + + const { profitShare, tenantShare } = await this.formulaService.itemTax( + transaction.item_id, + ); const tax = await this.taxService.taxKeyValue(); - const taxes = { ...tax, item_share: itemShare, profit_share: profitShare }; - const formulas = await this.formulaService.salesPriceFormula(); + /* 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(taxFormula), + ...this.initialValue(formulas), ...taxes, }; // const dpp = formulas.find((formula) => formula.value_for == 'dpp'); - const dpp = formulas; const dppValue = calculateFormula( dpp.formula_string, @@ -93,11 +115,14 @@ export class PriceCalculator { values['dpp_value'] = dppValue; let calledVariable = []; + const taxesValue = []; do { const valueFor = this.withNullValue(values, calledVariable); - const formula = taxFormula.find( - (formula) => `${formula.name}_value` == valueFor, - ); + + const formula = formulas.find((formula) => { + const name = formula['name'] ?? formula['value_for']; + return `${name}_value` == valueFor; + }); let result = null; try { @@ -109,26 +134,38 @@ export class PriceCalculator { } 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 == 'item_share', - ); - values['item_share_value'] = math.evaluate( - itemShareFormula.formula_string, - 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, - ); + // 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 }; + return { dpp_value: dppValue, tax_datas: values, taxesValue }; } calculatePrice(prices: any) { @@ -137,14 +174,15 @@ export class PriceCalculator { .filter((key) => key.endsWith('_value')) .reduce((acc, key) => ({ ...acc, [key]: data[key] }), {}); - const { dpp_value, item_share_value, profit_share_value, ...tax } = + const { dpp_value, tenant_share_value, profit_share_value, ...tax } = filteredObject; const taxes = this.sumPriceObject(tax); return { - total_profit_share: item_share_value, - total_share_tenant: profit_share_value, + total_profit_share: profit_share_value, + total_share_tenant: tenant_share_value, payment_total_tax: taxes, payment_total_dpp: dpp_value, + tax, }; } @@ -185,6 +223,28 @@ export class PriceCalculator { 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) { 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 d6b8bc7..9217f1b 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 @@ -64,7 +64,9 @@ 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 } = await this.calculator.calculate(data); + const { dpp_value, tax_datas, other } = await this.calculator.calculate( + data, + ); // calculateSalesFormula( // sales_price.formula_string, @@ -82,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'); diff --git a/src/modules/transaction/transaction/transaction.module.ts b/src/modules/transaction/transaction/transaction.module.ts index 82a2180..e08f2db 100644 --- a/src/modules/transaction/transaction/transaction.module.ts +++ b/src/modules/transaction/transaction/transaction.module.ts @@ -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'; @@ -40,6 +42,7 @@ import { PaymentMethodDataService } from '../payment-method/data/services/paymen 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], @@ -52,9 +55,12 @@ import { PriceCalculator } from './domain/usecases/calculator/price.calculator'; TransactionDemographyModel, TransactionItemBreakdownModel, TransactionTaxModel, + TransactionItemTaxModel, + TransactionBreakdownTaxModel, TaxModel, SalesPriceFormulaModel, PaymentMethodModel, + ItemModel, ], CONNECTION_NAME.DEFAULT, ),