feat(SPG-389) REST API CUD Refund

pull/27/head
Aswin Ashar Abdullah 2024-07-12 17:30:15 +07:00
parent 66fd9c16b2
commit 2ed4ce0199
42 changed files with 1542 additions and 16 deletions

View File

@ -55,6 +55,9 @@ import { ReportBookmarkModel } from './modules/reports/shared/models/report-book
import { ExportReportHistoryModel } from './modules/reports/shared/models/export-report-history.model'; import { ExportReportHistoryModel } from './modules/reports/shared/models/export-report-history.model';
import { CronModule } from './modules/configuration/cron/cron.module'; import { CronModule } from './modules/configuration/cron/cron.module';
import { MidtransModule } from './modules/configuration/midtrans/midtrans.module'; import { MidtransModule } from './modules/configuration/midtrans/midtrans.module';
import { RefundModule } from './modules/transaction/refund/refund.module';
import { RefundModel } from './modules/transaction/refund/data/models/refund.model';
import { RefundItemModel } from './modules/transaction/refund/data/models/refund-item.model';
@Module({ @Module({
imports: [ imports: [
@ -78,6 +81,8 @@ import { MidtransModule } from './modules/configuration/midtrans/midtrans.module
ItemRateModel, ItemRateModel,
LogModel, LogModel,
PaymentMethodModel, PaymentMethodModel,
RefundModel,
RefundItemModel,
SalesPriceFormulaModel, SalesPriceFormulaModel,
SeasonPeriodModel, SeasonPeriodModel,
SeasonTypeModel, SeasonTypeModel,
@ -119,6 +124,7 @@ import { MidtransModule } from './modules/configuration/midtrans/midtrans.module
PaymentMethodModule, PaymentMethodModule,
ProfitShareFormulaModule, ProfitShareFormulaModule,
ReconciliationModule, ReconciliationModule,
RefundModule,
SalesPriceFormulaModule, SalesPriceFormulaModule,
TaxModule, TaxModule,
TransactionModule, TransactionModule,

View File

@ -8,6 +8,7 @@ import * as _ from 'lodash';
export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager { export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
protected dataIds: string[]; protected dataIds: string[];
protected relations: string[] = [];
protected result: BatchResult; protected result: BatchResult;
protected dataStatus: STATUS; protected dataStatus: STATUS;
protected oldData: Entity; protected oldData: Entity;
@ -37,6 +38,7 @@ export abstract class BaseBatchUpdateStatusManager<Entity> extends BaseManager {
where: { where: {
id: id, id: id,
}, },
relations: this.relations,
}); });
if (!entity) { if (!entity) {

View File

@ -67,7 +67,7 @@ export abstract class BaseDeleteManager<Entity> extends BaseManager {
this.eventBus.publishAll([ this.eventBus.publishAll([
new topic.topic({ new topic.topic({
id: topic.data['id'], id: topic.data['id'],
old: null, old: this.data,
data: topic.data, data: topic.data,
user: this.user, user: this.user,
description: '', description: '',

View File

@ -6,7 +6,9 @@ export enum STATUS {
DRAFT = 'draft', DRAFT = 'draft',
EXPIRED = 'expired', EXPIRED = 'expired',
INACTIVE = 'inactive', INACTIVE = 'inactive',
PARTIAL_REFUND = 'partial refund',
PENDING = 'pending', PENDING = 'pending',
PROCESS_REFUND = 'proses refund',
REFUNDED = 'refunded', REFUNDED = 'refunded',
REJECTED = 'rejected', REJECTED = 'rejected',
SETTLED = 'settled', SETTLED = 'settled',

View File

@ -4,6 +4,7 @@ export enum MODULE_NAME {
ITEM_RATE = 'item-rates', ITEM_RATE = 'item-rates',
PAYMENT_METHOD = 'payment-methods', PAYMENT_METHOD = 'payment-methods',
RECONCILIATION = 'reconciliations', RECONCILIATION = 'reconciliations',
REFUND = 'refunds',
SEASON_TYPE = 'season-types', SEASON_TYPE = 'season-types',
SEASON_PERIOD = 'season-periods', SEASON_PERIOD = 'season-periods',
TAX = 'taxes', TAX = 'taxes',

View File

@ -6,6 +6,8 @@ export enum TABLE_NAME {
LOG = 'logs', LOG = 'logs',
PAYMENT_METHOD = 'payment_methods', PAYMENT_METHOD = 'payment_methods',
PRICE_FORMULA = 'price_formulas', PRICE_FORMULA = 'price_formulas',
REFUND = 'refunds',
REFUND_ITEM = 'refund_items',
SEASON_TYPE = 'season_types', SEASON_TYPE = 'season_types',
SEASON_PERIOD = 'season_periods', SEASON_PERIOD = 'season_periods',
TAX = 'taxes', TAX = 'taxes',

View File

@ -0,0 +1,27 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateTableTransaction1720767689625 implements MigrationInterface {
name = 'UpdateTableTransaction1720767689625';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transaction_items" ADD "qty_remaining" integer`,
);
await queryRunner.query(`ALTER TABLE "transaction_items" ADD "taxes" json`);
await queryRunner.query(
`ALTER TABLE "transactions" ADD "payment_total_dpp" numeric`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "transactions" DROP COLUMN "payment_total_dpp"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "taxes"`,
);
await queryRunner.query(
`ALTER TABLE "transaction_items" DROP COLUMN "qty_remaining"`,
);
}
}

View File

@ -0,0 +1,45 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class Refund1720768975877 implements MigrationInterface {
name = 'Refund1720768975877';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."refunds_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`CREATE TYPE "public"."refunds_type_enum" AS ENUM('pengembalian booking', 'pengembalian wahana')`,
);
await queryRunner.query(
`CREATE TABLE "refunds" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "creator_id" character varying(36), "creator_name" character varying(125), "editor_id" character varying(36), "editor_name" character varying(125), "created_at" bigint NOT NULL, "updated_at" bigint NOT NULL, "status" "public"."refunds_status_enum" NOT NULL DEFAULT 'draft', "type" "public"."refunds_type_enum" NOT NULL DEFAULT 'pengembalian booking', "code" character varying, "request_date" date, "refund_date" date, "refund_total" numeric, "bank_name" character varying, "bank_account_name" character varying, "bank_account_number" character varying, "transaction_id" uuid, CONSTRAINT "REL_8bb3b7579f49990d2e77684acd" UNIQUE ("transaction_id"), CONSTRAINT "PK_5106efb01eeda7e49a78b869738" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`CREATE TABLE "refund_items" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "qty_refund" numeric, "refund_total" numeric, "refund_item_id" uuid, "transaction_item_id" uuid, CONSTRAINT "REL_07b481a163c219f5de8fb1c90b" UNIQUE ("transaction_item_id"), CONSTRAINT "PK_ef892918375a6101948b90f1140" PRIMARY KEY ("id"))`,
);
await queryRunner.query(
`ALTER TABLE "refunds" ADD CONSTRAINT "FK_8bb3b7579f49990d2e77684acd4" FOREIGN KEY ("transaction_id") REFERENCES "transactions"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "refund_items" ADD CONSTRAINT "FK_2a4bd60fb8a9c37f902f4f3da67" FOREIGN KEY ("refund_item_id") REFERENCES "refunds"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
await queryRunner.query(
`ALTER TABLE "refund_items" ADD CONSTRAINT "FK_07b481a163c219f5de8fb1c90b3" FOREIGN KEY ("transaction_item_id") REFERENCES "transaction_items"("id") ON DELETE CASCADE ON UPDATE CASCADE`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "refund_items" DROP CONSTRAINT "FK_07b481a163c219f5de8fb1c90b3"`,
);
await queryRunner.query(
`ALTER TABLE "refund_items" DROP CONSTRAINT "FK_2a4bd60fb8a9c37f902f4f3da67"`,
);
await queryRunner.query(
`ALTER TABLE "refunds" DROP CONSTRAINT "FK_8bb3b7579f49990d2e77684acd4"`,
);
await queryRunner.query(`DROP TABLE "refund_items"`);
await queryRunner.query(`DROP TABLE "refunds"`);
await queryRunner.query(`DROP TYPE "public"."refunds_type_enum"`);
await queryRunner.query(`DROP TYPE "public"."refunds_status_enum"`);
}
}

View File

@ -0,0 +1,461 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class UpdateEnumStatus1720774145470 implements MigrationInterface {
name = 'UpdateEnumStatus1720774145470';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TYPE "public"."item_categories_status_enum" RENAME TO "item_categories_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."item_categories_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "item_categories" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "item_categories" ALTER COLUMN "status" TYPE "public"."item_categories_status_enum" USING "status"::"text"::"public"."item_categories_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "item_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."item_categories_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."season_types_status_enum" RENAME TO "season_types_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."season_types_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "season_types" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "season_types" ALTER COLUMN "status" TYPE "public"."season_types_status_enum" USING "status"::"text"::"public"."season_types_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "season_types" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."season_types_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."season_periods_status_enum" RENAME TO "season_periods_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."season_periods_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "season_periods" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "season_periods" ALTER COLUMN "status" TYPE "public"."season_periods_status_enum" USING "status"::"text"::"public"."season_periods_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "season_periods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."season_periods_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."items_status_enum" RENAME TO "items_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."items_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "items" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "items" ALTER COLUMN "status" TYPE "public"."items_status_enum" USING "status"::"text"::"public"."items_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "items" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."items_status_enum_old"`);
await queryRunner.query(
`ALTER TYPE "public"."users_status_enum" RENAME TO "users_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."users_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "status" TYPE "public"."users_status_enum" USING "status"::"text"::"public"."users_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."users_status_enum_old"`);
await queryRunner.query(
`ALTER TYPE "public"."user_privileges_status_enum" RENAME TO "user_privileges_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."user_privileges_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "user_privileges" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "user_privileges" ALTER COLUMN "status" TYPE "public"."user_privileges_status_enum" USING "status"::"text"::"public"."user_privileges_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "user_privileges" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."user_privileges_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."refunds_status_enum" RENAME TO "refunds_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."refunds_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "refunds" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "refunds" ALTER COLUMN "status" TYPE "public"."refunds_status_enum" USING "status"::"text"::"public"."refunds_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "refunds" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."refunds_status_enum_old"`);
await queryRunner.query(
`ALTER TYPE "public"."transactions_status_enum" RENAME TO "transactions_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "status" TYPE "public"."transactions_status_enum" USING "status"::"text"::"public"."transactions_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."transactions_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."transactions_reconciliation_status_enum" RENAME TO "transactions_reconciliation_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_reconciliation_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" TYPE "public"."transactions_reconciliation_status_enum" USING "reconciliation_status"::"text"::"public"."transactions_reconciliation_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."transactions_reconciliation_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."transactions_sending_invoice_status_enum" RENAME TO "transactions_sending_invoice_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_sending_invoice_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "sending_invoice_status" TYPE "public"."transactions_sending_invoice_status_enum" USING "sending_invoice_status"::"text"::"public"."transactions_sending_invoice_status_enum"`,
);
await queryRunner.query(
`DROP TYPE "public"."transactions_sending_invoice_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."transactions_sending_qr_status_enum" RENAME TO "transactions_sending_qr_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_sending_qr_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "sending_qr_status" TYPE "public"."transactions_sending_qr_status_enum" USING "sending_qr_status"::"text"::"public"."transactions_sending_qr_status_enum"`,
);
await queryRunner.query(
`DROP TYPE "public"."transactions_sending_qr_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."vip_categories_status_enum" RENAME TO "vip_categories_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."vip_categories_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "vip_categories" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "vip_categories" ALTER COLUMN "status" TYPE "public"."vip_categories_status_enum" USING "status"::"text"::"public"."vip_categories_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "vip_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."vip_categories_status_enum_old"`,
);
await queryRunner.query(
`ALTER TYPE "public"."taxes_status_enum" RENAME TO "taxes_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."taxes_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "taxes" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "taxes" ALTER COLUMN "status" TYPE "public"."taxes_status_enum" USING "status"::"text"::"public"."taxes_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "taxes" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."taxes_status_enum_old"`);
await queryRunner.query(
`ALTER TYPE "public"."payment_methods_status_enum" RENAME TO "payment_methods_status_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."payment_methods_status_enum" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'partial refund', 'pending', 'proses refund', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "payment_methods" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "payment_methods" ALTER COLUMN "status" TYPE "public"."payment_methods_status_enum" USING "status"::"text"::"public"."payment_methods_status_enum"`,
);
await queryRunner.query(
`ALTER TABLE "payment_methods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."payment_methods_status_enum_old"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."payment_methods_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "payment_methods" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "payment_methods" ALTER COLUMN "status" TYPE "public"."payment_methods_status_enum_old" USING "status"::"text"::"public"."payment_methods_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "payment_methods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."payment_methods_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."payment_methods_status_enum_old" RENAME TO "payment_methods_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."taxes_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "taxes" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "taxes" ALTER COLUMN "status" TYPE "public"."taxes_status_enum_old" USING "status"::"text"::"public"."taxes_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "taxes" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."taxes_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."taxes_status_enum_old" RENAME TO "taxes_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."vip_categories_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "vip_categories" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "vip_categories" ALTER COLUMN "status" TYPE "public"."vip_categories_status_enum_old" USING "status"::"text"::"public"."vip_categories_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "vip_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."vip_categories_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."vip_categories_status_enum_old" RENAME TO "vip_categories_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_sending_qr_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "sending_qr_status" TYPE "public"."transactions_sending_qr_status_enum_old" USING "sending_qr_status"::"text"::"public"."transactions_sending_qr_status_enum_old"`,
);
await queryRunner.query(
`DROP TYPE "public"."transactions_sending_qr_status_enum"`,
);
await queryRunner.query(
`ALTER TYPE "public"."transactions_sending_qr_status_enum_old" RENAME TO "transactions_sending_qr_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_sending_invoice_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "sending_invoice_status" TYPE "public"."transactions_sending_invoice_status_enum_old" USING "sending_invoice_status"::"text"::"public"."transactions_sending_invoice_status_enum_old"`,
);
await queryRunner.query(
`DROP TYPE "public"."transactions_sending_invoice_status_enum"`,
);
await queryRunner.query(
`ALTER TYPE "public"."transactions_sending_invoice_status_enum_old" RENAME TO "transactions_sending_invoice_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_reconciliation_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" TYPE "public"."transactions_reconciliation_status_enum_old" USING "reconciliation_status"::"text"::"public"."transactions_reconciliation_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "reconciliation_status" SET DEFAULT 'draft'`,
);
await queryRunner.query(
`DROP TYPE "public"."transactions_reconciliation_status_enum"`,
);
await queryRunner.query(
`ALTER TYPE "public"."transactions_reconciliation_status_enum_old" RENAME TO "transactions_reconciliation_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."transactions_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "status" TYPE "public"."transactions_status_enum_old" USING "status"::"text"::"public"."transactions_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "transactions" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."transactions_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."transactions_status_enum_old" RENAME TO "transactions_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."refunds_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "refunds" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "refunds" ALTER COLUMN "status" TYPE "public"."refunds_status_enum_old" USING "status"::"text"::"public"."refunds_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "refunds" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."refunds_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."refunds_status_enum_old" RENAME TO "refunds_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."user_privileges_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "user_privileges" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "user_privileges" ALTER COLUMN "status" TYPE "public"."user_privileges_status_enum_old" USING "status"::"text"::"public"."user_privileges_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "user_privileges" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."user_privileges_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."user_privileges_status_enum_old" RENAME TO "user_privileges_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."users_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "status" TYPE "public"."users_status_enum_old" USING "status"::"text"::"public"."users_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "users" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."users_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."users_status_enum_old" RENAME TO "users_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."items_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "items" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "items" ALTER COLUMN "status" TYPE "public"."items_status_enum_old" USING "status"::"text"::"public"."items_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "items" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."items_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."items_status_enum_old" RENAME TO "items_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."season_periods_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "season_periods" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "season_periods" ALTER COLUMN "status" TYPE "public"."season_periods_status_enum_old" USING "status"::"text"::"public"."season_periods_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "season_periods" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."season_periods_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."season_periods_status_enum_old" RENAME TO "season_periods_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."season_types_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "season_types" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "season_types" ALTER COLUMN "status" TYPE "public"."season_types_status_enum_old" USING "status"::"text"::"public"."season_types_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "season_types" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."season_types_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."season_types_status_enum_old" RENAME TO "season_types_status_enum"`,
);
await queryRunner.query(
`CREATE TYPE "public"."item_categories_status_enum_old" AS ENUM('active', 'cancel', 'confirmed', 'draft', 'expired', 'inactive', 'pending', 'refunded', 'rejected', 'settled', 'waiting')`,
);
await queryRunner.query(
`ALTER TABLE "item_categories" ALTER COLUMN "status" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "item_categories" ALTER COLUMN "status" TYPE "public"."item_categories_status_enum_old" USING "status"::"text"::"public"."item_categories_status_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "item_categories" ALTER COLUMN "status" SET DEFAULT 'draft'`,
);
await queryRunner.query(`DROP TYPE "public"."item_categories_status_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."item_categories_status_enum_old" RENAME TO "item_categories_status_enum"`,
);
}
}

View File

@ -5,6 +5,7 @@ import { STATUS } from 'src/core/strings/constants/base.constants';
import { ItemType } from 'src/modules/item-related/item-category/constants'; import { ItemType } from 'src/modules/item-related/item-category/constants';
import { LimitType } from 'src/modules/item-related/item/constants'; import { LimitType } from 'src/modules/item-related/item/constants';
import { PaymentMethodType } from 'src/modules/transaction/payment-method/constants'; import { PaymentMethodType } from 'src/modules/transaction/payment-method/constants';
import { RefundType } from 'src/modules/transaction/refund/constants';
@ApiTags('configuration - constant') @ApiTags('configuration - constant')
@Controller('v1/constant') @Controller('v1/constant')
@ -44,4 +45,9 @@ export class ConstantController {
async transactionType(): Promise<any> { async transactionType(): Promise<any> {
return ['counter', 'admin', 'online']; return ['counter', 'admin', 'online'];
} }
@Get('refund-type')
async refundType(): Promise<any> {
return Object.values(RefundType);
}
} }

View File

@ -23,9 +23,7 @@ export class CouchDataController {
try { try {
const n = this.nanoInstance; const n = this.nanoInstance;
await n.db.create(entity.name); await n.db.create(entity.name);
} catch (error) { } catch (error) {}
console.log(error, 'dsa');
}
} }
@Post('doc') @Post('doc')
@ -33,7 +31,6 @@ export class CouchDataController {
try { try {
const nano = this.nanoInstance; const nano = this.nanoInstance;
const people = nano.db.use('string'); const people = nano.db.use('string');
console.log(await people.info(), entity);
// const data = { // const data = {
// id: '1212', // id: '1212',
// name: 'dsadas', // name: 'dsadas',
@ -54,9 +51,7 @@ export class CouchDataController {
.on('error', (e) => { .on('error', (e) => {
console.error('error', e); console.error('error', e);
}); });
} catch (error) { } catch (error) {}
console.log(error, 'dsa');
}
} }
@Get() @Get()
@ -67,8 +62,6 @@ export class CouchDataController {
return people; return people;
// return people.get(); // return people.get();
} catch (error) { } catch (error) {}
console.log(error, 'dsa');
}
} }
} }

View File

@ -0,0 +1,4 @@
export enum RefundType {
BOOKING = 'pengembalian booking',
WAHANA = 'pengembalian wahana',
}

View File

@ -0,0 +1,38 @@
import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model';
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from 'typeorm';
import { RefundItemEntity } from '../../domain/entities/refund-item.entity';
import { TransactionItemModel } from 'src/modules/transaction/transaction/data/models/transaction-item.model';
import { RefundModel } from './refund.model';
@Entity(TABLE_NAME.REFUND_ITEM)
export class RefundItemModel
extends BaseCoreModel<RefundItemEntity>
implements RefundItemEntity
{
@Column('decimal', { name: 'qty_refund', nullable: true })
qty_refund: number;
@Column('decimal', { name: 'refund_total', nullable: true })
refund_total: number;
// transaction to refund
@Column('decimal', { name: 'refund_item_id', nullable: true })
refund_item_id: number;
@ManyToOne(() => RefundModel, (model) => model.refund_items, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'refund_item_id' })
refund: RefundModel;
// transaction to transaction item
@Column('varchar', { name: 'transaction_item_id', nullable: true })
transaction_item_id: string;
@OneToOne(() => TransactionItemModel, (model) => model.refund, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'transaction_item_id' })
transaction_item: TransactionItemModel;
}

View File

@ -0,0 +1,60 @@
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { RefundEntity } from '../../domain/entities/refund.entity';
import { Column, Entity, JoinColumn, OneToMany, OneToOne } from 'typeorm';
import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model';
import { TransactionModel } from 'src/modules/transaction/transaction/data/models/transaction.model';
import { RefundItemModel } from './refund-item.model';
import { RefundType } from '../../constants';
@Entity(TABLE_NAME.REFUND)
export class RefundModel
extends BaseStatusModel<RefundEntity>
implements RefundEntity
{
@Column('enum', {
name: 'type',
enum: RefundType,
default: RefundType.BOOKING,
})
type: RefundType;
@Column('varchar', { name: 'code', nullable: true })
code: string;
@Column('date', { name: 'request_date', nullable: true })
request_date: Date;
@Column('date', { name: 'refund_date', nullable: true })
refund_date: Date;
@Column('decimal', { name: 'refund_total', nullable: true })
refund_total: number;
// bank info
@Column('varchar', { name: 'bank_name', nullable: true })
bank_name: string;
@Column('varchar', { name: 'bank_account_name', nullable: true })
bank_account_name: string;
@Column('varchar', { name: 'bank_account_number', nullable: true })
bank_account_number: string;
// relations to item
@OneToMany(() => RefundItemModel, (model) => model.refund, {
cascade: true,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
refund_items: RefundItemModel[];
// relation to transaction
@Column('varchar', { name: 'transaction_id', nullable: true })
transaction_id: string;
@OneToOne(() => TransactionModel, (model) => model.refund, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinColumn({ name: 'transaction_id' })
transaction: TransactionModel;
}

View File

@ -0,0 +1,17 @@
import { Injectable } from '@nestjs/common';
import { BaseDataService } from 'src/core/modules/data/service/base-data.service';
import { RefundEntity } from '../../domain/entities/refund.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { RefundModel } from '../models/refund.model';
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { Repository } from 'typeorm';
@Injectable()
export class RefundDataService extends BaseDataService<RefundEntity> {
constructor(
@InjectRepository(RefundModel, CONNECTION_NAME.DEFAULT)
private repo: Repository<RefundModel>,
) {
super(repo);
}
}

View File

@ -0,0 +1,5 @@
import { IEvent } from 'src/core/strings/constants/interface.constants';
export class RefundChangeStatusEvent {
constructor(public readonly data: IEvent) {}
}

View File

@ -0,0 +1,5 @@
import { IEvent } from 'src/core/strings/constants/interface.constants';
export class RefundCreatedEvent {
constructor(public readonly data: IEvent) {}
}

View File

@ -0,0 +1,5 @@
import { IEvent } from 'src/core/strings/constants/interface.constants';
export class RefundDeletedEvent {
constructor(public readonly data: IEvent) {}
}

View File

@ -0,0 +1,5 @@
import { IEvent } from 'src/core/strings/constants/interface.constants';
export class RefundUpdatedEvent {
constructor(public readonly data: IEvent) {}
}

View File

@ -0,0 +1,7 @@
import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity';
export interface RefundItemEntity extends BaseCoreEntity {
qty_refund: number;
transaction_item_id?: string;
}

View File

@ -0,0 +1,16 @@
import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity';
import { RefundType } from '../../constants';
export interface RefundEntity extends BaseStatusEntity {
type: RefundType;
code: string;
request_date: Date;
refund_date: Date;
refund_total: number;
bank_name: string;
bank_account_name: string;
bank_account_number: string;
transaction_id: string;
}

View File

@ -0,0 +1,57 @@
import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager';
import { RefundEntity } from '../../entities/refund.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { RefundModel } from '../../../data/models/refund.model';
import { RefundChangeStatusEvent } from '../../entities/event/refund-change-status.event';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import {
HttpStatus,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
import { STATUS } from 'src/core/strings/constants/base.constants';
@Injectable()
export class BatchCancelRefundManager extends BaseBatchUpdateStatusManager<RefundEntity> {
validateData(data: RefundEntity): Promise<void> {
if (![STATUS.REFUNDED, STATUS.PENDING].includes(data.status)) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Failed! only data with status ${STATUS.REFUNDED} and ${STATUS.PENDING} can be cancelled`,
error: 'Unprocessable Entity',
});
}
return;
}
beforeProcess(): Promise<void> {
return;
}
afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return RefundModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundChangeStatusEvent,
},
];
}
getResult(): BatchResult {
return this.result;
}
}

View File

@ -0,0 +1,72 @@
import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager';
import { RefundEntity } from '../../entities/refund.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { RefundModel } from '../../../data/models/refund.model';
import { RefundChangeStatusEvent } from '../../entities/event/refund-change-status.event';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import {
HttpStatus,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
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)) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Failed! only data with status ${STATUS.DRAFT} and ${STATUS.PENDING} can be confirmed`,
error: 'Unprocessable Entity',
});
}
if (this.data.status == STATUS.DRAFT) {
Object.assign(this.data, {
code: `RF-${data?.['transaction']?.invoice_code.split('-')[1]}`,
request_date: new Date(),
status: STATUS.PENDING,
});
} else if (this.data.status == STATUS.PENDING) {
Object.assign(this.data, {
refund_date: new Date(),
status: STATUS.REFUNDED,
});
}
return;
}
beforeProcess(): Promise<void> {
this.relations = ['transaction'];
return;
}
afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return RefundModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundChangeStatusEvent,
relations: ['transaction'],
},
];
}
getResult(): BatchResult {
return this.result;
}
}

View File

@ -0,0 +1,45 @@
import { BaseBatchDeleteManager } from 'src/core/modules/domain/usecase/managers/base-batch-delete.manager';
import { RefundEntity } from '../../entities/refund.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { RefundModel } from '../../../data/models/refund.model';
import { RefundDeletedEvent } from '../../entities/event/refund-deleted.event';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { Injectable } from '@nestjs/common';
@Injectable()
export class BatchDeleteRefundManager extends BaseBatchDeleteManager<RefundEntity> {
async beforeProcess(): Promise<void> {
return;
}
async validateData(data: RefundEntity): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return RefundModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundDeletedEvent,
},
];
}
getResult(): BatchResult {
return this.result;
}
}

View File

@ -0,0 +1,57 @@
import {
HttpStatus,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager';
import { RefundEntity } from '../../entities/refund.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { RefundModel } from '../../../data/models/refund.model';
import { RefundChangeStatusEvent } from '../../entities/event/refund-change-status.event';
import { STATUS } from 'src/core/strings/constants/base.constants';
@Injectable()
export class CancelRefundManager extends BaseUpdateStatusManager<RefundEntity> {
getResult(): string {
return `Success active data ${this.result.code}`;
}
async validateProcess(): Promise<void> {
if (![STATUS.REFUNDED, STATUS.PENDING].includes(this.data.status)) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Failed! only data with status ${STATUS.REFUNDED} and ${STATUS.PENDING} can be cancelled`,
error: 'Unprocessable Entity',
});
}
return;
}
async beforeProcess(): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return RefundModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundChangeStatusEvent,
data: this.data,
},
];
}
}

View File

@ -0,0 +1,77 @@
import {
HttpStatus,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager';
import { RefundEntity } from '../../entities/refund.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { RefundModel } from '../../../data/models/refund.model';
import { RefundChangeStatusEvent } from '../../entities/event/refund-change-status.event';
import { STATUS } from 'src/core/strings/constants/base.constants';
@Injectable()
export class ConfirmRefundManager extends BaseUpdateStatusManager<RefundEntity> {
getResult(): string {
return `Success active data ${this.result.code}`;
}
async validateProcess(): Promise<void> {
if (![STATUS.DRAFT, STATUS.PENDING].includes(this.oldData.status)) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Failed! only data with status ${STATUS.DRAFT} and ${STATUS.PENDING} can be confirmed`,
error: 'Unprocessable Entity',
});
}
return;
}
async beforeProcess(): Promise<void> {
const data = await this.dataService.getOneByOptions({
where: {
id: this.data.id,
},
relations: ['transaction'],
});
if (data.status == STATUS.DRAFT) {
Object.assign(this.data, {
code: `RF-${data.transaction?.invoice_code?.split('-')[1]}`,
request_date: new Date(),
status: STATUS.PENDING,
});
} else if (data.status == STATUS.PENDING) {
Object.assign(this.data, {
refund_date: new Date(),
status: STATUS.REFUNDED,
});
}
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return RefundModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundChangeStatusEvent,
relations: ['transaction'],
},
];
}
}

