diff --git a/src/core/helpers/query/default-filter.helper.ts b/src/core/helpers/query/default-filter.helper.ts index 9189025..10f1f81 100644 --- a/src/core/helpers/query/default-filter.helper.ts +++ b/src/core/helpers/query/default-filter.helper.ts @@ -10,39 +10,14 @@ export function setQueryFilterDefault( baseFilter: BaseFilterEntity, tableName: TABLE_NAME, ): SelectQueryBuilder { - // 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 diff --git a/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts b/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts index 127875a..4125276 100644 --- a/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-batch-update-status.manager.ts @@ -107,7 +107,7 @@ export abstract class BaseBatchUpdateStatusManager 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, diff --git a/src/core/modules/domain/usecase/managers/base-index.manager.ts b/src/core/modules/domain/usecase/managers/base-index.manager.ts index 9c757fa..89c2f54 100644 --- a/src/core/modules/domain/usecase/managers/base-index.manager.ts +++ b/src/core/modules/domain/usecase/managers/base-index.manager.ts @@ -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 extends BaseReadManager { protected result: PaginationResponse; @@ -19,6 +20,7 @@ export abstract class BaseIndexManager extends BaseReadManager { } async process(): Promise { + const specificFilter = this.specificFilter; const { joinRelations, selectRelations, countRelations } = this.relations; if (joinRelations?.length) @@ -40,10 +42,26 @@ export abstract class BaseIndexManager 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( this.queryBuilder, this.tableName, - this.specificFilter, + specificFilter, ).getFilter(); getOrderBy(this.filterParam, this.queryBuilder, this.tableName); diff --git a/src/database/migrations/1721284234428-update-midtrans-column-transaction.ts b/src/database/migrations/1721284234428-update-midtrans-column-transaction.ts deleted file mode 100644 index 681c011..0000000 --- a/src/database/migrations/1721284234428-update-midtrans-column-transaction.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { MigrationInterface, QueryRunner } from 'typeorm'; - -export class UpdateMidtransColumnTransaction1721284234428 - implements MigrationInterface -{ - name = 'UpdateMidtransColumnTransaction1721284234428'; - - public async up(queryRunner: QueryRunner): Promise { - 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 { - await queryRunner.query( - `ALTER TABLE "transactions" ADD "payment_midtrans_token" character varying`, - ); - await queryRunner.query( - `ALTER TABLE "transactions" ADD "payment_midtrans_url" character varying`, - ); - } -} diff --git a/src/database/migrations/1721385120750-update-column-transaction.ts b/src/database/migrations/1721385120750-update-column-transaction.ts new file mode 100644 index 0000000..ebec03d --- /dev/null +++ b/src/database/migrations/1721385120750-update-column-transaction.ts @@ -0,0 +1,25 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateColumnTransaction1721385120750 + implements MigrationInterface +{ + name = 'UpdateColumnTransaction1721385120750'; + + public async up(queryRunner: QueryRunner): Promise { + 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 { + await queryRunner.query( + `ALTER TABLE "transactions" DROP COLUMN "discount_percentage"`, + ); + await queryRunner.query( + `ALTER TABLE "transactions" ADD "discount_percentage" integer`, + ); + } +} diff --git a/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts b/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts index f050ba1..e3fb8ad 100644 --- a/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts +++ b/src/modules/configuration/mail/domain/handlers/payment-transaction.handler.ts @@ -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({ diff --git a/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts b/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts index bacc411..7c29f07 100644 --- a/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts +++ b/src/modules/item-related/item-rate/domain/usecases/managers/index-item-rate.manager.ts @@ -72,6 +72,7 @@ export class IndexItemRateManager extends BaseIndexManager { 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 { }); } + queryBuilder.andWhere(`${this.tableName}.status In (:...statuses)`, { + statuses: [STATUS.ACTIVE], + }); return queryBuilder; } } diff --git a/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts index e688dbb..c15a1d0 100644 --- a/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts +++ b/src/modules/item-related/item/domain/usecases/managers/index-item.manager.ts @@ -85,6 +85,8 @@ export class IndexItemManager extends BaseIndexManager { 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; diff --git a/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts b/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts index 220e5b7..ed74d61 100644 --- a/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts +++ b/src/modules/season-related/season-period/domain/usecases/managers/get-current-period.manager.ts @@ -11,6 +11,7 @@ import { STATUS } from 'src/core/strings/constants/base.constants'; @Injectable() export class CurrentSeasonPeriodManager extends BaseIndexManager { async prepareData(): Promise { + this.filterParam.limit = 10; return; } diff --git a/src/modules/transaction/profit-share-formula/domain/usecases/managers/update-profit-share-formula.manager.ts b/src/modules/transaction/profit-share-formula/domain/usecases/managers/update-profit-share-formula.manager.ts index 59139db..f9151e1 100644 --- a/src/modules/transaction/profit-share-formula/domain/usecases/managers/update-profit-share-formula.manager.ts +++ b/src/modules/transaction/profit-share-formula/domain/usecases/managers/update-profit-share-formula.manager.ts @@ -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 { async validateProcess(): Promise { + const taxes = await this.dataServiceFirstOpt.getManyByOptions({ + where: { + status: In([STATUS.ACTIVE]), + }, + }); + + calculateProfitFormula(this.data.formula_string, taxes, 10000, 50, true); return; } diff --git a/src/modules/transaction/profit-share-formula/domain/usecases/profit-share-formula-data.orchestrator.ts b/src/modules/transaction/profit-share-formula/domain/usecases/profit-share-formula-data.orchestrator.ts index 2b3806f..a1d68df 100644 --- a/src/modules/transaction/profit-share-formula/domain/usecases/profit-share-formula-data.orchestrator.ts +++ b/src/modules/transaction/profit-share-formula/domain/usecases/profit-share-formula-data.orchestrator.ts @@ -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 { @@ -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(); } diff --git a/src/modules/transaction/profit-share-formula/profit-share-formula.module.ts b/src/modules/transaction/profit-share-formula/profit-share-formula.module.ts index ea4105b..31ff6f2 100644 --- a/src/modules/transaction/profit-share-formula/profit-share-formula.module.ts +++ b/src/modules/transaction/profit-share-formula/profit-share-formula.module.ts @@ -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 {} diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts index b70dd80..5db3089 100644 --- a/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/batch-cancel-reconciliation.manager.ts @@ -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 { - validateData(data: TransactionEntity): Promise { + async validateData(data: TransactionEntity): Promise { + 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, diff --git a/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts index 8874667..fb48668 100644 --- a/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts +++ b/src/modules/transaction/reconciliation/domain/usecases/managers/cancel-reconciliation.manager.ts @@ -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 { - 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`, diff --git a/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts index e904591..678566a 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/batch-confirm-refund.manager.ts @@ -16,8 +16,14 @@ import { STATUS } from 'src/core/strings/constants/base.constants'; @Injectable() export class BatchConfirmRefundManager extends BaseBatchUpdateStatusManager { - validateData(data: RefundEntity): Promise { - if (![STATUS.DRAFT, STATUS.PENDING].includes(data.status)) { + async validateData(data: RefundEntity): Promise { + 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`, diff --git a/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts index f56080f..1de469c 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/confirm-refund.manager.ts @@ -38,6 +38,14 @@ export class ConfirmRefundManager extends BaseUpdateStatusManager 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]}`, diff --git a/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts index 7b90581..5b630e8 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/create-refund.manager.ts @@ -30,7 +30,14 @@ export class CreateRefundManager extends BaseCreateManager { 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`, diff --git a/src/modules/transaction/refund/domain/usecases/managers/update-refund.manager.ts b/src/modules/transaction/refund/domain/usecases/managers/update-refund.manager.ts index 5674c3f..86e8602 100644 --- a/src/modules/transaction/refund/domain/usecases/managers/update-refund.manager.ts +++ b/src/modules/transaction/refund/domain/usecases/managers/update-refund.manager.ts @@ -17,7 +17,14 @@ import { STATUS } from 'src/core/strings/constants/base.constants'; @Injectable() export class UpdateRefundManager extends BaseUpdateManager { async validateProcess(): Promise { - 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`, diff --git a/src/modules/transaction/refund/domain/usecases/refund-data.orchestrator.ts b/src/modules/transaction/refund/domain/usecases/refund-data.orchestrator.ts index 065ef91..c4500a1 100644 --- a/src/modules/transaction/refund/domain/usecases/refund-data.orchestrator.ts +++ b/src/modules/transaction/refund/domain/usecases/refund-data.orchestrator.ts @@ -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 { 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 { 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(); } diff --git a/src/modules/transaction/refund/refund.module.ts b/src/modules/transaction/refund/refund.module.ts index fbae71e..c45fc5a 100644 --- a/src/modules/transaction/refund/refund.module.ts +++ b/src/modules/transaction/refund/refund.module.ts @@ -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, 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 new file mode 100644 index 0000000..b699e1c --- /dev/null +++ b/src/modules/transaction/sales-price-formula/domain/usecases/managers/helpers/calculation-formula.helper.ts @@ -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, + }; + } +} diff --git a/src/modules/transaction/sales-price-formula/domain/usecases/managers/update-sales-price-formula.manager.ts b/src/modules/transaction/sales-price-formula/domain/usecases/managers/update-sales-price-formula.manager.ts index 89d24ee..1849e7a 100644 --- a/src/modules/transaction/sales-price-formula/domain/usecases/managers/update-sales-price-formula.manager.ts +++ b/src/modules/transaction/sales-price-formula/domain/usecases/managers/update-sales-price-formula.manager.ts @@ -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 { async validateProcess(): Promise { + const taxes = await this.dataServiceFirstOpt.getManyByOptions({ + where: { + status: In([STATUS.ACTIVE]), + }, + }); + + calculateSalesFormula(this.data.formula_string, taxes, 10000, true); return; } diff --git a/src/modules/transaction/sales-price-formula/domain/usecases/sales-price-formula-data.orchestrator.ts b/src/modules/transaction/sales-price-formula/domain/usecases/sales-price-formula-data.orchestrator.ts index 3b17c87..70408d4 100644 --- a/src/modules/transaction/sales-price-formula/domain/usecases/sales-price-formula-data.orchestrator.ts +++ b/src/modules/transaction/sales-price-formula/domain/usecases/sales-price-formula-data.orchestrator.ts @@ -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 { @@ -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(); } 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 a6e803f..accd83c 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 @@ -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, diff --git a/src/modules/transaction/transaction/data/models/transaction.model.ts b/src/modules/transaction/transaction/data/models/transaction.model.ts index 786623f..c181d7a 100644 --- a/src/modules/transaction/transaction/data/models/transaction.model.ts +++ b/src/modules/transaction/transaction/data/models/transaction.model.ts @@ -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 }) diff --git a/src/modules/transaction/transaction/domain/usecases/handlers/refund-update.handler.ts b/src/modules/transaction/transaction/domain/usecases/handlers/refund-update.handler.ts index 3d83ed7..7d15c32 100644 --- a/src/modules/transaction/transaction/domain/usecases/handlers/refund-update.handler.ts +++ b/src/modules/transaction/transaction/domain/usecases/handlers/refund-update.handler.ts @@ -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() 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 0f3a259..f26b6b5 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 @@ -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, - }; - } } diff --git a/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts b/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts index a53c28a..32be4b0 100644 --- a/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts +++ b/src/modules/transaction/transaction/domain/usecases/managers/confirm-transaction.manager.ts @@ -56,7 +56,10 @@ export class ConfirmTransactionManager extends BaseUpdateStatusManager { - 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; diff --git a/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts b/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts index cde5623..342e5c1 100644 --- a/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts +++ b/src/modules/transaction/transaction/domain/usecases/managers/index-transaction.manager.ts @@ -48,6 +48,7 @@ export class IndexTransactionManager extends BaseIndexManager `${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`,