feat(SPG-359) REST API CUD Item / Tenant Item

pull/2/head
ashar 2024-06-11 15:07:03 +07:00
parent f5c4b1ffdf
commit b2b0ade6b4
33 changed files with 1176 additions and 10 deletions

View File

@ -26,6 +26,8 @@ import { VipCategoryModule } from './modules/transaction/vip-category/vip-catego
import { VipCategoryModel } from './modules/transaction/vip-category/data/models/vip-category.model'; import { VipCategoryModel } from './modules/transaction/vip-category/data/models/vip-category.model';
import { VipCodeModule } from './modules/transaction/vip-code/vip-code.module'; import { VipCodeModule } from './modules/transaction/vip-code/vip-code.module';
import { VipCodeModel } from './modules/transaction/vip-code/data/models/vip-code.model'; import { VipCodeModel } from './modules/transaction/vip-code/data/models/vip-code.model';
import { ItemModule } from './modules/item-related/item/item.module';
import { ItemModel } from './modules/item-related/item/data/models/item.model';
@Module({ @Module({
imports: [ imports: [
@ -46,6 +48,7 @@ import { VipCodeModel } from './modules/transaction/vip-code/data/models/vip-cod
UserModel, UserModel,
LogModel, LogModel,
ErrorLogModel, ErrorLogModel,
ItemModel,
ItemCategoryModel, ItemCategoryModel,
VipCategoryModel, VipCategoryModel,
VipCodeModel, VipCodeModel,
@ -66,6 +69,7 @@ import { VipCodeModel } from './modules/transaction/vip-code/data/models/vip-cod
// Item // Item
ItemCategoryModule, ItemCategoryModule,
ItemModule,
// transaction // transaction
VipCategoryModule, VipCategoryModule,

View File

@ -1,4 +1,5 @@
export enum MODULE_NAME { export enum MODULE_NAME {
ITEM = 'items',
ITEM_CATEGORY = 'item-categories', ITEM_CATEGORY = 'item-categories',
TENANT = 'tenants', TENANT = 'tenants',
USER = 'users', USER = 'users',

View File

@ -1,5 +1,6 @@
export enum TABLE_NAME { export enum TABLE_NAME {
ERROR_LOG = 'log_errors', ERROR_LOG = 'log_errors',
ITEM = 'items',
ITEM_CATEGORY = 'item_categories', ITEM_CATEGORY = 'item_categories',
LOG = 'logs', LOG = 'logs',
TENANT = 'tenants', TENANT = 'tenants',

View File

@ -0,0 +1,15 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class Item1718085330130 implements MigrationInterface {
name = 'Item1718085330130';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "items" ADD "image" character varying`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TABLE "items" DROP COLUMN "image"`);
}
}

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 { ItemCategoryEntity } from '../../domain/entities/item-category.entity'; import { ItemCategoryEntity } from '../../domain/entities/item-category.entity';
import { Column, Entity } from 'typeorm'; import { Column, Entity, OneToMany } 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 { ItemType } from '../../constants'; import { ItemType } from '../../constants';
import { ItemModel } from 'src/modules/item-related/item/data/models/item.model';
@Entity(TABLE_NAME.ITEM_CATEGORY) @Entity(TABLE_NAME.ITEM_CATEGORY)
export class ItemCategoryModel export class ItemCategoryModel
@ -18,4 +19,10 @@ export class ItemCategoryModel
default: ItemType.TIKET_MASUK, default: ItemType.TIKET_MASUK,
}) })
item_type: ItemType; item_type: ItemType;
@OneToMany(() => ItemModel, (model) => model.item_category, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
items: ItemModel[];
} }

View File

@ -1,6 +1,6 @@
import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto'; import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto';
import { ItemCategoryEntity } from '../../domain/entities/item-category.entity'; import { ItemCategoryEntity } from '../../domain/entities/item-category.entity';
import { IsEnum, IsString, ValidateIf } from 'class-validator'; import { IsString } from 'class-validator';
import { ItemType } from '../../constants'; import { ItemType } from '../../constants';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
@ -14,13 +14,9 @@ export class ItemCategoryDto
@ApiProperty({ @ApiProperty({
type: 'string', type: 'string',
required: false, required: true,
description: `Select (${JSON.stringify(Object.values(ItemType))})`, description: `Select (${JSON.stringify(Object.values(ItemType))})`,
example: ItemType.BUNDLING, example: ItemType.BUNDLING,
}) })
@ValidateIf((body) => body.order_type)
@IsEnum(ItemType, {
message: `must be a valid enum ${JSON.stringify(Object.values(ItemType))}`,
})
item_type: ItemType; item_type: ItemType;
} }

View File

@ -0,0 +1,5 @@
export enum LimitType {
NO_LIMIT = 'no limit',
TIME_LIMIT = 'time limit',
QTY_LIMIT = 'qty limit',
}

View File

@ -0,0 +1,99 @@
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { ItemEntity } from '../../domain/entities/item.entity';
import {
Column,
Entity,
JoinColumn,
JoinTable,
ManyToMany,
ManyToOne,
} from 'typeorm';
import { BaseStatusModel } from 'src/core/modules/data/model/base-status.model';
import { ItemType } from 'src/modules/item-related/item-category/constants';
import { LimitType } from '../../constants';
import { ItemCategoryModel } from 'src/modules/item-related/item-category/data/models/item-category.model';
import { UserModel } from 'src/modules/user-related/user/data/models/user.model';
@Entity(TABLE_NAME.ITEM)
export class ItemModel
extends BaseStatusModel<ItemEntity>
implements ItemEntity
{
@Column('varchar', { name: 'name' })
name: string;
@Column('varchar', { name: 'image', nullable: true })
image: string;
@Column('enum', {
name: 'item_type',
enum: ItemType,
default: ItemType.TIKET_MASUK,
})
item_type: ItemType;
@Column('bigint', { name: 'hpp', nullable: true })
hpp: number;
@Column('int', { name: 'sales_margin', nullable: true })
sales_margin: number;
@Column('bigint', { name: 'base_price', nullable: true })
base_price: number;
@Column('boolean', { name: 'use_queue', default: false })
use_queue: boolean;
@Column('boolean', { name: 'show_to_booking', default: false })
show_to_booking: boolean;
@Column('enum', {
name: 'limit_type',
enum: LimitType,
default: LimitType.NO_LIMIT,
})
limit_type: LimitType;
@Column('int', { name: 'limit_value', nullable: true })
limit_value: number;
// relation ke item category
@Column('varchar', { name: 'item_category_id', nullable: true })
item_category_id: number;
@ManyToOne(() => ItemCategoryModel, (model) => model.items, {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'item_category_id' })
item_category: ItemCategoryModel;
// relation ke tenant
// ? karena item bisajadi merupakan item dari tenant
@Column('varchar', { name: 'tenant_id', nullable: true })
tenant_id: number;
@ManyToOne(() => UserModel, (model) => model.items, {
onUpdate: 'CASCADE',
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'tenant_id' })
tenant: UserModel;
// relasi ke diri sendiri
// ?type bundling bisa punya relasi ke item (item adalah diri sendiri)
@ManyToMany(() => ItemModel, (model) => model.bundling_items, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
@JoinTable({
name: 'item_bundlings',
joinColumn: {
name: 'item_bundling_id',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'item_id',
referencedColumnName: 'id',
},
})
bundling_items: ItemModel[];
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,18 @@
import { BaseStatusEntity } from 'src/core/modules/domain/entities/base-status.entity';
import { ItemType } from 'src/modules/item-related/item-category/constants';
import { LimitType } from '../../constants';
export interface ItemEntity extends BaseStatusEntity {
name: string;
item_type: ItemType;
image: string;
hpp: number;
sales_margin: number;
base_price: number;
limit_type: LimitType;
limit_value: number;
use_queue: boolean;
show_to_booking: boolean;
}

View File

@ -0,0 +1,130 @@
import { Injectable } from '@nestjs/common';
import { CreateItemManager } from './managers/create-item.manager';
import { ItemDataService } from '../../data/services/item-data.service';
import { ItemEntity } from '../entities/item.entity';
import { DeleteItemManager } from './managers/delete-item.manager';
import { UpdateItemManager } from './managers/update-item.manager';
import { BaseDataTransactionOrchestrator } from 'src/core/modules/domain/usecase/orchestrators/base-data-transaction.orchestrator';
import { ActiveItemManager } from './managers/active-item.manager';
import { InactiveItemManager } from './managers/inactive-item.manager';
import { ConfirmItemManager } from './managers/confirm-item.manager';
import { STATUS } from 'src/core/strings/constants/base.constants';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { BatchConfirmItemManager } from './managers/batch-confirm-item.manager';
import { BatchInactiveItemManager } from './managers/batch-inactive-item.manager';
import { BatchActiveItemManager } from './managers/batch-active-item.manager';
import { BatchDeleteItemManager } from './managers/batch-delete-item.manager';
import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
@Injectable()
export class ItemDataOrchestrator extends BaseDataTransactionOrchestrator<ItemEntity> {
constructor(
private createManager: CreateItemManager,
private updateManager: UpdateItemManager,
private deleteManager: DeleteItemManager,
private activeManager: ActiveItemManager,
private confirmManager: ConfirmItemManager,
private inactiveManager: InactiveItemManager,
private batchDeleteManager: BatchDeleteItemManager,
private batchActiveManager: BatchActiveItemManager,
private batchConfirmManager: BatchConfirmItemManager,
private batchInactiveManager: BatchInactiveItemManager,
private serviceData: ItemDataService,
) {
super();
}
async create(data, tenantId?: string): Promise<ItemEntity> {
if (tenantId) {
Object.assign(data, {
tenant_id: tenantId,
});
}
this.createManager.setData(data);
this.createManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.createManager.execute();
return this.createManager.getResult();
}
async update(dataId, data, tenantId?: string): Promise<ItemEntity> {
if (tenantId) {
Object.assign(data, {
tenant_id: tenantId,
});
}
this.updateManager.setData(dataId, data);
this.updateManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.updateManager.execute();
return this.updateManager.getResult();
}
async delete(dataId, tenantId?: string): Promise<String> {
this.deleteManager.setData(dataId);
this.deleteManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.deleteManager.execute();
return this.deleteManager.getResult();
}
async batchDelete(
dataIds: string[],
tenantId?: string,
): Promise<BatchResult> {
this.batchDeleteManager.setData(dataIds);
this.batchDeleteManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.batchDeleteManager.execute();
return this.batchDeleteManager.getResult();
}
async active(dataId, tenantId?: string): Promise<String> {
this.activeManager.setData(dataId, STATUS.ACTIVE);
this.activeManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.activeManager.execute();
return this.activeManager.getResult();
}
async batchActive(
dataIds: string[],
tenantId?: string,
): Promise<BatchResult> {
this.batchActiveManager.setData(dataIds, STATUS.ACTIVE);
this.batchActiveManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.batchActiveManager.execute();
return this.batchActiveManager.getResult();
}
async confirm(dataId, tenantId?: string): Promise<String> {
this.confirmManager.setData(dataId, STATUS.ACTIVE);
this.confirmManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.confirmManager.execute();
return this.confirmManager.getResult();
}
async batchConfirm(
dataIds: string[],
tenantId?: string,
): Promise<BatchResult> {
this.batchConfirmManager.setData(dataIds, STATUS.ACTIVE);
this.batchConfirmManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.batchConfirmManager.execute();
return this.batchConfirmManager.getResult();
}
async inactive(dataId, tenantId?: string): Promise<String> {
this.inactiveManager.setData(dataId, STATUS.INACTIVE);
this.inactiveManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.inactiveManager.execute();
return this.inactiveManager.getResult();
}
async batchInactive(
dataIds: string[],
tenantId?: string,
): Promise<BatchResult> {
this.batchInactiveManager.setData(dataIds, STATUS.INACTIVE);
this.batchInactiveManager.setService(this.serviceData, TABLE_NAME.ITEM);
await this.batchInactiveManager.execute();
return this.batchInactiveManager.getResult();
}
}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager';
import { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event';
@Injectable()
export class ActiveItemManager extends BaseUpdateStatusManager<ItemEntity> {
getResult(): string {
return `Success active data ${this.result.name}`;
}
async validateProcess(): Promise<void> {
return;
}
async beforeProcess(): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemChangeStatusEvent,
data: this.data,
},
];
}
}

View File

@ -0,0 +1,45 @@
import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager';
import { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { Injectable } from '@nestjs/common';
@Injectable()
export class BatchActiveItemManager extends BaseBatchUpdateStatusManager<ItemEntity> {
validateData(data: ItemEntity): Promise<void> {
return;
}
beforeProcess(): Promise<void> {
return;
}
afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemChangeStatusEvent,
},
];
}
getResult(): BatchResult {
return this.result;
}
}