View File

@ -0,0 +1,54 @@
import {
HttpStatus,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
import {
EventTopics,
columnUniques,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { RefundEntity } from '../../entities/refund.entity';
import { RefundModel } from '../../../data/models/refund.model';
import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager';
import { RefundCreatedEvent } from '../../entities/event/refund-created.event';
import { STATUS } from 'src/core/strings/constants/base.constants';
@Injectable()
export class CreateRefundManager extends BaseCreateManager<RefundEntity> {
async beforeProcess(): Promise<void> {
// if (this.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',
// });
// }
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get uniqueColumns(): columnUniques[] {
return [];
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundCreatedEvent,
data: this.data,
},
];
}
get entityTarget(): any {
return RefundModel;
}
}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager';
import { RefundEntity } from '../../entities/refund.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { RefundModel } from '../../../data/models/refund.model';
import { RefundDeletedEvent } from '../../entities/event/refund-deleted.event';
@Injectable()
export class DeleteRefundManager extends BaseDeleteManager<RefundEntity> {
getResult(): string {
return `Success`;
}
async validateProcess(): Promise<void> {
return;
}
async beforeProcess(): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return RefundModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundDeletedEvent,
data: this.data,
},
];
}
}

View File

@ -0,0 +1,58 @@
import {
HttpStatus,
Injectable,
UnprocessableEntityException,
} from '@nestjs/common';
import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager';
import { RefundEntity } from '../../entities/refund.entity';
import { RefundModel } from '../../../data/models/refund.model';
import { RefundUpdatedEvent } from '../../entities/event/refund-updated.event';
import {
EventTopics,
columnUniques,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
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) {
throw new UnprocessableEntityException({
statusCode: HttpStatus.UNPROCESSABLE_ENTITY,
message: `Failed! only transaction with status ${STATUS.SETTLED} can be refund`,
error: 'Unprocessable Entity',
});
}
return;
}
async beforeProcess(): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get uniqueColumns(): columnUniques[] {
return [];
}
get entityTarget(): any {
return RefundModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: RefundUpdatedEvent,
data: this.data,
},
];
}
}

