feat: validation delete tindakan

This commit is contained in:
yosaphatprs 2025-11-28 10:34:50 +07:00
parent 4d4565299d
commit cbcf4ad897
7 changed files with 232 additions and 39 deletions

View File

@ -112,6 +112,7 @@ export class AuditService {
).filter((record): record is AuditRecordPayload => record !== null); ).filter((record): record is AuditRecordPayload => record !== null);
if (records.length > 0) { if (records.length > 0) {
console.log(records);
await this.prisma.$transaction( await this.prisma.$transaction(
records.map((record) => records.map((record) =>
this.prisma.audit.upsert({ this.prisma.audit.upsert({
@ -121,7 +122,7 @@ export class AuditService {
event: record.event, event: record.event,
payload: record.payload, payload: record.payload,
timestamp: record.timestamp, timestamp: record.timestamp,
user_id: record.user_id, user_id: BigInt(record.user_id),
last_sync: record.last_sync, last_sync: record.last_sync,
result: record.result, result: record.result,
}, },
@ -133,7 +134,6 @@ export class AuditService {
if (nextBookmark === '' || nextBookmark === bookmark) { if (nextBookmark === '' || nextBookmark === bookmark) {
const completeData = { status: 'COMPLETED' }; const completeData = { status: 'COMPLETED' };
this.logger.log('Mengirim selesai via WebSocket:', completeData);
this.auditGateway.sendComplete(completeData); this.auditGateway.sendComplete(completeData);
break; break;
} }
@ -213,6 +213,7 @@ export class AuditService {
const timestamp = this.parseTimestamp(value.timestamp) ?? now; const timestamp = this.parseTimestamp(value.timestamp) ?? now;
const userId = value.user_id; const userId = value.user_id;
const blockchainHash: string | undefined = value.payload; const blockchainHash: string | undefined = value.payload;
let data: any = null;
if (!blockchainHash) { if (!blockchainHash) {
return null; return null;
@ -224,6 +225,7 @@ export class AuditService {
const obatId = this.extractNumericId(logId); const obatId = this.extractNumericId(logId);
if (obatId !== null) { if (obatId !== null) {
const obat = await this.obatService.getObatById(obatId); const obat = await this.obatService.getObatById(obatId);
data = obat;
if (obat) { if (obat) {
dbHash = this.obatService.createHashingPayload({ dbHash = this.obatService.createHashingPayload({
obat: obat.obat, obat: obat.obat,
@ -237,6 +239,7 @@ export class AuditService {
if (rekamMedisId) { if (rekamMedisId) {
const rekamMedis = const rekamMedis =
await this.rekamMedisService.getRekamMedisById(rekamMedisId); await this.rekamMedisService.getRekamMedisById(rekamMedisId);
data = rekamMedis;
if (rekamMedis) { if (rekamMedis) {
dbHash = this.rekamMedisService.createHashingPayload({ dbHash = this.rekamMedisService.createHashingPayload({
dokter_id: 123, dokter_id: 123,
@ -252,6 +255,7 @@ export class AuditService {
if (tindakanId !== null) { if (tindakanId !== null) {
const tindakanDokter = const tindakanDokter =
await this.tindakanDokterService.getTindakanDokterById(tindakanId); await this.tindakanDokterService.getTindakanDokterById(tindakanId);
data = tindakanDokter;
if (tindakanDokter) { if (tindakanDokter) {
dbHash = this.tindakanDokterService.createHashingPayload({ dbHash = this.tindakanDokterService.createHashingPayload({
id_visit: tindakanDokter.id_visit, id_visit: tindakanDokter.id_visit,
@ -270,10 +274,13 @@ export class AuditService {
let isNotTampered = false; let isNotTampered = false;
const eventType = logEntry.value.event?.split('_').at(-1); const eventType = logEntry.value.event?.split('_').at(-1);
const isDeleteEvent = eventType === 'deleted';
const hasRow = Boolean(data);
if (eventType === 'deleted') { if (!hasRow) {
isNotTampered = true; isNotTampered = isDeleteEvent;
console.log('Detected deleted event, marking as not tempered'); } else if (isDeleteEvent || data.deleted_status === 'DELETED') {
isNotTampered = isDeleteEvent && data.deleted_status === 'DELETED';
} else { } else {
const hashesMatch = const hashesMatch =
dbHash && (await this.compareData(blockchainHash, dbHash)); dbHash && (await this.compareData(blockchainHash, dbHash));
@ -287,7 +294,7 @@ export class AuditService {
progress_count: index ?? 0, progress_count: index ?? 0,
}; };
this.logger.log('Mengirim progres via WebSocket:', progressData); // this.logger.log('Mengirim progres via WebSocket:', progressData);
this.auditGateway.sendProgress(progressData); this.auditGateway.sendProgress(progressData);
return { return {

View File

@ -1,6 +1,7 @@
import { import {
Body, Body,
Controller, Controller,
Delete,
Get, Get,
Param, Param,
Post, Post,
@ -69,4 +70,13 @@ export class ObatController {
async getObatLogs(@Param('id') id: string) { async getObatLogs(@Param('id') id: string) {
return await this.obatService.getLogObatById(id); 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);
}
} }

View File

@ -56,6 +56,11 @@ export class ObatService {
take: take, take: take,
where: { where: {
obat: obat ? { contains: obat } : undefined, obat: obat ? { contains: obat } : undefined,
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
}, },
orderBy: orderBy orderBy: orderBy
? { [Object.keys(orderBy)[0]]: order || 'asc' } ? { [Object.keys(orderBy)[0]]: order || 'asc' }
@ -65,6 +70,11 @@ export class ObatService {
const count = await this.prisma.pemberian_obat.count({ const count = await this.prisma.pemberian_obat.count({
where: { where: {
obat: obat ? { contains: obat } : undefined, 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() { async countObat() {
return this.prisma.pemberian_obat.count(); return this.prisma.pemberian_obat.count();
} }

View File

@ -195,6 +195,11 @@ export class RekammedisService {
: undefined, : undefined,
jenis_kelamin: jkCharacter ? { equals: jkCharacter } : undefined, jenis_kelamin: jkCharacter ? { equals: jkCharacter } : undefined,
kode_diagnosa: kode_diagnosa ? { contains: kode_diagnosa } : undefined, kode_diagnosa: kode_diagnosa ? { contains: kode_diagnosa } : undefined,
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
...golDarahFilter, ...golDarahFilter,
...tindakLanjutFilter, ...tindakLanjutFilter,
}; };
@ -469,15 +474,28 @@ export class RekammedisService {
throw new Error(`Rekam Medis with id_visit ${id_visit} not found`); throw new Error(`Rekam Medis with id_visit ${id_visit} not found`);
} }
try { try {
const response = await this.prisma.validation_queue.create({ const response = await this.prisma.$transaction(async (tx) => {
data: { const createdQueue = await tx.validation_queue.create({
table_name: 'rekam_medis', data: {
action: 'DELETE', table_name: 'rekam_medis',
record_id: id_visit, action: 'DELETE',
dataPayload: data, record_id: id_visit,
user_id_request: user.sub, dataPayload: data,
status: 'PENDING', 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; return response;
} catch (error) { } 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 { try {
const deletedRekamMedis = await this.prisma.rekam_medis.delete({ const deletedRekamMedis = await this.prisma.$transaction(async (tx) => {
where: { id_visit }, 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; return deletedRekamMedis;
} catch (error) { } catch (error) {

View File

@ -79,6 +79,11 @@ export class TindakanDokterService {
kategori_tindakanArray.length > 0 kategori_tindakanArray.length > 0
? { in: kategori_tindakanArray } ? { in: kategori_tindakanArray }
: undefined, : undefined,
OR: [
{ deleted_status: null },
{ deleted_status: 'DELETE_VALIDATION' },
{ deleted_status: { not: 'DELETED' } },
],
}, },
orderBy: orderBy orderBy: orderBy
? { [Object.keys(orderBy)[0]]: order || 'asc' } ? { [Object.keys(orderBy)[0]]: order || 'asc' }
@ -331,16 +336,36 @@ export class TindakanDokterService {
); );
} }
return this.prisma.validation_queue.create({ try {
data: { const validationQueue = await this.prisma.$transaction(async (tx) => {
table_name: 'pemberian_tindakan', const queue = await tx.validation_queue.create({
action: 'DELETE', data: {
dataPayload: existingTindakan, table_name: 'pemberian_tindakan',
record_id: tindakanId.toString(), action: 'DELETE',
user_id_request: user.sub, dataPayload: existingTindakan,
status: 'PENDING', 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) { async deleteTindakanDokterFromDBAndBlockchain(id: number, userId: number) {
@ -359,8 +384,9 @@ export class TindakanDokterService {
try { try {
const deletedTindakan = await this.prisma.$transaction(async (tx) => { 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 }, where: { id: tindakanId },
data: { deleted_status: 'DELETED' },
}); });
const logPayload = JSON.stringify(deleted); const logPayload = JSON.stringify(deleted);
const payloadHash = sha256(logPayload); const payloadHash = sha256(logPayload);

View File

@ -46,7 +46,10 @@ export class ValidationService {
); );
}, },
approveDelete: async (queue: any) => { 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: { pemberian_tindakan: {
@ -97,6 +100,12 @@ export class ValidationService {
Number(queue.user_id_request), 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({ const updated = await this.prisma.validation_queue.update({
where: { id: validationQueue.id }, where: { id: validationQueue.id },
data: { data: {
record_id: approvalResult.id.toString(), record_id:
validationQueue.table_name === 'rekam_medis'
? approvalResult.id_visit
: approvalResult.id.toString(),
status: 'APPROVED', status: 'APPROVED',
user_id_process: Number(user.sub), user_id_process: Number(user.sub),
processed_at: new Date(), processed_at: new Date(),

View File

@ -155,14 +155,12 @@ const handleUpdate = (item: ObatData) => {
}; };
const handleDelete = async (item: ObatData) => { const handleDelete = async (item: ObatData) => {
if (confirm(`Apakah Anda yakin ingin menghapus obat "${item.obat}"?`)) { try {
try { await api.delete(`/obat/${item.id}`);
await api.delete(`/obat/${item.id}`); await fetchData();
await fetchData(); } catch (error) {
} catch (error) { console.error("Error deleting obat:", error);
console.error("Error deleting obat:", error); alert("Gagal menghapus data obat");
alert("Gagal menghapus data obat");
}
} }
}; };