feat: validasi update obat for admin

This commit is contained in:
yosaphatprs 2025-11-26 15:28:04 +07:00
parent 1cab0a2348
commit 3b209ca566
7 changed files with 191 additions and 77 deletions

View File

@ -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')

View File

@ -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 },

View File

@ -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() {

View File

@ -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],

View File

@ -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() {

View File

@ -221,13 +221,6 @@ onMounted(async () => {
</svg>
<span>Berhasil memperbarui data!</span>
</div>
<!-- <div class="text-sm">
<p>ID: {{ data?.id }}</p>
<p>ID Visit: {{ data?.id_visit }}</p>
<p>Obat: {{ data?.obat }}</p>
<p>Jumlah Obat: {{ data?.jumlah_obat }}</p>
<p>Aturan Pakai: {{ data?.aturan_pakai }}</p>
</div> -->
<div>
<form @submit.prevent="handleSubmit">
<FieldInput
@ -266,14 +259,16 @@ onMounted(async () => {
v-model="data.aturan_pakai"
:is-disabled="false"
/>
<ButtonDark
type="submit"
:text="isLoading ? 'Menyimpan...' : 'Simpan Perubahan'"
class="mt-4"
:isLoading="isLoading"
>
Simpan Perubahan
</ButtonDark>
<div class="flex justify-end">
<ButtonDark
type="submit"
:text="isLoading ? 'Menyimpan...' : 'Simpan Perubahan'"
class="mt-4"
:isLoading="isLoading"
>
Simpan Perubahan
</ButtonDark>
</div>
</form>
</div>
<hr />

View File

@ -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;