View File

@ -0,0 +1,85 @@
import { Injectable } from '@nestjs/common';
import { CreateRefundManager } from './managers/create-refund.manager';
import { RefundDataService } from '../../data/services/refund-data.service';
import { RefundEntity } from '../entities/refund.entity';
import { DeleteRefundManager } from './managers/delete-refund.manager';
import { UpdateRefundManager } from './managers/update-refund.manager';
import { ConfirmRefundManager } from './managers/confirm-refund.manager';
import { STATUS } from 'src/core/strings/constants/base.constants';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { BatchConfirmRefundManager } from './managers/batch-confirm-refund.manager';
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';
@Injectable()
export class RefundDataOrchestrator {
constructor(
private createManager: CreateRefundManager,
private updateManager: UpdateRefundManager,
private deleteManager: DeleteRefundManager,
private cancelManager: CancelRefundManager,
private confirmManager: ConfirmRefundManager,
private batchDeleteManager: BatchDeleteRefundManager,
private batchCancelManager: BatchCancelRefundManager,
private batchConfirmManager: BatchConfirmRefundManager,
private serviceData: RefundDataService,
) {}
async create(data): Promise<RefundEntity> {
this.createManager.setData(data);
this.createManager.setService(this.serviceData, TABLE_NAME.REFUND);
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);
await this.updateManager.execute();
return this.updateManager.getResult();
}
async delete(dataId): Promise<string> {
this.deleteManager.setData(dataId);
this.deleteManager.setService(this.serviceData, TABLE_NAME.REFUND);
await this.deleteManager.execute();
return this.deleteManager.getResult();
}
async batchDelete(dataIds: string[]): Promise<BatchResult> {
this.batchDeleteManager.setData(dataIds);
this.batchDeleteManager.setService(this.serviceData, TABLE_NAME.REFUND);
await this.batchDeleteManager.execute();
return this.batchDeleteManager.getResult();
}
async cancel(dataId): Promise<string> {
this.cancelManager.setData(dataId, STATUS.CANCEL);
this.cancelManager.setService(this.serviceData, TABLE_NAME.REFUND);
await this.cancelManager.execute();
return this.cancelManager.getResult();
}
async batchCancel(dataIds: string[]): Promise<BatchResult> {
this.batchCancelManager.setData(dataIds, STATUS.CANCEL);
this.batchCancelManager.setService(this.serviceData, TABLE_NAME.REFUND);
await this.batchCancelManager.execute();
return this.batchCancelManager.getResult();
}
async confirm(dataId): Promise<string> {
this.confirmManager.setData(dataId, STATUS.PENDING);
this.confirmManager.setService(this.serviceData, TABLE_NAME.REFUND);
await this.confirmManager.execute();
return this.confirmManager.getResult();
}
async batchConfirm(dataIds: string[]): Promise<BatchResult> {
this.batchConfirmManager.setData(dataIds, STATUS.PENDING);
this.batchConfirmManager.setService(this.serviceData, TABLE_NAME.REFUND);
await this.batchConfirmManager.execute();
return this.batchConfirmManager.getResult();
}
}

