2025-10-27 06:41:51 +00:00
|
|
|
import { Injectable } from '@nestjs/common';
|
|
|
|
|
import { PrismaService } from '../prisma/prisma.service';
|
|
|
|
|
import { Prisma, rekam_medis } from '@dist/generated/prisma';
|
|
|
|
|
import { CreateRekamMedisDto } from './dto/create-rekammedis.dto';
|
|
|
|
|
import { CreateLogDto } from '../log/dto/create-log.dto';
|
|
|
|
|
|
|
|
|
|
@Injectable()
|
|
|
|
|
export class RekammedisService {
|
2025-10-31 09:43:24 +00:00
|
|
|
// Define known values as constants to avoid hardcoding everywhere
|
|
|
|
|
private readonly KNOWN_BLOOD_TYPES = ['A', 'B', 'AB', 'O'];
|
|
|
|
|
private readonly KNOWN_TINDAK_LANJUT = [
|
|
|
|
|
'Dipulangkan untuk Kontrol',
|
|
|
|
|
'Dirawat',
|
|
|
|
|
'Dirujuk ke RS',
|
|
|
|
|
'Konsul Ke Poli Lain',
|
|
|
|
|
'Konsultasi Dokter Spesialis',
|
|
|
|
|
'Kontrol',
|
|
|
|
|
'Kontrol Ulang',
|
|
|
|
|
'Masuk Rawat Inap',
|
|
|
|
|
'Meninggal Dunia Sebelum Dirawat',
|
|
|
|
|
'Meninggal Dunia Setelah Dirawat',
|
|
|
|
|
'Pulang',
|
|
|
|
|
'Rencana Operasi',
|
|
|
|
|
'Rujuk Balik',
|
|
|
|
|
'Selesai Pelayanan IGD',
|
|
|
|
|
'Selesai Pelayanan Rawat Jalan',
|
|
|
|
|
];
|
|
|
|
|
|
2025-10-27 06:41:51 +00:00
|
|
|
constructor(private prisma: PrismaService) {}
|
|
|
|
|
|
|
|
|
|
async getAllRekamMedis(params: {
|
|
|
|
|
take?: number;
|
|
|
|
|
skip?: number;
|
|
|
|
|
page?: number;
|
|
|
|
|
orderBy?: any;
|
|
|
|
|
no_rm?: string;
|
|
|
|
|
order?: 'asc' | 'desc';
|
2025-10-31 09:43:24 +00:00
|
|
|
id_visit?: string;
|
|
|
|
|
nama_pasien?: string;
|
|
|
|
|
tanggal_start?: string;
|
|
|
|
|
tanggal_end?: string;
|
|
|
|
|
umur_min?: string;
|
|
|
|
|
umur_max?: string;
|
|
|
|
|
jenis_kelamin?: string;
|
|
|
|
|
gol_darah?: string;
|
|
|
|
|
kode_diagnosa?: string;
|
|
|
|
|
tindak_lanjut?: string;
|
2025-10-30 05:15:06 +00:00
|
|
|
}) {
|
2025-10-31 09:43:24 +00:00
|
|
|
const {
|
|
|
|
|
skip,
|
|
|
|
|
page,
|
|
|
|
|
orderBy,
|
|
|
|
|
order,
|
|
|
|
|
no_rm,
|
|
|
|
|
id_visit,
|
|
|
|
|
nama_pasien,
|
|
|
|
|
tanggal_start,
|
|
|
|
|
tanggal_end,
|
|
|
|
|
umur_min,
|
|
|
|
|
umur_max,
|
|
|
|
|
jenis_kelamin,
|
|
|
|
|
kode_diagnosa,
|
|
|
|
|
} = params;
|
|
|
|
|
|
|
|
|
|
const golDarahArray = params.gol_darah?.split(',') || [];
|
|
|
|
|
const tindakLanjutArray = params.tindak_lanjut?.split(',') || [];
|
|
|
|
|
console.log('Params Received:', params);
|
2025-10-27 06:41:51 +00:00
|
|
|
const take = params.take ? parseInt(params.take.toString()) : 10;
|
|
|
|
|
const skipValue = skip
|
|
|
|
|
? parseInt(skip.toString())
|
|
|
|
|
: page
|
|
|
|
|
? (parseInt(page.toString()) - 1) * take
|
|
|
|
|
: 0;
|
|
|
|
|
|
2025-10-31 09:43:24 +00:00
|
|
|
const buildMultiSelectFilter = (
|
|
|
|
|
fieldName: string,
|
|
|
|
|
selectedValues: string[],
|
|
|
|
|
knownValues: string[],
|
|
|
|
|
unknownLabel: string,
|
|
|
|
|
includesDash: boolean = false,
|
|
|
|
|
) => {
|
|
|
|
|
if (selectedValues.length === 0) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const hasKnownValues = selectedValues.some((val) =>
|
|
|
|
|
knownValues.includes(val),
|
|
|
|
|
);
|
|
|
|
|
const hasUnknown = selectedValues.includes(unknownLabel);
|
|
|
|
|
const totalOptions = knownValues.length + 1;
|
|
|
|
|
|
|
|
|
|
if (selectedValues.length === totalOptions) {
|
|
|
|
|
return undefined;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasUnknown && !hasKnownValues) {
|
|
|
|
|
const conditions: any[] = [{ [fieldName]: { equals: null } }];
|
|
|
|
|
|
|
|
|
|
if (includesDash) {
|
|
|
|
|
conditions.push({ [fieldName]: { equals: '-' } });
|
|
|
|
|
conditions.push({ [fieldName]: { notIn: knownValues } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return conditions.length > 1 ? { OR: conditions } : conditions[0];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hasKnownValues && hasUnknown) {
|
|
|
|
|
const knownSelected = selectedValues.filter(
|
|
|
|
|
(val) => val !== unknownLabel,
|
|
|
|
|
);
|
|
|
|
|
const conditions: any[] = [
|
|
|
|
|
{ [fieldName]: { in: knownSelected } },
|
|
|
|
|
{ [fieldName]: { equals: null } },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (includesDash) {
|
|
|
|
|
conditions.push({ [fieldName]: { equals: '-' } });
|
|
|
|
|
conditions.push({ [fieldName]: { notIn: knownValues } });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { OR: conditions };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return { [fieldName]: { in: selectedValues } };
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const golDarahFilter = buildMultiSelectFilter(
|
|
|
|
|
'gol_darah',
|
|
|
|
|
golDarahArray,
|
|
|
|
|
this.KNOWN_BLOOD_TYPES,
|
|
|
|
|
'Tidak Tahu',
|
|
|
|
|
true,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const tindakLanjutFilter = buildMultiSelectFilter(
|
|
|
|
|
'tindak_lanjut',
|
|
|
|
|
tindakLanjutArray,
|
|
|
|
|
this.KNOWN_TINDAK_LANJUT,
|
|
|
|
|
'Belum Ada Keterangan',
|
|
|
|
|
false,
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const whereClause = {
|
|
|
|
|
no_rm: no_rm ? { startsWith: no_rm } : undefined,
|
|
|
|
|
id_visit: id_visit ? { contains: id_visit } : undefined,
|
|
|
|
|
nama_pasien: nama_pasien ? { contains: nama_pasien } : undefined,
|
|
|
|
|
waktu_visit:
|
|
|
|
|
tanggal_start && tanggal_end
|
|
|
|
|
? {
|
|
|
|
|
gte: new Date(tanggal_start),
|
|
|
|
|
lte: new Date(tanggal_end),
|
|
|
|
|
}
|
|
|
|
|
: undefined,
|
|
|
|
|
umur:
|
|
|
|
|
umur_min && umur_max
|
|
|
|
|
? {
|
|
|
|
|
gte: parseInt(umur_min, 10),
|
|
|
|
|
lte: parseInt(umur_max, 10),
|
|
|
|
|
}
|
|
|
|
|
: undefined,
|
|
|
|
|
jenis_kelamin: jenis_kelamin ? { equals: jenis_kelamin } : undefined,
|
|
|
|
|
kode_diagnosa: kode_diagnosa ? { contains: kode_diagnosa } : undefined,
|
|
|
|
|
...golDarahFilter,
|
|
|
|
|
...tindakLanjutFilter,
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-27 06:41:51 +00:00
|
|
|
const results = await this.prisma.rekam_medis.findMany({
|
|
|
|
|
skip: skipValue,
|
|
|
|
|
take: take,
|
2025-10-31 09:43:24 +00:00
|
|
|
where: whereClause,
|
2025-10-27 06:41:51 +00:00
|
|
|
orderBy: orderBy
|
|
|
|
|
? { [orderBy]: order || 'asc' }
|
|
|
|
|
: { waktu_visit: order ? order : 'asc' },
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-30 05:15:06 +00:00
|
|
|
const count = await this.prisma.rekam_medis.count({
|
2025-10-31 09:43:24 +00:00
|
|
|
where: whereClause,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const umurMin = await this.prisma.rekam_medis.findMany({
|
|
|
|
|
distinct: ['umur'],
|
|
|
|
|
orderBy: {
|
|
|
|
|
umur: 'asc',
|
|
|
|
|
},
|
|
|
|
|
select: {
|
|
|
|
|
umur: true,
|
2025-10-30 05:15:06 +00:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-31 09:43:24 +00:00
|
|
|
const umurMax = await this.prisma.rekam_medis.findMany({
|
|
|
|
|
distinct: ['umur'],
|
|
|
|
|
orderBy: {
|
|
|
|
|
umur: 'desc',
|
|
|
|
|
},
|
|
|
|
|
select: {
|
|
|
|
|
umur: true,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const rangeUmur = {
|
|
|
|
|
min: umurMin.length > 0 ? umurMin[0].umur : null,
|
|
|
|
|
max: umurMax.length > 0 ? umurMax[0].umur : null,
|
|
|
|
|
};
|
|
|
|
|
|
2025-10-30 05:15:06 +00:00
|
|
|
return {
|
|
|
|
|
...results,
|
|
|
|
|
totalCount: count,
|
2025-10-31 09:43:24 +00:00
|
|
|
rangeUmur: rangeUmur,
|
2025-10-30 05:15:06 +00:00
|
|
|
};
|
2025-10-27 06:41:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async createRekamMedis(data: CreateRekamMedisDto) {
|
|
|
|
|
const latestId = await this.prisma.rekam_medis.findFirst({
|
|
|
|
|
orderBy: { waktu_visit: 'desc' },
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let newId = '';
|
|
|
|
|
let xCounter = 0;
|
|
|
|
|
let rekamMedis: Prisma.rekam_medisCreateInput;
|
|
|
|
|
|
|
|
|
|
for (let i = (latestId?.id_visit?.length ?? 0) - 1; i >= 0; i--) {
|
|
|
|
|
if (latestId?.id_visit[i] === 'X') {
|
|
|
|
|
xCounter++;
|
|
|
|
|
} else {
|
|
|
|
|
newId = latestId?.id_visit?.substring(0, i + 1) || '';
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (xCounter < 1) {
|
|
|
|
|
newId = (parseInt(latestId?.id_visit || '0', 10) + 1).toString();
|
|
|
|
|
} else {
|
|
|
|
|
newId = (parseInt(newId || '0', 10) + 1).toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rekamMedis = {
|
|
|
|
|
...data,
|
|
|
|
|
id_visit: newId,
|
|
|
|
|
waktu_visit: new Date(),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const logData: CreateLogDto = {
|
|
|
|
|
event: 'rekam_medis_created',
|
|
|
|
|
payload: {
|
|
|
|
|
dokter_id: 123,
|
|
|
|
|
visit_id: newId,
|
|
|
|
|
anamnese: data.anamnese,
|
|
|
|
|
jenis_kasus: data.jenis_kasus,
|
|
|
|
|
tindak_lanjut: data.tindak_lanjut,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const newRekamMedis = await this.prisma.$transaction(async (tx) => {
|
|
|
|
|
const createdRekamMedis = await tx.rekam_medis.create({
|
|
|
|
|
data: rekamMedis,
|
|
|
|
|
});
|
|
|
|
|
|
2025-10-30 05:15:06 +00:00
|
|
|
await tx.blockchain_log_queue.create({
|
|
|
|
|
data: {
|
|
|
|
|
event: logData.event,
|
|
|
|
|
user_id: 9,
|
|
|
|
|
payload: logData.payload,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Input Into Fabric Here
|
2025-10-27 06:41:51 +00:00
|
|
|
|
|
|
|
|
return createdRekamMedis;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return newRekamMedis;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Error creating Rekam Medis:', error);
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|