From 52c82a9a4136ed10403ab6b83cbac06ed9546381 Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 11 Sep 2024 16:43:03 +0700 Subject: [PATCH 01/11] feat(SPG-953): penyesuaian API Create dan Update pengguna baru dengan menambahkan opsi admin antrian --- .../auth/domain/managers/login.manager.ts | 3 +++ .../usecases/managers/create-user.manager.ts | 5 ----- .../usecases/managers/detail-user.manager.ts | 1 + .../domain/usecases/managers/index-user.manager.ts | 1 + .../usecases/managers/update-user.manager.ts | 9 --------- .../user/infrastructure/dto/update-user.dto.ts | 14 +++----------- .../user/infrastructure/dto/user.dto.ts | 14 +++----------- 7 files changed, 11 insertions(+), 36 deletions(-) diff --git a/src/modules/configuration/auth/domain/managers/login.manager.ts b/src/modules/configuration/auth/domain/managers/login.manager.ts index d6ab1b3..bc1b26f 100644 --- a/src/modules/configuration/auth/domain/managers/login.manager.ts +++ b/src/modules/configuration/auth/domain/managers/login.manager.ts @@ -13,6 +13,8 @@ import { EventTopics } from 'src/core/strings/constants/interface.constants'; import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; import { UserLoginEvent } from '../entities/login.event'; +import { Not } from 'typeorm'; +import { UserRole } from 'src/modules/user-related/user/constants'; @Injectable() export class LoginManager extends BaseCustomManager { @@ -36,6 +38,7 @@ export class LoginManager extends BaseCustomManager { where: { username: this.data.username, status: STATUS.ACTIVE, + role: Not(UserRole.QUEUE_ADMIN), }, relations: [ 'user_privilege', diff --git a/src/modules/user-related/user/domain/usecases/managers/create-user.manager.ts b/src/modules/user-related/user/domain/usecases/managers/create-user.manager.ts index ba2c477..f57e40d 100644 --- a/src/modules/user-related/user/domain/usecases/managers/create-user.manager.ts +++ b/src/modules/user-related/user/domain/usecases/managers/create-user.manager.ts @@ -15,12 +15,7 @@ import { SALT_OR_ROUNDS } from 'src/core/strings/constants/base.constants'; @Injectable() export class CreateUserManager extends BaseCreateManager { async beforeProcess(): Promise { - let role = UserRole.STAFF; - if (this.data.is_super_admin || !this.data.user_privilege) - role = UserRole.SUPERADMIN; - Object.assign(this.data, { - role: role, password: await hashPassword(this.data.password, SALT_OR_ROUNDS), }); diff --git a/src/modules/user-related/user/domain/usecases/managers/detail-user.manager.ts b/src/modules/user-related/user/domain/usecases/managers/detail-user.manager.ts index 7e28045..7a69f00 100644 --- a/src/modules/user-related/user/domain/usecases/managers/detail-user.manager.ts +++ b/src/modules/user-related/user/domain/usecases/managers/detail-user.manager.ts @@ -36,6 +36,7 @@ export class DetailUserManager extends BaseDetailManager { `${this.tableName}.status`, `${this.tableName}.name`, `${this.tableName}.username`, + `${this.tableName}.role`, `${this.tableName}.created_at`, `${this.tableName}.creator_name`, `${this.tableName}.updated_at`, diff --git a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts index 1d6869c..dbabb32 100644 --- a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts +++ b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts @@ -41,6 +41,7 @@ export class IndexUserManager extends BaseIndexManager { `${this.tableName}.status`, `${this.tableName}.name`, `${this.tableName}.username`, + `${this.tableName}.role`, `${this.tableName}.created_at`, `${this.tableName}.creator_name`, `${this.tableName}.updated_at`, diff --git a/src/modules/user-related/user/domain/usecases/managers/update-user.manager.ts b/src/modules/user-related/user/domain/usecases/managers/update-user.manager.ts index 360bd70..3644a63 100644 --- a/src/modules/user-related/user/domain/usecases/managers/update-user.manager.ts +++ b/src/modules/user-related/user/domain/usecases/managers/update-user.manager.ts @@ -8,7 +8,6 @@ import { columnUniques, validateRelations, } from 'src/core/strings/constants/interface.constants'; -import { UserRole } from '../../../constants'; @Injectable() export class UpdateUserManager extends BaseUpdateManager { @@ -17,14 +16,6 @@ export class UpdateUserManager extends BaseUpdateManager { } async beforeProcess(): Promise { - let role = UserRole.STAFF; - if (this.data.is_super_admin || !this.data.user_privilege) - role = UserRole.SUPERADMIN; - - Object.assign(this.data, { - role: role, - }); - return; } diff --git a/src/modules/user-related/user/infrastructure/dto/update-user.dto.ts b/src/modules/user-related/user/infrastructure/dto/update-user.dto.ts index e26664d..d2daca3 100644 --- a/src/modules/user-related/user/infrastructure/dto/update-user.dto.ts +++ b/src/modules/user-related/user/infrastructure/dto/update-user.dto.ts @@ -15,14 +15,9 @@ export class UpdateUserDto extends BaseStatusDto implements UserEntity { @IsString() username: string; - @ApiProperty({ - name: 'is_super_admin', - type: Boolean, - required: true, - example: false, - }) - @IsBoolean() - is_super_admin: boolean; + @ApiProperty({ name: 'role', required: true, example: UserRole.STAFF }) + @IsString() + role: UserRole; @ApiProperty({ name: 'user_privilege', @@ -45,9 +40,6 @@ export class UpdateUserDto extends BaseStatusDto implements UserEntity { @Exclude() password: string; - @Exclude() - role: UserRole; - @Exclude() refresh_token: string; } diff --git a/src/modules/user-related/user/infrastructure/dto/user.dto.ts b/src/modules/user-related/user/infrastructure/dto/user.dto.ts index 8d96616..97156f4 100644 --- a/src/modules/user-related/user/infrastructure/dto/user.dto.ts +++ b/src/modules/user-related/user/infrastructure/dto/user.dto.ts @@ -31,14 +31,9 @@ export class UserDto extends BaseStatusDto implements UserEntity { @ValidateIf((body) => body.user_privilege) user_privilege: UserPrivilegeModel; - @ApiProperty({ - name: 'is_super_admin', - type: Boolean, - required: true, - example: false, - }) - @IsBoolean() - is_super_admin: boolean; + @ApiProperty({ name: 'role', required: true, example: UserRole.STAFF }) + @IsString() + role: UserRole; @Exclude() share_margin: number; @@ -46,9 +41,6 @@ export class UserDto extends BaseStatusDto implements UserEntity { @Exclude() email: string; - @Exclude() - role: UserRole; - @Exclude() refresh_token: string; } From 88b4c661394ed4836369a3a79fda23ae5ad83f8b Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Wed, 11 Sep 2024 18:15:16 +0700 Subject: [PATCH 02/11] feat: adjustment column dpp and total pajak at income item master --- .../transaction-report/configs/income-per-item-master.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts index c0a8d36..6b25b35 100644 --- a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts +++ b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts @@ -128,15 +128,15 @@ export default { format: DATA_FORMAT.CURRENCY, }, { - column: 'tr_item__payment_total_dpp', - query: 'tr_item.payment_total_dpp', + column: 'tr_item_bundling__payment_total_dpp', + query: 'tr_item_bundling.payment_total_dpp', label: 'DPP', type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, }, { - column: 'tr_item__payment_total_tax', - query: 'tr_item.payment_total_tax', + column: 'tr_item_bundling__payment_total_tax', + query: 'tr_item_bundling.payment_total_tax', label: 'Total Pajak', type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, From 3e920755bd37975527d8e3be14d54eea252f301f Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 12 Sep 2024 17:16:56 +0700 Subject: [PATCH 03/11] feat: create feature log user login and create auth admin queue --- src/app.module.ts | 5 + src/core/helpers/constant/index.ts | 4 + src/core/strings/constants/table.constants.ts | 2 + .../1726115025759-add-table-user-login.ts | 21 +++ .../1726122619596-update-table-user-login.ts | 23 +++ .../1726123955427-add-table-log-user-login.ts | 23 +++ src/modules/configuration/auth/auth.module.ts | 18 ++- .../domain/auth-admin-queue.orchestrator.ts | 28 ++++ .../admin-queue/login-admin-queue.manager.ts | 146 ++++++++++++++++++ .../admin-queue/logout-admin-queue.manager.ts | 65 ++++++++ .../auth/domain/managers/login.manager.ts | 14 +- .../auth/domain/managers/logout.manager.ts | 14 +- .../auth-admin-queue.controller.ts | 29 ++++ .../log/data/models/log-user-login.model.ts | 28 ++++ .../data/services/log-user-login.service.ts | 21 +++ .../domain/entities/log-user-login.entity.ts | 11 ++ .../domain/entities/log-user-login.event.ts | 5 + .../domain/handlers/log-user-login.handler.ts | 14 ++ src/modules/configuration/log/log.module.ts | 7 +- .../user/data/models/user-login.model.ts | 30 ++++ .../user/data/models/user.model.ts | 16 +- .../user/domain/entities/user-login.entity.ts | 6 + .../usecases/managers/index-user.manager.ts | 5 +- .../domain/usecases/user-data.orchestrator.ts | 1 + src/modules/user-related/user/user.module.ts | 6 +- 25 files changed, 526 insertions(+), 16 deletions(-) create mode 100644 src/core/helpers/constant/index.ts create mode 100644 src/database/migrations/1726115025759-add-table-user-login.ts create mode 100644 src/database/migrations/1726122619596-update-table-user-login.ts create mode 100644 src/database/migrations/1726123955427-add-table-log-user-login.ts create mode 100644 src/modules/configuration/auth/domain/auth-admin-queue.orchestrator.ts create mode 100644 src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts create mode 100644 src/modules/configuration/auth/domain/managers/admin-queue/logout-admin-queue.manager.ts create mode 100644 src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts create mode 100644 src/modules/configuration/log/data/models/log-user-login.model.ts create mode 100644 src/modules/configuration/log/data/services/log-user-login.service.ts create mode 100644 src/modules/configuration/log/domain/entities/log-user-login.entity.ts create mode 100644 src/modules/configuration/log/domain/entities/log-user-login.event.ts create mode 100644 src/modules/configuration/log/domain/handlers/log-user-login.handler.ts create mode 100644 src/modules/user-related/user/data/models/user-login.model.ts create mode 100644 src/modules/user-related/user/domain/entities/user-login.entity.ts diff --git a/src/app.module.ts b/src/app.module.ts index ca71ede..fabac75 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -77,6 +77,8 @@ import { ExportModule } from './modules/configuration/export/export.module'; import { TransactionDemographyModel } from './modules/transaction/transaction/data/models/transaction-demography.model'; import { SupersetModule } from './modules/configuration/superset/superset.module'; import { GateScanModule } from './modules/gates/gate.module'; +import { UserLoginModel } from './modules/user-related/user/data/models/user-login.model'; +import { LogUserLoginModel } from './modules/configuration/log/data/models/log-user-login.model'; @Module({ imports: [ @@ -102,6 +104,7 @@ import { GateScanModule } from './modules/gates/gate.module'; ItemCategoryModel, ItemRateModel, LogModel, + LogUserLoginModel, NewsModel, PaymentMethodModel, PosLogModel, @@ -118,6 +121,8 @@ import { GateScanModule } from './modules/gates/gate.module'; TransactionDemographyModel, TransactionItemBreakdownModel, UserModel, + UserLoginModel, + VipCategoryModel, VipCodeModel, diff --git a/src/core/helpers/constant/index.ts b/src/core/helpers/constant/index.ts new file mode 100644 index 0000000..fdb844c --- /dev/null +++ b/src/core/helpers/constant/index.ts @@ -0,0 +1,4 @@ +export enum LogUserType { + login = 'login', + logout = 'logout', +} diff --git a/src/core/strings/constants/table.constants.ts b/src/core/strings/constants/table.constants.ts index 9fa6af6..f6d4b21 100644 --- a/src/core/strings/constants/table.constants.ts +++ b/src/core/strings/constants/table.constants.ts @@ -24,6 +24,8 @@ export enum TABLE_NAME { TRANSACTION_TAX = 'transaction_taxes', TRANSACTION_DEMOGRAPHY = 'transaction_demographies', USER = 'users', + USER_LOGIN = 'users_login', + LOG_USER_LOGIN = 'log_users_login', USER_PRIVILEGE = 'user_privileges', USER_PRIVILEGE_CONFIGURATION = 'user_privilege_configurations', VIP_CATEGORY = 'vip_categories', diff --git a/src/database/migrations/1726115025759-add-table-user-login.ts b/src/database/migrations/1726115025759-add-table-user-login.ts new file mode 100644 index 0000000..5099208 --- /dev/null +++ b/src/database/migrations/1726115025759-add-table-user-login.ts @@ -0,0 +1,21 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTableUserLogin1726115025759 implements MigrationInterface { + name = 'AddTableUserLogin1726115025759'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TABLE "users_login" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "login_date" bigint NOT NULL, "login_token" character varying, "user_id" uuid, CONSTRAINT "REL_2a80a213b51423ce5b8211f058" UNIQUE ("user_id"), CONSTRAINT "PK_e564194a9a22f8c623354284f75" PRIMARY KEY ("id"))`, + ); + await queryRunner.query( + `ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`, + ); + await queryRunner.query(`DROP TABLE "users_login"`); + } +} diff --git a/src/database/migrations/1726122619596-update-table-user-login.ts b/src/database/migrations/1726122619596-update-table-user-login.ts new file mode 100644 index 0000000..4855807 --- /dev/null +++ b/src/database/migrations/1726122619596-update-table-user-login.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class UpdateTableUserLogin1726122619596 implements MigrationInterface { + name = 'UpdateTableUserLogin1726122619596'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`, + ); + await queryRunner.query( + `ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "users_login" DROP CONSTRAINT "FK_2a80a213b51423ce5b8211f0584"`, + ); + await queryRunner.query( + `ALTER TABLE "users_login" ADD CONSTRAINT "FK_2a80a213b51423ce5b8211f0584" FOREIGN KEY ("user_id") REFERENCES "users"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`, + ); + } +} diff --git a/src/database/migrations/1726123955427-add-table-log-user-login.ts b/src/database/migrations/1726123955427-add-table-log-user-login.ts new file mode 100644 index 0000000..91a5788 --- /dev/null +++ b/src/database/migrations/1726123955427-add-table-log-user-login.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddTableLogUserLogin1726123955427 implements MigrationInterface { + name = 'AddTableLogUserLogin1726123955427'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `CREATE TYPE "public"."log_users_login_type_enum" AS ENUM('login', 'logout')`, + ); + await queryRunner.query( + `CREATE TYPE "public"."log_users_login_role_enum" AS ENUM('superadmin', 'staff', 'tenant', 'queue_admin')`, + ); + await queryRunner.query( + `CREATE TABLE "log_users_login" ("id" uuid NOT NULL DEFAULT uuid_generate_v4(), "type" "public"."log_users_login_type_enum", "role" "public"."log_users_login_role_enum", "user_id" uuid, "username" character varying, "created_at" bigint, CONSTRAINT "PK_75141588aa6ee560504f7d3adce" PRIMARY KEY ("id"))`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query(`DROP TABLE "log_users_login"`); + await queryRunner.query(`DROP TYPE "public"."log_users_login_role_enum"`); + await queryRunner.query(`DROP TYPE "public"."log_users_login_type_enum"`); + } +} diff --git a/src/modules/configuration/auth/auth.module.ts b/src/modules/configuration/auth/auth.module.ts index 84b5ed8..c16d27f 100644 --- a/src/modules/configuration/auth/auth.module.ts +++ b/src/modules/configuration/auth/auth.module.ts @@ -9,6 +9,10 @@ import { CqrsModule } from '@nestjs/cqrs'; import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; import { UserDataService } from 'src/modules/user-related/user/data/services/user-data.service'; +import { AuthAdminQueueController } from './infrastructure/auth-admin-queue.controller'; +import { AuthAdminQueueOrchestrator } from './domain/auth-admin-queue.orchestrator'; +import { LoginAdminQueueManager } from './domain/managers/admin-queue/login-admin-queue.manager'; +import { LogoutAdminQueueManager } from './domain/managers/admin-queue/logout-admin-queue.manager'; @Module({ imports: [ @@ -16,7 +20,17 @@ import { UserDataService } from 'src/modules/user-related/user/data/services/use TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), CqrsModule, ], - controllers: [AuthController], - providers: [LoginManager, LogoutManager, UserDataService, AuthOrchestrator], + controllers: [AuthController, AuthAdminQueueController], + providers: [ + LoginManager, + LogoutManager, + UserDataService, + AuthOrchestrator, + + // ADMIN QUEUE + AuthAdminQueueOrchestrator, + LoginAdminQueueManager, + LogoutAdminQueueManager, + ], }) export class AuthModule {} diff --git a/src/modules/configuration/auth/domain/auth-admin-queue.orchestrator.ts b/src/modules/configuration/auth/domain/auth-admin-queue.orchestrator.ts new file mode 100644 index 0000000..0a755bf --- /dev/null +++ b/src/modules/configuration/auth/domain/auth-admin-queue.orchestrator.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@nestjs/common'; +import { LoginAdminQueueManager } from './managers/admin-queue/login-admin-queue.manager'; +import { LogoutAdminQueueManager } from './managers/admin-queue/logout-admin-queue.manager'; +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UserDataService } from 'src/modules/user-related/user/data/services/user-data.service'; + +@Injectable() +export class AuthAdminQueueOrchestrator { + constructor( + private loginManager: LoginAdminQueueManager, + private logoutManager: LogoutAdminQueueManager, + private serviceData: UserDataService, + ) {} + + async login(data): Promise { + this.loginManager.setData(data); + this.loginManager.setService(this.serviceData, TABLE_NAME.USER); + await this.loginManager.execute(); + return this.loginManager.getResult(); + } + + async logout(id?: string): Promise { + if (id) this.logoutManager.setData({ id }); + this.logoutManager.setService(this.serviceData, TABLE_NAME.USER); + await this.logoutManager.execute(); + return this.logoutManager.getResult(); + } +} diff --git a/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts b/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts new file mode 100644 index 0000000..465e4eb --- /dev/null +++ b/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts @@ -0,0 +1,146 @@ +import { + HttpStatus, + Inject, + Injectable, + Logger, + UnauthorizedException, +} from '@nestjs/common'; +import { validatePassword } from 'src/core/helpers/password/bcrypt.helpers'; +import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager'; +import { SessionService } from 'src/core/sessions'; +import { STATUS } from 'src/core/strings/constants/base.constants'; +import { EventTopics } from 'src/core/strings/constants/interface.constants'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; +import { In } from 'typeorm'; +import { UserRole } from 'src/modules/user-related/user/constants'; +import { LogUserType } from 'src/core/helpers/constant'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; + +@Injectable() +export class LoginAdminQueueManager extends BaseCustomManager { + @Inject() + protected session: SessionService; + protected token; + protected userLogin; + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async process(): Promise { + // get user active by username + this.userLogin = await this.dataService.getOneByOptions({ + where: { + username: this.data.username, + status: STATUS.ACTIVE, + role: In([UserRole.QUEUE_ADMIN, UserRole.SUPERADMIN]), + }, + relations: ['user_login'], + }); + if (!this.userLogin) this.throwError(); + + // validasi password + const valid = await validatePassword( + this.data.password, + this.userLogin?.password, + ); + if (!valid) this.throwError(); + + if ( + this.userLogin.user_login && + this.userLogin.role === UserRole.QUEUE_ADMIN + ) { + throw new UnauthorizedException({ + statusCode: HttpStatus.UNAUTHORIZED, + message: `Gagal! akun anda sudah login di perangkat lain.`, + error: 'Unauthorized', + }); + } + + // * Disini untuk isi token + const tokenData = { + id: this.userLogin.id, + name: this.userLogin.name, + username: this.userLogin.username, + role: this.userLogin.role, + user_privilege_id: this.userLogin.user_privilege_id, + }; + + Logger.debug('Sign Token Admin Queue', 'LoginAdminQueueManager'); + this.token = this.session.createAccessToken(tokenData); + + Logger.debug('refreshToken Admin Queue', 'LoginAdminQueueManager'); + const refreshToken = this.session.createAccessToken(tokenData); + + Logger.debug('Update Refresh Token Admin Queue', 'LoginAdminQueueManager'); + + const newDataUser = { refresh_token: refreshToken }; + if (this.userLogin.role === UserRole.QUEUE_ADMIN) { + Object.assign(newDataUser, { + user_login: { + user_id: this.userLogin.id, + login_token: this.token, + login_date: new Date().getTime(), + }, + }); + } + // Update refresh token + await this.dataService.update( + this.queryRunner, + this.entityTarget, + { id: this.userLogin.id }, + newDataUser, + ); + await this.publishEvents(); + + Logger.debug('Process Login Admin Queue Done', 'LoginAdminQueueManager'); + return; + } + + async afterProcess(): Promise { + return; + } + + getResult() { + return { + id: this.userLogin.id, + name: this.userLogin.name, + username: this.userLogin.username, + role: this.userLogin.role, + token: this.token, + }; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: LogUserLoginEvent, + data: { + type: LogUserType.login, + role: this.userLogin.role, + user_id: this.userLogin.id, + username: this.userLogin.username, + created_at: new Date().getTime(), + }, + }, + ]; + } + + // !throw errornya akan sama, untuk security + throwError() { + throw new UnauthorizedException({ + statusCode: HttpStatus.UNAUTHORIZED, + message: `Gagal! username atau password tidak sesuai`, + error: 'Unauthorized', + }); + } +} diff --git a/src/modules/configuration/auth/domain/managers/admin-queue/logout-admin-queue.manager.ts b/src/modules/configuration/auth/domain/managers/admin-queue/logout-admin-queue.manager.ts new file mode 100644 index 0000000..b40f7ad --- /dev/null +++ b/src/modules/configuration/auth/domain/managers/admin-queue/logout-admin-queue.manager.ts @@ -0,0 +1,65 @@ +import { LogUserType } from 'src/core/helpers/constant'; +import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base-custom.manager'; +import { EventTopics } from 'src/core/strings/constants/interface.constants'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; +import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; +import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; + +export class LogoutAdminQueueManager extends BaseCustomManager { + protected userLogin; + + async validateProcess(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async process(): Promise { + const id = this.data?.id ?? this.user.id; + + this.userLogin = await this.dataService.getOneByOptions({ + where: { id }, + }); + + await this.dataService.update( + this.queryRunner, + this.entityTarget, + { id: this.userLogin.id }, + { + refresh_token: null, + user_login: null, + }, + ); + await this.publishEvents(); + return; + } + + async afterProcess(): Promise { + return; + } + + getResult() { + return `Success Logout User`; + } + + get entityTarget(): any { + return UserModel; + } + + get eventTopics(): EventTopics[] { + return [ + { + topic: LogUserLoginEvent, + data: { + type: LogUserType.logout, + role: this.userLogin.role, + user_id: this.userLogin.id, + username: this.userLogin.name, + created_at: new Date().getTime(), + }, + }, + ]; + } +} diff --git a/src/modules/configuration/auth/domain/managers/login.manager.ts b/src/modules/configuration/auth/domain/managers/login.manager.ts index bc1b26f..0ef6596 100644 --- a/src/modules/configuration/auth/domain/managers/login.manager.ts +++ b/src/modules/configuration/auth/domain/managers/login.manager.ts @@ -12,9 +12,10 @@ import { STATUS } from 'src/core/strings/constants/base.constants'; import { EventTopics } from 'src/core/strings/constants/interface.constants'; import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; -import { UserLoginEvent } from '../entities/login.event'; import { Not } from 'typeorm'; import { UserRole } from 'src/modules/user-related/user/constants'; +import { LogUserType } from 'src/core/helpers/constant'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; @Injectable() export class LoginManager extends BaseCustomManager { @@ -80,6 +81,7 @@ export class LoginManager extends BaseCustomManager { }, ); + await this.publishEvents(); Logger.debug('Process Login Done', 'LoginManager'); return; } @@ -122,11 +124,13 @@ export class LoginManager extends BaseCustomManager { get eventTopics(): EventTopics[] { return [ { - topic: UserLoginEvent, + topic: LogUserLoginEvent, data: { - id: this.userLogin.id, - type: 'login', - timestamp: new Date().getTime(), + type: LogUserType.login, + role: this.userLogin.role, + user_id: this.userLogin.id, + username: this.userLogin.username, + created_at: new Date().getTime(), }, }, ]; diff --git a/src/modules/configuration/auth/domain/managers/logout.manager.ts b/src/modules/configuration/auth/domain/managers/logout.manager.ts index 482a84c..cb1b18b 100644 --- a/src/modules/configuration/auth/domain/managers/logout.manager.ts +++ b/src/modules/configuration/auth/domain/managers/logout.manager.ts @@ -2,7 +2,8 @@ import { BaseCustomManager } from 'src/core/modules/domain/usecase/managers/base import { EventTopics } from 'src/core/strings/constants/interface.constants'; import { UserModel } from 'src/modules/user-related/user/data/models/user.model'; import { UserEntity } from 'src/modules/user-related/user/domain/entities/user.entity'; -import { UserLogoutEvent } from '../entities/logout.event'; +import { LogUserType } from 'src/core/helpers/constant'; +import { LogUserLoginEvent } from 'src/modules/configuration/log/domain/entities/log-user-login.event'; export class LogoutManager extends BaseCustomManager { async validateProcess(): Promise { @@ -23,6 +24,7 @@ export class LogoutManager extends BaseCustomManager { }, ); + await this.publishEvents(); return; } @@ -41,11 +43,13 @@ export class LogoutManager extends BaseCustomManager { get eventTopics(): EventTopics[] { return [ { - topic: UserLogoutEvent, + topic: LogUserLoginEvent, data: { - id: this.user.id, - type: 'logout', - timestamp: new Date().getTime(), + type: LogUserType.logout, + role: this.user.role, + user_id: this.user.id, + username: this.user.name, + created_at: new Date().getTime(), }, }, ]; diff --git a/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts b/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts new file mode 100644 index 0000000..1503130 --- /dev/null +++ b/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts @@ -0,0 +1,29 @@ +import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common'; +import { ExcludePrivilege, Public } from 'src/core/guards'; +import { ApiBearerAuth } from '@nestjs/swagger'; +import { LoginDto } from './dto/login.dto'; +import { AuthAdminQueueOrchestrator } from '../domain/auth-admin-queue.orchestrator'; + +@Controller('v1/auth/queue') +export class AuthAdminQueueController { + constructor(private orchestrator: AuthAdminQueueOrchestrator) {} + + @Post() + @Public(true) + async login(@Body() body: LoginDto) { + return await this.orchestrator.login(body); + } + + @ApiBearerAuth('JWT') + @Public(false) + @ExcludePrivilege() + @Delete('logout') + async logoout() { + return await this.orchestrator.logout(); + } + + @Put(':id/logout') + async logoutQueueAdmin(@Param('id') dataId: string) { + return await this.orchestrator.logout(dataId); + } +} diff --git a/src/modules/configuration/log/data/models/log-user-login.model.ts b/src/modules/configuration/log/data/models/log-user-login.model.ts new file mode 100644 index 0000000..cda2429 --- /dev/null +++ b/src/modules/configuration/log/data/models/log-user-login.model.ts @@ -0,0 +1,28 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UserEntity } from '../../../../user-related/user/domain/entities/user.entity'; +import { Column, Entity } from 'typeorm'; +import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model'; +import { LogUserLoginEntity } from '../../domain/entities/log-user-login.entity'; +import { UserRole } from '../../../../user-related/user/constants'; +import { LogUserType } from 'src/core/helpers/constant'; + +@Entity(TABLE_NAME.LOG_USER_LOGIN) +export class LogUserLoginModel + extends BaseCoreModel + implements LogUserLoginEntity +{ + @Column({ type: 'enum', enum: LogUserType, nullable: true }) + type: LogUserType; + + @Column({ type: 'enum', enum: UserRole, nullable: true }) + role: UserRole; + + @Column({ type: 'uuid', nullable: true }) + user_id: string; + + @Column({ type: 'varchar', nullable: true }) + username: string; + + @Column({ type: 'bigint', nullable: true }) + created_at: number; +} diff --git a/src/modules/configuration/log/data/services/log-user-login.service.ts b/src/modules/configuration/log/data/services/log-user-login.service.ts new file mode 100644 index 0000000..5d1865c --- /dev/null +++ b/src/modules/configuration/log/data/services/log-user-login.service.ts @@ -0,0 +1,21 @@ +import { BaseDataService } from 'src/core/modules/data/service/base-data.service'; +import { Injectable } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Repository } from 'typeorm'; +import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; +import { LogUserLoginEntity } from '../../domain/entities/log-user-login.entity'; +import { LogUserLoginModel } from '../models/log-user-login.model'; + +@Injectable() +export class LogUserLoginService extends BaseDataService { + constructor( + @InjectRepository(LogUserLoginModel, CONNECTION_NAME.DEFAULT) + private repo: Repository, + ) { + super(repo); + } + + async saveData(data) { + this.repo.save(data); + } +} diff --git a/src/modules/configuration/log/domain/entities/log-user-login.entity.ts b/src/modules/configuration/log/domain/entities/log-user-login.entity.ts new file mode 100644 index 0000000..308c43c --- /dev/null +++ b/src/modules/configuration/log/domain/entities/log-user-login.entity.ts @@ -0,0 +1,11 @@ +import { LogUserType } from 'src/core/helpers/constant'; +import { UserRole } from '../../../../user-related/user/constants'; +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; + +export interface LogUserLoginEntity extends BaseCoreEntity { + type: LogUserType; + role: UserRole; + user_id: string; + username: string; + created_at: number; +} diff --git a/src/modules/configuration/log/domain/entities/log-user-login.event.ts b/src/modules/configuration/log/domain/entities/log-user-login.event.ts new file mode 100644 index 0000000..813681f --- /dev/null +++ b/src/modules/configuration/log/domain/entities/log-user-login.event.ts @@ -0,0 +1,5 @@ +import { IEvent } from 'src/core/strings/constants/interface.constants'; + +export class LogUserLoginEvent { + constructor(public readonly data: IEvent) {} +} diff --git a/src/modules/configuration/log/domain/handlers/log-user-login.handler.ts b/src/modules/configuration/log/domain/handlers/log-user-login.handler.ts new file mode 100644 index 0000000..c22ec5d --- /dev/null +++ b/src/modules/configuration/log/domain/handlers/log-user-login.handler.ts @@ -0,0 +1,14 @@ +import { EventsHandler, IEventHandler } from '@nestjs/cqrs'; + +import { LogUserLoginEvent } from '../entities/log-user-login.event'; +import { LogUserLoginService } from '../../data/services/log-user-login.service'; + +@EventsHandler(LogUserLoginEvent) +export class LogUserLoginHandler implements IEventHandler { + constructor(private service: LogUserLoginService) {} + + async handle(event: LogUserLoginEvent) { + const data = event.data.data; + await this.service.saveData(data); + } +} diff --git a/src/modules/configuration/log/log.module.ts b/src/modules/configuration/log/log.module.ts index 06002f9..7eaa8cc 100644 --- a/src/modules/configuration/log/log.module.ts +++ b/src/modules/configuration/log/log.module.ts @@ -12,12 +12,15 @@ import { LogService } from './data/services/log.service'; import { PosLogModel } from './data/models/pos-log.model'; import { PosLogService } from './data/services/pos-log.service'; import { RecordPosLogHandler } from './domain/handlers/pos-log.handler'; +import { LogUserLoginModel } from './data/models/log-user-login.model'; +import { LogUserLoginService } from './data/services/log-user-login.service'; +import { LogUserLoginHandler } from './domain/handlers/log-user-login.handler'; @Module({ imports: [ ConfigModule.forRoot(), TypeOrmModule.forFeature( - [LogModel, ErrorLogModel, PosLogModel], + [LogModel, ErrorLogModel, PosLogModel, LogUserLoginModel], CONNECTION_NAME.DEFAULT, ), CqrsModule, @@ -27,10 +30,12 @@ import { RecordPosLogHandler } from './domain/handlers/pos-log.handler'; RecordLogHandler, RecordPosLogHandler, RecordErrorLogHandler, + LogUserLoginHandler, LogService, PosLogService, ErrorLogService, + LogUserLoginService, ], }) export class LogModule {} diff --git a/src/modules/user-related/user/data/models/user-login.model.ts b/src/modules/user-related/user/data/models/user-login.model.ts new file mode 100644 index 0000000..9f39afe --- /dev/null +++ b/src/modules/user-related/user/data/models/user-login.model.ts @@ -0,0 +1,30 @@ +import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; +import { UserEntity } from '../../domain/entities/user.entity'; +import { Column, Entity, JoinColumn, OneToOne } from 'typeorm'; +import { UserLoginEntity } from '../../domain/entities/user-login.entity'; +import { UserModel } from './user.model'; +import { BaseCoreModel } from 'src/core/modules/data/model/base-core.model'; + +@Entity(TABLE_NAME.USER_LOGIN) +export class UserLoginModel + extends BaseCoreModel + implements UserLoginEntity +{ + @Column({ type: 'bigint', nullable: false }) + login_date: number; + + @Column({ type: 'varchar', name: 'login_token', nullable: true }) + login_token: string; + + @Column('varchar', { name: 'user_id', nullable: true }) + user_id: string; + + @OneToOne(() => UserModel, (model) => model.user_login, { + onDelete: 'CASCADE', + onUpdate: 'CASCADE', + nullable: false, + orphanedRowAction: 'delete', + }) + @JoinColumn({ name: 'user_id' }) + user: UserModel; +} diff --git a/src/modules/user-related/user/data/models/user.model.ts b/src/modules/user-related/user/data/models/user.model.ts index 55eb6a9..1d8c33c 100644 --- a/src/modules/user-related/user/data/models/user.model.ts +++ b/src/modules/user-related/user/data/models/user.model.ts @@ -1,10 +1,18 @@ import { TABLE_NAME } from 'src/core/strings/constants/table.constants'; import { UserEntity } from '../../domain/entities/user.entity'; -import { Column, Entity, JoinColumn, ManyToOne, OneToMany } from 'typeorm'; +import { + Column, + Entity, + JoinColumn, + ManyToOne, + OneToMany, + OneToOne, +} from 'typeorm'; 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 { UserRole } from '../../constants'; import { ItemModel } from 'src/modules/item-related/item/data/models/item.model'; +import { UserLoginModel } from './user-login.model'; @Entity(TABLE_NAME.USER) export class UserModel @@ -48,4 +56,10 @@ export class UserModel onUpdate: 'CASCADE', }) items: ItemModel[]; + + // relasi ke user login for admin queue + @OneToOne(() => UserLoginModel, (model) => model.user, { + cascade: true, + }) + user_login: UserLoginModel; } diff --git a/src/modules/user-related/user/domain/entities/user-login.entity.ts b/src/modules/user-related/user/domain/entities/user-login.entity.ts new file mode 100644 index 0000000..975223c --- /dev/null +++ b/src/modules/user-related/user/domain/entities/user-login.entity.ts @@ -0,0 +1,6 @@ +import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entity'; + +export interface UserLoginEntity extends BaseCoreEntity { + login_date: number; + login_token: string; +} diff --git a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts index dbabb32..92eaa14 100644 --- a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts +++ b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts @@ -28,7 +28,7 @@ export class IndexUserManager extends BaseIndexManager { joinRelations: [], // relation join and select (relasi yang ingin ditampilkan), - selectRelations: ['user_privilege'], + selectRelations: ['user_privilege', 'user_login'], // relation yang hanya ingin dihitung (akan return number) countRelations: [], @@ -49,6 +49,9 @@ export class IndexUserManager extends BaseIndexManager { 'user_privilege.id', 'user_privilege.name', + + 'user_login.id', + 'user_login.login_date', ]; } diff --git a/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts b/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts index b77e469..ea2b9ec 100644 --- a/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts +++ b/src/modules/user-related/user/domain/usecases/user-data.orchestrator.ts @@ -31,6 +31,7 @@ export class UserDataOrchestrator extends BaseDataTransactionOrchestrator Date: Thu, 12 Sep 2024 17:54:48 +0700 Subject: [PATCH 04/11] feat(SPG-885): add api get item queue --- .../domain/usecases/item-read.orchestrator.ts | 9 +++ .../managers/index-queue-item.manager.ts | 68 +++++++++++++++++++ .../dto/filter-item-queue.dto.ts | 6 ++ .../infrastructure/item-read.controller.ts | 18 ++++- src/modules/item-related/item/item.module.ts | 14 +++- 5 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 src/modules/item-related/item/domain/usecases/managers/index-queue-item.manager.ts create mode 100644 src/modules/item-related/item/infrastructure/dto/filter-item-queue.dto.ts diff --git a/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts b/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts index 5f7a8eb..b5aa996 100644 --- a/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts +++ b/src/modules/item-related/item/domain/usecases/item-read.orchestrator.ts @@ -10,11 +10,13 @@ import { ItemRateReadService } from 'src/modules/item-related/item-rate/data/ser import { FilterItemRateDto } from 'src/modules/item-related/item-rate/infrastructure/dto/filter-item-rate.dto'; import { ItemRateEntity } from 'src/modules/item-related/item-rate/domain/entities/item-rate.entity'; import { IndexItemRatesManager } from './managers/index-item-rates.manager'; +import { IndexItemQueueManager } from './managers/index-queue-item.manager'; @Injectable() export class ItemReadOrchestrator extends BaseReadOrchestrator { constructor( private indexManager: IndexItemManager, + private indexQueueManager: IndexItemQueueManager, private detailManager: DetailItemManager, private indexRateManager: IndexItemRatesManager, private serviceData: ItemReadService, @@ -30,6 +32,13 @@ export class ItemReadOrchestrator extends BaseReadOrchestrator { return this.indexManager.getResult(); } + async indexQueue(params): Promise> { + this.indexQueueManager.setFilterParam(params); + this.indexQueueManager.setService(this.serviceData, TABLE_NAME.ITEM); + await this.indexQueueManager.execute(); + return this.indexQueueManager.getResult(); + } + async detail(dataId: string): Promise { this.detailManager.setData(dataId); this.detailManager.setService(this.serviceData, TABLE_NAME.ITEM); diff --git a/src/modules/item-related/item/domain/usecases/managers/index-queue-item.manager.ts b/src/modules/item-related/item/domain/usecases/managers/index-queue-item.manager.ts new file mode 100644 index 0000000..b1fe5b3 --- /dev/null +++ b/src/modules/item-related/item/domain/usecases/managers/index-queue-item.manager.ts @@ -0,0 +1,68 @@ +import { Injectable } from '@nestjs/common'; +import { BaseIndexManager } from 'src/core/modules/domain/usecase/managers/base-index.manager'; +import { ItemEntity } from '../../entities/item.entity'; +import { SelectQueryBuilder } from 'typeorm'; +import { + Param, + RelationParam, +} from 'src/core/modules/domain/entities/base-filter.entity'; +import { STATUS } from 'src/core/strings/constants/base.constants'; + +@Injectable() +export class IndexItemQueueManager extends BaseIndexManager { + async prepareData(): Promise { + return; + } + + async beforeProcess(): Promise { + return; + } + + async afterProcess(): Promise { + return; + } + + get relations(): RelationParam { + return { + // relation only join (for query purpose) + joinRelations: [], + + // relation join and select (relasi yang ingin ditampilkan), + selectRelations: [], + + // relation yang hanya ingin dihitung (akan return number) + countRelations: [], + }; + } + + get selects(): string[] { + return [ + `${this.tableName}.id`, + `${this.tableName}.created_at`, + `${this.tableName}.name`, + ]; + } + + get specificFilter(): Param[] { + return [ + { + cols: `${this.tableName}.name`, + data: this.filterParam.names, + }, + ]; + } + + setQueryFilter( + queryBuilder: SelectQueryBuilder, + ): SelectQueryBuilder { + queryBuilder.andWhere(`${this.tableName}.status = :status`, { + status: STATUS.ACTIVE, + }); + + queryBuilder.andWhere(`${this.tableName}.use_queue = :queue`, { + queue: true, + }); + + return queryBuilder; + } +} diff --git a/src/modules/item-related/item/infrastructure/dto/filter-item-queue.dto.ts b/src/modules/item-related/item/infrastructure/dto/filter-item-queue.dto.ts new file mode 100644 index 0000000..86f2d47 --- /dev/null +++ b/src/modules/item-related/item/infrastructure/dto/filter-item-queue.dto.ts @@ -0,0 +1,6 @@ +import { BaseFilterDto } from 'src/core/modules/infrastructure/dto/base-filter.dto'; +import { BaseFilterEntity } from 'src/core/modules/domain/entities/base-filter.entity'; + +export class FilterItemQueueDto + extends BaseFilterDto + implements BaseFilterEntity {} diff --git a/src/modules/item-related/item/infrastructure/item-read.controller.ts b/src/modules/item-related/item/infrastructure/item-read.controller.ts index a889ac0..91c7ee5 100644 --- a/src/modules/item-related/item/infrastructure/item-read.controller.ts +++ b/src/modules/item-related/item/infrastructure/item-read.controller.ts @@ -6,7 +6,7 @@ import { ItemEntity } from '../domain/entities/item.entity'; import { ItemReadOrchestrator } from '../domain/usecases/item-read.orchestrator'; import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; import { MODULE_NAME } from 'src/core/strings/constants/module.constants'; -import { Public } from 'src/core/guards'; +import { ExcludePrivilege, Public } from 'src/core/guards'; import { ItemRateEntity } from '../../item-rate/domain/entities/item-rate.entity'; import { FilterItemRateDto } from '../../item-rate/infrastructure/dto/filter-item-rate.dto'; @@ -40,3 +40,19 @@ export class ItemReadController { return await this.orchestrator.indexRate(params); } } + +@ApiTags(`Item Queue - Read`) +@Controller(`v1/item-queue`) +@Public(true) +export class ItemReadQueueController { + constructor(private orchestrator: ItemReadOrchestrator) {} + + @Get() + @Pagination() + @ExcludePrivilege() + async indexQueue( + @Query() params: FilterItemDto, + ): Promise> { + return await this.orchestrator.indexQueue(params); + } +} diff --git a/src/modules/item-related/item/item.module.ts b/src/modules/item-related/item/item.module.ts index bb35be2..76e0fe6 100644 --- a/src/modules/item-related/item/item.module.ts +++ b/src/modules/item-related/item/item.module.ts @@ -4,7 +4,10 @@ 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 { + ItemReadController, + ItemReadQueueController, +} 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'; @@ -26,6 +29,7 @@ import { ItemRateModel } from '../item-rate/data/models/item-rate.model'; import { ItemRateReadService } from '../item-rate/data/services/item-rate-read.service'; import { IndexItemRatesManager } from './domain/usecases/managers/index-item-rates.manager'; import { UpdateItemRatePriceManager } from './domain/usecases/managers/update-item-rate-price.manager'; +import { IndexItemQueueManager } from './domain/usecases/managers/index-queue-item.manager'; @Global() @Module({ @@ -37,9 +41,14 @@ import { UpdateItemRatePriceManager } from './domain/usecases/managers/update-it ), CqrsModule, ], - controllers: [ItemDataController, ItemReadController], + controllers: [ + ItemDataController, + ItemReadController, + ItemReadQueueController, + ], providers: [ IndexItemManager, + IndexItemQueueManager, IndexItemRatesManager, DetailItemManager, CreateItemManager, @@ -63,6 +72,7 @@ import { UpdateItemRatePriceManager } from './domain/usecases/managers/update-it ], exports: [ IndexItemManager, + IndexItemQueueManager, IndexItemRatesManager, DetailItemManager, CreateItemManager, From c897e4fcde34e5469b1d1ee3435c44ed3db6ec7b Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Thu, 12 Sep 2024 19:17:46 +0700 Subject: [PATCH 05/11] feat(SPG-885): add validation login admin queue --- .../1726139426994-add-column-item_id.ts | 17 +++++++ .../1726141393404-add-column-item_name.ts | 23 +++++++++ src/modules/configuration/auth/auth.module.ts | 6 ++- .../admin-queue/login-admin-queue.manager.ts | 51 +++++++++++++------ .../auth-admin-queue.controller.ts | 4 +- .../auth/infrastructure/dto/login.dto.ts | 18 +++++++ .../configuration/couch/couch.module.ts | 2 + .../log/data/models/log-user-login.model.ts | 6 +++ .../domain/entities/log-user-login.entity.ts | 2 + .../user-related/tenant/tenant.module.ts | 6 ++- .../user/data/models/user-login.model.ts | 6 +++ .../user/data/services/user-data.service.ts | 12 ++++- .../user/domain/entities/user-login.entity.ts | 2 + .../usecases/managers/index-user.manager.ts | 2 + 14 files changed, 136 insertions(+), 21 deletions(-) create mode 100644 src/database/migrations/1726139426994-add-column-item_id.ts create mode 100644 src/database/migrations/1726141393404-add-column-item_name.ts diff --git a/src/database/migrations/1726139426994-add-column-item_id.ts b/src/database/migrations/1726139426994-add-column-item_id.ts new file mode 100644 index 0000000..30ce865 --- /dev/null +++ b/src/database/migrations/1726139426994-add-column-item_id.ts @@ -0,0 +1,17 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddColumnItemId1726139426994 implements MigrationInterface { + name = 'AddColumnItemId1726139426994'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query(`ALTER TABLE "users_login" ADD "item_id" uuid`); + await queryRunner.query(`ALTER TABLE "log_users_login" ADD "item_id" uuid`); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "log_users_login" DROP COLUMN "item_id"`, + ); + await queryRunner.query(`ALTER TABLE "users_login" DROP COLUMN "item_id"`); + } +} diff --git a/src/database/migrations/1726141393404-add-column-item_name.ts b/src/database/migrations/1726141393404-add-column-item_name.ts new file mode 100644 index 0000000..f67870b --- /dev/null +++ b/src/database/migrations/1726141393404-add-column-item_name.ts @@ -0,0 +1,23 @@ +import { MigrationInterface, QueryRunner } from 'typeorm'; + +export class AddColumnItemName1726141393404 implements MigrationInterface { + name = 'AddColumnItemName1726141393404'; + + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "users_login" ADD "item_name" character varying`, + ); + await queryRunner.query( + `ALTER TABLE "log_users_login" ADD "item_name" character varying`, + ); + } + + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.query( + `ALTER TABLE "log_users_login" DROP COLUMN "item_name"`, + ); + await queryRunner.query( + `ALTER TABLE "users_login" DROP COLUMN "item_name"`, + ); + } +} diff --git a/src/modules/configuration/auth/auth.module.ts b/src/modules/configuration/auth/auth.module.ts index c16d27f..82e0c62 100644 --- a/src/modules/configuration/auth/auth.module.ts +++ b/src/modules/configuration/auth/auth.module.ts @@ -13,11 +13,15 @@ import { AuthAdminQueueController } from './infrastructure/auth-admin-queue.cont import { AuthAdminQueueOrchestrator } from './domain/auth-admin-queue.orchestrator'; import { LoginAdminQueueManager } from './domain/managers/admin-queue/login-admin-queue.manager'; import { LogoutAdminQueueManager } from './domain/managers/admin-queue/logout-admin-queue.manager'; +import { UserLoginModel } from 'src/modules/user-related/user/data/models/user-login.model'; @Module({ imports: [ ConfigModule.forRoot(), - TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), + TypeOrmModule.forFeature( + [UserModel, UserLoginModel], + CONNECTION_NAME.DEFAULT, + ), CqrsModule, ], controllers: [AuthController, AuthAdminQueueController], diff --git a/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts b/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts index 465e4eb..51ac335 100644 --- a/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts +++ b/src/modules/configuration/auth/domain/managers/admin-queue/login-admin-queue.manager.ts @@ -33,6 +33,10 @@ export class LoginAdminQueueManager extends BaseCustomManager { } async process(): Promise { + const itemLogin = await this.dataService.getLoginUserByItem( + this.data.item_id, + ); + // get user active by username this.userLogin = await this.dataService.getOneByOptions({ where: { @@ -42,6 +46,7 @@ export class LoginAdminQueueManager extends BaseCustomManager { }, relations: ['user_login'], }); + if (!this.userLogin) this.throwError(); // validasi password @@ -51,13 +56,22 @@ export class LoginAdminQueueManager extends BaseCustomManager { ); if (!valid) this.throwError(); - if ( - this.userLogin.user_login && - this.userLogin.role === UserRole.QUEUE_ADMIN - ) { + const userLoginItem = await this.dataService.getOneByOptions({ + where: { + id: itemLogin?.user_id, + }, + }); + + if (this.userLogin.user_login) { throw new UnauthorizedException({ statusCode: HttpStatus.UNAUTHORIZED, - message: `Gagal! akun anda sudah login di perangkat lain.`, + message: `Akun anda sudah login di perangkat lain.`, + error: 'Unauthorized', + }); + } else if (itemLogin) { + throw new UnauthorizedException({ + statusCode: HttpStatus.UNAUTHORIZED, + message: `"${userLoginItem.name}" masih login sebagai admin antrian `, error: 'Unauthorized', }); } @@ -69,6 +83,8 @@ export class LoginAdminQueueManager extends BaseCustomManager { username: this.userLogin.username, role: this.userLogin.role, user_privilege_id: this.userLogin.user_privilege_id, + item_id: this.data.item_id, + item_name: this.data.item_name, }; Logger.debug('Sign Token Admin Queue', 'LoginAdminQueueManager'); @@ -79,22 +95,21 @@ export class LoginAdminQueueManager extends BaseCustomManager { Logger.debug('Update Refresh Token Admin Queue', 'LoginAdminQueueManager'); - const newDataUser = { refresh_token: refreshToken }; - if (this.userLogin.role === UserRole.QUEUE_ADMIN) { - Object.assign(newDataUser, { - user_login: { - user_id: this.userLogin.id, - login_token: this.token, - login_date: new Date().getTime(), - }, - }); - } // Update refresh token await this.dataService.update( this.queryRunner, this.entityTarget, { id: this.userLogin.id }, - newDataUser, + { + refresh_token: refreshToken, + user_login: { + user_id: this.userLogin.id, + login_token: this.token, + login_date: new Date().getTime(), + item_id: this.data.item_id, + item_name: this.data.item_name, + }, + }, ); await this.publishEvents(); @@ -113,6 +128,8 @@ export class LoginAdminQueueManager extends BaseCustomManager { username: this.userLogin.username, role: this.userLogin.role, token: this.token, + item_id: this.data.item_id, + item_name: this.data.item_name, }; } @@ -130,6 +147,8 @@ export class LoginAdminQueueManager extends BaseCustomManager { user_id: this.userLogin.id, username: this.userLogin.username, created_at: new Date().getTime(), + item_id: this.data.item_id, + item_name: this.data.item_name, }, }, ]; diff --git a/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts b/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts index 1503130..41da84e 100644 --- a/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts +++ b/src/modules/configuration/auth/infrastructure/auth-admin-queue.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Delete, Param, Post, Put } from '@nestjs/common'; import { ExcludePrivilege, Public } from 'src/core/guards'; import { ApiBearerAuth } from '@nestjs/swagger'; -import { LoginDto } from './dto/login.dto'; +import { LoginQueueDto } from './dto/login.dto'; import { AuthAdminQueueOrchestrator } from '../domain/auth-admin-queue.orchestrator'; @Controller('v1/auth/queue') @@ -10,7 +10,7 @@ export class AuthAdminQueueController { @Post() @Public(true) - async login(@Body() body: LoginDto) { + async login(@Body() body: LoginQueueDto) { return await this.orchestrator.login(body); } diff --git a/src/modules/configuration/auth/infrastructure/dto/login.dto.ts b/src/modules/configuration/auth/infrastructure/dto/login.dto.ts index 267d433..7036ea8 100644 --- a/src/modules/configuration/auth/infrastructure/dto/login.dto.ts +++ b/src/modules/configuration/auth/infrastructure/dto/login.dto.ts @@ -11,3 +11,21 @@ export class LoginDto implements LoginRequest { @IsString() password: string; } + +export class LoginQueueDto implements LoginRequest { + @ApiProperty({ name: 'username', required: true, default: 'superadmin' }) + @IsString() + username: string; + + @ApiProperty({ name: 'password', required: true, default: 'Eigen123!' }) + @IsString() + password: string; + + @ApiProperty({ name: 'item_id', required: true, default: 'string' }) + @IsString() + item_id: string; + + @ApiProperty({ name: 'item_name', required: true, default: 'string' }) + @IsString() + item_name: string; +} diff --git a/src/modules/configuration/couch/couch.module.ts b/src/modules/configuration/couch/couch.module.ts index c1fd388..6fd848f 100644 --- a/src/modules/configuration/couch/couch.module.ts +++ b/src/modules/configuration/couch/couch.module.ts @@ -51,6 +51,7 @@ import { import { SeasonPeriodDataService } from 'src/modules/season-related/season-period/data/services/season-period-data.service'; import { SeasonPeriodModel } from 'src/modules/season-related/season-period/data/models/season-period.model'; import { TransactionDemographyModel } from 'src/modules/transaction/transaction/data/models/transaction-demography.model'; +import { UserLoginModel } from 'src/modules/user-related/user/data/models/user-login.model'; @Module({ imports: [ @@ -61,6 +62,7 @@ import { TransactionDemographyModel } from 'src/modules/transaction/transaction/ ItemRateModel, SeasonPeriodModel, UserModel, + UserLoginModel, TransactionModel, TransactionTaxModel, TransactionItemModel, diff --git a/src/modules/configuration/log/data/models/log-user-login.model.ts b/src/modules/configuration/log/data/models/log-user-login.model.ts index cda2429..f831f78 100644 --- a/src/modules/configuration/log/data/models/log-user-login.model.ts +++ b/src/modules/configuration/log/data/models/log-user-login.model.ts @@ -20,6 +20,12 @@ export class LogUserLoginModel @Column({ type: 'uuid', nullable: true }) user_id: string; + @Column({ type: 'uuid', nullable: true }) + item_id: string; + + @Column({ type: 'varchar', nullable: true }) + item_name: string; + @Column({ type: 'varchar', nullable: true }) username: string; diff --git a/src/modules/configuration/log/domain/entities/log-user-login.entity.ts b/src/modules/configuration/log/domain/entities/log-user-login.entity.ts index 308c43c..c68ab17 100644 --- a/src/modules/configuration/log/domain/entities/log-user-login.entity.ts +++ b/src/modules/configuration/log/domain/entities/log-user-login.entity.ts @@ -6,6 +6,8 @@ export interface LogUserLoginEntity extends BaseCoreEntity { type: LogUserType; role: UserRole; user_id: string; + item_id: string; + item_name: string; username: string; created_at: number; } diff --git a/src/modules/user-related/tenant/tenant.module.ts b/src/modules/user-related/tenant/tenant.module.ts index fe10160..24f54b4 100644 --- a/src/modules/user-related/tenant/tenant.module.ts +++ b/src/modules/user-related/tenant/tenant.module.ts @@ -25,11 +25,15 @@ import { UserDataService } from '../user/data/services/user-data.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'; +import { UserLoginModel } from '../user/data/models/user-login.model'; @Module({ imports: [ ConfigModule.forRoot(), - TypeOrmModule.forFeature([UserModel], CONNECTION_NAME.DEFAULT), + TypeOrmModule.forFeature( + [UserModel, UserLoginModel], + CONNECTION_NAME.DEFAULT, + ), CqrsModule, ], controllers: [ diff --git a/src/modules/user-related/user/data/models/user-login.model.ts b/src/modules/user-related/user/data/models/user-login.model.ts index 9f39afe..31b47e5 100644 --- a/src/modules/user-related/user/data/models/user-login.model.ts +++ b/src/modules/user-related/user/data/models/user-login.model.ts @@ -19,6 +19,12 @@ export class UserLoginModel @Column('varchar', { name: 'user_id', nullable: true }) user_id: string; + @Column({ type: 'uuid', nullable: true }) + item_id: string; + + @Column({ type: 'varchar', nullable: true }) + item_name: string; + @OneToOne(() => UserModel, (model) => model.user_login, { onDelete: 'CASCADE', onUpdate: 'CASCADE', diff --git a/src/modules/user-related/user/data/services/user-data.service.ts b/src/modules/user-related/user/data/services/user-data.service.ts index 4f69f7c..bac12c5 100644 --- a/src/modules/user-related/user/data/services/user-data.service.ts +++ b/src/modules/user-related/user/data/services/user-data.service.ts @@ -4,14 +4,24 @@ import { UserEntity } from '../../domain/entities/user.entity'; import { InjectRepository } from '@nestjs/typeorm'; import { UserModel } from '../models/user.model'; import { CONNECTION_NAME } from 'src/core/strings/constants/base.constants'; -import { Repository } from 'typeorm'; +import { IsNull, Not, Repository } from 'typeorm'; +import { UserLoginModel } from '../models/user-login.model'; @Injectable() export class UserDataService extends BaseDataService { constructor( @InjectRepository(UserModel, CONNECTION_NAME.DEFAULT) private repo: Repository, + + @InjectRepository(UserLoginModel, CONNECTION_NAME.DEFAULT) + private repoLoginUser: Repository, ) { super(repo); } + + async getLoginUserByItem(itemId: string) { + return this.repoLoginUser.findOne({ + where: { item_id: itemId, user_id: Not(IsNull()) }, + }); + } } diff --git a/src/modules/user-related/user/domain/entities/user-login.entity.ts b/src/modules/user-related/user/domain/entities/user-login.entity.ts index 975223c..1c5dd0c 100644 --- a/src/modules/user-related/user/domain/entities/user-login.entity.ts +++ b/src/modules/user-related/user/domain/entities/user-login.entity.ts @@ -3,4 +3,6 @@ import { BaseCoreEntity } from 'src/core/modules/domain/entities/base-core.entit export interface UserLoginEntity extends BaseCoreEntity { login_date: number; login_token: string; + item_id: string; + item_name: string; } diff --git a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts index 92eaa14..120e459 100644 --- a/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts +++ b/src/modules/user-related/user/domain/usecases/managers/index-user.manager.ts @@ -52,6 +52,8 @@ export class IndexUserManager extends BaseIndexManager { 'user_login.id', 'user_login.login_date', + 'user_login.item_id', + 'user_login.item_name', ]; } From 50ac2d97a4875dae4fe155863ba0517c49eba394 Mon Sep 17 00:00:00 2001 From: irfan Date: Fri, 13 Sep 2024 03:27:14 +0000 Subject: [PATCH 06/11] ci: update Docker file use NPM instead of Yarn --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9b354f4..9e4d87e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,8 @@ FROM node:18.17-alpine as builder RUN apk add --no-cache git WORKDIR /app COPY . . -RUN yarn install -RUN yarn build +RUN npm install +RUN npm run build FROM node:18.17-alpine ARG env_target WORKDIR /app From 27a0c56af782993e624f0ffd192589cb2915fafd Mon Sep 17 00:00:00 2001 From: Firman Ramdhani <33869609+firmanramdhani@users.noreply.github.com> Date: Fri, 13 Sep 2024 10:48:45 +0700 Subject: [PATCH 07/11] feat: exclude privilege logout --- .../configuration/auth/infrastructure/auth.controller.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/modules/configuration/auth/infrastructure/auth.controller.ts b/src/modules/configuration/auth/infrastructure/auth.controller.ts index ef3bd7c..9c0bf10 100644 --- a/src/modules/configuration/auth/infrastructure/auth.controller.ts +++ b/src/modules/configuration/auth/infrastructure/auth.controller.ts @@ -1,5 +1,5 @@ import { Body, Controller, Delete, Post } from '@nestjs/common'; -import { Public } from 'src/core/guards'; +import { ExcludePrivilege, Public } from 'src/core/guards'; import { AuthOrchestrator } from '../domain/auth.orchestrator'; import { ApiBearerAuth } from '@nestjs/swagger'; import { LoginDto } from './dto/login.dto'; @@ -16,6 +16,7 @@ export class AuthController { @ApiBearerAuth('JWT') @Public(false) + @ExcludePrivilege() @Delete('logout') async logoout() { return await this.orchestrator.logout(); From b2659def9a9ace37c63f7018a5d13b1baef7b95e Mon Sep 17 00:00:00 2001 From: irfan Date: Fri, 13 Sep 2024 03:55:52 +0000 Subject: [PATCH 08/11] ci: rollback to yarn, NPM is s*ck --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 9e4d87e..9b354f4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,8 +2,8 @@ FROM node:18.17-alpine as builder RUN apk add --no-cache git WORKDIR /app COPY . . -RUN npm install -RUN npm run build +RUN yarn install +RUN yarn build FROM node:18.17-alpine ARG env_target WORKDIR /app From 01fbedab77db0995a3cb0e20d81c874f1c711db8 Mon Sep 17 00:00:00 2001 From: irfan Date: Fri, 13 Sep 2024 10:37:15 +0000 Subject: [PATCH 09/11] fix: income report --- .../transaction-report/configs/income.ts | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/modules/reports/shared/configs/transaction-report/configs/income.ts b/src/modules/reports/shared/configs/transaction-report/configs/income.ts index 5fc0a77..0e5e5d5 100644 --- a/src/modules/reports/shared/configs/transaction-report/configs/income.ts +++ b/src/modules/reports/shared/configs/transaction-report/configs/income.ts @@ -102,16 +102,9 @@ export default { format: DATA_FORMAT.CURRENCY, }, { - column: 'main__payment_total_dpp', - query: 'main.payment_total_dpp', - label: 'DPP', - type: DATA_TYPE.MEASURE, - format: DATA_FORMAT.CURRENCY, - }, - { - column: 'main__payment_total_tax', - query: 'main.payment_total_tax', - label: 'Total Pajak', + column: 'main__payment_sub_total', + query: 'main.payment_sub_total', + label: 'Subtotal', type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, }, @@ -136,6 +129,27 @@ export default { type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, }, + { + column: 'main__payment_total_dpp', + query: 'main.payment_total_dpp', + label: 'DPP', + type: DATA_TYPE.MEASURE, + format: DATA_FORMAT.CURRENCY, + }, + { + column: 'main__payment_total_tax', + query: 'main.payment_total_tax', + label: 'Total Pajak', + type: DATA_TYPE.MEASURE, + format: DATA_FORMAT.CURRENCY, + }, + { + column: 'main__payment_total_share', + query: 'main.payment_total_share', + label: 'Profit Share', + type: DATA_TYPE.MEASURE, + format: DATA_FORMAT.CURRENCY, + }, { column: 'refund__refund_date', query: 'refund.refund_date', From 10049abc55d58e3baf428a9c20ecb95336663afe Mon Sep 17 00:00:00 2001 From: irfan Date: Fri, 13 Sep 2024 10:38:32 +0000 Subject: [PATCH 10/11] fix: income item report --- .../configs/income-per-item.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts index 0889a41..9cb1684 100644 --- a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts +++ b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item.ts @@ -112,6 +112,13 @@ export default { type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, }, + { + column: 'tr_item__total_price', + query: 'tr_item.total_price', + label: 'Total Penjualan', + type: DATA_TYPE.MEASURE, + format: DATA_FORMAT.CURRENCY, + }, { column: 'tr_item__payment_total_dpp', query: 'tr_item.payment_total_dpp', @@ -127,9 +134,16 @@ export default { format: DATA_FORMAT.CURRENCY, }, { - column: 'tr_item__total_price', - query: 'tr_item.total_price', - label: 'Total Penjualan', + column: 'tr_item__total_profit_share', + query: 'tr_item.total_profit_share', + label: 'Profit Share', + type: DATA_TYPE.MEASURE, + format: DATA_FORMAT.CURRENCY, + }, + { + column: 'tr_item__total_share_tenant', + query: 'tr_item.total_share_tenant', + label: 'Tenant Share', type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, }, From 2ee96a617f1a155b9d5946a9255cf3ae5c60c404 Mon Sep 17 00:00:00 2001 From: irfan Date: Fri, 13 Sep 2024 10:39:04 +0000 Subject: [PATCH 11/11] fix: income item breakdown report --- .../configs/income-per-item-master.ts | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts index 6b25b35..0de61c5 100644 --- a/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts +++ b/src/modules/reports/shared/configs/transaction-report/configs/income-per-item-master.ts @@ -127,6 +127,13 @@ export default { type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, }, + { + column: 'tr_item__total_price', + query: 'tr_item.total_price', + label: 'Total Penjualan', + type: DATA_TYPE.MEASURE, + format: DATA_FORMAT.CURRENCY, + }, { column: 'tr_item_bundling__payment_total_dpp', query: 'tr_item_bundling.payment_total_dpp', @@ -142,9 +149,16 @@ export default { format: DATA_FORMAT.CURRENCY, }, { - column: 'tr_item__total_price', - query: 'tr_item.total_price', - label: 'Total Penjualan', + column: 'tr_item_bundling__total_profit_share', + query: 'tr_item_bundling.total_profit_share', + label: 'Profit Share', + type: DATA_TYPE.MEASURE, + format: DATA_FORMAT.CURRENCY, + }, + { + column: 'tr_item_bundling__total_share_tenant', + query: 'tr_item_bundling.total_share_tenant', + label: 'Tenant Share', type: DATA_TYPE.MEASURE, format: DATA_FORMAT.CURRENCY, },