View File

View File

@ -0,0 +1,67 @@
import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto';
import { RefundEntity } from '../../domain/entities/refund.entity';
import { ApiProperty } from '@nestjs/swagger';
import { IsNumber, IsString, ValidateIf } from 'class-validator';
import { Exclude } from 'class-transformer';
import { TransactionEntity } from 'src/modules/transaction/transaction/domain/entities/transaction.entity';
import { RefundType } from '../../constants';
export class RefundDto extends BaseStatusDto implements RefundEntity {
@ApiProperty({
type: String,
required: true,
example: RefundType.BOOKING,
})
@IsString()
type: RefundType;
@ApiProperty({
type: Number,
example: 1750000,
})
@IsNumber()
refund_total: number;
@ApiProperty({
type: String,
required: false,
example: 'BCA',
})
bank_name: string;
@ApiProperty({
type: String,
required: false,
example: 'andhika',
})
bank_account_name: string;
@ApiProperty({
type: String,
required: false,
example: '64222456',
})
bank_account_number: string;
@ApiProperty({
type: Object,
required: true,
example: {
id: 'uuid',
invoice_code: 'INV-',
},
})
transaction: TransactionEntity;
@Exclude()
code: string;
@Exclude()
request_date: Date;
@Exclude()
refund_date: Date;
@Exclude()
transaction_id: string;
}

