From 3b209ca5661a73812423119f77cdf45f00f0f697 Mon Sep 17 00:00:00 2001 From: yosaphatprs Date: Wed, 26 Nov 2025 15:28:04 +0700 Subject: [PATCH] feat: validasi update obat for admin --- .../api/src/modules/obat/obat.controller.ts | 2 +- .../api/src/modules/obat/obat.service.spec.ts | 5 +- backend/api/src/modules/obat/obat.service.ts | 201 +++++++++++++----- .../modules/validation/validation.module.ts | 9 +- .../modules/validation/validation.service.ts | 24 ++- .../views/dashboard/obat/UpdateObatView.vue | 25 +-- .../views/dashboard/validasi/ValidasiView.vue | 2 +- 7 files changed, 191 insertions(+), 77 deletions(-) diff --git a/backend/api/src/modules/obat/obat.controller.ts b/backend/api/src/modules/obat/obat.controller.ts index 521742a..f1c5d79 100644 --- a/backend/api/src/modules/obat/obat.controller.ts +++ b/backend/api/src/modules/obat/obat.controller.ts @@ -61,7 +61,7 @@ export class ObatController { @Body() dto: UpdateObatDto, @CurrentUser() user: ActiveUserPayload, ) { - return await this.obatService.updateObatById(id, dto, user); + return await this.obatService.updateObat(id, dto, user); } @Get('/:id/log') diff --git a/backend/api/src/modules/obat/obat.service.spec.ts b/backend/api/src/modules/obat/obat.service.spec.ts index 90edaa3..0904a8b 100644 --- a/backend/api/src/modules/obat/obat.service.spec.ts +++ b/backend/api/src/modules/obat/obat.service.spec.ts @@ -157,6 +157,7 @@ describe('ObatService', () => { describe('updateObatById', () => { const updatePayload: UpdateObatDto = { + id_visit: 'VISIT-1', obat: 'Ibuprofen', jumlah_obat: 1, aturan_pakai: '2x1', @@ -165,12 +166,12 @@ describe('ObatService', () => { it('updates obat and stores log', async () => { prisma.pemberian_obat.update.mockResolvedValueOnce({ id: 99, - id_visit: 'VISIT-1', ...updatePayload, + id_visit: 'VISIT-1', }); logService.storeLog.mockResolvedValueOnce({ txId: 'updated' }); - const result = await service.updateObatById(99, updatePayload, mockUser); + const result = await service.updateObat(99, updatePayload, mockUser); expect(prisma.pemberian_obat.update).toHaveBeenCalledWith({ where: { id: 99 }, diff --git a/backend/api/src/modules/obat/obat.service.ts b/backend/api/src/modules/obat/obat.service.ts index 06de4eb..a0588c8 100644 --- a/backend/api/src/modules/obat/obat.service.ts +++ b/backend/api/src/modules/obat/obat.service.ts @@ -18,8 +18,6 @@ export class ObatService { } determineStatus(rawFabricLog: any, index: number, arrLength: number): any { - console.log('Inside determineStatus with index:', index); - console.log('Raw Fabric Log:', rawFabricLog); const flatLog = { ...rawFabricLog.value, txId: rawFabricLog.txId, @@ -102,6 +100,7 @@ export class ObatService { const latestPayload = rawLogs[0].value.payload; const isTampered = currentDataHash !== latestPayload; + console.log(latestPayload, currentDataHash, isTampered); const chronologicalLogs = [...rawLogs]; @@ -116,72 +115,162 @@ export class ObatService { }; } - async createObat(dto: CreateObatDto, user: ActiveUserPayload) { - const visitExists = await this.prisma.rekam_medis.findUnique({ - where: { id_visit: dto.id_visit }, + async isIdVisitExists(id_visit: string) { + const existing = await this.prisma.rekam_medis.findUnique({ + where: { id_visit: id_visit }, }); + return !!existing; + } - if (!visitExists) { + async createObat(dto: CreateObatDto, user: ActiveUserPayload) { + if (!(await this.isIdVisitExists(dto.id_visit))) { + throw new BadRequestException(`ID Visit ${dto.id_visit} tidak ditemukan`); + } + + try { + return await this.prisma.validation_queue.create({ + data: { + table_name: 'pemberian_obat', + action: 'CREATE', + dataPayload: { + ...dto, + }, + status: 'PENDING', + user_id_request: Number(user.sub), + }, + }); + } catch (error) { + console.error('Error creating Obat:', error); + throw error; + } + } + + async createObatToDBAndBlockchain(dto: CreateObatDto, userId: number) { + if (!(await this.isIdVisitExists(dto.id_visit))) { throw new BadRequestException(`Visit with id ${dto.id_visit} not found`); } - const res = await this.prisma.pemberian_obat.create({ - data: { - id_visit: dto.id_visit, - obat: dto.obat, - jumlah_obat: dto.jumlah_obat, - aturan_pakai: dto.aturan_pakai, - }, - }); - - console.log('Created Obat:', dto); - - const logPayload = JSON.stringify(dto); - const payloadHash = sha256(logPayload); - const data = { - id: `OBAT_${res.id}`, - event: 'obat_created', - user_id: user.sub, - payload: payloadHash, - }; - const logResult = await this.logService.storeLog(data); - return { - ...res, - ...logResult, - }; + try { + const createObat = await this.prisma.$transaction(async (tx) => { + const res = await tx.pemberian_obat.create({ + data: { + ...dto, + }, + }); + const logPayload = JSON.stringify({ + obat: dto.obat, + jumlah_obat: dto.jumlah_obat, + aturan_pakai: dto.aturan_pakai, + }); + const payloadHash = sha256(logPayload); + const data = { + id: `OBAT_${res.id}`, + event: 'obat_created', + user_id: userId, + payload: payloadHash, + }; + const logResult = await this.logService.storeLog(data); + return { + ...res, + ...logResult, + }; + }); + return createObat; + } catch (error) { + console.error('Error creating Obat:', error); + throw error; + } } - async updateObatById( + async updateObatToDBAndBlockchain( id: number, dto: UpdateObatDto, - user: ActiveUserPayload, + userId: number, ) { - const res = await this.prisma.pemberian_obat.update({ - where: { - id: id, - }, - data: { - obat: dto.obat, - jumlah_obat: dto.jumlah_obat, - aturan_pakai: dto.aturan_pakai, - }, - }); + const obatId = Number(id); - console.log('Updated Obat:', dto); + if (isNaN(obatId)) { + throw new BadRequestException('ID obat tidak valid'); + } - const logPayload = JSON.stringify(dto); - const payloadHash = sha256(logPayload); - const data = { - id: `OBAT_${id}`, - event: 'obat_updated', - user_id: user.sub, - payload: payloadHash, - }; - const logResult = await this.logService.storeLog(data); - return { - ...res, - ...logResult, - }; + if (!(await this.getObatById(obatId))) { + throw new BadRequestException(`Obat with id ${obatId} not found`); + } + + try { + const updateObat = await this.prisma.$transaction(async (tx) => { + const updatedObat = await tx.pemberian_obat.update({ + where: { id: obatId }, + data: { + ...dto, + }, + }); + const logPayload = JSON.stringify({ + obat: dto.obat, + jumlah_obat: dto.jumlah_obat, + aturan_pakai: dto.aturan_pakai, + }); + const payloadHash = sha256(logPayload); + const data = { + id: `OBAT_${updatedObat.id}`, + event: 'obat_updated', + user_id: userId.toString(), + payload: payloadHash, + }; + const logResult = await this.logService.storeLog(data); + return { + ...updatedObat, + ...logResult, + }; + }); + return updateObat; + } catch (error) { + console.error('Error updating Obat:', error); + throw error; + } + } + + async updateObat(id: number, dto: UpdateObatDto, user: ActiveUserPayload) { + const obatId = Number(id); + + if (isNaN(obatId)) { + throw new BadRequestException('ID obat tidak valid'); + } + + const existingObat = await this.getObatById(obatId); + + if (!existingObat) { + throw new BadRequestException( + `Pemberian obat dengan ID ${obatId} tidak ditemukan`, + ); + } + + const hasUpdates = + dto.obat !== existingObat.obat || + dto.jumlah_obat !== existingObat.jumlah_obat || + dto.aturan_pakai !== existingObat.aturan_pakai; + + if (!hasUpdates) { + throw new BadRequestException('Tidak ada perubahan data obat'); + } + + try { + return await this.prisma.validation_queue.create({ + data: { + table_name: 'pemberian_obat', + action: 'UPDATE', + dataPayload: { + ...dto, + }, + record_id: obatId.toString(), + user_id_request: Number(user.sub), + status: 'PENDING', + }, + }); + } catch (error) { + console.error('Error updating Obat:', error); + throw error; + } } async countObat() { diff --git a/backend/api/src/modules/validation/validation.module.ts b/backend/api/src/modules/validation/validation.module.ts index aba7e98..85a7535 100644 --- a/backend/api/src/modules/validation/validation.module.ts +++ b/backend/api/src/modules/validation/validation.module.ts @@ -5,9 +5,16 @@ import { PrismaModule } from '../prisma/prisma.module'; import { LogModule } from '../log/log.module'; import { RekamMedisModule } from '../rekammedis/rekammedis.module'; import { TindakanDokterModule } from '../tindakandokter/tindakandokter.module'; +import { ObatModule } from '../obat/obat.module'; @Module({ - imports: [PrismaModule, LogModule, RekamMedisModule, TindakanDokterModule], + imports: [ + PrismaModule, + LogModule, + RekamMedisModule, + TindakanDokterModule, + ObatModule, + ], controllers: [ValidationController], providers: [ValidationService], exports: [ValidationService], diff --git a/backend/api/src/modules/validation/validation.service.ts b/backend/api/src/modules/validation/validation.service.ts index 7339afa..bc13d4a 100644 --- a/backend/api/src/modules/validation/validation.service.ts +++ b/backend/api/src/modules/validation/validation.service.ts @@ -4,6 +4,7 @@ import { ActiveUserPayload } from '../auth/decorator/current-user.decorator'; import { RekammedisService } from '../rekammedis/rekammedis.service'; import { CreateRekamMedisDto } from '../rekammedis/dto/create-rekammedis.dto'; import { TindakanDokterService } from '../tindakandokter/tindakandokter.service'; +import { ObatService } from '../obat/obat.service'; @Injectable() export class ValidationService { @@ -11,6 +12,7 @@ export class ValidationService { private prisma: PrismaService, private rekamMedisService: RekammedisService, private tindakanDokterService: TindakanDokterService, + private obatService: ObatService, ) {} private handlers: Record< @@ -71,7 +73,27 @@ export class ValidationService { ); }, }, - pemberian_obat: {}, + pemberian_obat: { + approveCreate: async (queue: any) => { + const payload = queue.dataPayload; + const result = await this.obatService.createObatToDBAndBlockchain( + payload, + Number(queue.user_id_request), + ); + return { + ...result, + id: result.id, + }; + }, + approveUpdate: async (queue: any) => { + const payload = queue.dataPayload; + return await this.obatService.updateObatToDBAndBlockchain( + queue.record_id, + payload, + Number(queue.user_id_request), + ); + }, + }, }; async getAllValidationsQueue() { diff --git a/frontend/hospital-log/src/views/dashboard/obat/UpdateObatView.vue b/frontend/hospital-log/src/views/dashboard/obat/UpdateObatView.vue index e7d2853..89b112c 100644 --- a/frontend/hospital-log/src/views/dashboard/obat/UpdateObatView.vue +++ b/frontend/hospital-log/src/views/dashboard/obat/UpdateObatView.vue @@ -221,13 +221,6 @@ onMounted(async () => { Berhasil memperbarui data! -
{ v-model="data.aturan_pakai" :is-disabled="false" /> - - Simpan Perubahan - +
+ + Simpan Perubahan + +

diff --git a/frontend/hospital-log/src/views/dashboard/validasi/ValidasiView.vue b/frontend/hospital-log/src/views/dashboard/validasi/ValidasiView.vue index 9ddb1c8..1f3743f 100644 --- a/frontend/hospital-log/src/views/dashboard/validasi/ValidasiView.vue +++ b/frontend/hospital-log/src/views/dashboard/validasi/ValidasiView.vue @@ -65,7 +65,7 @@ const normalizedData = (rawData: any[]): ValidationLog[] => { return "Rekam Medis"; case "pemberian_tindakan": return "Tindakan Dokter"; - case "obat": + case "pemberian_obat": return "Obat"; default: return tableName;