View File

@ -0,0 +1,45 @@
import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager';
import { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { Injectable } from '@nestjs/common';
@Injectable()
export class BatchConfirmItemManager extends BaseBatchUpdateStatusManager<ItemEntity> {
validateData(data: ItemEntity): Promise<void> {
return;
}
beforeProcess(): Promise<void> {
return;
}
afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemChangeStatusEvent,
},
];
}
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 { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemDeletedEvent } from '../../entities/event/item-deleted.event';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { Injectable } from '@nestjs/common';
@Injectable()
export class BatchDeleteItemManager extends BaseBatchDeleteManager<ItemEntity> {
async beforeProcess(): Promise<void> {
return;
}
async validateData(data: ItemEntity): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemDeletedEvent,
},
];
}
getResult(): BatchResult {
return this.result;
}
}

View File

@ -0,0 +1,45 @@
import { BaseBatchUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-batch-update-status.manager';
import { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { Injectable } from '@nestjs/common';
@Injectable()
export class BatchInactiveItemManager extends BaseBatchUpdateStatusManager<ItemEntity> {
validateData(data: ItemEntity): Promise<void> {
return;
}
beforeProcess(): Promise<void> {
return;
}
afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemChangeStatusEvent,
},
];
}
getResult(): BatchResult {
return this.result;
}
}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager';
import { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event';
@Injectable()
export class ConfirmItemManager extends BaseUpdateStatusManager<ItemEntity> {
getResult(): string {
return `Success active data ${this.result.name}`;
}
async validateProcess(): Promise<void> {
return;
}
async beforeProcess(): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemChangeStatusEvent,
data: this.data,
},
];
}
}

