Compare commits
4 Commits
developmen
...
fix/report
Author | SHA1 | Date |
---|---|---|
|
0d75cbb5a9 | |
|
d9b0b4ec85 | |
|
19494b3328 | |
|
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_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',
|
||||
|
|
|
@ -127,6 +127,13 @@ export default <ReportConfigEntity>{
|
|||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item__total_price',
|
||||
query: 'tr_item.total_price',
|
||||
label: 'Total Penjualan',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item_bundling__payment_total_dpp',
|
||||
query: 'tr_item_bundling.payment_total_dpp',
|
||||
|
@ -142,9 +149,16 @@ export default <ReportConfigEntity>{
|
|||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item__total_price',
|
||||
query: 'tr_item.total_price',
|
||||
label: 'Total Penjualan',
|
||||
column: 'tr_item_bundling__total_profit_share',
|
||||
query: 'tr_item_bundling.total_profit_share',
|
||||
label: 'Profit Share',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item_bundling__total_share_tenant',
|
||||
query: 'tr_item_bundling.total_share_tenant',
|
||||
label: 'Tenant Share',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
|
|
|
@ -112,6 +112,13 @@ export default <ReportConfigEntity>{
|
|||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item__total_price',
|
||||
query: 'tr_item.total_price',
|
||||
label: 'Total Penjualan',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item__payment_total_dpp',
|
||||
query: 'tr_item.payment_total_dpp',
|
||||
|
@ -127,9 +134,16 @@ export default <ReportConfigEntity>{
|
|||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item__total_price',
|
||||
query: 'tr_item.total_price',
|
||||
label: 'Total Penjualan',
|
||||
column: 'tr_item__total_profit_share',
|
||||
query: 'tr_item.total_profit_share',
|
||||
label: 'Profit Share',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'tr_item__total_share_tenant',
|
||||
query: 'tr_item.total_share_tenant',
|
||||
label: 'Tenant Share',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
|
|
|
@ -102,16 +102,9 @@ export default <ReportConfigEntity>{
|
|||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'main__payment_total_dpp',
|
||||
query: 'main.payment_total_dpp',
|
||||
label: 'DPP',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'main__payment_total_tax',
|
||||
query: 'main.payment_total_tax',
|
||||
label: 'Total Pajak',
|
||||
column: 'main__payment_sub_total',
|
||||
query: 'main.payment_sub_total',
|
||||
label: 'Subtotal',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
|
@ -136,6 +129,27 @@ export default <ReportConfigEntity>{
|
|||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'main__payment_total_dpp',
|
||||
query: 'main.payment_total_dpp',
|
||||
label: 'DPP',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'main__payment_total_tax',
|
||||
query: 'main.payment_total_tax',
|
||||
label: 'Total Pajak',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'main__payment_total_share',
|
||||
query: 'main.payment_total_share',
|
||||
label: 'Profit Share',
|
||||
type: DATA_TYPE.MEASURE,
|
||||
format: DATA_FORMAT.CURRENCY,
|
||||
},
|
||||
{
|
||||
column: 'refund__refund_date',
|
||||
query: 'refund.refund_date',
|
||||
|
|
|
@ -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<SalesPriceFormulaEntity> {
|
||||
|
@ -14,4 +15,12 @@ export class SalesPriceFormulaDataService extends BaseDataService<SalesPriceForm
|
|||
) {
|
||||
super(repo);
|
||||
}
|
||||
|
||||
profitShareFormula() {
|
||||
return this.repo.find({
|
||||
where: {
|
||||
type: FormulaType.PROFIT_SHARE,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,37 @@
|
|||
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,
|
||||
) {
|
||||
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(
|
||||
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 { 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;
|
||||
}
|
||||
|
||||
return keyVal;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<TransactionBund
|
|||
@Column('bigint', { nullable: true })
|
||||
item_rates: number;
|
||||
|
||||
@Column('decimal', { nullable: true })
|
||||
total_profit_share: number;
|
||||
|
||||
@ManyToOne(() => TransactionItemModel, (model) => model.bundling_items, {
|
||||
onDelete: 'CASCADE',
|
||||
onUpdate: 'CASCADE',
|
||||
|
@ -129,3 +136,57 @@ export class TransactionItemBreakdownModel extends BaseCoreModel<TransactionBund
|
|||
@JoinColumn({ name: 'transaction_item_id' })
|
||||
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 { 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);
|
||||
|
|
Loading…
Reference in New Issue