View File

@ -0,0 +1,68 @@
import {
Body,
Controller,
Delete,
Param,
Patch,
Post,
Put,
} from '@nestjs/common';
import { RefundDataOrchestrator } from '../domain/usecases/refund-data.orchestrator';
import { RefundDto } from './dto/refund.dto';
import { MODULE_NAME } from 'src/core/strings/constants/module.constants';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { RefundEntity } from '../domain/entities/refund.entity';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto';
import { Public } from 'src/core/guards';
@ApiTags(`${MODULE_NAME.REFUND.split('-').join(' ')} - data`)
@Controller(`v1/${MODULE_NAME.REFUND}`)
@Public(false)
@ApiBearerAuth('JWT')
export class RefundDataController {
constructor(private orchestrator: RefundDataOrchestrator) {}
@Post()
async create(@Body() data: RefundDto): Promise<RefundEntity> {
return await this.orchestrator.create(data);
}
@Put('/batch-delete')
async batchDeleted(@Body() body: BatchIdsDto): Promise<BatchResult> {
return await this.orchestrator.batchDelete(body.ids);
}
@Patch(':id/cancel')
async cancel(@Param('id') dataId: string): Promise<string> {
return await this.orchestrator.cancel(dataId);
}
@Put('/batch-cancel')
async batchcancel(@Body() body: BatchIdsDto): Promise<BatchResult> {
return await this.orchestrator.batchCancel(body.ids);
}
@Patch(':id/confirm')
async confirm(@Param('id') dataId: string): Promise<string> {
return await this.orchestrator.confirm(dataId);
}
@Put('/batch-confirm')
async batchConfirm(@Body() body: BatchIdsDto): Promise<BatchResult> {
return await this.orchestrator.batchConfirm(body.ids);
}
@Put(':id')
async update(
@Param('id') dataId: string,
@Body() data: RefundDto,
): Promise<RefundEntity> {
return await this.orchestrator.update(dataId, data);
}
@Delete(':id')
async delete(@Param('id') dataId: string): Promise<string> {
return await this.orchestrator.delete(dataId);
}
}