View File

@ -0,0 +1,51 @@
import { Injectable } from '@nestjs/common';
import {
EventTopics,
columnUniques,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemEntity } from '../../entities/item.entity';
import { ItemModel } from '../../../data/models/item.model';
import { BaseCreateManager } from 'src/core/modules/domain/usecase/managers/base-create.manager';
import { ItemCreatedEvent } from '../../entities/event/item-created.event';
@Injectable()
export class CreateItemManager extends BaseCreateManager<ItemEntity> {
async beforeProcess(): Promise<void> {
Object.assign(this.data, {
item_type: this.data.item_type.toLowerCase(),
});
if (this.data.limit_type) {
Object.assign(this.data, {
limit_type: this.data.limit_type.toLowerCase(),
});
}
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get uniqueColumns(): columnUniques[] {
return [];
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemCreatedEvent,
data: this.data,
},
];
}
get entityTarget(): any {
return ItemModel;
}
}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { BaseDeleteManager } from 'src/core/modules/domain/usecase/managers/base-delete.manager';
import { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemDeletedEvent } from '../../entities/event/item-deleted.event';
@Injectable()
export class DeleteItemManager extends BaseDeleteManager<ItemEntity> {
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 ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemDeletedEvent,
data: this.data,
},
];
}
}

View File

