582 lines
15 KiB
Go
582 lines
15 KiB
Go
package encounter
|
|
|
|
import (
|
|
// std
|
|
"errors"
|
|
"fmt"
|
|
erc "simrs-vx/internal/domain/references/clinical"
|
|
ere "simrs-vx/internal/domain/references/encounter"
|
|
ero "simrs-vx/internal/domain/references/organization"
|
|
"strings"
|
|
|
|
// external
|
|
dg "github.com/karincake/apem/db-gorm-pg"
|
|
gh "github.com/karincake/getuk"
|
|
"gorm.io/gorm"
|
|
|
|
// pkg
|
|
plh "simrs-vx/pkg/lib-helper"
|
|
pl "simrs-vx/pkg/logger"
|
|
pu "simrs-vx/pkg/use-case-helper"
|
|
|
|
// internal
|
|
eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist"
|
|
ea "simrs-vx/internal/domain/main-entities/ambulatory"
|
|
e "simrs-vx/internal/domain/main-entities/encounter"
|
|
er "simrs-vx/internal/domain/main-entities/rehab"
|
|
erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist"
|
|
es "simrs-vx/internal/domain/main-entities/soapi"
|
|
)
|
|
|
|
func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.Encounter, error) {
|
|
pl.SetLogInfo(event, nil, "started", "DBCreate")
|
|
|
|
data := e.Encounter{}
|
|
setData(&input, &data)
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
if err := tx.Create(&data).Error; err != nil {
|
|
return nil, plh.HandleCreateError(input, event, err)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return &data, nil
|
|
}
|
|
|
|
func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Encounter, *e.MetaDto, error) {
|
|
if input.AuthInfo.User_Id == 0 {
|
|
return nil, nil, plh.HandleListError(input, event, errors.New("user_id is required"))
|
|
}
|
|
|
|
pl.SetLogInfo(event, input, "started", "DBReadList")
|
|
data := []e.Encounter{}
|
|
pagination := gh.Pagination{}
|
|
count := int64(0)
|
|
meta := e.MetaDto{}
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
tx = tx.Model(&e.Encounter{})
|
|
|
|
if input.AuthInfo.Doctor_Id != nil {
|
|
tx.Where("\"Responsible_Doctor_Id\" = ?", *input.AuthInfo.Doctor_Id)
|
|
}
|
|
|
|
tx.Scopes(gh.Preload(input.Includes)).
|
|
Scopes(gh.Filter(input.FilterDto)).
|
|
Count(&count).
|
|
Scopes(gh.Paginate(input, &pagination)).
|
|
Order("\"CreatedAt\" DESC")
|
|
|
|
if err := tx.Find(&data).Error; err != nil {
|
|
if err == gorm.ErrRecordNotFound {
|
|
return nil, &meta, nil
|
|
}
|
|
return nil, nil, plh.HandleListError(input, event, err)
|
|
}
|
|
|
|
meta.Count = int(count)
|
|
meta.PageNumber = pagination.PageNumber
|
|
meta.PageSize = pagination.PageSize
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return data, &meta, nil
|
|
}
|
|
|
|
func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e.Encounter, error) {
|
|
pl.SetLogInfo(event, input, "started", "DBReadDetail")
|
|
data := e.Encounter{}
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
if err := tx.Scopes(gh.Preload(input.Includes)).First(&data, input.Id).Error; err != nil {
|
|
if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil {
|
|
return nil, processedErr
|
|
}
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return &data, nil
|
|
}
|
|
|
|
func UpdateData(input e.UpdateDto, data *e.Encounter, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, data, "started", "DBUpdate")
|
|
setDataUpdate(input, data)
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
if err := tx.Save(&data).Error; err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-update-fail",
|
|
Detail: "Database update failed",
|
|
Raw: err,
|
|
}
|
|
return pl.SetLogError(event, input)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|
|
|
|
func DeleteData(data *e.Encounter, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, data, "started", "DBDelete")
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
if err := tx.Delete(&data).Error; err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-delete-fail",
|
|
Detail: "Database delete failed",
|
|
Raw: err,
|
|
}
|
|
return pl.SetLogError(event, data)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|
|
|
|
func updateDischargeData(input e.DischargeDto, data *e.Encounter, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, data, "started", "DBUpdateDischarge")
|
|
setDataDischarge(input, data)
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
if err := tx.Save(&data).Error; err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-update-fail",
|
|
Detail: "Database update failed",
|
|
Raw: err,
|
|
}
|
|
return pl.SetLogError(event, input)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|
|
|
|
func IsDone(encounter_id uint, event *pl.Event, dbx ...*gorm.DB) bool {
|
|
pl.SetLogInfo(event, nil, "started", "DBIsDone")
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
var data e.Encounter
|
|
if err := tx.Where("\"Id\" = ?", encounter_id).First(&data).Error; err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-get-fail",
|
|
Detail: "Database get failed",
|
|
Raw: err,
|
|
}
|
|
return false
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return data.IsDone()
|
|
}
|
|
|
|
func UpdateStatusData(input e.UpdateStatusDto, data *e.Encounter, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, data, "started", "DBUpdate")
|
|
setDataUpdateStatus(input, data)
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
if err := tx.Save(&data).Error; err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-update-fail",
|
|
Detail: "Database update failed",
|
|
Raw: err,
|
|
}
|
|
return pl.SetLogError(event, input)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|
|
|
|
func updateCheckInData(input e.CheckinDto, data *e.Encounter, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, data, "started", "DBUpdate")
|
|
setDataCheckIn(input, data)
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
if err := tx.Save(&data).Error; err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-update-fail",
|
|
Detail: "Database update failed",
|
|
Raw: err,
|
|
}
|
|
return pl.SetLogError(event, input)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|
|
|
|
func updateLatestResponsibleDoctorHist(input e.CheckinDto, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, "started", "DBUpdate")
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
subQuery := tx.
|
|
Select("\"Id\"").
|
|
Model(&erdh.ResponsibleDoctorHist{}).
|
|
Where("\"Encounter_Id\" = ?", input.Id).
|
|
Order("\"CreatedAt\" DESC").
|
|
Limit(1)
|
|
|
|
result := tx.
|
|
Model(&erdh.ResponsibleDoctorHist{}).
|
|
Where("\"Id\" = (?)", subQuery).
|
|
Update("\"FinishedAt\"", input.StartedAt)
|
|
|
|
if result.Error != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-update-fail",
|
|
Detail: "Database update failed",
|
|
Raw: result.Error,
|
|
}
|
|
return pl.SetLogError(event, input)
|
|
}
|
|
|
|
if result.RowsAffected == 0 {
|
|
pl.SetLogInfo(event, input, "no previous data found to update")
|
|
return nil
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|
|
|
|
func updateLatestAdmEmployeeHist(input e.CheckinDto, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, "started", "DBUpdate")
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
subQuery := tx.
|
|
Select("\"Id\"").
|
|
Model(&eaeh.AdmEmployeeHist{}).
|
|
Where("\"Encounter_Id\" = ?", input.Id).
|
|
Order("\"CreatedAt\" DESC").
|
|
Limit(1)
|
|
|
|
result := tx.
|
|
Model(&eaeh.AdmEmployeeHist{}).
|
|
Where("\"Id\" = (?)", subQuery).
|
|
Update("\"FinishedAt\"", input.StartedAt)
|
|
|
|
if result.Error != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-update-fail",
|
|
Detail: "Database update failed",
|
|
Raw: result.Error,
|
|
}
|
|
return pl.SetLogError(event, input)
|
|
}
|
|
|
|
if result.RowsAffected == 0 {
|
|
pl.SetLogInfo(event, input, "no previous data found to update")
|
|
return nil
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|
|
|
|
func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, bool, error) {
|
|
pl.SetLogInfo(event, nil, "started", "DBGetRecentEncounterAdm")
|
|
|
|
var (
|
|
tx = dg.I
|
|
recentEncounterAdm e.Encounter
|
|
countEncounterSeries int64
|
|
)
|
|
|
|
err := tx.
|
|
Scopes(gh.Preload("Rehab,Responsible_Doctor")).
|
|
Joins("JOIN \"Ambulatory\" ON \"Ambulatory\".\"Encounter_Id\" = \"Encounter\".\"Id\"").
|
|
Where("\"Patient_Id\" = ?", i.Patient_Id).
|
|
Where("\"Ambulatory\".\"Class_Code\" = ? AND \"Ambulatory\".\"VisitMode_Code\" = ?", ere.ACCRme, ere.VMCAdm).
|
|
Order("\"CreatedAt\" DESC").
|
|
First(&recentEncounterAdm).Error
|
|
if err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "read-recentEncounter-fail",
|
|
Detail: "Database read failed",
|
|
Raw: err,
|
|
}
|
|
return e.Encounter{}, false, pl.SetLogError(event, i)
|
|
}
|
|
|
|
err = tx.
|
|
Debug().
|
|
Model(&e.Encounter{}).
|
|
Joins("JOIN \"Ambulatory\" ON \"Ambulatory\".\"Encounter_Id\" = \"Encounter\".\"Id\"").
|
|
Where("\"Patient_Id\" = ?", i.Patient_Id).
|
|
Where("\"Ambulatory\".\"Class_Code\" = ? AND \"Ambulatory\".\"VisitMode_Code\" = ?", ere.ACCRme, ere.VMCSeries).
|
|
Where("\"Encounter\".\"CreatedAt\" > ?", recentEncounterAdm.CreatedAt).
|
|
Count(&countEncounterSeries).Error
|
|
if err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "read-countEncounter-fail",
|
|
Detail: "Database read failed",
|
|
Raw: err,
|
|
}
|
|
return e.Encounter{}, false, pl.SetLogError(event, i)
|
|
}
|
|
|
|
return recentEncounterAdm, countEncounterSeries < int64(*recentEncounterAdm.Rehab.AllocatedVisitCount), nil
|
|
}
|
|
|
|
func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.CreateDto, err error) {
|
|
var data []es.Soapi
|
|
|
|
pl.SetLogInfo(event, enc, "started", "DBReadList")
|
|
|
|
if enc.Responsible_Doctor == nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "no responsible-doctor found",
|
|
Detail: "Encounter does not have responsible-doctor",
|
|
}
|
|
}
|
|
|
|
err = dg.I.Debug().
|
|
Model(&es.Soapi{}).
|
|
Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\"").
|
|
Where("\"Encounter_Id\" = ?", enc.Id).
|
|
Where("\"Employee\".\"Position_Code\" = ?", ero.EPCDoc).
|
|
Where("\"Soapi\".\"TypeCode\" IN ?", []erc.SoapiTypeCode{erc.STCEEarlyMedic, erc.STCFunc}).
|
|
Where("\"Soapi\".\"Employee_Id\" = ?", *enc.Responsible_Doctor.Employee_Id).
|
|
Find(&data).Error
|
|
|
|
if err != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Raw: err,
|
|
}
|
|
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
event.ErrInfo.Code = "data-not-found"
|
|
event.ErrInfo.Detail = "Data not found"
|
|
return nil, pl.SetLogError(event, enc)
|
|
}
|
|
|
|
event.ErrInfo.Code = "read-fail"
|
|
event.ErrInfo.Detail = "Database read failed"
|
|
return nil, pl.SetLogError(event, enc)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
|
|
for _, s := range data {
|
|
// set data soapi for copy
|
|
dataSoapi = append(dataSoapi, es.CreateDto{
|
|
Employee_Id: s.Employee_Id,
|
|
Time: s.Time,
|
|
TypeCode: s.TypeCode,
|
|
Value: s.Value,
|
|
})
|
|
}
|
|
|
|
if len(dataSoapi) < 2 {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "missing-soapi",
|
|
Detail: fmt.Sprintf("Missing required Soapi types"),
|
|
}
|
|
return nil, pl.SetLogError(event, enc)
|
|
}
|
|
return
|
|
}
|
|
|
|
func getSoapiByTypeCode(encounterId uint, event *pl.Event, mode string) (err error) {
|
|
pl.SetLogInfo(event, encounterId, "started", "DBReadList")
|
|
|
|
var (
|
|
dataAmbulatory ea.Ambulatory
|
|
dataSoapi []es.Soapi
|
|
)
|
|
|
|
// Get Data Ambulatory
|
|
if err = dg.I.
|
|
Where("\"Encounter_Id\" = ?", encounterId).
|
|
First(&dataAmbulatory).Error; err != nil {
|
|
return setDBError(event, err, encounterId)
|
|
}
|
|
|
|
// Set Query for get data Soapi
|
|
tx := dg.I.
|
|
Model(&es.Soapi{}).
|
|
Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\"").
|
|
Where("\"Encounter_Id\" = ?", encounterId).
|
|
Where("\"Employee\".\"Position_Code\" = ?", ero.EPCDoc)
|
|
|
|
// Set Case
|
|
switch {
|
|
case dataAmbulatory.Class_Code == ere.ACCReg:
|
|
tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", erc.STCEEarlyMedic)
|
|
case dataAmbulatory.Class_Code == ere.ACCRme && dataAmbulatory.VisitMode_Code == ere.VMCAdm:
|
|
tx = tx.Where("\"Soapi\".\"TypeCode\" IN ?", []erc.SoapiTypeCode{erc.STCEEarlyMedic, erc.STCFunc, erc.STCEarlyRehab})
|
|
case dataAmbulatory.Class_Code == ere.ACCRme && dataAmbulatory.VisitMode_Code == ere.VMCSeries:
|
|
tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", erc.STCEarlyRehab)
|
|
}
|
|
|
|
if err = tx.Find(&dataSoapi).Error; err != nil {
|
|
return setDBError(event, err, encounterId)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return validateExistedSoapi(dataSoapi, &dataAmbulatory, event, mode)
|
|
}
|
|
|
|
func validateExistedSoapi(dataSoapi []es.Soapi, dataAmbulatory *ea.Ambulatory, event *pl.Event, mode string) error {
|
|
typeExist := make(map[erc.SoapiTypeCode]bool)
|
|
for _, s := range dataSoapi {
|
|
typeExist[s.TypeCode] = true
|
|
}
|
|
|
|
required := []erc.SoapiTypeCode{}
|
|
|
|
switch {
|
|
case dataAmbulatory.Class_Code == ere.ACCReg:
|
|
required = []erc.SoapiTypeCode{erc.STCEEarlyMedic}
|
|
case dataAmbulatory.Class_Code == ere.ACCRme && dataAmbulatory.VisitMode_Code == ere.VMCAdm:
|
|
required = []erc.SoapiTypeCode{erc.STCEEarlyMedic, erc.STCFunc, erc.STCEarlyRehab}
|
|
case dataAmbulatory.Class_Code == ere.ACCRme && dataAmbulatory.VisitMode_Code == ere.VMCSeries:
|
|
required = []erc.SoapiTypeCode{erc.STCEarlyRehab}
|
|
}
|
|
|
|
var missing, existing []string
|
|
for _, code := range required {
|
|
if typeExist[code] {
|
|
existing = append(existing, string(code))
|
|
} else {
|
|
missing = append(missing, string(code))
|
|
}
|
|
}
|
|
|
|
switch mode {
|
|
case "check-in":
|
|
if len(existing) > 0 {
|
|
return setSoapiError(event, fmt.Sprintf("Soapi type(s) %s exist, can't check-in", strings.Join(existing, ", ")))
|
|
}
|
|
case "check-out":
|
|
if len(missing) > 0 {
|
|
return setSoapiError(event, fmt.Sprintf("Soapi type(s) %s not found, can't check-out", strings.Join(missing, ", ")))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func setSoapiError(event *pl.Event, detail string) error {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "invalid-soapi-state",
|
|
Detail: detail,
|
|
}
|
|
return pl.SetLogError(event, detail)
|
|
}
|
|
|
|
func setDBError(event *pl.Event, err error, ctx any) error {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Raw: err,
|
|
Code: "read-fail",
|
|
Detail: "Database read failed",
|
|
}
|
|
return pl.SetLogError(event, ctx)
|
|
}
|
|
|
|
func updateRehabDoctor(input er.UpdateDto, event *pl.Event, dbx ...*gorm.DB) error {
|
|
pl.SetLogInfo(event, "started", "DBUpdate")
|
|
|
|
var tx *gorm.DB
|
|
if len(dbx) > 0 {
|
|
tx = dbx[0]
|
|
} else {
|
|
tx = dg.I
|
|
}
|
|
|
|
result := tx.
|
|
Model(&er.Rehab{}).
|
|
Where("\"Encounter_Id\" = (?)", input.Encounter_Id).
|
|
Update("\"Doctor_Id\"", input.Doctor_Id)
|
|
|
|
if result.Error != nil {
|
|
event.Status = "failed"
|
|
event.ErrInfo = pl.ErrorInfo{
|
|
Code: "data-update-fail",
|
|
Detail: "Database update failed",
|
|
Raw: result.Error,
|
|
}
|
|
return pl.SetLogError(event, input)
|
|
}
|
|
|
|
pl.SetLogInfo(event, nil, "complete")
|
|
return nil
|
|
}
|