diff --git a/backend/api/src/modules/audit/audit.service.ts b/backend/api/src/modules/audit/audit.service.ts index 0f24b4f..1b030b5 100644 --- a/backend/api/src/modules/audit/audit.service.ts +++ b/backend/api/src/modules/audit/audit.service.ts @@ -112,6 +112,7 @@ export class AuditService { ).filter((record): record is AuditRecordPayload => record !== null); if (records.length > 0) { + console.log(records); await this.prisma.$transaction( records.map((record) => this.prisma.audit.upsert({ @@ -121,7 +122,7 @@ export class AuditService { event: record.event, payload: record.payload, timestamp: record.timestamp, - user_id: record.user_id, + user_id: BigInt(record.user_id), last_sync: record.last_sync, result: record.result, }, @@ -133,7 +134,6 @@ export class AuditService { if (nextBookmark === '' || nextBookmark === bookmark) { const completeData = { status: 'COMPLETED' }; - this.logger.log('Mengirim selesai via WebSocket:', completeData); this.auditGateway.sendComplete(completeData); break; } @@ -213,6 +213,7 @@ export class AuditService { const timestamp = this.parseTimestamp(value.timestamp) ?? now; const userId = value.user_id; const blockchainHash: string | undefined = value.payload; + let data: any = null; if (!blockchainHash) { return null; @@ -224,6 +225,7 @@ export class AuditService { const obatId = this.extractNumericId(logId); if (obatId !== null) { const obat = await this.obatService.getObatById(obatId); + data = obat; if (obat) { dbHash = this.obatService.createHashingPayload({ obat: obat.obat, @@ -237,6 +239,7 @@ export class AuditService { if (rekamMedisId) { const rekamMedis = await this.rekamMedisService.getRekamMedisById(rekamMedisId); + data = rekamMedis; if (rekamMedis) { dbHash = this.rekamMedisService.createHashingPayload({ dokter_id: 123, @@ -252,6 +255,7 @@ export class AuditService { if (tindakanId !== null) { const tindakanDokter = await this.tindakanDokterService.getTindakanDokterById(tindakanId); + data = tindakanDokter; if (tindakanDokter) { dbHash = this.tindakanDokterService.createHashingPayload({ id_visit: tindakanDokter.id_visit, @@ -270,10 +274,13 @@ export class AuditService { let isNotTampered = false; const eventType = logEntry.value.event?.split('_').at(-1); + const isDeleteEvent = eventType === 'deleted'; + const hasRow = Boolean(data); - if (eventType === 'deleted') { - isNotTampered = true; - console.log('Detected deleted event, marking as not tempered'); + if (!hasRow) { + isNotTampered = isDeleteEvent; + } else if (isDeleteEvent || data.deleted_status === 'DELETED') { + isNotTampered = isDeleteEvent && data.deleted_status === 'DELETED'; } else { const hashesMatch = dbHash && (await this.compareData(blockchainHash, dbHash)); @@ -287,7 +294,7 @@ export class AuditService { progress_count: index ?? 0, }; - this.logger.log('Mengirim progres via WebSocket:', progressData); + // this.logger.log('Mengirim progres via WebSocket:', progressData); this.auditGateway.sendProgress(progressData); return { diff --git a/backend/api/src/modules/obat/obat.controller.ts b/backend/api/src/modules/obat/obat.controller.ts index f1c5d79..b1e5c0a 100644 --- a/backend/api/src/modules/obat/obat.controller.ts +++ b/backend/api/src/modules/obat/obat.controller.ts @@ -1,6 +1,7 @@ import { Body, Controller, + Delete, Get, Param, Post, @@ -69,4 +70,13 @@ export class ObatController { async getObatLogs(@Param('id') id: string) { return await this.obatService.getLogObatById(id); } + + @Delete(':id') + @UseGuards(AuthGuard) + async deleteObatById( + @Param('id') id: number, + @CurrentUser() user: ActiveUserPayload, + ) { + return await this.obatService.deleteObat(id, user); + } } diff --git a/backend/api/src/modules/obat/obat.service.ts b/backend/api/src/modules/obat/obat.service.ts index a0588c8..cc1815e 100644 --- a/backend/api/src/modules/obat/obat.service.ts +++ b/backend/api/src/modules/obat/obat.service.ts @@ -56,6 +56,11 @@ export class ObatService { take: take, where: { obat: obat ? { contains: obat } : undefined, + OR: [ + { deleted_status: null }, + { deleted_status: 'DELETE_VALIDATION' }, + { deleted_status: { not: 'DELETED' } }, + ], }, orderBy: orderBy ? { [Object.keys(orderBy)[0]]: order || 'asc' } @@ -65,6 +70,11 @@ export class ObatService { const count = await this.prisma.pemberian_obat.count({ where: { obat: obat ? { contains: obat } : undefined, + OR: [ + { deleted_status: null }, + { deleted_status: 'DELETE_VALIDATION' }, + { deleted_status: { not: 'DELETED' } }, + ], }, }); @@ -273,6 +283,89 @@ export class ObatService { } } + async deleteObat(id: number, user: ActiveUserPayload) { + const existingObat = await this.getObatById(id); + if (!existingObat) { + throw new BadRequestException(`Obat with id ${id} not found`); + } + + try { + const response = await this.prisma.$transaction(async (tx) => { + const createdValidationQueue = + await this.prisma.validation_queue.create({ + data: { + table_name: 'pemberian_obat', + action: 'DELETE', + dataPayload: { + ...existingObat, + }, + record_id: id.toString(), + user_id_request: Number(user.sub), + status: 'PENDING', + }, + }); + + const updatedObat = await tx.pemberian_obat.update({ + where: { id: id }, + data: { + deleted_status: 'DELETE_VALIDATION', + }, + }); + + return createdValidationQueue; + }); + return response; + } catch (error) { + console.error('Error deleting Obat:', error); + throw error; + } + } + + async deleteObatFromDBAndBlockchain(id: number, userId: number) { + 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(`Obat dengan ID ${obatId} tidak ditemukan`); + } + + try { + const deleteObat = await this.prisma.$transaction(async (tx) => { + const deletedObat = await tx.pemberian_obat.update({ + where: { id: obatId }, + data: { + deleted_status: 'DELETED', + }, + }); + const logPayload = JSON.stringify({ + obat: existingObat.obat, + jumlah_obat: existingObat.jumlah_obat, + aturan_pakai: existingObat.aturan_pakai, + }); + const payloadHash = sha256(logPayload); + const data = { + id: `OBAT_${deletedObat.id}`, + event: 'obat_deleted', + user_id: userId.toString(), + payload: payloadHash, + }; + const logResult = await this.logService.storeLog(data); + return { + ...deletedObat, + ...logResult, + }; + }); + return deleteObat; + } catch (error) { + console.error('Error deleting Obat:', error); + throw error; + } + } + async countObat() { return this.prisma.pemberian_obat.count(); } diff --git a/backend/api/src/modules/rekammedis/rekammedis.service.ts b/backend/api/src/modules/rekammedis/rekammedis.service.ts index fc3d7d9..4309aed 100644 --- a/backend/api/src/modules/rekammedis/rekammedis.service.ts +++ b/backend/api/src/modules/rekammedis/rekammedis.service.ts @@ -195,6 +195,11 @@ export class RekammedisService { : undefined, jenis_kelamin: jkCharacter ? { equals: jkCharacter } : undefined, kode_diagnosa: kode_diagnosa ? { contains: kode_diagnosa } : undefined, + OR: [ + { deleted_status: null }, + { deleted_status: 'DELETE_VALIDATION' }, + { deleted_status: { not: 'DELETED' } }, + ], ...golDarahFilter, ...tindakLanjutFilter, }; @@ -469,15 +474,28 @@ export class RekammedisService { throw new Error(`Rekam Medis with id_visit ${id_visit} not found`); } try { - const response = await this.prisma.validation_queue.create({ - data: { - table_name: 'rekam_medis', - action: 'DELETE', - record_id: id_visit, - dataPayload: data, - user_id_request: user.sub, - status: 'PENDING', - }, + const response = await this.prisma.$transaction(async (tx) => { + const createdQueue = await tx.validation_queue.create({ + data: { + table_name: 'rekam_medis', + action: 'DELETE', + record_id: id_visit, + dataPayload: data, + user_id_request: user.sub, + status: 'PENDING', + }, + }); + + const updatedRekamMedis = await tx.rekam_medis.update({ + where: { id_visit }, + data: { + deleted_status: 'DELETE_VALIDATION', + }, + }); + return { + ...createdQueue, + rekam_medis: updatedRekamMedis, + }; }); return response; } catch (error) { @@ -486,10 +504,39 @@ export class RekammedisService { } } - async deleteRekamMedisFromDB(id_visit: string) { + async deleteRekamMedisFromDBAndBlockchain(id_visit: string, userId: number) { + const existing = await this.getRekamMedisById(id_visit); + if (!existing) { + throw new Error(`Rekam Medis with id_visit ${id_visit} not found`); + } + try { - const deletedRekamMedis = await this.prisma.rekam_medis.delete({ - where: { id_visit }, + const deletedRekamMedis = await this.prisma.$transaction(async (tx) => { + const deleted = await tx.rekam_medis.update({ + data: { + deleted_status: 'DELETED', + }, + where: { id_visit }, + }); + + const logPayload = { + dokter_id: 123, + visit_id: id_visit, + anamnese: deleted.anamnese, + jenis_kasus: deleted.jenis_kasus, + tindak_lanjut: deleted.tindak_lanjut, + }; + const logPayloadString = JSON.stringify(logPayload); + const payloadHash = sha256(logPayloadString); + const logDto = { + id: `REKAM_${id_visit}`, + event: 'rekam_medis_deleted', + user_id: userId.toString(), + payload: payloadHash, + }; + + await this.log.storeLog(logDto); + return deleted; }); return deletedRekamMedis; } catch (error) { diff --git a/backend/api/src/modules/tindakandokter/tindakandokter.service.ts b/backend/api/src/modules/tindakandokter/tindakandokter.service.ts index a90fb8f..e1d9e83 100644 --- a/backend/api/src/modules/tindakandokter/tindakandokter.service.ts +++ b/backend/api/src/modules/tindakandokter/tindakandokter.service.ts @@ -79,6 +79,11 @@ export class TindakanDokterService { kategori_tindakanArray.length > 0 ? { in: kategori_tindakanArray } : undefined, + OR: [ + { deleted_status: null }, + { deleted_status: 'DELETE_VALIDATION' }, + { deleted_status: { not: 'DELETED' } }, + ], }, orderBy: orderBy ? { [Object.keys(orderBy)[0]]: order || 'asc' } @@ -331,16 +336,36 @@ export class TindakanDokterService { ); } - return this.prisma.validation_queue.create({ - data: { - table_name: 'pemberian_tindakan', - action: 'DELETE', - dataPayload: existingTindakan, - record_id: tindakanId.toString(), - user_id_request: user.sub, - status: 'PENDING', - }, - }); + try { + const validationQueue = await this.prisma.$transaction(async (tx) => { + const queue = await tx.validation_queue.create({ + data: { + table_name: 'pemberian_tindakan', + action: 'DELETE', + dataPayload: existingTindakan, + record_id: tindakanId.toString(), + user_id_request: user.sub, + status: 'PENDING', + }, + }); + + const updatedTindakan = await tx.pemberian_tindakan.update({ + where: { id: tindakanId }, + data: { + deleted_status: 'DELETE_VALIDATION', + }, + }); + return { + ...queue, + tindakan: updatedTindakan, + }; + }); + + return validationQueue; + } catch (error) { + console.error('Error deleting Tindakan Dokter:', error); + throw error; + } } async deleteTindakanDokterFromDBAndBlockchain(id: number, userId: number) { @@ -359,8 +384,9 @@ export class TindakanDokterService { try { const deletedTindakan = await this.prisma.$transaction(async (tx) => { - const deleted = await tx.pemberian_tindakan.delete({ + const deleted = await tx.pemberian_tindakan.update({ where: { id: tindakanId }, + data: { deleted_status: 'DELETED' }, }); const logPayload = JSON.stringify(deleted); const payloadHash = sha256(logPayload); diff --git a/backend/api/src/modules/validation/validation.service.ts b/backend/api/src/modules/validation/validation.service.ts index c7f6045..60ce9f1 100644 --- a/backend/api/src/modules/validation/validation.service.ts +++ b/backend/api/src/modules/validation/validation.service.ts @@ -46,7 +46,10 @@ export class ValidationService { ); }, approveDelete: async (queue: any) => { - return this.rekamMedisService.deleteRekamMedisFromDB(queue.record_id); + return this.rekamMedisService.deleteRekamMedisFromDBAndBlockchain( + queue.record_id, + Number(queue.user_id_request), + ); }, }, pemberian_tindakan: { @@ -97,6 +100,12 @@ export class ValidationService { Number(queue.user_id_request), ); }, + approveDelete: async (queue: any) => { + return this.obatService.deleteObatFromDBAndBlockchain( + Number(queue.record_id), + queue.user_id_request, + ); + }, }, }; @@ -196,7 +205,10 @@ export class ValidationService { const updated = await this.prisma.validation_queue.update({ where: { id: validationQueue.id }, data: { - record_id: approvalResult.id.toString(), + record_id: + validationQueue.table_name === 'rekam_medis' + ? approvalResult.id_visit + : approvalResult.id.toString(), status: 'APPROVED', user_id_process: Number(user.sub), processed_at: new Date(), diff --git a/frontend/hospital-log/src/views/dashboard/obat/ObatView.vue b/frontend/hospital-log/src/views/dashboard/obat/ObatView.vue index 2528f36..fbd5124 100644 --- a/frontend/hospital-log/src/views/dashboard/obat/ObatView.vue +++ b/frontend/hospital-log/src/views/dashboard/obat/ObatView.vue @@ -155,14 +155,12 @@ const handleUpdate = (item: ObatData) => { }; const handleDelete = async (item: ObatData) => { - if (confirm(`Apakah Anda yakin ingin menghapus obat "${item.obat}"?`)) { - try { - await api.delete(`/obat/${item.id}`); - await fetchData(); - } catch (error) { - console.error("Error deleting obat:", error); - alert("Gagal menghapus data obat"); - } + try { + await api.delete(`/obat/${item.id}`); + await fetchData(); + } catch (error) { + console.error("Error deleting obat:", error); + alert("Gagal menghapus data obat"); } };