@ -0,0 +1,45 @@
import { Injectable } from '@nestjs/common';
import { BaseUpdateStatusManager } from 'src/core/modules/domain/usecase/managers/base-update-status.manager';
import { ItemEntity } from '../../entities/item.entity';
import {
EventTopics,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
import { ItemModel } from '../../../data/models/item.model';
import { ItemChangeStatusEvent } from '../../entities/event/item-change-status.event';
@Injectable()
export class InactiveItemManager extends BaseUpdateStatusManager<ItemEntity> {
getResult(): string {
return `Success inactive data ${this.result.name}`;
}
async validateProcess(): Promise<void> {
return;
}
async beforeProcess(): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemChangeStatusEvent,
data: this.data,
},
];
}
}

View File

@ -0,0 +1,46 @@
import { Injectable } from '@nestjs/common';
import { BaseUpdateManager } from 'src/core/modules/domain/usecase/managers/base-update.manager';
import { ItemEntity } from '../../entities/item.entity';
import { ItemModel } from '../../../data/models/item.model';
import { ItemUpdatedEvent } from '../../entities/event/item-updated.event';
import {
EventTopics,
columnUniques,
validateRelations,
} from 'src/core/strings/constants/interface.constants';
@Injectable()
export class UpdateItemManager extends BaseUpdateManager<ItemEntity> {
async validateProcess(): Promise<void> {
return;
}
async beforeProcess(): Promise<void> {
return;
}
async afterProcess(): Promise<void> {
return;
}
get validateRelations(): validateRelations[] {
return [];
}
get uniqueColumns(): columnUniques[] {
return [];
}
get entityTarget(): any {
return ItemModel;
}
get eventTopics(): EventTopics[] {
return [
{
topic: ItemUpdatedEvent,
data: this.data,
},
];
}
}