View File

@ -0,0 +1,54 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants';
import { RefundDataService } from './data/services/refund-data.service';
import { RefundReadService } from './data/services/refund-read.service';
import { RefundReadController } from './infrastructure/refund-read.controller';
import { RefundReadOrchestrator } from './domain/usecases/refund-read.orchestrator';
import { RefundDataController } from './infrastructure/refund-data.controller';
import { RefundDataOrchestrator } from './domain/usecases/refund-data.orchestrator';
import { CreateRefundManager } from './domain/usecases/managers/create-refund.manager';
import { CqrsModule } from '@nestjs/cqrs';
import { IndexRefundManager } from './domain/usecases/managers/index-refund.manager';
import { DeleteRefundManager } from './domain/usecases/managers/delete-refund.manager';
import { UpdateRefundManager } from './domain/usecases/managers/update-refund.manager';
import { ConfirmRefundManager } from './domain/usecases/managers/confirm-refund.manager';
import { DetailRefundManager } from './domain/usecases/managers/detail-refund.manager';
import { BatchDeleteRefundManager } from './domain/usecases/managers/batch-delete-refund.manager';
import { BatchConfirmRefundManager } from './domain/usecases/managers/batch-confirm-refund.manager';
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';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forFeature(
[RefundModel, RefundItemModel],
CONNECTION_NAME.DEFAULT,
),
CqrsModule,
],
controllers: [RefundDataController, RefundReadController],
providers: [
IndexRefundManager,
DetailRefundManager,
CreateRefundManager,
DeleteRefundManager,
UpdateRefundManager,
ConfirmRefundManager,
CancelRefundManager,
BatchDeleteRefundManager,
BatchConfirmRefundManager,
BatchCancelRefundManager,
RefundDataService,
RefundReadService,
RefundDataOrchestrator,
RefundReadOrchestrator,
],
})
export class RefundModule {}

