Files
simrsx-be/internal/use-case/main-use-case/encounter/helper.go
T
2025-10-27 17:20:46 +07:00

435 lines
12 KiB
Go

/*
DESCRIPTION:
Any functions that are used internally by the use-case
*/
package encounter
import (
"errors"
"fmt"
uir "simrs-vx/internal/use-case/main-use-case/internal-reference"
"strings"
"time"
"gorm.io/gorm"
pl "simrs-vx/pkg/logger"
pu "simrs-vx/pkg/use-case-helper"
edo "simrs-vx/internal/domain/main-entities/device-order"
ed "simrs-vx/internal/domain/main-entities/doctor"
emo "simrs-vx/internal/domain/main-entities/material-order"
emco "simrs-vx/internal/domain/main-entities/mcu-order"
em "simrs-vx/internal/domain/main-entities/medication"
emei "simrs-vx/internal/domain/main-entities/medication-item"
emi "simrs-vx/internal/domain/main-entities/medicine-mix"
emmi "simrs-vx/internal/domain/main-entities/medicine-mix-item"
ep "simrs-vx/internal/domain/main-entities/prescription"
epi "simrs-vx/internal/domain/main-entities/prescription-item"
eu "simrs-vx/internal/domain/main-entities/unit"
// udo "simrs-vx/internal/use-case/main-use-case/device-order"
es "simrs-vx/internal/domain/main-entities/soapi"
um "simrs-vx/internal/use-case/main-use-case/medication"
umei "simrs-vx/internal/use-case/main-use-case/medication-item"
umi "simrs-vx/internal/use-case/main-use-case/medicine-mix"
ummi "simrs-vx/internal/use-case/main-use-case/medicine-mix-item"
up "simrs-vx/internal/use-case/main-use-case/prescription"
upi "simrs-vx/internal/use-case/main-use-case/prescription-item"
e "simrs-vx/internal/domain/main-entities/encounter"
erc "simrs-vx/internal/domain/references/common"
erg "simrs-vx/internal/domain/references/organization"
)
func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) {
var inputSrc *e.CreateDto
if inputT, ok := any(input).(*e.CreateDto); ok {
inputSrc = inputT
} else {
inputTemp := any(input).(*e.UpdateDto)
inputSrc = &inputTemp.CreateDto
}
data.Patient_Id = inputSrc.Patient_Id
data.RegisteredAt = inputSrc.RegisteredAt
data.Class_Code = inputSrc.Class_Code
data.Unit_Id = inputSrc.Unit_Id
data.Specialist_Id = inputSrc.Specialist_Id
data.Subspecialist_Id = inputSrc.Subspecialist_Id
data.VisitDate = inputSrc.VisitDate
data.PaymentMethod_Code = inputSrc.PaymentMethod_Code
data.InsuranceCompany_Id = inputSrc.InsuranceCompany_Id
data.Member_Number = inputSrc.Member_Number
data.Ref_Number = inputSrc.Ref_Number
data.Trx_Number = inputSrc.Trx_Number
data.Appointment_Doctor_Id = inputSrc.Appointment_Doctor_Id
data.Adm_Employee_Id = inputSrc.Adm_Employee_Id
data.Responsible_Doctor_Id = inputSrc.Responsible_Doctor_Id
data.RefSource_Name = inputSrc.RefSource_Name
data.Appointment_Id = inputSrc.Appointment_Id
data.Status_Code = erc.DSCProcess
data.RefType_Code = &inputSrc.RefTypeCode
data.NewStatus = inputSrc.NewStatus
}
func setDataUpdate(src e.UpdateDto, dst *e.Encounter) {
dst.Appointment_Doctor_Id = src.Appointment_Doctor_Id
dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id
dst.Unit_Id = src.Unit_Id
dst.Specialist_Id = src.Specialist_Id
dst.Subspecialist_Id = src.Subspecialist_Id
dst.VisitDate = src.VisitDate
}
func setDataDischarge(src e.DischargeDto, dst *e.Encounter) {
dst.Discharge_Method_Code = src.Discharge_Method_Code
dst.EarlyEducation = src.EarlyEducation
dst.MedicalDischargeEducation = src.MedicalDischargeEducation
dst.AdmDischargeEducation = src.AdmDischargeEducation
dst.DischargeReason = src.DischargeReason
dst.Status_Code = erc.DSCDone
now := time.Now()
dst.Discharge_Date = &now
dst.FinishedAt = &now
}
func checkSoapiByDocExists(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "checkSoapiByDocExists")
var soapies []es.Soapi
err := tx.
Preload("Employee").
Preload("Employee.User").
Where("\"Encounter_Id\" = ?", encounter_id).Find(&soapies).Error
if err != nil {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-get-fail",
Detail: "get soapi failed",
Raw: err,
}
return pl.SetLogError(event, nil)
}
if len(soapies) == 0 {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-notFound",
Detail: "no soapi found for encounter",
Raw: errors.New("soapi not found"),
}
return pl.SetLogError(event, nil)
}
for _, s := range soapies {
if s.Employee != nil && *s.Employee.Position_Code == erg.EPCDoc {
return nil
}
}
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-update-fail",
Detail: "no soapi written by a doctor found",
Raw: errors.New("all soapi employees are not doctors"),
}
return pl.SetLogError(event, nil)
}
func createMedication(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "createMedication")
prescription, err := up.ReadDetailData(ep.ReadDetailDto{Encounter_Id: &encounter_id}, event, tx)
if err != nil {
return err
}
prescriptionItem, _, err := upi.ReadListData(epi.ReadListDto{
FilterDto: epi.FilterDto{Prescription_Id: &prescription.Id},
Includes: "medicineMix,medicineMix-MixItems"}, event, tx)
if err != nil {
return err
}
if len(prescriptionItem) == 0 {
return nil
}
medicationCreate := em.CreateDto{
Encounter_Id: &encounter_id,
IssuedAt: pu.GetTimeNow(),
Status_Code: erc.DSCNew,
}
medication, err := um.CreateData(medicationCreate, event, tx)
if err != nil {
return err
}
for _, prescriptionItem := range prescriptionItem {
if prescriptionItem.IsMix {
medMix_id, err := createMedicineMixAndItem(*prescriptionItem.MedicineMix, event, tx)
if err != nil {
return err
}
prescriptionItem.MedicineMix_Id = medMix_id
}
err := createMedicationItem(medication.Id, prescriptionItem, event, tx)
if err != nil {
return err
}
}
return nil
}
func createMedicineMixAndItem(input emi.MedicineMix, event *pl.Event, tx *gorm.DB) (*uint, error) {
pl.SetLogInfo(event, nil, "started", "createMedicineMix")
medicineMixCreate := emi.CreateDto{
Name: input.Name,
Uom_Code: input.Uom_Code,
}
medicineMix, err := umi.CreateData(medicineMixCreate, event, tx)
if err != nil {
return nil, err
}
// recreate medicineMixItem with new medicineMix_id to keep medMixItem remain the same for prescriptionItem that is created
for _, medicineMixItem := range input.MixItems {
medicineMixItemCreate := emmi.CreateDto{
MedicineMix_Id: &medicineMix.Id,
Medicine_Id: medicineMixItem.Medicine_Id,
Dose: medicineMixItem.Dose,
}
_, err := ummi.CreateData(medicineMixItemCreate, event, tx)
if err != nil {
return nil, err
}
}
return &medicineMix.Id, nil
}
func createMedicationItem(medication_id uint, input epi.PrescriptionItem, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "createMedicationItem")
medicationItemCreate := emei.CreateDto{
Medication_Id: &medication_id,
IsMix: input.IsMix,
Medicine_Id: input.Medicine_Id,
MedicineMix_Id: input.MedicineMix_Id,
Usage: input.Usage,
Interval: input.Interval,
IntervalUnit_Code: input.IntervalUnit_Code,
Quantity: input.Quantity,
}
_, err := umei.CreateData(medicationItemCreate, event, tx)
if err != nil {
return err
}
return nil
}
func checkNewOrdersExist(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "CheckNewOrdersExist")
var errs []string
if err := getDeviceOrders(encounter_id, event, tx); err != nil {
errs = append(errs, err.Error())
}
if err := getMaterialOrders(encounter_id, event, tx); err != nil {
errs = append(errs, err.Error())
}
if err := getMcuOrders(encounter_id, event, tx); err != nil {
errs = append(errs, err.Error())
}
if len(errs) > 0 {
return fmt.Errorf("encounter has open orders: %s", strings.Join(errs, "; "))
}
return nil
}
func getDeviceOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "getDeviceOrders")
var orders []edo.DeviceOrder
err := tx.Where("\"Encounter_Id\" = ? AND \"Status_Code\" = ?", encounter_id, erc.DSCNew).Find(&orders).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil
}
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-get-fail",
Detail: "get device order failed",
Raw: err,
}
return pl.SetLogError(event, nil)
}
if len(orders) > 0 {
return fmt.Errorf("encounter has %d device orders", len(orders))
}
return nil
}
func getMaterialOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "getMaterialOrders")
var orders []emo.MaterialOrder
err := tx.Where("\"Encounter_Id\" = ? AND \"Status_Code\" = ?", encounter_id, erc.DSCNew).Find(&orders).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil
}
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-get-fail",
Detail: "get material order failed",
Raw: err,
}
return pl.SetLogError(event, nil)
}
if len(orders) > 0 {
return fmt.Errorf("encounter has %d material orders", len(orders))
}
return nil
}
func getMcuOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "getMcuOrders")
var orders []emco.McuOrder
err := tx.Where("\"Encounter_Id\" = ? AND \"Status_Code\" = ?", encounter_id, erc.DSCNew).Find(&orders).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil
}
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-get-fail",
Detail: "get mcu order failed",
Raw: err,
}
return pl.SetLogError(event, nil)
}
if len(orders) > 0 {
return fmt.Errorf("encounter has %d mcu orders", len(orders))
}
return nil
}
func setDataUpdateStatus(src e.UpdateStatusDto, dst *e.Encounter) {
dst.Status_Code = src.StatusCode
}
func setDataCheckIn(src e.CheckinDto, dst *e.Encounter) {
if src.Adm_Employee_Id != nil {
dst.Adm_Employee_Id = src.Adm_Employee_Id
}
dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id
dst.StartedAt = src.StartedAt
}
func createInternalReferences(input e.DischargeDto, event *pl.Event, tx *gorm.DB) error {
unitIDs := make(map[uint16]struct{})
doctorIDs := make(map[uint]struct{})
for _, ref := range *input.InternalReferences {
if ref.Unit_Id != nil {
unitIDs[*ref.Unit_Id] = struct{}{}
}
if ref.Doctor_Id != nil {
doctorIDs[*ref.Doctor_Id] = struct{}{}
}
}
// validate unitIds
if len(unitIDs) > 0 {
var ids []uint16
for id := range unitIDs {
ids = append(ids, id)
}
units, err := getUnits(ids, event, tx)
if err != nil {
return fmt.Errorf("failed to fetch units: %w", err)
}
if len(units) != len(ids) {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-validation-fail",
Detail: "unit_id not found",
}
return pl.SetLogError(event, nil)
}
}
// validate doctorIds
if len(doctorIDs) > 0 {
var ids []uint
for id := range doctorIDs {
ids = append(ids, id)
}
doctors, err := getDoctors(ids, event, tx)
if err != nil {
return fmt.Errorf("failed to fetch doctors: %w", err)
}
if len(doctors) != len(ids) {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-validation-fail",
Detail: "doctor_id not found",
}
return pl.SetLogError(event, nil)
}
}
if err := uir.CreateBulkData(*input.InternalReferences, input.Id, event, tx); err != nil {
return err
}
return nil
}
func getUnits(unitIds []uint16, event *pl.Event, tx *gorm.DB) ([]eu.Unit, error) {
pl.SetLogInfo(event, nil, "started", "getUnits")
var units []eu.Unit
err := tx.Where("\"Id\" IN ?", unitIds).Find(&units).Error
if err != nil {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-get-fail",
Detail: "get units",
Raw: err,
}
return nil, pl.SetLogError(event, nil)
}
return units, nil
}
func getDoctors(doctorIds []uint, event *pl.Event, tx *gorm.DB) ([]ed.Doctor, error) {
pl.SetLogInfo(event, nil, "started", "getDoctors")
var doctors []ed.Doctor
err := tx.Where("\"Id\" IN ?", doctorIds).Find(&doctors).Error
if err != nil {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-get-fail",
Detail: "get doctors",
Raw: err,
}
return nil, pl.SetLogError(event, nil)
}
return doctors, nil
}