commit
000c3f1800
|
@ -10,39 +10,14 @@ export function setQueryFilterDefault(
|
|||
baseFilter: BaseFilterEntity,
|
||||
tableName: TABLE_NAME,
|
||||
): SelectQueryBuilder<any> {
|
||||
// filter berdasarkan statuses
|
||||
if (!!baseFilter.statuses) {
|
||||
queryBuilder.andWhere(
|
||||
new Brackets((qb) => {
|
||||
baseFilter.statuses.map((status) => {
|
||||
// trim search
|
||||
const statusData = status.includes("'")
|
||||
? status.trim().replace(/'/g, "''").replace(/\s+/g, ' ')
|
||||
: status.trim().replace(/\s+/g, ' ');
|
||||
|
||||
// jika searching status terdapat dalam enum, maka dia mencari specific data
|
||||
// ? karena jika tidak, ketika dia search "active" maka "inactive" juga ikut
|
||||
if (STATUS[statusData.toUpperCase()])
|
||||
qb.orWhere(`${tableName}.status = :statusData`, {
|
||||
statusData: statusData,
|
||||
});
|
||||
else
|
||||
qb['orWhere'](
|
||||
`${tableName}.status::text ILIKE '%${[statusData]}%'`,
|
||||
);
|
||||
});
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
// filter berdasarkan id pembuat
|
||||
if (!!baseFilter.created_ids)
|
||||
new WhereInQueryHelper(
|
||||
queryBuilder,
|
||||
tableName,
|
||||
'created_id',
|
||||
'creator_id',
|
||||
baseFilter.created_ids,
|
||||
'created_ids',
|
||||
'creator_ids',
|
||||
).getQuery();
|
||||
|
||||
// filter berdasarkan tanggal terakhir dibuat
|
||||
|
|
|
@ -107,7 +107,7 @@ export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
|
|||
if (!this.eventTopics.length) return;
|
||||
for (const topic of this.eventTopics) {
|
||||
let data;
|
||||
if (!topic.relations) {
|
||||
if (topic.relations?.length) {
|
||||
data = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: dataNew.id,
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
} from 'src/core/helpers/query/default-filter.helper';
|
||||
import { Param } from '../../entities/base-filter.entity';
|
||||
import { joinRelationHelper } from 'src/core/helpers/query/join-relations.helper';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
|
||||
export abstract class BaseIndexManager<Entity> extends BaseReadManager {
|
||||
protected result: PaginationResponse<Entity>;
|
||||
|
@ -19,6 +20,7 @@ export abstract class BaseIndexManager<Entity> extends BaseReadManager {
|
|||
}
|
||||
|
||||
async process(): Promise<void> {
|
||||
const specificFilter = this.specificFilter;
|
||||
const { joinRelations, selectRelations, countRelations } = this.relations;
|
||||
|
||||
if (joinRelations?.length)
|
||||
|
@ -40,10 +42,26 @@ export abstract class BaseIndexManager<Entity> extends BaseReadManager {
|
|||
|
||||
if (this.selects?.length) this.queryBuilder.select(this.selects);
|
||||
|
||||
if (this.filterParam.statuses?.length > 0) {
|
||||
const data = this.filterParam.statuses.map((status) => {
|
||||
const statusData = status.includes("'")
|
||||
? status.trim().replace(/'/g, "''").replace(/\s+/g, ' ')
|
||||
: status.trim().replace(/\s+/g, ' ');
|
||||
|
||||
// jika searching status terdapat dalam enum, maka dia mencari specific data
|
||||
// ? karena jika tidak, ketika dia search "active" maka "inactive" juga ikut
|
||||
return STATUS[statusData.toUpperCase()] ?? statusData;
|
||||
});
|
||||
specificFilter.push({
|
||||
cols: `${this.tableName}.status::text`,
|
||||
data: data,
|
||||
});
|
||||
}
|
||||
|
||||
new SpecificSearchFilter<Entity>(
|
||||
this.queryBuilder,
|
||||
this.tableName,
|
||||
this.specificFilter,
|
||||
specificFilter,
|
||||
).getFilter();
|
||||
|
||||
getOrderBy(this.filterParam, this.queryBuilder, this.tableName);
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateMidtransColumnTransaction1721284234428
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'UpdateMidtransColumnTransaction1721284234428';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "payment_midtrans_url"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "payment_midtrans_token"`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "payment_midtrans_token" character varying`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "payment_midtrans_url" character varying`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
import { MigrationInterface, QueryRunner } from 'typeorm';
|
||||
|
||||
export class UpdateColumnTransaction1721385120750
|
||||
implements MigrationInterface
|
||||
{
|
||||
name = 'UpdateColumnTransaction1721385120750';
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "discount_percentage"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "discount_percentage" numeric`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" DROP COLUMN "discount_percentage"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "transactions" ADD "discount_percentage" integer`,
|
||||
);
|
||||
}
|
||||
}
|
|
@ -24,7 +24,8 @@ export class PaymentTransactionHandler
|
|||
if (
|
||||
old_data.status == STATUS.DRAFT &&
|
||||
current_data.status == STATUS.PENDING &&
|
||||
current_data.payment_type != TransactionPaymentType.COUNTER
|
||||
current_data.payment_type != TransactionPaymentType.COUNTER &&
|
||||
!!current_data.customer_email
|
||||
) {
|
||||
if (current_data.payment_type != TransactionPaymentType.MIDTRANS) {
|
||||
payments = await this.paymentService.getManyByOptions({
|
||||
|
|
|
@ -72,6 +72,7 @@ export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
|||
get selects(): string[] {
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.status`,
|
||||
`${this.tableName}.created_at`,
|
||||
`${this.tableName}.name`,
|
||||
`${this.tableName}.base_price`,
|
||||
|
@ -126,6 +127,9 @@ export class IndexItemRateManager extends BaseIndexManager<ItemEntity> {
|
|||
});
|
||||
}
|
||||
|
||||
queryBuilder.andWhere(`${this.tableName}.status In (:...statuses)`, {
|
||||
statuses: [STATUS.ACTIVE],
|
||||
});
|
||||
return queryBuilder;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,6 +85,8 @@ export class IndexItemManager extends BaseIndexManager<ItemEntity> {
|
|||
queryBuilder.andWhere(`${this.tableName}.tenant_id In (:...tenantIds)`, {
|
||||
tenantIds: this.filterParam.tenant_ids,
|
||||
});
|
||||
} else if (!this.filterParam.all_item) {
|
||||
queryBuilder.andWhere(`${this.tableName}.tenant_id Is Null`);
|
||||
}
|
||||
|
||||
return queryBuilder;
|
||||
|
|
|
@ -11,6 +11,7 @@ import { STATUS } from 'src/core/strings/constants/base.constants';
|
|||
@Injectable()
|
||||
export class CurrentSeasonPeriodManager extends BaseIndexManager<SeasonPeriodEntity> {
|
||||
async prepareData(): Promise<void> {
|
||||
this.filterParam.limit = 10;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,20 @@ 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';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateProfitShareFormulaManager extends BaseUpdateManager<SalesPriceFormulaEntity> {
|
||||
async validateProcess(): Promise<void> {
|
||||
const taxes = await this.dataServiceFirstOpt.getManyByOptions({
|
||||
where: {
|
||||
status: In([STATUS.ACTIVE]),
|
||||
},
|
||||
});
|
||||
|
||||
calculateProfitFormula(this.data.formula_string, taxes, 10000, 50, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,14 @@ import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
|||
import { SalesPriceFormulaDataService } from 'src/modules/transaction/sales-price-formula/data/services/sales-price-formula-data.service';
|
||||
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 { TaxDataService } from 'src/modules/transaction/tax/data/services/tax-data.service';
|
||||
|
||||
@Injectable()
|
||||
export class ProfitShareFormulaDataOrchestrator {
|
||||
constructor(
|
||||
private updateManager: UpdateProfitShareFormulaManager,
|
||||
private serviceData: SalesPriceFormulaDataService,
|
||||
private taxService: TaxDataService,
|
||||
) {}
|
||||
|
||||
async update(data): Promise<SalesPriceFormulaEntity> {
|
||||
|
@ -20,7 +22,11 @@ export class ProfitShareFormulaDataOrchestrator {
|
|||
});
|
||||
|
||||
this.updateManager.setData(formula.id, data);
|
||||
this.updateManager.setService(this.serviceData, TABLE_NAME.PRICE_FORMULA);
|
||||
this.updateManager.setService(
|
||||
this.serviceData,
|
||||
TABLE_NAME.PRICE_FORMULA,
|
||||
this.taxService,
|
||||
);
|
||||
await this.updateManager.execute();
|
||||
return this.updateManager.getResult();
|
||||
}
|
||||
|
|
|
@ -10,11 +10,16 @@ import { CqrsModule } from '@nestjs/cqrs';
|
|||
import { UpdateProfitShareFormulaManager } from './domain/usecases/managers/update-profit-share-formula.manager';
|
||||
import { DetailProfitShareFormulaManager } from './domain/usecases/managers/detail-profit-share-formula.manager';
|
||||
import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales-price-formula.model';
|
||||
import { TaxDataService } from '../tax/data/services/tax-data.service';
|
||||
import { TaxModel } from '../tax/data/models/tax.model';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
TypeOrmModule.forFeature([SalesPriceFormulaModel], CONNECTION_NAME.DEFAULT),
|
||||
TypeOrmModule.forFeature(
|
||||
[SalesPriceFormulaModel, TaxModel],
|
||||
CONNECTION_NAME.DEFAULT,
|
||||
),
|
||||
CqrsModule,
|
||||
],
|
||||
controllers: [
|
||||
|
@ -27,6 +32,8 @@ import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales
|
|||
|
||||
ProfitShareFormulaDataOrchestrator,
|
||||
ProfitShareFormulaReadOrchestrator,
|
||||
|
||||
TaxDataService,
|
||||
],
|
||||
})
|
||||
export class ProfitShareFormulaModule {}
|
||||
|
|
|
@ -11,10 +11,25 @@ import {
|
|||
} from '@nestjs/common';
|
||||
import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity';
|
||||
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
|
||||
@Injectable()
|
||||
export class BatchCancelReconciliationManager extends BaseBatchUpdateStatusManager<TransactionEntity> {
|
||||
validateData(data: TransactionEntity): Promise<void> {
|
||||
async validateData(data: TransactionEntity): Promise<void> {
|
||||
const transaction = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: data.id,
|
||||
},
|
||||
});
|
||||
|
||||
if (transaction.status != STATUS.SETTLED) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! cant cancel transaction not settled`,
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(data, {
|
||||
reconciliation_mdr: this.data.reconciliation_mdr ?? null,
|
||||
reconciliation_confirm_by: this.user.name,
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
UnprocessableEntityException,
|
||||
} from '@nestjs/common';
|
||||
import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import {
|
||||
EventTopics,
|
||||
validateRelations,
|
||||
|
@ -18,7 +19,20 @@ export class CancelReconciliationManager extends BaseUpdateStatusManager<Transac
|
|||
}
|
||||
|
||||
async validateProcess(): Promise<void> {
|
||||
if (this.data.is_recap_transaction) {
|
||||
// untuk dapat current status
|
||||
const transaction = await this.dataService.getOneByOptions({
|
||||
where: {
|
||||
id: this.dataId,
|
||||
},
|
||||
});
|
||||
|
||||
if (transaction.status != STATUS.SETTLED) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! cant cancel transaction not settled`,
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
} else if (this.data.is_recap_transaction) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! cant cancel recap data`,
|
||||
|
|
|
@ -16,8 +16,14 @@ import { STATUS } from 'src/core/strings/constants/base.constants';
|
|||
|
||||
@Injectable()
|
||||
export class BatchConfirmRefundManager extends BaseBatchUpdateStatusManager<RefundEntity> {
|
||||
validateData(data: RefundEntity): Promise<void> {
|
||||
if (![STATUS.DRAFT, STATUS.PENDING].includes(data.status)) {
|
||||
async validateData(data: RefundEntity): Promise<void> {
|
||||
if (data?.['transaction']?.status != STATUS.SETTLED) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! only transaction with status ${STATUS.SETTLED} can be refund`,
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
} else if (![STATUS.DRAFT, STATUS.PENDING].includes(data.status)) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! only data with status ${STATUS.DRAFT} and ${STATUS.PENDING} can be confirmed`,
|
||||
|
|
|
@ -38,6 +38,14 @@ export class ConfirmRefundManager extends BaseUpdateStatusManager<RefundEntity>
|
|||
relations: ['transaction'],
|
||||
});
|
||||
|
||||
if (data.transaction.status != STATUS.SETTLED) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! only transaction with status ${STATUS.SETTLED} can be refund`,
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
}
|
||||
|
||||
if (data.status == STATUS.DRAFT) {
|
||||
Object.assign(this.data, {
|
||||
code: `RF-${data.transaction?.invoice_code?.split('-')[1]}`,
|
||||
|
|
|
@ -30,7 +30,14 @@ export class CreateRefundManager extends BaseCreateManager<RefundEntity> {
|
|||
refund_items: refund_items,
|
||||
});
|
||||
|
||||
if (this.data.transaction?.status != STATUS.SETTLED) {
|
||||
const transaction = await this.dataServiceFirstOpt.getOneByOptions({
|
||||
where: {
|
||||
id: this.data.transaction.id,
|
||||
status: STATUS.SETTLED,
|
||||
},
|
||||
});
|
||||
|
||||
if (!transaction) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! only transaction with status ${STATUS.SETTLED} can be refund`,
|
||||
|
|
|
@ -17,7 +17,14 @@ import { STATUS } from 'src/core/strings/constants/base.constants';
|
|||
@Injectable()
|
||||
export class UpdateRefundManager extends BaseUpdateManager<RefundEntity> {
|
||||
async validateProcess(): Promise<void> {
|
||||
if (this.data.transaction?.status != STATUS.SETTLED) {
|
||||
const transaction = await this.dataServiceFirstOpt.getOneByOptions({
|
||||
where: {
|
||||
id: this.data.transaction.id,
|
||||
status: STATUS.SETTLED,
|
||||
},
|
||||
});
|
||||
|
||||
if (!transaction) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! only transaction with status ${STATUS.SETTLED} can be refund`,
|
||||
|
|
|
@ -12,6 +12,7 @@ import { BatchDeleteRefundManager } from './managers/batch-delete-refund.manager
|
|||
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
||||
import { CancelRefundManager } from './managers/cancel-refund.manager';
|
||||
import { BatchCancelRefundManager } from './managers/batch-cancel-refund.manager';
|
||||
import { TransactionDataService } from 'src/modules/transaction/transaction/data/services/transaction-data.service';
|
||||
|
||||
@Injectable()
|
||||
export class RefundDataOrchestrator {
|
||||
|
@ -25,18 +26,27 @@ export class RefundDataOrchestrator {
|
|||
private batchCancelManager: BatchCancelRefundManager,
|
||||
private batchConfirmManager: BatchConfirmRefundManager,
|
||||
private serviceData: RefundDataService,
|
||||
private transactionDataService: TransactionDataService,
|
||||
) {}
|
||||
|
||||
async create(data): Promise<RefundEntity> {
|
||||
this.createManager.setData(data);
|
||||
this.createManager.setService(this.serviceData, TABLE_NAME.REFUND);
|
||||
this.createManager.setService(
|
||||
this.serviceData,
|
||||
TABLE_NAME.REFUND,
|
||||
this.transactionDataService,
|
||||
);
|
||||
await this.createManager.execute();
|
||||
return this.createManager.getResult();
|
||||
}
|
||||
|
||||
async update(dataId, data): Promise<RefundEntity> {
|
||||
this.updateManager.setData(dataId, data);
|
||||
this.updateManager.setService(this.serviceData, TABLE_NAME.REFUND);
|
||||
this.updateManager.setService(
|
||||
this.serviceData,
|
||||
TABLE_NAME.REFUND,
|
||||
this.transactionDataService,
|
||||
);
|
||||
await this.updateManager.execute();
|
||||
return this.updateManager.getResult();
|
||||
}
|
||||
|
|
|
@ -21,12 +21,14 @@ import { RefundModel } from './data/models/refund.model';
|
|||
import { BatchCancelRefundManager } from './domain/usecases/managers/batch-cancel-refund.manager';
|
||||
import { CancelRefundManager } from './domain/usecases/managers/cancel-refund.manager';
|
||||
import { RefundItemModel } from './data/models/refund-item.model';
|
||||
import { TransactionDataService } from '../transaction/data/services/transaction-data.service';
|
||||
import { TransactionModel } from '../transaction/data/models/transaction.model';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
TypeOrmModule.forFeature(
|
||||
[RefundModel, RefundItemModel],
|
||||
[RefundModel, RefundItemModel, TransactionModel],
|
||||
CONNECTION_NAME.DEFAULT,
|
||||
),
|
||||
CqrsModule,
|
||||
|
@ -46,6 +48,7 @@ import { RefundItemModel } from './data/models/refund-item.model';
|
|||
|
||||
RefundDataService,
|
||||
RefundReadService,
|
||||
TransactionDataService,
|
||||
|
||||
RefundDataOrchestrator,
|
||||
RefundReadOrchestrator,
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
import * as math from 'mathjs';
|
||||
import { Equation, parse } from 'algebra.js';
|
||||
import { HttpStatus, UnprocessableEntityException } from '@nestjs/common';
|
||||
|
||||
export function calculateSalesFormula(
|
||||
formula: string,
|
||||
taxes: object[],
|
||||
total: number,
|
||||
throwError = false,
|
||||
) {
|
||||
try {
|
||||
let { value, variable, tax_datas } = mappingTaxes(taxes, formula, total);
|
||||
|
||||
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');
|
||||
|
||||
value = math.evaluate(result);
|
||||
console.log(value, 'value');
|
||||
|
||||
return {
|
||||
dpp_value: value,
|
||||
tax_datas: tax_datas,
|
||||
};
|
||||
} catch (e) {
|
||||
returnError(throwError, e, taxes);
|
||||
}
|
||||
}
|
||||
|
||||
export function calculateProfitFormula(
|
||||
formula: string,
|
||||
taxes: object[],
|
||||
total: number,
|
||||
profit_share = 0,
|
||||
throwError = false,
|
||||
) {
|
||||
try {
|
||||
let { value, variable, tax_datas } = mappingTaxes(
|
||||
taxes,
|
||||
formula,
|
||||
total,
|
||||
profit_share,
|
||||
);
|
||||
|
||||
const result = math.simplify(formula, variable).toString();
|
||||
console.log(result, 'formula');
|
||||
|
||||
value = math.evaluate(result);
|
||||
console.log(value, 'value');
|
||||
|
||||
return {
|
||||
dpp_value: value,
|
||||
tax_datas: tax_datas,
|
||||
};
|
||||
} catch (e) {
|
||||
returnError(throwError, e, taxes);
|
||||
}
|
||||
}
|
||||
|
||||
function mappingTaxes(taxes, formula, total, profit_share = 0) {
|
||||
let value = 0;
|
||||
const variable = {};
|
||||
let tax_datas = [];
|
||||
const const_variable = ['profit_share', 'item_share', 'dpp'];
|
||||
|
||||
const regex = /([a-zA-Z0-9_]+)/g;
|
||||
|
||||
const matches: string[] = formula.match(regex);
|
||||
const uniqueMatches = new Set(matches);
|
||||
const keys = Array.from(uniqueMatches);
|
||||
|
||||
for (const key of keys) {
|
||||
if (!const_variable.includes(key)) {
|
||||
const keyData = taxes.find((tax) => tax.name == key);
|
||||
variable[key] = keyData.value / 100;
|
||||
|
||||
tax_datas.push({
|
||||
tax_id: keyData.id,
|
||||
tax_name: keyData.name,
|
||||
tax_value: keyData.value,
|
||||
tax_total_value: (keyData.value / 100) * Number(total),
|
||||
});
|
||||
} else {
|
||||
switch (key) {
|
||||
case 'profit_share':
|
||||
variable[key] = profit_share / 100;
|
||||
break;
|
||||
|
||||
case 'item_share':
|
||||
variable[key] = profit_share / 100;
|
||||
break;
|
||||
|
||||
case 'dpp':
|
||||
if (profit_share > 0) variable[key] = total;
|
||||
break;
|
||||
default:
|
||||
variable[key] = profit_share;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
value: value,
|
||||
variable: variable,
|
||||
tax_datas: tax_datas,
|
||||
};
|
||||
}
|
||||
|
||||
function returnError(throwError, e, taxes) {
|
||||
if (throwError) {
|
||||
throw new UnprocessableEntityException({
|
||||
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
|
||||
message: `Failed! Formula error`,
|
||||
error: 'Unprocessable Entity',
|
||||
});
|
||||
} else {
|
||||
console.log(e);
|
||||
|
||||
return {
|
||||
dpp_value: 0,
|
||||
tax_datas: taxes,
|
||||
};
|
||||
}
|
||||
}
|
|
@ -8,10 +8,20 @@ import {
|
|||
columnUniques,
|
||||
validateRelations,
|
||||
} from 'src/core/strings/constants/interface.constants';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { In } from 'typeorm';
|
||||
import { calculateSalesFormula } from './helpers/calculation-formula.helper';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateSalesPriceFormulaManager extends BaseUpdateManager<SalesPriceFormulaEntity> {
|
||||
async validateProcess(): Promise<void> {
|
||||
const taxes = await this.dataServiceFirstOpt.getManyByOptions({
|
||||
where: {
|
||||
status: In([STATUS.ACTIVE]),
|
||||
},
|
||||
});
|
||||
|
||||
calculateSalesFormula(this.data.formula_string, taxes, 10000, true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,12 +4,14 @@ import { SalesPriceFormulaEntity } from '../entities/sales-price-formula.entity'
|
|||
import { UpdateSalesPriceFormulaManager } from './managers/update-sales-price-formula.manager';
|
||||
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
|
||||
import { FormulaType } from '../../constants';
|
||||
import { TaxDataService } from 'src/modules/transaction/tax/data/services/tax-data.service';
|
||||
|
||||
@Injectable()
|
||||
export class SalesPriceFormulaDataOrchestrator {
|
||||
constructor(
|
||||
private updateManager: UpdateSalesPriceFormulaManager,
|
||||
private serviceData: SalesPriceFormulaDataService,
|
||||
private taxService: TaxDataService,
|
||||
) {}
|
||||
|
||||
async update(data): Promise<SalesPriceFormulaEntity> {
|
||||
|
@ -20,7 +22,11 @@ export class SalesPriceFormulaDataOrchestrator {
|
|||
});
|
||||
|
||||
this.updateManager.setData(formula.id, data);
|
||||
this.updateManager.setService(this.serviceData, TABLE_NAME.PRICE_FORMULA);
|
||||
this.updateManager.setService(
|
||||
this.serviceData,
|
||||
TABLE_NAME.PRICE_FORMULA,
|
||||
this.taxService,
|
||||
);
|
||||
await this.updateManager.execute();
|
||||
return this.updateManager.getResult();
|
||||
}
|
||||
|
|
|
@ -12,12 +12,17 @@ import { CqrsModule } from '@nestjs/cqrs';
|
|||
import { UpdateSalesPriceFormulaManager } from './domain/usecases/managers/update-sales-price-formula.manager';
|
||||
import { DetailSalesPriceFormulaManager } from './domain/usecases/managers/detail-sales-price-formula.manager';
|
||||
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';
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot(),
|
||||
TypeOrmModule.forFeature([SalesPriceFormulaModel], CONNECTION_NAME.DEFAULT),
|
||||
TypeOrmModule.forFeature(
|
||||
[SalesPriceFormulaModel, TaxModel],
|
||||
CONNECTION_NAME.DEFAULT,
|
||||
),
|
||||
CqrsModule,
|
||||
],
|
||||
controllers: [
|
||||
|
@ -28,6 +33,7 @@ import { SalesPriceFormulaModel } from './data/models/sales-price-formula.model'
|
|||
DetailSalesPriceFormulaManager,
|
||||
UpdateSalesPriceFormulaManager,
|
||||
|
||||
TaxDataService,
|
||||
SalesPriceFormulaDataService,
|
||||
SalesPriceFormulaReadService,
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ export class TransactionModel
|
|||
@Column('varchar', { name: 'discount_code', nullable: true })
|
||||
discount_code: string;
|
||||
|
||||
@Column('int', { name: 'discount_percentage', nullable: true })
|
||||
@Column('decimal', { name: 'discount_percentage', nullable: true })
|
||||
discount_percentage: number;
|
||||
|
||||
@Column('decimal', { name: 'discount_value', nullable: true })
|
||||
|
|
|
@ -18,7 +18,7 @@ export class RefundUpdatedHandler
|
|||
|
||||
if (
|
||||
old_data.status != current_data.data ||
|
||||
event.data.op == OPERATION.DELETE
|
||||
(event.data.op == OPERATION.DELETE && current_data.status != STATUS.DRAFT)
|
||||
) {
|
||||
const queryRunner = this.dataService
|
||||
.getRepository()
|
||||
|
|
|
@ -3,11 +3,10 @@ import { TransactionChangeStatusEvent } from '../../entities/event/transaction-c
|
|||
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 { FormulaType } from 'src/modules/transaction/sales-price-formula/constants';
|
||||
import * as math from 'mathjs';
|
||||
import { Equation, parse } from 'algebra.js';
|
||||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { TransactionDataService } from '../../../data/services/transaction-data.service';
|
||||
import { TransactionModel } from '../../../data/models/transaction.model';
|
||||
import { calculateSalesFormula } from 'src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper';
|
||||
|
||||
@EventsHandler(TransactionChangeStatusEvent)
|
||||
export class SettledTransactionHandler
|
||||
|
@ -59,7 +58,7 @@ export class SettledTransactionHandler
|
|||
.manager.connection.createQueryRunner();
|
||||
|
||||
// const profit_share_value = this.calculateFormula(profit_formula.formula_string, taxes, data.payment_total_net_profit ?? 0);
|
||||
const { dpp_value, tax_datas } = this.calculateSalesFormula(
|
||||
const { dpp_value, tax_datas } = calculateSalesFormula(
|
||||
sales_price.formula_string,
|
||||
taxes,
|
||||
data.payment_total_net_profit ?? 0,
|
||||
|
@ -74,49 +73,4 @@ export class SettledTransactionHandler
|
|||
|
||||
await this.dataService.create(queryRunner, TransactionModel, data);
|
||||
}
|
||||
|
||||
calculateSalesFormula(formula, taxes, total) {
|
||||
let value = 0;
|
||||
let tax_datas = [];
|
||||
const regex = /([a-zA-Z0-9_]+)/g;
|
||||
const variable = {};
|
||||
|
||||
const matches: string[] = formula.match(regex);
|
||||
const uniqueMatches = new Set(matches);
|
||||
const keys = Array.from(uniqueMatches).filter((key) => key != 'dpp');
|
||||
|
||||
for (const key of keys) {
|
||||
const keyData = taxes.find((tax) => tax.name == key);
|
||||
variable[key] = keyData.value / 100;
|
||||
|
||||
tax_datas.push({
|
||||
tax_id: keyData.id,
|
||||
tax_name: keyData.name,
|
||||
tax_value: keyData.value,
|
||||
tax_total_value: (keyData.value / 100) * Number(total),
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
value = math.evaluate(result);
|
||||
console.log(value);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
|
||||
return {
|
||||
dpp_value: value,
|
||||
tax_datas: tax_datas,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,7 +56,10 @@ export class ConfirmTransactionManager extends BaseUpdateStatusManager<Transacti
|
|||
}
|
||||
|
||||
Object.assign(this.data, {
|
||||
invoice_code: await generateInvoiceCodeHelper(this.dataService),
|
||||
invoice_code:
|
||||
this.data.payment_type == TransactionPaymentType.COUNTER
|
||||
? null
|
||||
: await generateInvoiceCodeHelper(this.dataService),
|
||||
status: freeTransaction ? STATUS.ACTIVE : STATUS.PENDING,
|
||||
});
|
||||
return;
|
||||
|
|
|
@ -35,11 +35,14 @@ export class DetailTransactionManager extends BaseDetailManager<TransactionEntit
|
|||
get selects(): string[] {
|
||||
return [
|
||||
`${this.tableName}.id`,
|
||||
`${this.tableName}.creator_counter_no`,
|
||||
`${this.tableName}.creator_name`,
|
||||
`${this.tableName}.created_at`,
|
||||
`${this.tableName}.updated_at`,
|
||||
`${this.tableName}.editor_name`,
|
||||
`${this.tableName}.invoice_code`,
|
||||
`${this.tableName}.invoice_date`,
|
||||
`${this.tableName}.settlement_date`,
|
||||
|
||||
`${this.tableName}.season_period_id`,
|
||||
`${this.tableName}.season_period_name`,
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { STATUS } from 'src/core/strings/constants/base.constants';
|
||||
import { TransactionType } from 'src/modules/transaction/transaction/constants';
|
||||
import {
|
||||
TransactionPaymentType,
|
||||
TransactionType,
|
||||
} from 'src/modules/transaction/transaction/constants';
|
||||
|
||||
export function mappingTransaction(data) {
|
||||
let payment_type_bank: any = null;
|
||||
|
@ -73,15 +76,31 @@ export function mappingTransaction(data) {
|
|||
|
||||
export function mappingRevertTransaction(data, type) {
|
||||
if (type == TransactionType.COUNTER) {
|
||||
if (data.booking_id) {
|
||||
Object.assign(data, {
|
||||
editor_id: data.pos_admin?.id,
|
||||
editor_name: data.pos_admin?.name,
|
||||
edited_at: new Date(data.created_at),
|
||||
});
|
||||
} else {
|
||||
Object.assign(data, {
|
||||
id: data._id,
|
||||
creator_counter_no: data.pos_number,
|
||||
creator_id: data.pos_admin?.id,
|
||||
creator_name: data.pos_admin?.name,
|
||||
});
|
||||
}
|
||||
|
||||
Object.assign(data, {
|
||||
id: data.booking_id ?? data._id,
|
||||
invoice_code: data.code,
|
||||
creator_counter_no: Number(data.pos_number),
|
||||
status: STATUS.SETTLED,
|
||||
booking_date: data.created_at,
|
||||
settlement_date: data.created_at,
|
||||
payment_type: data.payment_type,
|
||||
settlement_date: new Date(data.created_at),
|
||||
payment_date: new Date(data.created_at),
|
||||
invoice_date: new Date(data.created_at),
|
||||
payment_type:
|
||||
data.payment_type == 'cc'
|
||||
? TransactionPaymentType.CC
|
||||
: data.payment_type,
|
||||
payment_card_information: data.card_information,
|
||||
payment_code_reference: data.payment_code,
|
||||
discount_code_id: data.discount_code?.id,
|
||||
|
@ -116,7 +135,8 @@ export function mappingRevertTransaction(data, type) {
|
|||
});
|
||||
|
||||
data.items?.map((item) => {
|
||||
const total_price = Number(item.item.base_price) * Number(item.qty);
|
||||
const total_price =
|
||||
Number(item.item.price ?? item.item.base_price) * Number(item.qty);
|
||||
const share_margin = item.item.tenant?.share_margin ?? 0;
|
||||
const total_share_tenant =
|
||||
share_margin > 0 ? (Number(share_margin) / 100) * total_price : 0;
|
||||
|
|
|
@ -48,6 +48,7 @@ export class IndexTransactionManager extends BaseIndexManager<TransactionEntity>
|
|||
`${this.tableName}.id`,
|
||||
`${this.tableName}.status`,
|
||||
`${this.tableName}.invoice_code`,
|
||||
`${this.tableName}.creator_counter_no`,
|
||||
`${this.tableName}.booking_date`,
|
||||
`${this.tableName}.no_of_group`,
|
||||
`${this.tableName}.type`,
|
||||
|
|
Loading…
Reference in New Issue