View File

View File

@ -0,0 +1,122 @@
import { BaseStatusDto } from 'src/core/modules/infrastructure/dto/base-status.dto';
import { ItemEntity } from '../../domain/entities/item.entity';
import { ItemType } from 'src/modules/item-related/item-category/constants';
import { LimitType } from '../../constants';
import { ApiProperty } from '@nestjs/swagger';
import {
IsArray,
IsBoolean,
IsNumber,
IsObject,
IsString,
ValidateIf,
} from 'class-validator';
import { ItemCategoryEntity } from 'src/modules/item-related/item-category/domain/entities/item-category.entity';
export class ItemDto extends BaseStatusDto implements ItemEntity {
@ApiProperty({
type: String,
required: true,
example: 'Entrace Ticket',
})
@IsString()
name: string;
@ApiProperty({
type: String,
required: false,
example: '...',
})
@IsString()
@ValidateIf((body) => body.image)
image: string;
@ApiProperty({
type: 'string',
required: true,
description: `Select (${JSON.stringify(Object.values(ItemType))})`,
example: ItemType.BUNDLING,
})
@IsString()
item_type: ItemType;
@ApiProperty({
type: Object,
required: false,
example: {
id: 'uuid',
},
})
@IsObject()
@ValidateIf((body) => body.item_category)
item_category: ItemCategoryEntity;
@ApiProperty({
type: Number,
required: false,
example: 100000,
})
@IsNumber()
@ValidateIf((body) => body.item_type.toLowerCase() != ItemType.FREE_GIFT)
hpp: number;
@ApiProperty({
type: Number,
required: false,
example: 50,
})
@IsNumber()
@ValidateIf((body) => body.item_type.toLowerCase() != ItemType.FREE_GIFT)
sales_margin: number;
@ApiProperty({
type: Number,
required: false,
example: 100000,
})
@IsNumber()
@ValidateIf((body) => body.item_type.toLowerCase() != ItemType.FREE_GIFT)
base_price: number;
@ApiProperty({
type: 'string',
required: false,
description: `Select (${JSON.stringify(Object.values(LimitType))})`,
example: LimitType.NO_LIMIT,
})
@IsString()
@ValidateIf((body) => body.item_type.toLowerCase() == ItemType.WAHANA)
limit_type: LimitType;
@ApiProperty({
type: Number,
required: false,
example: 60,
})
@IsNumber()
@ValidateIf((body) => body.item_type.toLowerCase() == ItemType.WAHANA)
limit_value: number;
@ApiProperty({ type: Boolean, required: false })
@IsBoolean()
@ValidateIf((body) => body.use_queue)
use_queue: boolean;
@ApiProperty({ type: Boolean, required: false })
@IsBoolean()
@ValidateIf((body) => body.show_to_booking)
show_to_booking: boolean;
@ApiProperty({
name: 'bundling_items',
type: [Object],
example: [
{
id: 'uuid',
},
],
})
@IsArray()
@ValidateIf((body) => body.item_type.toLowerCase() == ItemType.BUNDLING)
bundling_items: ItemEntity[];
}