View File

@ -1,8 +1,9 @@
import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; import { Column, Entity, JoinColumn, ManyToOne, OneToOne } from 'typeorm';
import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model'; import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model';
import { TransactionItemEntity } from '../../domain/entities/transaction-item.entity'; import { TransactionItemEntity } from '../../domain/entities/transaction-item.entity';
import { TransactionModel } from './transaction.model'; import { TransactionModel } from './transaction.model';
import { RefundItemModel } from 'src/modules/transaction/refund/data/models/refund-item.model';
@Entity(TABLE_NAME.TRANSACTION_ITEM) @Entity(TABLE_NAME.TRANSACTION_ITEM)
export class TransactionItemModel export class TransactionItemModel
@ -60,6 +61,12 @@ export class TransactionItemModel
@Column('int', { name: 'qty', nullable: true }) @Column('int', { name: 'qty', nullable: true })
qty: number; qty: number;
@Column('int', { name: 'qty_remaining', nullable: true })
qty_remaining: number;
@Column('json', { name: 'taxes', nullable: true })
taxes: string;
@Column('varchar', { name: 'transaction_id', nullable: true }) @Column('varchar', { name: 'transaction_id', nullable: true })
transaction_id: string; transaction_id: string;
@ManyToOne(() => TransactionModel, (model) => model.items, { @ManyToOne(() => TransactionModel, (model) => model.items, {
@ -68,4 +75,12 @@ export class TransactionItemModel
}) })
@JoinColumn({ name: 'transaction_id' }) @JoinColumn({ name: 'transaction_id' })
transaction: TransactionModel; transaction: TransactionModel;
// relations to refund
@OneToOne(() => RefundItemModel, (model) => model.transaction_item, {
cascade: true,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
refund: RefundItemModel;
} }