View File

@ -0,0 +1,78 @@
import {
Body,
Controller,
Delete,
Param,
Patch,
Post,
Put,
} from '@nestjs/common';
import { ItemDataOrchestrator } from '../domain/usecases/item-data.orchestrator';
import { ItemDto } from './dto/item.dto';
import { MODULE_NAME } from 'src/core/strings/constants/module.constants';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { ItemEntity } from '../domain/entities/item.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.ITEM.split('-').join(' ')} - data`)
@Controller(MODULE_NAME.ITEM)
@Public(false)
@ApiBearerAuth('JWT')
export class ItemDataController {
constructor(private orchestrator: ItemDataOrchestrator) {}
@Post()
async create(@Body() data: ItemDto): Promise<ItemEntity> {
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/active')
async active(@Param('id') dataId: string): Promise<String> {
return await this.orchestrator.active(dataId);
}
@Put('/batch-active')
async batchActive(@Body() body: BatchIdsDto): Promise<BatchResult> {
return await this.orchestrator.batchActive(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);
}
@Patch(':id/inactive')
async inactive(@Param('id') dataId: string): Promise<String> {
return await this.orchestrator.inactive(dataId);
}
@Put('/batch-inactive')
async batchInactive(@Body() body: BatchIdsDto): Promise<BatchResult> {
return await this.orchestrator.batchInactive(body.ids);
}
@Put(':id')
async update(
@Param('id') dataId: string,
@Body() data: ItemDto,
): Promise<ItemEntity> {
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,75 @@
import { Global, 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 { ItemDataService } from './data/services/item-data.service';
import { ItemReadService } from './data/services/item-read.service';
import { ItemReadController } from './infrastructure/item-read.controller';
import { ItemReadOrchestrator } from './domain/usecases/item-read.orchestrator';
import { ItemDataController } from './infrastructure/item-data.controller';
import { ItemDataOrchestrator } from './domain/usecases/item-data.orchestrator';
import { CreateItemManager } from './domain/usecases/managers/create-item.manager';
import { CqrsModule } from '@nestjs/cqrs';
import { IndexItemManager } from './domain/usecases/managers/index-item.manager';
import { DeleteItemManager } from './domain/usecases/managers/delete-item.manager';
import { UpdateItemManager } from './domain/usecases/managers/update-item.manager';
import { ActiveItemManager } from './domain/usecases/managers/active-item.manager';
import { ConfirmItemManager } from './domain/usecases/managers/confirm-item.manager';
import { InactiveItemManager } from './domain/usecases/managers/inactive-item.manager';
import { DetailItemManager } from './domain/usecases/managers/detail-item.manager';
import { BatchDeleteItemManager } from './domain/usecases/managers/batch-delete-item.manager';
import { BatchActiveItemManager } from './domain/usecases/managers/batch-active-item.manager';
import { BatchConfirmItemManager } from './domain/usecases/managers/batch-confirm-item.manager';
import { BatchInactiveItemManager } from './domain/usecases/managers/batch-inactive-item.manager';
import { ItemModel } from './data/models/item.model';
@Global()
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forFeature([ItemModel], CONNECTION_NAME.DEFAULT),
CqrsModule,
],
controllers: [ItemDataController, ItemReadController],
providers: [
IndexItemManager,
DetailItemManager,
CreateItemManager,
DeleteItemManager,
UpdateItemManager,
ActiveItemManager,
ConfirmItemManager,
InactiveItemManager,
BatchDeleteItemManager,
BatchActiveItemManager,
BatchConfirmItemManager,
BatchInactiveItemManager,
ItemDataService,
ItemReadService,
ItemDataOrchestrator,
ItemReadOrchestrator,
],
exports: [
IndexItemManager,
DetailItemManager,
CreateItemManager,
DeleteItemManager,
UpdateItemManager,
ActiveItemManager,
ConfirmItemManager,
InactiveItemManager,
BatchDeleteItemManager,
BatchActiveItemManager,
BatchConfirmItemManager,
BatchInactiveItemManager,
ItemDataService,
ItemReadService,
ItemDataOrchestrator,
ItemReadOrchestrator,
],
})
export class ItemModule {}

View File

@ -24,7 +24,7 @@ export class VipCodeDto extends BaseDto implements VipCodeEntity {
@ApiProperty({ @ApiProperty({
name: 'vip_category', name: 'vip_category',
type: String, type: Object,
required: true, required: true,
example: { example: {
id: 'uuid', id: 'uuid',

View File

@ -0,0 +1,106 @@
import {
Body,
Controller,
Delete,
Param,
Patch,
Post,
Put,
} from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { Public } from 'src/core/guards';
import { BatchIdsDto } from 'src/core/modules/infrastructure/dto/base-batch.dto';
import { BatchResult } from 'src/core/response/domain/ok-response.interface';
import { MODULE_NAME } from 'src/core/strings/constants/module.constants';
import { ItemEntity } from 'src/modules/item-related/item/domain/entities/item.entity';
import { ItemDataOrchestrator } from 'src/modules/item-related/item/domain/usecases/item-data.orchestrator';
import { ItemDto } from 'src/modules/item-related/item/infrastructure/dto/item.dto';
@ApiTags(`${MODULE_NAME.TENANT.split('-').join(' ')} item - data`)
@Controller(`${MODULE_NAME.TENANT}/:tenant_id/item`)
@Public(false)
@ApiBearerAuth('JWT')
export class TenantItemDataController {
constructor(private orchestrator: ItemDataOrchestrator) {}
@Post()
async create(
@Param('tenant_id') tenant_id: string,
@Body() data: ItemDto,
): Promise<ItemEntity> {
return await this.orchestrator.create(data, tenant_id);
}
@Put('/batch-delete')
async batchDeleted(
@Param('tenant_id') tenant_id: string,
@Body() body: BatchIdsDto,
): Promise<BatchResult> {
return await this.orchestrator.batchDelete(body.ids);
}
@Patch(':id/active')
async active(
@Param('tenant_id') tenant_id: string,
@Param('id') dataId: string,
): Promise<String> {
return await this.orchestrator.active(dataId);
}
@Put('/batch-active')
async batchActive(
@Param('tenant_id') tenant_id: string,
@Body() body: BatchIdsDto,
): Promise<BatchResult> {
return await this.orchestrator.batchActive(body.ids);
}
@Patch(':id/confirm')
async confirm(
@Param('tenant_id') tenant_id: string,
@Param('id') dataId: string,
): Promise<String> {
return await this.orchestrator.confirm(dataId);
}
@Put('/batch-confirm')
async batchConfirm(
@Param('tenant_id') tenant_id: string,
@Body() body: BatchIdsDto,
): Promise<BatchResult> {
return await this.orchestrator.batchConfirm(body.ids);
}
@Patch(':id/inactive')
async inactive(
@Param('tenant_id') tenant_id: string,
@Param('id') dataId: string,
): Promise<String> {
return await this.orchestrator.inactive(dataId);
}
@Put('/batch-inactive')
async batchInactive(
@Param('tenant_id') tenant_id: string,
@Body() body: BatchIdsDto,
): Promise<BatchResult> {
return await this.orchestrator.batchInactive(body.ids);
}
@Put(':id')
async update(
@Param('tenant_id') tenant_id: string,
@Param('id') dataId: string,
@Body() data: ItemDto,
): Promise<ItemEntity> {
return await this.orchestrator.update(dataId, data);
}
@Delete(':id')
async delete(
@Param('tenant_id') tenant_id: string,
@Param('id') dataId: string,
): Promise<String> {
return await this.orchestrator.delete(dataId);
}
}

View File

@ -23,6 +23,8 @@ import { UserModel } from '../user/data/models/user.model';
import { UpdatePasswordTenantManager } from './domain/usecases/managers/update-password-tenant.manager'; import { UpdatePasswordTenantManager } from './domain/usecases/managers/update-password-tenant.manager';
import { UserDataService } from '../user/data/services/user-data.service'; import { UserDataService } from '../user/data/services/user-data.service';
import { UserReadService } from '../user/data/services/user-read.service'; import { UserReadService } from '../user/data/services/user-read.service';
import { TenantItemReadController } from './infrastructure/tenant-item-read.controller';
import { TenantItemDataController } from './infrastructure/tenant-item-data.controller';
@Module({ @Module({
imports: [ imports: [
@ -30,7 +32,12 @@ import { UserReadService } from '../user/data/services/user-read.service';
TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT),
CqrsModule, CqrsModule,
], ],
controllers: [TenantDataController, TenantReadController], controllers: [
TenantDataController,
TenantReadController,
TenantItemReadController,
TenantItemDataController,
],
providers: [ providers: [
IndexTenantManager, IndexTenantManager,
DetailTenantManager, DetailTenantManager,

View File

@ -1,9 +1,10 @@
import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { TABLE_NAME } from 'src/core/strings/constants/table.constants';
import { UserEntity } from '../../domain/entities/user.entity'; import { UserEntity } from '../../domain/entities/user.entity';
import { Column, Entity, JoinColumn, ManyToOne } from 'typeorm'; import { Column, Entity, JoinColumn, ManyToOne, OneToMany } 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 { UserPrivilegeModel } from 'src/modules/user-related/user-privilege/data/models/user-privilege.model'; import { UserPrivilegeModel } from 'src/modules/user-related/user-privilege/data/models/user-privilege.model';
import { UserRole } from '../../constants'; import { UserRole } from '../../constants';
import { ItemModel } from 'src/modules/item-related/item/data/models/item.model';
@Entity(TABLE_NAME.USER) @Entity(TABLE_NAME.USER)
export class UserModel export class UserModel
@ -40,4 +41,11 @@ export class UserModel
@Column('varchar', { name: 'email', nullable: true }) @Column('varchar', { name: 'email', nullable: true })
email: string; email: string;
// relasi ke tenant item
@OneToMany(() => ItemModel, (model) => model.tenant, {
onDelete: 'CASCADE',
onUpdate: 'CASCADE',
})
items: ItemModel[];
} }