View File

@ -1,6 +1,6 @@
import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { TransactionEntity } from '../../domain/entities/transaction.entity'; import { TransactionEntity } from '../../domain/entities/transaction.entity';
import { Column, Entity, OneToMany } from 'typeorm'; import { Column, Entity, OneToMany, OneToOne } from 'typeorm';
import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model'; import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model';
import { import {
TransactionType, TransactionType,
@ -11,6 +11,7 @@ import { TransactionItemEntity } from '../../domain/entities/transaction-item.en
import { TransactionItemModel } from './transaction-item.model'; import { TransactionItemModel } from './transaction-item.model';
import { TransactionTaxModel } from './transaction-tax.model'; import { TransactionTaxModel } from './transaction-tax.model';
import { STATUS } from 'src/core/strings/constants/base.constants'; import { STATUS } from 'src/core/strings/constants/base.constants';
import { RefundModel } from 'src/modules/transaction/refund/data/models/refund.model';
@Entity(TABLE_NAME.TRANSACTION) @Entity(TABLE_NAME.TRANSACTION)
export class TransactionModel export class TransactionModel
@ -214,4 +215,12 @@ export class TransactionModel
onUpdate: 'CASCADE', onUpdate: 'CASCADE',
}) })
taxes: TransactionTaxModel[]; taxes: TransactionTaxModel[];
// relations to refund
@OneToOne(() => RefundModel, (model) => model.transaction, {
cascade: true,
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
refund: RefundModel;
} }

View File

@ -22,4 +22,6 @@ export interface TransactionItemEntity extends BaseCoreEntity {
total_profit: number; total_profit: number;
total_share_tenant: number; total_share_tenant: number;
qty: number; qty: number;
qty_remaining: number;
taxes: string;
} }

View File

@ -0,0 +1,52 @@
import { EventsHandler, IEventHandler } from '@nestjs/cqrs';
import { RefundChangeStatusEvent } from 'src/modules/transaction/refund/domain/entities/event/refund-change-status.event';
import { TransactionDataService } from '../../../data/services/transaction-data.service';
import { OPERATION, STATUS } from 'src/core/strings/constants/base.constants';
import { TransactionModel } from '../../../data/models/transaction.model';
import { RefundDeletedEvent } from 'src/modules/transaction/refund/domain/entities/event/refund-deleted.event';
@EventsHandler(RefundChangeStatusEvent, RefundDeletedEvent)
export class RefundUpdatedHandler
implements IEventHandler<RefundChangeStatusEvent>
{
constructor(private dataService: TransactionDataService) {}
async handle(event: RefundChangeStatusEvent) {
const old_data = event.data.old;
const current_data = event.data.data;
let status: STATUS;
if (
old_data.status != current_data.data ||
event.data.op == OPERATION.DELETE
) {
const queryRunner = this.dataService
.getRepository()
.manager.connection.createQueryRunner();
const data = new TransactionModel();
const if_full_refund =
Number(current_data.refund_total ?? 0) ==
Number(current_data.transaction?.payment_total);
if (event.data.op == OPERATION.DELETE) status = STATUS.SETTLED;
else if (current_data.status == STATUS.PENDING)
status = STATUS.PROCESS_REFUND;
else if (current_data.status == STATUS.REFUNDED && if_full_refund)
status = STATUS.REFUNDED;
else if (current_data.status == STATUS.REFUNDED && !if_full_refund)
status = STATUS.PARTIAL_REFUND;
else if (current_data.status == STATUS.CANCEL) status = STATUS.SETTLED;
await this.dataService.update(
queryRunner,
TransactionModel,
{ id: current_data.transaction_id },
{
...data,
status: status,
},
);
}
}
}

View File

@ -30,6 +30,7 @@ import { SalesPriceFormulaDataService } from '../sales-price-formula/data/servic
import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales-price-formula.model'; import { SalesPriceFormulaModel } from '../sales-price-formula/data/models/sales-price-formula.model';
import { TaxModel } from '../tax/data/models/tax.model'; import { TaxModel } from '../tax/data/models/tax.model';
import { SettledTransactionHandler } from './domain/usecases/handlers/settled-transaction.handler'; import { SettledTransactionHandler } from './domain/usecases/handlers/settled-transaction.handler';
import { RefundUpdatedHandler } from './domain/usecases/handlers/refund-update.handler';
@Module({ @Module({
imports: [ imports: [
@ -48,6 +49,7 @@ import { SettledTransactionHandler } from './domain/usecases/handlers/settled-tr
], ],
controllers: [TransactionDataController, TransactionReadController], controllers: [TransactionDataController, TransactionReadController],
providers: [ providers: [
RefundUpdatedHandler,
PosTransactionHandler, PosTransactionHandler,
SettledTransactionHandler, SettledTransactionHandler,