Merge branch 'fix/anything-moko' of github.com:dikstub-rssa/simrs-be into fix/anything-moko

This commit is contained in:
dpurbosakti
2025-11-10 08:52:12 +07:00
24 changed files with 973 additions and 507 deletions

No files matched your search

+9 -9
View File
@@ -1,9 +1,9 @@
package main package main
import ( import (
m "simrs-vx/internal/interface/migration" m "simrs-vx/internal/interface/migration"
) )
func main() { func main() {
m.Migrate(m.Main) m.Migrate(m.Main)
} }
@@ -0,0 +1,6 @@
-- Modify "Ambulatory" table
ALTER TABLE "public"."Ambulatory" DROP COLUMN "VisitMode_Code";
-- Modify "InternalReference" table
ALTER TABLE "public"."InternalReference" ADD COLUMN "Status_Code" text NULL;
-- Modify "Rehab" table
ALTER TABLE "public"."Rehab" ADD COLUMN "Parent_Encounter_Id" bigint NULL, ADD COLUMN "ExpiredAt" timestamptz NULL, ADD COLUMN "VisitMode_Code" text NULL, ADD COLUMN "Status_Code" text NULL;
@@ -0,0 +1,2 @@
-- Modify "Rehab" table
ALTER TABLE "public"."Rehab" DROP COLUMN "Doctor_Id";
+13 -11
View File
@@ -1,4 +1,4 @@
h1:6umIsneejLUg0V9wUaB2HuCvEHD+2v/dwbxg+y23+wo= h1:VjwYayb6y4kUl9K9WTTwmXExlHijchOCRwnUYcRlhRI=
20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= 20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k=
20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= 20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0=
20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= 20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI=
@@ -85,13 +85,15 @@ h1:6umIsneejLUg0V9wUaB2HuCvEHD+2v/dwbxg+y23+wo=
20251106090021.sql h1:4JwdKgO8T46YhyWVJUxpRIwudBDlG8QN1brSOYmgQ20= 20251106090021.sql h1:4JwdKgO8T46YhyWVJUxpRIwudBDlG8QN1brSOYmgQ20=
20251106144745.sql h1:nqnQCzGrVJaq8ilOEOGXeRUL1dolj+OPWKuP8A92FRA= 20251106144745.sql h1:nqnQCzGrVJaq8ilOEOGXeRUL1dolj+OPWKuP8A92FRA=
20251107012049.sql h1:Pff4UqltGS3clSlGr0qq8CQM56L29wyxY0FC/N/YAhU= 20251107012049.sql h1:Pff4UqltGS3clSlGr0qq8CQM56L29wyxY0FC/N/YAhU=
20251107064812.sql h1:jjpcAi0B/geEOKWmmR6+1UhWMhjstWhWQcz9lUWrtTY= 20251107064812.sql h1:GB9a0ZfMYTIoGNmKUG+XcYUsTnRMFfT4/dAD71uCPc4=
20251107064937.sql h1:1nPu0THBf+YquFIJSE4pc1dA7r3EydH92cpp26ozysw= 20251107064937.sql h1:IC5pw1Ifj30hiE6dr5NMHXaSHoQI+vRd40N5ABgBHRI=
20251107071420.sql h1:Q+e+OqjdiuK/sghDQ5NxjU+u2zYl+vh/eRBlUz9Idhg= 20251107071420.sql h1:9NO3iyLEXEtWa2kSRjM/8LyzuVIk6pdFL2SuheWjB08=
20251107074318.sql h1:VbOWMw2rClEWgMnDSejXPqXkFoQ4odVsHn3/UAEiCYA= 20251107074318.sql h1:7fHbSRrdjOmHh/xwnjCLwoiB5cW5zeH+uxLV0vZbkIA=
20251107075050.sql h1:ZZaKJEWXIJ94/0/2Gzzz+HXjmebEs5eP8iUIot26/c8= 20251107075050.sql h1:np+3uTOnU9QNtK7Knaw8eRMhkyB9AwrtSNHphOBxbHY=
20251107080604.sql h1:aq+tINa0ULCZlJcUK2jaeGh6rRH4jJz3e2NrK47m0Ec= 20251107080604.sql h1:cXDBLPJDVWLTG6yEJqkJsOQ7p7VYxLM2SY+mwO8qSHo=
20251107081830.sql h1:Bl9kniyLWeMqd3nrvgCgiUpdcJWp8qX1F41JAM1WbiE= 20251107081830.sql h1:/S7OQZo4ZnK80t28g/JyiOTZtmWG/dP5Wg2zXNMQ/iE=
20251107091033.sql h1:ETTCaAHBT6wipXrEGXIepxLMdD2LNzprPAp99+jnW0w= 20251107091033.sql h1:/cbkF1nO/IjNSIfDJJx456KJtQ9rWFXOBFAkR/M2xiE=
20251107091209.sql h1:BUI9JlTzZxCdVHsrRbOye7m47AJEvXNhCaCVv/HzILs= 20251107091209.sql h1:jrLQOUeV8ji2fg0pnEcs1bw4ANUxzTSMXC/rrHLIY+M=
20251107091541.sql h1:/tW2uXv/o0Q2E4lQzlYNgEftvDqjf3c2AQpZ83Vb6zc= 20251107091541.sql h1:6UqbhQQRmzA2+eKu5lIvkwOkk+lH70QLZC8Pjpjcq68=
20251110012217.sql h1:+4ZcAtlZobLY/iTU1hwVEuqCCYtbiDs2N75jTBz1eQM=
20251110012306.sql h1:w3ZvpG2IaS4sZ9WxKcaMAjmy1JhsOPg8zmwjDv4tv7Y=
@@ -6,9 +6,8 @@ import (
) )
type CreateDto struct { type CreateDto struct {
Encounter_Id *uint `json:"encounter_id"` Encounter_Id *uint `json:"encounter_id"`
Class_Code ere.AmbulatoryClassCode `json:"class_code" validate:"maxLength=10"` Class_Code ere.AmbulatoryClassCode `json:"class_code" validate:"maxLength=10"`
VisitMode_Code ere.VisitModeCode `json:"visitMode_code"`
} }
type ReadListDto struct { type ReadListDto struct {
@@ -50,9 +49,8 @@ type ResponseDto struct {
func (d Ambulatory) ToResponse() ResponseDto { func (d Ambulatory) ToResponse() ResponseDto {
resp := ResponseDto{ resp := ResponseDto{
Encounter_Id: d.Encounter_Id, Encounter_Id: d.Encounter_Id,
Class_Code: d.Class_Code, Class_Code: d.Class_Code,
VisitMode_Code: d.VisitMode_Code,
} }
resp.Main = d.Main resp.Main = d.Main
return resp return resp
@@ -6,8 +6,7 @@ import (
) )
type Ambulatory struct { type Ambulatory struct {
ecore.Main // adjust this according to the needs ecore.Main // adjust this according to the needs
Encounter_Id *uint `json:"encounter_id"` Encounter_Id *uint `json:"encounter_id"`
Class_Code ere.AmbulatoryClassCode `json:"class_code" gorm:"size:10"` Class_Code ere.AmbulatoryClassCode `json:"class_code" gorm:"size:10"`
VisitMode_Code ere.VisitModeCode `json:"visitMode_code"`
} }
+43 -4
View File
@@ -1,6 +1,13 @@
package encounter package encounter
import ( import (
eam "simrs-vx/internal/domain/main-entities/ambulatory"
edc "simrs-vx/internal/domain/main-entities/death-cause"
eem "simrs-vx/internal/domain/main-entities/emergency"
eip "simrs-vx/internal/domain/main-entities/inpatient"
eir "simrs-vx/internal/domain/main-entities/internal-reference"
er "simrs-vx/internal/domain/main-entities/rehab/base"
// std // std
"time" "time"
@@ -19,7 +26,6 @@ import (
ea "simrs-vx/internal/domain/main-entities/appointment" ea "simrs-vx/internal/domain/main-entities/appointment"
ed "simrs-vx/internal/domain/main-entities/doctor" ed "simrs-vx/internal/domain/main-entities/doctor"
ee "simrs-vx/internal/domain/main-entities/employee" ee "simrs-vx/internal/domain/main-entities/employee"
eir "simrs-vx/internal/domain/main-entities/internal-reference"
ep "simrs-vx/internal/domain/main-entities/patient" ep "simrs-vx/internal/domain/main-entities/patient"
es "simrs-vx/internal/domain/main-entities/specialist" es "simrs-vx/internal/domain/main-entities/specialist"
ess "simrs-vx/internal/domain/main-entities/subspecialist" ess "simrs-vx/internal/domain/main-entities/subspecialist"
@@ -48,8 +54,10 @@ type CreateDto struct {
Appointment_Id *uint `json:"appointment_id"` Appointment_Id *uint `json:"appointment_id"`
RefTypeCode ere.RefTypeCode `json:"refTypeCode"` RefTypeCode ere.RefTypeCode `json:"refTypeCode"`
NewStatus bool `json:"newStatus"` NewStatus bool `json:"newStatus"`
VisitMode_Code *ere.VisitModeCode `json:"visitMode_code"` // if subClass_Code is rehab
AllocatedVisitCount *int `json:"allocatedVisitCount"` // if subClass_Code is rehab and VisitMode_Code is "adm" Id uint `json:"-"`
RecentEncounterAdm *Encounter `json:"-"` // if subClass_Code is rehab
VisitMode_Code ere.VisitModeCode `json:"-"` // if subClass_Code is rehab
pa.AuthInfo pa.AuthInfo
} }
@@ -114,7 +122,6 @@ type DischargeDto struct {
AdmDischargeEducation *string `json:"admDischargeEducation"` AdmDischargeEducation *string `json:"admDischargeEducation"`
DischargeReason *string `json:"dischargeReason"` DischargeReason *string `json:"dischargeReason"`
DeathCause *string `json:"deathCause"` DeathCause *string `json:"deathCause"`
InternalReferences *[]eir.CreateDto `json:"internalReferences,omitempty"`
} }
type CheckinDto struct { type CheckinDto struct {
@@ -125,6 +132,17 @@ type CheckinDto struct {
FinishedAt *time.Time `json:"finishedAt"` FinishedAt *time.Time `json:"finishedAt"`
} }
type SwitchUnitDto struct {
Id uint `json:"id"`
PolySwitchCode *ere.PolySwitchCode `json:"polySwitchCode"`
InternalReferences *[]eir.CreateDto `json:"internalReferences" validate:"required"`
}
type ApproveUnitDto struct {
Id uint `json:"id"`
InternalReferences_Id uint16 `json:"internalReferences_id" validate:"required"`
}
type ResponseDto struct { type ResponseDto struct {
ecore.Main ecore.Main
Patient_Id *uint `json:"patient_id"` Patient_Id *uint `json:"patient_id"`
@@ -159,6 +177,17 @@ type ResponseDto struct {
DischargeReason *string `json:"dischargeReason"` DischargeReason *string `json:"dischargeReason"`
Status_Code erc.DataStatusCode `json:"status_code"` Status_Code erc.DataStatusCode `json:"status_code"`
VclaimSep *evs.VclaimSep `json:"vclaimSep,omitempty"` VclaimSep *evs.VclaimSep `json:"vclaimSep,omitempty"`
StartedAt *time.Time `json:"startedAt"`
FinishedAt *time.Time `json:"finishedAt"`
Discharge_Date *time.Time `json:"discharge_date"`
InternalReferences *[]eir.InternalReference `json:"internalReferences,omitempty"`
DeathCause *edc.DeathCause `json:"deathCause,omitempty"`
NewStatus bool `json:"newStatus"`
Ambulatory *eam.Ambulatory `json:"ambulatory,omitempty"`
Emergency *eem.Emergency `json:"emergency,omitempty"`
Inpatient *eip.Inpatient `json:"inpatient,omitempty"`
Rehab *er.Basic `json:"rehab,omitempty"`
RehabChildren *[]er.Basic `json:"rehabChildren,omitempty"`
} }
func (d Encounter) ToResponse() ResponseDto { func (d Encounter) ToResponse() ResponseDto {
@@ -195,6 +224,16 @@ func (d Encounter) ToResponse() ResponseDto {
DischargeReason: d.DischargeReason, DischargeReason: d.DischargeReason,
Status_Code: d.Status_Code, Status_Code: d.Status_Code,
VclaimSep: d.VclaimSep, VclaimSep: d.VclaimSep,
StartedAt: d.StartedAt,
FinishedAt: d.FinishedAt,
Discharge_Date: d.Discharge_Date,
InternalReferences: d.InternalReferences,
DeathCause: d.DeathCause,
NewStatus: d.NewStatus,
Emergency: d.Emergency,
Inpatient: d.Inpatient,
Rehab: d.Rehab,
RehabChildren: d.RehabChildren,
} }
resp.Main = d.Main resp.Main = d.Main
return resp return resp
@@ -69,6 +69,7 @@ type Encounter struct {
Emergency *eem.Emergency `json:"emergency,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` Emergency *eem.Emergency `json:"emergency,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"`
Inpatient *eip.Inpatient `json:"inpatient,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` Inpatient *eip.Inpatient `json:"inpatient,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"`
Rehab *er.Basic `json:"rehab,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` Rehab *er.Basic `json:"rehab,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"`
RehabChildren *[]er.Basic `json:"rehabChildren,omitempty" gorm:"foreignKey:Parent_Encounter_Id;references:Id"`
} }
func (d Encounter) IsDone() bool { func (d Encounter) IsDone() bool {
@@ -1,15 +1,18 @@
package internal_reference package internal_reference
import ( import (
erc "simrs-vx/internal/domain/references/common"
ecore "simrs-vx/internal/domain/base-entities/core" ecore "simrs-vx/internal/domain/base-entities/core"
ed "simrs-vx/internal/domain/main-entities/doctor" ed "simrs-vx/internal/domain/main-entities/doctor"
eu "simrs-vx/internal/domain/main-entities/unit" eu "simrs-vx/internal/domain/main-entities/unit"
) )
type CreateDto struct { type CreateDto struct {
Encounter_Id *uint `json:"-"` Encounter_Id *uint `json:"-"`
Unit_Id *uint16 `json:"unit_id"` Unit_Id *uint16 `json:"unit_id"`
Doctor_Id *uint `json:"doctor_Id"` Doctor_Id *uint `json:"doctor_Id"`
Status_Code erc.DataApprovalCode `json:"status_code"`
} }
type ReadListDto struct { type ReadListDto struct {
@@ -19,9 +22,10 @@ type ReadListDto struct {
} }
type FilterDto struct { type FilterDto struct {
Encounter_Id *uint `json:"encounter-id"` Encounter_Id *uint `json:"encounter-id"`
Unit_Id *uint `json:"unit-id"` Unit_Id *uint `json:"unit-id"`
Doctor_Id *uint `json:"doctor-id"` Doctor_Id *uint `json:"doctor-id"`
Status_Code erc.DataApprovalCode `json:"status-code"`
} }
type ReadDetailDto struct { type ReadDetailDto struct {
@@ -46,11 +50,12 @@ type MetaDto struct {
type ResponseDto struct { type ResponseDto struct {
ecore.Main ecore.Main
Encounter_Id *uint `json:"encounter_id"` Encounter_Id *uint `json:"encounter_id"`
Unit_Id *uint16 `json:"unit_id"` Unit_Id *uint16 `json:"unit_id"`
Unit *eu.Unit `json:"unit,omitempty"` Unit *eu.Unit `json:"unit,omitempty"`
Doctor_Id *uint `json:"doctor_id"` Doctor_Id *uint `json:"doctor_id"`
Doctor *ed.Doctor `json:"doctor,omitempty"` Doctor *ed.Doctor `json:"doctor,omitempty"`
Status_Code *erc.DataApprovalCode `json:"status_code"`
} }
func (d InternalReference) ToResponse() ResponseDto { func (d InternalReference) ToResponse() ResponseDto {
@@ -60,6 +65,7 @@ func (d InternalReference) ToResponse() ResponseDto {
Unit: d.Unit, Unit: d.Unit,
Doctor_Id: d.Doctor_Id, Doctor_Id: d.Doctor_Id,
Doctor: d.Doctor, Doctor: d.Doctor,
Status_Code: d.Status_Code,
} }
resp.Main = d.Main resp.Main = d.Main
return resp return resp
@@ -1,6 +1,8 @@
package internal_reference package internal_reference
import ( import (
erc "simrs-vx/internal/domain/references/common"
ecore "simrs-vx/internal/domain/base-entities/core" ecore "simrs-vx/internal/domain/base-entities/core"
ed "simrs-vx/internal/domain/main-entities/doctor" ed "simrs-vx/internal/domain/main-entities/doctor"
eu "simrs-vx/internal/domain/main-entities/unit" eu "simrs-vx/internal/domain/main-entities/unit"
@@ -8,9 +10,10 @@ import (
type InternalReference struct { type InternalReference struct {
ecore.Main ecore.Main
Encounter_Id *uint `json:"encounter_id"` Encounter_Id *uint `json:"encounter_id"`
Unit_Id *uint16 `json:"unit_id"` Unit_Id *uint16 `json:"unit_id"`
Unit *eu.Unit `json:"unit,omitempty" gorm:"foreignKey:Unit_Id;references:Id"` Unit *eu.Unit `json:"unit,omitempty" gorm:"foreignKey:Unit_Id;references:Id"`
Doctor_Id *uint `json:"doctor_id"` Doctor_Id *uint `json:"doctor_id"`
Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Id;references:Id"` Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Id;references:Id"`
Status_Code *erc.DataApprovalCode `json:"status_code"`
} }
@@ -1,16 +1,21 @@
package base package base
import ( import (
erc "simrs-vx/internal/domain/references/common"
ere "simrs-vx/internal/domain/references/encounter"
"time"
ecore "simrs-vx/internal/domain/base-entities/core" ecore "simrs-vx/internal/domain/base-entities/core"
ed "simrs-vx/internal/domain/main-entities/doctor"
) )
type Basic struct { type Basic struct {
ecore.Main // adjust this according to the needs ecore.Main // adjust this according to the needs
Encounter_Id *uint `json:"encounter_id"` Parent_Encounter_Id *uint `json:"parent_encounter_id"`
Doctor_Id *uint `json:"doctor_id"` Encounter_Id *uint `json:"encounter_id"`
Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Id;references:Id"` AllocatedVisitCount *int `json:"allocatedVisitCount"`
AllocatedVisitCount *int `json:"allocatedVisitCount"` ExpiredAt *time.Time `json:"expiredAt"`
VisitMode_Code *ere.VisitModeCode `json:"visitMode_code"`
Status_Code *erc.DataStatusCode `json:"status_code"`
} }
func (Basic) TableName() string { func (Basic) TableName() string {
+23 -12
View File
@@ -1,14 +1,20 @@
package rehab package rehab
import ( import (
erc "simrs-vx/internal/domain/references/common"
ere "simrs-vx/internal/domain/references/encounter"
ecore "simrs-vx/internal/domain/base-entities/core" ecore "simrs-vx/internal/domain/base-entities/core"
ed "simrs-vx/internal/domain/main-entities/doctor" "time"
) )
type CreateDto struct { type CreateDto struct {
Encounter_Id *uint `json:"encounter_id"` Encounter_Id *uint `json:"encounter_id"`
Doctor_Id *uint `json:"doctor_id"` Parent_Encounter_Id *uint `json:"parent_encounter_id"`
AllocatedVisitCount *int `json:"allocatedVisitCount"` AllocatedVisitCount *int `json:"allocatedVisitCount"`
ExpiredAt *time.Time `json:"expiredAt"`
VisitMode_Code ere.VisitModeCode `json:"visitMode_code"`
Status_Code erc.DataStatusCode `json:"status_code"`
} }
type ReadListDto struct { type ReadListDto struct {
@@ -18,8 +24,9 @@ type ReadListDto struct {
} }
type FilterDto struct { type FilterDto struct {
Encounter_Id *uint `json:"encounter-id"` Encounter_Id *uint `json:"encounter-id"`
Doctor_Id *uint `json:"doctor-id"` Parent_Encounter_Id *uint `json:"parent-encounter-id"`
VisitMode_Code ere.VisitModeCode `json:"visitMode-code"`
} }
type ReadDetailDto struct { type ReadDetailDto struct {
@@ -44,18 +51,22 @@ type MetaDto struct {
type ResponseDto struct { type ResponseDto struct {
ecore.Main ecore.Main
Encounter_Id *uint `json:"encounter_id"` Encounter_Id *uint `json:"encounter_id"`
Doctor_Id *uint `json:"doctor_id"` Parent_Encounter_Id *uint `json:"parent_encounter_id"`
Doctor *ed.Doctor `json:"doctor,omitempty"` AllocatedVisitCount *int `json:"allocatedVisitCount"`
AllocatedVisitCount *int `json:"allocatedVisitCount"` ExpiredAt *time.Time `json:"expiredAt"`
VisitMode_Code *ere.VisitModeCode `json:"visitMode_code"`
Status_Code *erc.DataStatusCode `json:"status_code"`
} }
func (d Rehab) ToResponse() ResponseDto { func (d Rehab) ToResponse() ResponseDto {
resp := ResponseDto{ resp := ResponseDto{
Encounter_Id: d.Encounter_Id, Encounter_Id: d.Encounter_Id,
Doctor_Id: d.Doctor_Id, Parent_Encounter_Id: d.Parent_Encounter_Id,
Doctor: d.Doctor,
AllocatedVisitCount: d.AllocatedVisitCount, AllocatedVisitCount: d.AllocatedVisitCount,
ExpiredAt: d.ExpiredAt,
VisitMode_Code: d.VisitMode_Code,
Status_Code: d.Status_Code,
} }
resp.Main = d.Main resp.Main = d.Main
return resp return resp
+25 -25
View File
@@ -2,26 +2,26 @@
package clinical package clinical
type ( type (
SubjectCode string SubjectCode string
ObjectCode string ObjectCode string
AssessmentCode string AssessmentCode string
PlanCode string PlanCode string
InstructionCode string InstructionCode string
HeadToToeCode string HeadToToeCode string
McuUrgencyLevelCode string McuUrgencyLevelCode string
McuScopeCode string McuScopeCode string
SoapiTypeCode string SoapiTypeCode string
MedicalAction string MedicalActionTypeCode string
VehicleTypeCode string VehicleTypeCode string
GeneralEduCode string GeneralEduCode string
SpecialEduCode string SpecialEduCode string
EduAssessmentCode string EduAssessmentCode string
AbilityCode string AbilityCode string
WillCode string WillCode string
MedObstacleCode string MedObstacleCode string
LearnMethodCode string LearnMethodCode string
LangClassCode string LangClassCode string
TranslatorSrcCode string TranslatorSrcCode string
) )
const ( const (
@@ -124,11 +124,11 @@ const (
STCProgress SoapiTypeCode = "progress" // CPPT STCProgress SoapiTypeCode = "progress" // CPPT
STCDevRecord SoapiTypeCode = "dev-record" // Catatan Perkembangan STCDevRecord SoapiTypeCode = "dev-record" // Catatan Perkembangan
MAChemo MedicalAction = "chemo" MATCChemo MedicalActionTypeCode = "chemo"
MAHemo MedicalAction = "hemo" MATCHemo MedicalActionTypeCode = "hemo"
MAThalasemia MedicalAction = "thalasemia" MATCThalasemia MedicalActionTypeCode = "thalasemia"
MAEchocardio MedicalAction = "echocardio" MATCEchocardio MedicalActionTypeCode = "echocardio"
MASpirometry MedicalAction = "spirometry" MATCSpirometry MedicalActionTypeCode = "spirometry"
VTCAmbulance VehicleTypeCode = "ambulance" // Ambulans VTCAmbulance VehicleTypeCode = "ambulance" // Ambulans
VTCTransport VehicleTypeCode = "transport" // Transport VTCTransport VehicleTypeCode = "transport" // Transport
@@ -15,6 +15,7 @@ type (
DataAvailabilityCode string DataAvailabilityCode string
DataVerifiedCode string DataVerifiedCode string
CrudCode string CrudCode string
DataApprovalCode string
) )
const ( const (
@@ -96,6 +97,10 @@ const (
CCRead CrudCode = "r" // Read CCRead CrudCode = "r" // Read
CCUpdate CrudCode = "u" // Update CCUpdate CrudCode = "u" // Update
CCDelete CrudCode = "d" // Delete CCDelete CrudCode = "d" // Delete
DACNew DataApprovalCode = "new"
DACApproved DataApprovalCode = "approved"
DACRejected DataApprovalCode = "rejected"
) )
func GetDayCodes() map[DayCode]string { func GetDayCodes() map[DayCode]string {
@@ -18,6 +18,7 @@ type (
AllPaymentMethodCode string AllPaymentMethodCode string
SEPRefTypeCode string SEPRefTypeCode string
VisitModeCode string VisitModeCode string
PolySwitchCode string
) )
const ( const (
@@ -107,6 +108,9 @@ const (
VMCAdm VisitModeCode = "adm" VMCAdm VisitModeCode = "adm"
VMCSeries VisitModeCode = "series" VMCSeries VisitModeCode = "series"
PSCConsulPoly PolySwitchCode = "consul-poly" // Konsultasi Poliklinik Lain
PSCConsulExecutive PolySwitchCode = "consul-executive" // Konsultasi Antar Dokter Eksekutif
) )
func (ec EncounterClassCode) Code() string { func (ec EncounterClassCode) Code() string {
@@ -3,18 +3,17 @@ package encounter
import ( import (
"net/http" "net/http"
pa "simrs-vx/internal/lib/auth"
d "github.com/karincake/dodol"
rw "github.com/karincake/risoles" rw "github.com/karincake/risoles"
sf "github.com/karincake/semprit" sf "github.com/karincake/semprit"
// ua "github.com/karincake/tumpeng/auth/svc" // ua "github.com/karincake/tumpeng/auth/svc"
erc "simrs-vx/internal/domain/references/common"
e "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/encounter"
u "simrs-vx/internal/use-case/main-use-case/encounter" u "simrs-vx/internal/use-case/main-use-case/encounter"
erc "simrs-vx/internal/domain/references/common"
pa "simrs-vx/internal/lib/auth"
d "github.com/karincake/dodol"
) )
type myBase struct{} type myBase struct{}
@@ -31,7 +30,13 @@ func (obj myBase) Create(w http.ResponseWriter, r *http.Request) {
if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res {
return return
} }
if valid := validateRequestCreate(w, dto); !valid {
// validate SubClass
if err := verifyClassCode(dto); err != nil {
rw.DataResponse(w, nil, d.FieldError{
Code: dataValidationFail,
Message: err.Error(),
})
return return
} }
@@ -192,3 +197,40 @@ func (obj myBase) Skip(w http.ResponseWriter, r *http.Request) {
res, err := u.UpdateStatusCode(dto) res, err := u.UpdateStatusCode(dto)
rw.DataResponse(w, res, err) rw.DataResponse(w, res, err)
} }
func (obj myBase) RequestSwitchUnit(w http.ResponseWriter, r *http.Request) {
dto := e.SwitchUnitDto{}
id := rw.ValidateInt(w, "id", r.PathValue("id"))
if id <= 0 {
return
}
if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res {
return
}
// validate request body
if valid := validateRequestSwitchUnit(w, dto); !valid {
return
}
dto.Id = uint(id)
res, err := u.RequestSwitchUnit(dto)
rw.DataResponse(w, res, err)
}
func (obj myBase) ApproveSwitchUnit(w http.ResponseWriter, r *http.Request) {
dto := e.ApproveUnitDto{}
id := rw.ValidateInt(w, "id", r.PathValue("id"))
if id <= 0 {
return
}
if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res {
return
}
dto.Id = uint(id)
res, err := u.ApproveSwitchUnit(dto)
rw.DataResponse(w, res, err)
}
@@ -1,10 +1,14 @@
package encounter package encounter
import ( import (
"errors"
"fmt" "fmt"
"net/http" "net/http"
e "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/encounter"
ere "simrs-vx/internal/domain/references/encounter" ere "simrs-vx/internal/domain/references/encounter"
ua "simrs-vx/internal/use-case/main-use-case/ambulatory"
ue "simrs-vx/internal/use-case/main-use-case/emergency"
ui "simrs-vx/internal/use-case/main-use-case/inpatient"
d "github.com/karincake/dodol" d "github.com/karincake/dodol"
rw "github.com/karincake/risoles" rw "github.com/karincake/risoles"
@@ -12,42 +16,32 @@ import (
const dataValidationFail = "data-validation-fail" const dataValidationFail = "data-validation-fail"
func verifyClassCode(input e.CreateDto) (err error) {
switch input.Class_Code {
case ere.ECAmbulatory:
_, err = ua.CheckClassCode(input.SubClass_Code)
case ere.ECEmergency:
_, err = ue.CheckClassCode(input.SubClass_Code)
case ere.ECInpatient:
_, err = ui.CheckClassCode(input.SubClass_Code)
default:
return errors.New("invalid encounter class code")
}
if err != nil {
return
}
return nil
}
func validateRequestCheckout(w http.ResponseWriter, i e.DischargeDto) (valid bool) { func validateRequestCheckout(w http.ResponseWriter, i e.DischargeDto) (valid bool) {
switch *i.Discharge_Method_Code { if *i.Discharge_Method_Code == ere.DMCDeath && i.DeathCause == nil {
case ere.DMCDeath: rw.DataResponse(w, nil, d.FieldError{
if i.DeathCause == nil { Code: dataValidationFail,
rw.DataResponse(w, nil, d.FieldError{ Message: "deathCause required if discharge_method_code is death",
Code: dataValidationFail, })
Message: "deathCause required if discharge_method_code is death", return
})
return
}
case ere.DMCConsulPoly, ere.DMCConsulExecutive:
if i.InternalReferences == nil {
rw.DataResponse(w, nil, d.FieldError{
Code: dataValidationFail,
Message: fmt.Sprintf("internalReferences required if discharge_method_code is %s", *i.Discharge_Method_Code),
})
return
}
for _, v := range *i.InternalReferences {
if v.Unit_Id == nil {
rw.DataResponse(w, nil, d.FieldError{
Code: dataValidationFail,
Message: "internalReferences.unit_id required",
})
return
}
if v.Doctor_Id == nil {
rw.DataResponse(w, nil, d.FieldError{
Code: dataValidationFail,
Message: "internalReferences.doctor_id required",
})
return
}
}
} }
return true return true
} }
@@ -64,18 +58,30 @@ func validateRequestCheckIn(w http.ResponseWriter, i e.CheckinDto) (valid bool)
return true return true
} }
func validateRequestCreate(w http.ResponseWriter, i e.CreateDto) (valid bool) { func validateRequestSwitchUnit(w http.ResponseWriter, i e.SwitchUnitDto) (valid bool) {
switch { if i.InternalReferences == nil {
case i.Class_Code == ere.ECAmbulatory: rw.DataResponse(w, nil, d.FieldError{
// field allocatedVisitCount required if ambulatory visitMode_Code is adm Code: dataValidationFail,
if ere.AmbulatoryClassCode(*i.SubClass_Code) == ere.ACCRehab && *i.VisitMode_Code == ere.VMCAdm { Message: fmt.Sprintf("internalReferences required"),
if *i.AllocatedVisitCount == 0 { })
rw.DataResponse(w, nil, d.FieldError{ return
Code: dataValidationFail, }
Message: "allocatedVisitCount required",
}) for _, v := range *i.InternalReferences {
return if v.Unit_Id == nil {
} rw.DataResponse(w, nil, d.FieldError{
Code: dataValidationFail,
Message: "internalReferences.unit_id required",
})
return
}
if v.Doctor_Id == nil {
rw.DataResponse(w, nil, d.FieldError{
Code: dataValidationFail,
Message: "internalReferences.doctor_id required",
})
return
} }
} }
+13 -11
View File
@@ -145,17 +145,19 @@ func SetRoutes() http.Handler {
hc.RegCrud(r, "/v1/material-order-item", materialorderitem.O) hc.RegCrud(r, "/v1/material-order-item", materialorderitem.O)
hk.GroupRoutes("/v1/encounter", r, auth.GuardMW, hk.MapHandlerFunc{ hk.GroupRoutes("/v1/encounter", r, auth.GuardMW, hk.MapHandlerFunc{
"GET /": encounter.O.GetList, "GET /": encounter.O.GetList,
"GET /{id}": encounter.O.GetDetail, "GET /{id}": encounter.O.GetDetail,
"POST /": encounter.O.Create, "POST /": encounter.O.Create,
"PATCH /{id}": encounter.O.Update, "PATCH /{id}": encounter.O.Update,
"DELETE /{id}": encounter.O.Delete, "DELETE /{id}": encounter.O.Delete,
"PATCH /{id}/check-out": encounter.O.CheckOut, "PATCH /{id}/check-out": encounter.O.CheckOut,
"PATCH /{id}/check-in": encounter.O.CheckIn, "PATCH /{id}/check-in": encounter.O.CheckIn,
"PATCH /{id}/proccess": encounter.O.Process, "PATCH /{id}/proccess": encounter.O.Process,
"PATCH /{id}/cancel": encounter.O.Cancel, "PATCH /{id}/cancel": encounter.O.Cancel,
"PATCH /{id}/reject": encounter.O.Reject, "PATCH /{id}/reject": encounter.O.Reject,
"PATCH /{id}/skip": encounter.O.Skip, "PATCH /{id}/skip": encounter.O.Skip,
"PATCH /{id}/req-switch-unit": encounter.O.RequestSwitchUnit,
"PATCH /{id}/approve-switch-unit": encounter.O.ApproveSwitchUnit,
}) })
hk.GroupRoutes("/v1/mcu-order", r, auth.GuardMW, hk.MapHandlerFunc{ hk.GroupRoutes("/v1/mcu-order", r, auth.GuardMW, hk.MapHandlerFunc{
"GET /": mcuorder.O.GetList, "GET /": mcuorder.O.GetList,
@@ -22,7 +22,6 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Ambulatory) {
data.Encounter_Id = inputSrc.Encounter_Id data.Encounter_Id = inputSrc.Encounter_Id
data.Class_Code = inputSrc.Class_Code data.Class_Code = inputSrc.Class_Code
data.VisitMode_Code = inputSrc.VisitMode_Code
} }
func CheckClassCode(input *string) (ere.AmbulatoryClassCode, error) { func CheckClassCode(input *string) (ere.AmbulatoryClassCode, error) {
+241 -166
View File
@@ -2,7 +2,7 @@ package encounter
import ( import (
"errors" "errors"
authhelper "simrs-vx/internal/lib/auth" "fmt"
"strconv" "strconv"
"time" "time"
@@ -17,28 +17,18 @@ import (
ere "simrs-vx/internal/domain/references/encounter" ere "simrs-vx/internal/domain/references/encounter"
eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist"
ea "simrs-vx/internal/domain/main-entities/ambulatory"
ec "simrs-vx/internal/domain/main-entities/chemo"
edc "simrs-vx/internal/domain/main-entities/death-cause" edc "simrs-vx/internal/domain/main-entities/death-cause"
ed "simrs-vx/internal/domain/main-entities/doctor"
ee "simrs-vx/internal/domain/main-entities/emergency"
eem "simrs-vx/internal/domain/main-entities/employee" eem "simrs-vx/internal/domain/main-entities/employee"
e "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/encounter"
ei "simrs-vx/internal/domain/main-entities/inpatient" eir "simrs-vx/internal/domain/main-entities/internal-reference"
er "simrs-vx/internal/domain/main-entities/rehab"
erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist"
es "simrs-vx/internal/domain/main-entities/soapi" es "simrs-vx/internal/domain/main-entities/soapi"
uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist"
ua "simrs-vx/internal/use-case/main-use-case/ambulatory"
uc "simrs-vx/internal/use-case/main-use-case/chemo"
udc "simrs-vx/internal/use-case/main-use-case/death-cause" udc "simrs-vx/internal/use-case/main-use-case/death-cause"
ud "simrs-vx/internal/use-case/main-use-case/doctor"
ue "simrs-vx/internal/use-case/main-use-case/emergency"
uem "simrs-vx/internal/use-case/main-use-case/employee" uem "simrs-vx/internal/use-case/main-use-case/employee"
ui "simrs-vx/internal/use-case/main-use-case/inpatient" uir "simrs-vx/internal/use-case/main-use-case/internal-reference"
ur "simrs-vx/internal/use-case/main-use-case/rehab" urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist"
us "simrs-vx/internal/use-case/main-use-case/soapi"
) )
const source = "encounter" const source = "encounter"
@@ -46,8 +36,11 @@ const source = "encounter"
var now = time.Now() var now = time.Now()
func Create(input e.CreateDto) (*d.Data, error) { func Create(input e.CreateDto) (*d.Data, error) {
data := e.Encounter{} var (
createSoapi := []es.CreateDto{} data e.Encounter
recentSoapiDataforCopy []es.CreateDto
err error
)
event := pl.Event{ event := pl.Event{
Feature: "Create", Feature: "Create",
@@ -57,51 +50,36 @@ func Create(input e.CreateDto) (*d.Data, error) {
// Start log // Start log
pl.SetLogInfo(&event, input, "started", "create") pl.SetLogInfo(&event, input, "started", "create")
// validate SubClass // validate rehab bpjs
var subCode interface{} if input.RefTypeCode == ere.RTCBpjs && input.Class_Code == ere.ECAmbulatory && ere.AmbulatoryClassCode(*input.SubClass_Code) == ere.ACCRehab {
subCode, err := verifyClassCode(input) // get latest rehab data
if err != nil { recentRehabData, err := getLatestRehabData(input, &event)
return nil, err
}
// verify whether the allocated visit count has not exceeded the limit
if input.Class_Code == ere.ECAmbulatory && subCode.(ere.AmbulatoryClassCode) == ere.ACCRehab &&
*input.VisitMode_Code == ere.VMCSeries {
dataEncounter, valid, err := verifyAllocatedVisitCount(input, &event)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if !valid { if recentRehabData != nil {
event.Status = "failed" // determine VisitModeCode
event.ErrInfo = pl.ErrorInfo{ input.VisitMode_Code, input.RecentEncounterAdm, err = determineVisitMode(recentRehabData, input, &event)
Code: "visit-limit-exceeded", if err != nil {
Detail: "Encounter has exceeded the allowed number of visits", return nil, err
Raw: errors.New("visit count exceeds allowed limit"),
} }
return nil, pl.SetLogError(&event, input) } else {
input.VisitMode_Code = ere.VMCAdm
} }
// get data soapi // if visitMode_Code is series, then get data soapi for copy
createSoapi, err = getSoapiEncounterAdm(dataEncounter, &event) if input.VisitMode_Code == ere.VMCSeries {
if err != nil { // get data soapi
return nil, err recentSoapiDataforCopy, err = getSoapiEncounterAdm(*input.RecentEncounterAdm, &event)
if err != nil {
return nil, err
}
} }
} }
// check if patient is new in the hospital // check if patient is new in the hospital
dataPatient, err := ReadList(e.ReadListDto{ input.NewStatus, err = identifyPatientStatus(input)
FilterDto: e.FilterDto{Patient_Id: input.Patient_Id},
AuthInfo: authhelper.AuthInfo{User_Id: input.User_Id}})
if err != nil {
return nil, err
}
if list, ok := dataPatient.Data.([]e.ResponseDto); ok {
if len(list) < 1 {
input.NewStatus = true
}
}
err = dg.I.Transaction(func(tx *gorm.DB) error { err = dg.I.Transaction(func(tx *gorm.DB) error {
mwRunner := newMiddlewareRunner(&event, tx) mwRunner := newMiddlewareRunner(&event, tx)
@@ -122,74 +100,18 @@ func Create(input e.CreateDto) (*d.Data, error) {
return err return err
} else { } else {
data = *resData data = *resData
input.Id = data.Id
} }
switch input.Class_Code { // insert ambulatory/emergency/inpatient
case ere.ECAmbulatory: err = insertdataClassCode(input, recentSoapiDataforCopy, &event, tx)
subCodeAmbulatory := subCode.(ere.AmbulatoryClassCode) if err != nil {
ambCreate := ea.CreateDto{ return err
Encounter_Id: &data.Id,
Class_Code: subCodeAmbulatory,
VisitMode_Code: *input.VisitMode_Code,
}
_, err = ua.CreateData(ambCreate, &event, tx)
if err != nil {
return err
}
if subCodeAmbulatory == ere.ACCChemo {
chemoCreate := ec.CreateDto{
Encounter_Id: &data.Id,
Status_Code: erc.DVCNew,
SrcUnit_Id: input.Unit_Id,
}
_, err = uc.CreateData(chemoCreate, &event, tx)
if err != nil {
return err
}
}
if subCodeAmbulatory == ere.ACCRehab && *input.VisitMode_Code == ere.VMCAdm {
// create data rehab
if _, err = ur.CreateData(er.CreateDto{
Encounter_Id: &data.Id,
Doctor_Id: input.Appointment_Doctor_Id,
AllocatedVisitCount: input.AllocatedVisitCount}, &event, tx); err != nil {
return err
}
} else if subCodeAmbulatory == ere.ACCRehab && *input.VisitMode_Code == ere.VMCSeries {
// Insert Soapi
if err = us.CreateBulkData(createSoapi, data.Id, &event, tx); err != nil {
return err
}
}
case ere.ECEmergency:
emerCreate := ee.CreateDto{
Encounter_Id: &data.Id,
Class_Code: subCode.(ere.EmergencyClassCode),
}
_, err = ue.CreateData(emerCreate, &event, tx)
if err != nil {
return err
}
case ere.ECInpatient:
inpCreate := ei.CreateDto{
Encounter_Id: &data.Id,
Class_Code: subCode.(ere.InpatientClassCode),
Infra_Id: input.Infra_Id,
}
_, err = ui.CreateData(inpCreate, &event, tx)
if err != nil {
return err
}
default:
return errors.New("invalid encounter class code")
} }
// insert adm_employee_hist // insert adm_employee_hist
if _, err := uaeh.CreateData(eaeh.CreateDto{ if _, err := uaeh.CreateData(eaeh.CreateDto{
Encounter_Id: &data.Main.Id, Encounter_Id: &data.Id,
Employee_Id: data.Adm_Employee_Id, Employee_Id: data.Adm_Employee_Id,
StartedAt: &now}, &event, tx); err != nil { StartedAt: &now}, &event, tx); err != nil {
return err return err
@@ -441,7 +363,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) {
} }
func CheckOut(input e.DischargeDto) (*d.Data, error) { func CheckOut(input e.DischargeDto) (*d.Data, error) {
rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Ambulatory"} rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Ambulatory,Rehab"}
var data *e.Encounter var data *e.Encounter
var err error var err error
@@ -471,10 +393,16 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) {
if data.Ambulatory != nil && (data.Ambulatory.Class_Code == ere.ACCReg || data.Ambulatory.Class_Code == ere.ACCRehab) { if data.Ambulatory != nil && (data.Ambulatory.Class_Code == ere.ACCReg || data.Ambulatory.Class_Code == ere.ACCRehab) {
// validate if soapi exist // validate if soapi exist
err = getSoapiByTypeCode(input.Id, *data.Ambulatory, &event, "check-out") if err = getSoapiByTypeCode(data, &event, "check-out"); err != nil {
if err != nil {
return err return err
} }
if data.Ambulatory.Class_Code == ere.ACCRehab {
// verify and update rehabData if visit count has reached the allowed limit
if err = verifyRehabLimit(data, &event, tx); err != nil {
return err
}
}
} else { } else {
// chemo TBC // chemo TBC
if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil {
@@ -497,26 +425,20 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) {
} }
// update finishedAt in latest responsible_doctor_hist // update finishedAt in latest responsible_doctor_hist
if err = updateLatestResponsibleDoctorHist(e.CheckinDto{Id: input.Id, StartedAt: &now}, &event, tx); err != nil { if err = updateLatestResponsibleDoctorHist(e.CheckinDto{Id: input.Id, FinishedAt: &now}, &event, tx); err != nil {
return err return err
} }
// update finishedAt in latest adm_employee_hist // update finishedAt in latest adm_employee_hist
if err = updateLatestAdmEmployeeHist(e.CheckinDto{Id: input.Id, StartedAt: &now}, &event, tx); err != nil { if err = updateLatestAdmEmployeeHist(e.CheckinDto{Id: input.Id, FinishedAt: &now}, &event, tx); err != nil {
return err return err
} }
switch *input.Discharge_Method_Code { // insert data death-cause
case ere.DMCDeath: if *input.Discharge_Method_Code == ere.DMCDeath {
// insert data death-cause
if _, err = udc.CreateData(edc.CreateDto{Encounter_Id: &input.Id, Value: input.DeathCause}, &event, tx); err != nil { if _, err = udc.CreateData(edc.CreateDto{Encounter_Id: &input.Id, Value: input.DeathCause}, &event, tx); err != nil {
return err return err
} }
case ere.DMCConsulPoly, ere.DMCConsulExecutive:
// bulk insert internal-references
if err = createInternalReferences(input, &event, tx); err != nil {
return err
}
} }
pl.SetLogInfo(&event, nil, "complete") pl.SetLogInfo(&event, nil, "complete")
@@ -645,13 +567,13 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) {
// validate if soapi exist // validate if soapi exist
if data.Ambulatory != nil && (data.Ambulatory.Class_Code == ere.ACCReg || data.Ambulatory.Class_Code == ere.ACCRehab) { if data.Ambulatory != nil && (data.Ambulatory.Class_Code == ere.ACCReg || data.Ambulatory.Class_Code == ere.ACCRehab) {
err = getSoapiByTypeCode(input.Id, *data.Ambulatory, &event, "check-in") err = getSoapiByTypeCode(data, &event, "check-in")
if err != nil { if err != nil {
return err return err
} }
} }
// Insert responsible_doctor_hist if responsible_doctor_id has changed && update latest history // Upsert responsible_doctor_hist if responsible_doctor_id has changed
if data.Responsible_Doctor_Id == nil || *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { if data.Responsible_Doctor_Id == nil || *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id {
// upsert responsibleDoctorHist // upsert responsibleDoctorHist
if err = upsertResponsibleDoctorHist(erdh.CreateDto{ if err = upsertResponsibleDoctorHist(erdh.CreateDto{
@@ -663,7 +585,7 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) {
} }
} }
// Insert adm_employee_hist if adm_employee_id has changed && update latest history // Upsert adm_employee_hist if adm_employee_id has changed
if input.Adm_Employee_Id != nil && *input.Adm_Employee_Id != *data.Adm_Employee_Id { if input.Adm_Employee_Id != nil && *input.Adm_Employee_Id != *data.Adm_Employee_Id {
// upsert admEmployeeHist // upsert admEmployeeHist
if err = upsertAdmEmployeeHist(eaeh.CreateDto{ if err = upsertAdmEmployeeHist(eaeh.CreateDto{
@@ -680,15 +602,6 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) {
return err return err
} }
if data.Ambulatory.Class_Code == ere.ACCRehab {
if err := updateRehabDoctor(er.UpdateDto{CreateDto: er.CreateDto{
Encounter_Id: &data.Id,
Doctor_Id: input.Responsible_Doctor_Id,
}}, &event, tx); err != nil {
return err
}
}
pl.SetLogInfo(&event, nil, "complete") pl.SetLogInfo(&event, nil, "complete")
return nil return nil
@@ -708,43 +621,205 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) {
}, nil }, nil
} }
func validateForeignKey(input e.CheckinDto) error { func RequestSwitchUnit(input e.SwitchUnitDto) (*d.Data, error) {
// validate employee_Id rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Responsible_Doctor"}
if input.Adm_Employee_Id != nil { var data *e.Encounter
if _, err := uem.ReadDetail(eem.ReadDetailDto{Id: uint16(*input.Adm_Employee_Id)}); err != nil { var err error
event := pl.Event{
Feature: "RequestSwitchUnit",
Source: source,
}
// Start log
pl.SetLogInfo(&event, input, "started", "checkOut")
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 unit
if err = validateUnitIds(unitIDs, &event); err != nil {
return nil, err
}
// validate doctor
if err = validateDoctorIds(doctorIDs, &event); err != nil {
return nil, err
}
err = dg.I.Transaction(func(tx *gorm.DB) error {
pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail")
if data, err = ReadDetailData(rdDto, &event, tx); err != nil {
return err return err
} }
}
// validate doctor_id //if data.IsDone() {
if input.Responsible_Doctor_Id != nil { // event.Status = "failed"
if _, err := ud.ReadDetail(ed.ReadDetailDto{Id: uint16(*input.Responsible_Doctor_Id)}); err != nil { // event.ErrInfo = pl.ErrorInfo{
// Code: "data-state-mismatch",
// Detail: "encounter is done",
// Raw: errors.New("encounter is done"),
// }
// return pl.SetLogError(&event, input)
//}
// verify Soapi exist for current responsible doctor
dataSoapi, err := getSoapiByResponsibleDoctor(*data, &event)
if err != nil {
return err return err
} }
if len(dataSoapi) < 1 {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "missing-soapi",
Detail: fmt.Sprintf("Missing soapi from latest responsible doctor"),
}
return pl.SetLogError(&event, input)
}
// bulk internal references
if err := uir.CreateBulkData(*input.InternalReferences, input.Id, &event, tx); err != nil {
return err
}
pl.SetLogInfo(&event, nil, "complete")
return nil
})
if err != nil {
return nil, err
} }
return nil return &d.Data{
Meta: d.IS{
"source": source,
"structure": "single-data",
"status": "requestSwitchUnit",
},
Data: data.ToResponse(),
}, nil
} }
func verifyClassCode(input e.CreateDto) (subCode interface{}, err error) { func ApproveSwitchUnit(input e.ApproveUnitDto) (*d.Data, error) {
switch input.Class_Code { rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Responsible_Doctor"}
case ere.ECAmbulatory: var data *e.Encounter
subCode, err = ua.CheckClassCode(input.SubClass_Code) var err error
if err != nil {
return nil, err event := pl.Event{
} Feature: "ApproveSwitchUnit",
case ere.ECEmergency: Source: source,
subCode, err = ue.CheckClassCode(input.SubClass_Code)
if err != nil {
return nil, err
}
case ere.ECInpatient:
subCode, err = ui.CheckClassCode(input.SubClass_Code)
if err != nil {
return nil, err
}
default:
return nil, errors.New("invalid encounter class code")
} }
return
// Start log
pl.SetLogInfo(&event, input, "started", "approveSwitchUnit")
err = dg.I.Transaction(func(tx *gorm.DB) error {
pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail")
if data, err = ReadDetailData(rdDto, &event, tx); err != nil {
return err
}
// get internal reference
irData, err := uir.ReadDetailData(eir.ReadDetailDto{
Id: input.InternalReferences_Id, Includes: "Doctor"}, &event, tx)
if err != nil {
return err
}
if *irData.Status_Code != erc.DACNew {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "data-state-mismatch",
Detail: "internal references is approve",
Raw: errors.New("internal references is approve"),
}
return pl.SetLogError(&event, input)
}
//if data.IsDone() {
// event.Status = "failed"
// event.ErrInfo = pl.ErrorInfo{
// Code: "data-state-mismatch",
// Detail: "encounter is done",
// Raw: errors.New("encounter is done"),
// }
// return pl.SetLogError(&event, input)
//}
// verify Soapi exist for current responsible doctor
dataSoapi, err := getSoapiByResponsibleDoctor(*data, &event)
if err != nil {
return err
}
if len(dataSoapi) < 1 {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "missing-soapi",
Detail: fmt.Sprintf("Missing soapi from latest responsible doctor"),
}
return pl.SetLogError(&event, input)
}
// update internal reference
if err = uir.UpdateData(eir.UpdateDto{
Id: input.InternalReferences_Id,
CreateDto: eir.CreateDto{
Encounter_Id: &input.Id,
Doctor_Id: irData.Doctor_Id,
Unit_Id: irData.Unit_Id,
Status_Code: erc.DACApproved,
}}, irData, &event, tx); err != nil {
return err
}
// update encounter
if err = updateEncounterApproveSwitchUnit(*irData, &event, tx); err != nil {
return err
}
// update latest responsible doctor hist
if err = updateLatestResponsibleDoctorHist(e.CheckinDto{Id: input.Id, FinishedAt: &now}, &event, tx); err != nil {
return err
}
// create responsible doctor based on internal reference data
if _, err = urdh.CreateData(erdh.CreateDto{
Encounter_Id: &input.Id,
Doctor_Id: irData.Doctor_Id,
StartedAt: &now,
}, &event, tx); err != nil {
return err
}
// update data response
data.Responsible_Doctor_Id = irData.Doctor_Id
data.Unit = irData.Unit
data.Specialist_Id = irData.Doctor.Specialist_Id
data.Subspecialist_Id = irData.Doctor.Subspecialist_Id
pl.SetLogInfo(&event, nil, "complete")
return nil
})
if err != nil {
return nil, err
}
return &d.Data{
Meta: d.IS{
"source": source,
"structure": "single-data",
"status": "updated",
},
Data: data.ToResponse(),
}, nil
} }
@@ -10,6 +10,8 @@ import (
"strings" "strings"
"time" "time"
authhelper "simrs-vx/internal/lib/auth"
dg "github.com/karincake/apem/db-gorm-pg" dg "github.com/karincake/apem/db-gorm-pg"
"gorm.io/gorm" "gorm.io/gorm"
@@ -23,9 +25,13 @@ import (
eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist"
ea "simrs-vx/internal/domain/main-entities/ambulatory" ea "simrs-vx/internal/domain/main-entities/ambulatory"
ec "simrs-vx/internal/domain/main-entities/chemo"
edo "simrs-vx/internal/domain/main-entities/device-order" edo "simrs-vx/internal/domain/main-entities/device-order"
ed "simrs-vx/internal/domain/main-entities/doctor" ed "simrs-vx/internal/domain/main-entities/doctor"
ee "simrs-vx/internal/domain/main-entities/emergency"
eem "simrs-vx/internal/domain/main-entities/employee"
e "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/encounter"
ei "simrs-vx/internal/domain/main-entities/inpatient"
emo "simrs-vx/internal/domain/main-entities/material-order" emo "simrs-vx/internal/domain/main-entities/material-order"
emco "simrs-vx/internal/domain/main-entities/mcu-order" emco "simrs-vx/internal/domain/main-entities/mcu-order"
em "simrs-vx/internal/domain/main-entities/medication" em "simrs-vx/internal/domain/main-entities/medication"
@@ -36,19 +42,26 @@ import (
epi "simrs-vx/internal/domain/main-entities/prescription-item" epi "simrs-vx/internal/domain/main-entities/prescription-item"
er "simrs-vx/internal/domain/main-entities/rehab" er "simrs-vx/internal/domain/main-entities/rehab"
erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist"
es "simrs-vx/internal/domain/main-entities/soapi"
eu "simrs-vx/internal/domain/main-entities/unit" eu "simrs-vx/internal/domain/main-entities/unit"
// udo "simrs-vx/internal/use-case/main-use-case/device-order" // udo "simrs-vx/internal/use-case/main-use-case/device-order"
es "simrs-vx/internal/domain/main-entities/soapi"
uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist"
uir "simrs-vx/internal/use-case/main-use-case/internal-reference" ua "simrs-vx/internal/use-case/main-use-case/ambulatory"
uc "simrs-vx/internal/use-case/main-use-case/chemo"
ud "simrs-vx/internal/use-case/main-use-case/doctor"
ue "simrs-vx/internal/use-case/main-use-case/emergency"
uem "simrs-vx/internal/use-case/main-use-case/employee"
ui "simrs-vx/internal/use-case/main-use-case/inpatient"
um "simrs-vx/internal/use-case/main-use-case/medication" um "simrs-vx/internal/use-case/main-use-case/medication"
umei "simrs-vx/internal/use-case/main-use-case/medication-item" umei "simrs-vx/internal/use-case/main-use-case/medication-item"
umi "simrs-vx/internal/use-case/main-use-case/medicine-mix" umi "simrs-vx/internal/use-case/main-use-case/medicine-mix"
ummi "simrs-vx/internal/use-case/main-use-case/medicine-mix-item" ummi "simrs-vx/internal/use-case/main-use-case/medicine-mix-item"
up "simrs-vx/internal/use-case/main-use-case/prescription" up "simrs-vx/internal/use-case/main-use-case/prescription"
upi "simrs-vx/internal/use-case/main-use-case/prescription-item" upi "simrs-vx/internal/use-case/main-use-case/prescription-item"
ur "simrs-vx/internal/use-case/main-use-case/rehab"
urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist"
us "simrs-vx/internal/use-case/main-use-case/soapi"
) )
func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) { func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) {
@@ -104,6 +117,19 @@ func setDataDischarge(src e.DischargeDto, dst *e.Encounter) {
dst.FinishedAt = &now dst.FinishedAt = &now
} }
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 checkSoapiByDocExists(encounter_id uint, event *pl.Event, tx *gorm.DB) error { func checkSoapiByDocExists(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "checkSoapiByDocExists") pl.SetLogInfo(event, nil, "started", "checkSoapiByDocExists")
var soapies []es.Soapi var soapies []es.Soapi
@@ -335,113 +361,6 @@ func getMcuOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error {
return nil 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
}
func upsertResponsibleDoctorHist(input erdh.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { func upsertResponsibleDoctorHist(input erdh.CreateDto, event *pl.Event, dbx ...*gorm.DB) error {
pl.SetLogInfo(event, nil, "started", "DBCreate") pl.SetLogInfo(event, nil, "started", "DBCreate")
@@ -566,7 +485,7 @@ func updateLatestResponsibleDoctorHist(input e.CheckinDto, event *pl.Event, dbx
result := tx. result := tx.
Model(&erdh.ResponsibleDoctorHist{}). Model(&erdh.ResponsibleDoctorHist{}).
Where("\"Id\" = (?)", subQuery). Where("\"Id\" = (?)", subQuery).
Update("\"FinishedAt\"", input.StartedAt) Update("\"FinishedAt\"", input.FinishedAt)
if result.Error != nil { if result.Error != nil {
event.Status = "failed" event.Status = "failed"
@@ -607,7 +526,7 @@ func updateLatestAdmEmployeeHist(input e.CheckinDto, event *pl.Event, dbx ...*go
result := tx. result := tx.
Model(&eaeh.AdmEmployeeHist{}). Model(&eaeh.AdmEmployeeHist{}).
Where("\"Id\" = (?)", subQuery). Where("\"Id\" = (?)", subQuery).
Update("\"FinishedAt\"", input.StartedAt) Update("\"FinishedAt\"", input.FinishedAt)
if result.Error != nil { if result.Error != nil {
event.Status = "failed" event.Status = "failed"
@@ -628,9 +547,7 @@ func updateLatestAdmEmployeeHist(input e.CheckinDto, event *pl.Event, dbx ...*go
return nil return nil
} }
func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.CreateDto, err error) { func getSoapiByResponsibleDoctor(enc e.Encounter, event *pl.Event) (data []es.Soapi, err error) {
var data []es.Soapi
pl.SetLogInfo(event, enc, "started", "DBReadList") pl.SetLogInfo(event, enc, "started", "DBReadList")
if enc.Responsible_Doctor == nil { if enc.Responsible_Doctor == nil {
@@ -642,6 +559,7 @@ func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.Crea
} }
err = dg.I. err = dg.I.
Debug().
Model(&es.Soapi{}). Model(&es.Soapi{}).
Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\""). Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\"").
Where("\"Encounter_Id\" = ?", enc.Id). Where("\"Encounter_Id\" = ?", enc.Id).
@@ -653,21 +571,22 @@ func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.Crea
if err != nil { if err != nil {
event.Status = "failed" event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{ event.ErrInfo = pl.ErrorInfo{
Raw: err, Raw: err,
Code: "read-fail",
Detail: "Database read failed",
} }
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) return nil, pl.SetLogError(event, enc)
} }
pl.SetLogInfo(event, nil, "complete") pl.SetLogInfo(event, nil, "complete")
return
}
func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.CreateDto, err error) {
data, err := getSoapiByResponsibleDoctor(enc, event)
if err != nil {
return nil, err
}
for _, s := range data { for _, s := range data {
// set data soapi for copy // set data soapi for copy
@@ -690,39 +609,46 @@ func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.Crea
return return
} }
func getSoapiByTypeCode(encounterId uint, dataAmbulatory ea.Ambulatory, event *pl.Event, mode string) (err error) { func getSoapiByTypeCode(encounter *e.Encounter, event *pl.Event, mode string) (err error) {
pl.SetLogInfo(event, encounterId, "started", "DBReadList") pl.SetLogInfo(event, encounter, "started", "DBReadList")
var ( var (
dataSoapi []es.Soapi dataSoapi []es.Soapi
amb = encounter.Ambulatory
rehab = encounter.Rehab
) )
// Set Query for get data Soapi // Set Query for get data Soapi
tx := dg.I. tx := dg.I.
Model(&es.Soapi{}). Model(&es.Soapi{}).
Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\""). Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\"").
Where("\"Encounter_Id\" = ?", encounterId). Where("\"Encounter_Id\" = ?", encounter.Id).
Where("\"Employee\".\"Position_Code\" = ?", erg.EPCDoc) Where("\"Employee\".\"Position_Code\" = ?", erg.EPCDoc)
// Set Case // Set Case
switch { switch {
case dataAmbulatory.Class_Code == ere.ACCReg: case amb.Class_Code == ere.ACCReg:
tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEEarlyMedic) tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEEarlyMedic)
case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCAdm: case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCAdm:
tx = tx.Where("\"Soapi\".\"TypeCode\" IN ?", []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab}) tx = tx.Where("\"Soapi\".\"TypeCode\" IN ?", []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab})
case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCSeries: case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCSeries:
tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEarlyRehab) tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEarlyRehab)
} }
if err = tx.Find(&dataSoapi).Error; err != nil { if err = tx.Find(&dataSoapi).Error; err != nil {
return setDBError(event, err, encounterId) return setDBError(event, err, encounter)
} }
pl.SetLogInfo(event, nil, "complete") pl.SetLogInfo(event, nil, "complete")
return validateExistedSoapi(dataSoapi, &dataAmbulatory, event, mode) return validateExistedSoapi(dataSoapi, encounter, event, mode)
} }
func validateExistedSoapi(dataSoapi []es.Soapi, dataAmbulatory *ea.Ambulatory, event *pl.Event, mode string) error { func validateExistedSoapi(dataSoapi []es.Soapi, dataEncounter *e.Encounter, event *pl.Event, mode string) error {
var (
amb = dataEncounter.Ambulatory
rehab = dataEncounter.Rehab
)
typeExist := make(map[ercl.SoapiTypeCode]bool) typeExist := make(map[ercl.SoapiTypeCode]bool)
for _, s := range dataSoapi { for _, s := range dataSoapi {
typeExist[s.TypeCode] = true typeExist[s.TypeCode] = true
@@ -731,11 +657,11 @@ func validateExistedSoapi(dataSoapi []es.Soapi, dataAmbulatory *ea.Ambulatory, e
required := []ercl.SoapiTypeCode{} required := []ercl.SoapiTypeCode{}
switch { switch {
case dataAmbulatory.Class_Code == ere.ACCReg: case amb.Class_Code == ere.ACCReg:
required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic} required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic}
case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCAdm: case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCAdm:
required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab} required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab}
case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCSeries: case amb.Class_Code == ere.ACCRehab && *rehab.VisitMode_Code == ere.VMCSeries:
required = []ercl.SoapiTypeCode{ercl.STCEarlyRehab} required = []ercl.SoapiTypeCode{ercl.STCEarlyRehab}
} }
@@ -762,6 +688,231 @@ func validateExistedSoapi(dataSoapi []es.Soapi, dataAmbulatory *ea.Ambulatory, e
return nil return nil
} }
func identifyPatientStatus(input e.CreateDto) (isNewPatient bool, err error) {
dataPatient, err := ReadList(e.ReadListDto{
FilterDto: e.FilterDto{Patient_Id: input.Patient_Id},
AuthInfo: authhelper.AuthInfo{User_Id: input.User_Id}})
if err != nil {
return
}
if list, ok := dataPatient.Data.([]e.ResponseDto); ok {
if len(list) < 1 {
isNewPatient = true
}
}
return
}
func determineVisitMode(recentRehabData *er.Rehab, input e.CreateDto, event *pl.Event) (ere.VisitModeCode, *e.Encounter, error) {
var (
visitModeCode ere.VisitModeCode
recentAdmEncounterData e.Encounter
isQuotaValid bool
err error
)
switch *recentRehabData.Status_Code {
case erc.DSCProcess:
visitModeCode = ere.VMCSeries
// verify whether the allocated visit count has not exceeded the limit
recentAdmEncounterData, isQuotaValid, err = verifyAllocatedVisitCount(input, event)
if err != nil {
return "", nil, err
}
if !isQuotaValid || recentRehabData.ExpiredAt.Before(*pu.GetTimeNow()) {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "visit-limit-exceeded",
Detail: "Encounter has exceeded the allowed number of visits or expired",
Raw: errors.New("visit count exceeds allowed limit"),
}
return "", nil, pl.SetLogError(event, input)
}
case erc.DSCDone:
visitModeCode = ere.VMCAdm
default:
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "invalid-status",
Detail: fmt.Sprintf("Unknown rehab status: %v", *recentRehabData.Status_Code),
Raw: errors.New("unsupported rehab status"),
}
return "", nil, pl.SetLogError(event, input)
}
return visitModeCode, &recentAdmEncounterData, nil
}
func insertdataClassCode(input e.CreateDto, soapiData []es.CreateDto, event *pl.Event, tx *gorm.DB) (err error) {
switch input.Class_Code {
case ere.ECAmbulatory:
subCode := ere.AmbulatoryClassCode(*input.SubClass_Code)
ambCreate := ea.CreateDto{
Encounter_Id: &input.Id,
Class_Code: subCode,
}
// create data Ambulatory
_, err = ua.CreateData(ambCreate, event, tx)
if err != nil {
return err
}
// insert chemo/rehab
err = insertDataSubClassAmbulatory(input, soapiData, event, tx)
if err != nil {
return err
}
case ere.ECEmergency:
subCode := ere.EmergencyClassCode(*input.SubClass_Code)
emerCreate := ee.CreateDto{
Encounter_Id: &input.Id,
Class_Code: subCode,
}
// create data emergency
_, err = ue.CreateData(emerCreate, event, tx)
if err != nil {
return err
}
case ere.ECInpatient:
subCode := ere.InpatientClassCode(*input.SubClass_Code)
inpCreate := ei.CreateDto{
Encounter_Id: &input.Id,
Class_Code: subCode,
Infra_Id: input.Infra_Id,
}
// create data inpatient
_, err = ui.CreateData(inpCreate, event, tx)
if err != nil {
return err
}
default:
return errors.New("invalid encounter class code")
}
return
}
func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, event *pl.Event, tx *gorm.DB) (err error) {
subCode := ere.AmbulatoryClassCode(*input.SubClass_Code)
switch {
case subCode == ere.ACCChemo:
chemoCreate := ec.CreateDto{
Encounter_Id: &input.Id,
Status_Code: erc.DVCNew,
SrcUnit_Id: input.Unit_Id,
}
// create data chemo
_, err = uc.CreateData(chemoCreate, event, tx)
if err != nil {
return err
}
case subCode == ere.ACCRehab:
rehabData := er.CreateDto{
Encounter_Id: &input.Id,
VisitMode_Code: input.VisitMode_Code,
Status_Code: erc.DSCProcess,
}
// if visitMode_code is series, then bulk insert soapi
if input.VisitMode_Code == ere.VMCSeries {
rehabData.Parent_Encounter_Id = &input.RecentEncounterAdm.Id
// Insert Soapi
if err = us.CreateBulkData(soapiData, input.Id, event, tx); err != nil {
return err
}
}
// create rehab
if _, err = ur.CreateData(rehabData, event, tx); err != nil {
return err
}
}
return
}
func verifyRehabLimit(data *e.Encounter, event *pl.Event, tx *gorm.DB) error {
// get data encounter adm
encounterAdmData, _, err := verifyAllocatedVisitCount(e.CreateDto{Patient_Id: data.Patient_Id}, event)
if err != nil {
return err
}
// Check if the visit count has reached the allowed limit
// Mark latest rehab status as 'done' if exceeded.
if len(*encounterAdmData.RehabChildren) >= *encounterAdmData.Rehab.AllocatedVisitCount {
err = updateRehabStatus(er.UpdateDto{
Id: uint16(data.Rehab.Id),
CreateDto: er.CreateDto{Status_Code: erc.DSCDone}}, event, tx)
if err != nil {
return err
}
}
return nil
}
func updateRehabStatus(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("\"Id\" = (?)", input.Id).
Update("\"Status_Code\"", input.Status_Code)
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
}
func validateForeignKey(input e.CheckinDto) error {
// validate employee_Id
if input.Adm_Employee_Id != nil {
if _, err := uem.ReadDetail(eem.ReadDetailDto{Id: uint16(*input.Adm_Employee_Id)}); err != nil {
return err
}
}
// validate doctor_id
if input.Responsible_Doctor_Id != nil {
if _, err := ud.ReadDetail(ed.ReadDetailDto{Id: uint16(*input.Responsible_Doctor_Id)}); err != nil {
return err
}
}
return nil
}
func setSoapiError(event *pl.Event, detail string) error { func setSoapiError(event *pl.Event, detail string) error {
event.Status = "failed" event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{ event.ErrInfo = pl.ErrorInfo{
@@ -781,31 +932,82 @@ func setDBError(event *pl.Event, err error, ctx any) error {
return pl.SetLogError(event, ctx) return pl.SetLogError(event, ctx)
} }
func updateRehabDoctor(input er.UpdateDto, event *pl.Event, dbx ...*gorm.DB) error { func getUnits(unitIds []uint16, event *pl.Event) ([]eu.Unit, error) {
pl.SetLogInfo(event, "started", "DBUpdate") pl.SetLogInfo(event, nil, "started", "getUnits")
var units []eu.Unit
var tx *gorm.DB err := dg.I.Where("\"Id\" IN ?", unitIds).Find(&units).Error
if len(dbx) > 0 { if err != nil {
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.Status = "failed"
event.ErrInfo = pl.ErrorInfo{ event.ErrInfo = pl.ErrorInfo{
Code: "data-update-fail", Code: "data-get-fail",
Detail: "Database update failed", Detail: "get units",
Raw: result.Error, Raw: err,
}
return nil, pl.SetLogError(event, nil)
}
return units, nil
}
func getDoctors(doctorIds []uint, event *pl.Event) ([]ed.Doctor, error) {
pl.SetLogInfo(event, nil, "started", "getDoctors")
var doctors []ed.Doctor
err := dg.I.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
}
func validateUnitIds(unitIDs map[uint16]struct{}, event *pl.Event) error {
if len(unitIDs) > 0 {
var ids []uint16
for id := range unitIDs {
ids = append(ids, id)
}
units, err := getUnits(ids, event)
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)
}
}
return nil
}
func validateDoctorIds(doctorIDs map[uint]struct{}, event *pl.Event) error {
if len(doctorIDs) > 0 {
var ids []uint
for id := range doctorIDs {
ids = append(ids, id)
}
doctors, err := getDoctors(ids, event)
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)
} }
return pl.SetLogError(event, input)
} }
pl.SetLogInfo(event, nil, "complete")
return nil return nil
} }
@@ -3,8 +3,7 @@ package encounter
import ( import (
// std // std
"errors" "errors"
ere "simrs-vx/internal/domain/references/encounter" eir "simrs-vx/internal/domain/main-entities/internal-reference"
// external // external
dg "github.com/karincake/apem/db-gorm-pg" dg "github.com/karincake/apem/db-gorm-pg"
gh "github.com/karincake/getuk" gh "github.com/karincake/getuk"
@@ -15,9 +14,14 @@ import (
pl "simrs-vx/pkg/logger" pl "simrs-vx/pkg/logger"
pu "simrs-vx/pkg/use-case-helper" pu "simrs-vx/pkg/use-case-helper"
ere "simrs-vx/internal/domain/references/encounter"
e "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/encounter"
er "simrs-vx/internal/domain/main-entities/rehab"
) )
const ErrorReadFailed = "Database read failed"
func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.Encounter, error) { func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.Encounter, error) {
pl.SetLogInfo(event, nil, "started", "DBCreate") pl.SetLogInfo(event, nil, "started", "DBCreate")
@@ -251,20 +255,49 @@ func updateCheckInData(input e.CheckinDto, data *e.Encounter, event *pl.Event, d
return nil return nil
} }
func getLatestRehabData(i e.CreateDto, event *pl.Event) (recentRehabData *er.Rehab, err error) {
pl.SetLogInfo(event, nil, "started", "DBGetLatestRehab")
var (
tx = dg.I
)
err = tx.
Joins("JOIN \"Encounter\" ON \"Encounter\".\"Id\" = \"Rehab\".\"Encounter_Id\"").
Where("\"Encounter\".\"Patient_Id\" = ?", i.Patient_Id).
Order("\"CreatedAt\" DESC").
First(&recentRehabData).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil
}
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "read-recentRehab-fail",
Detail: ErrorReadFailed,
Raw: err,
}
return nil, pl.SetLogError(event, i)
}
return
}
func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, bool, error) { func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, bool, error) {
pl.SetLogInfo(event, nil, "started", "DBGetRecentEncounterAdm") pl.SetLogInfo(event, nil, "started", "DBGetRecentEncounterAdm")
var ( var (
tx = dg.I tx = dg.I
recentEncounterAdm e.Encounter recentEncounterAdm e.Encounter
countEncounterSeries int64 valid = true
) )
err := tx. err := tx.
Scopes(gh.Preload("Rehab,Responsible_Doctor")). Scopes(gh.Preload("RehabChildren,Rehab,Responsible_Doctor")).
Joins("JOIN \"Ambulatory\" ON \"Ambulatory\".\"Encounter_Id\" = \"Encounter\".\"Id\""). Joins("JOIN \"Rehab\" ON \"Rehab\".\"Encounter_Id\" = \"Encounter\".\"Id\"").
Where("\"Patient_Id\" = ?", i.Patient_Id). Where("\"Encounter\".\"Patient_Id\" = ?", i.Patient_Id).
Where("\"Ambulatory\".\"Class_Code\" = ? AND \"Ambulatory\".\"VisitMode_Code\" = ?", ere.ACCRehab, ere.VMCAdm). Where("\"Rehab\".\"VisitMode_Code\" = ?", ere.VMCAdm).
Order("\"CreatedAt\" DESC"). Order("\"CreatedAt\" DESC").
First(&recentEncounterAdm).Error First(&recentEncounterAdm).Error
if err != nil { if err != nil {
@@ -277,22 +310,41 @@ func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, boo
return e.Encounter{}, false, pl.SetLogError(event, i) return e.Encounter{}, false, pl.SetLogError(event, i)
} }
err = tx. // validate count rehab children
Model(&e.Encounter{}). if recentEncounterAdm.RehabChildren != nil {
Joins("JOIN \"Ambulatory\" ON \"Ambulatory\".\"Encounter_Id\" = \"Encounter\".\"Id\""). valid = len(*recentEncounterAdm.RehabChildren) < *recentEncounterAdm.Rehab.AllocatedVisitCount
Where("\"Patient_Id\" = ?", i.Patient_Id).
Where("\"Ambulatory\".\"Class_Code\" = ? AND \"Ambulatory\".\"VisitMode_Code\" = ?", ere.ACCRehab, 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 return recentEncounterAdm, valid, nil
}
func updateEncounterApproveSwitchUnit(input eir.InternalReference, event *pl.Event, dbx ...*gorm.DB) (err error) {
pl.SetLogInfo(event, nil, "started", "DBCreate")
var tx *gorm.DB
if len(dbx) > 0 {
tx = dbx[0]
} else {
tx = dg.I
}
if err := tx.Model(&e.Encounter{}).
Where("\"Id\" = ?", input.Encounter_Id).
Updates(map[string]interface{}{
"Responsible_Doctor_Id": input.Doctor_Id,
"Unit_Id": input.Unit_Id,
"Specialist_Id": input.Doctor.Specialist_Id,
"Subspecialist_Id": input.Doctor.Subspecialist_Id,
}).Error; err != nil {
event.Status = "failed"
event.ErrInfo = pl.ErrorInfo{
Code: "update-fail",
Detail: "Failed to update encounter approve switch unit",
Raw: err,
}
return pl.SetLogError(event, input)
}
pl.SetLogInfo(event, input, "complete")
return nil
} }
@@ -6,6 +6,7 @@ package internal_reference
import ( import (
ir "simrs-vx/internal/domain/main-entities/internal-reference" ir "simrs-vx/internal/domain/main-entities/internal-reference"
erc "simrs-vx/internal/domain/references/common"
) )
func setData[T *ir.CreateDto | *ir.UpdateDto](input T, data *ir.InternalReference) { func setData[T *ir.CreateDto | *ir.UpdateDto](input T, data *ir.InternalReference) {
@@ -20,16 +21,19 @@ func setData[T *ir.CreateDto | *ir.UpdateDto](input T, data *ir.InternalReferenc
data.Encounter_Id = inputSrc.Encounter_Id data.Encounter_Id = inputSrc.Encounter_Id
data.Unit_Id = inputSrc.Unit_Id data.Unit_Id = inputSrc.Unit_Id
data.Doctor_Id = inputSrc.Doctor_Id data.Doctor_Id = inputSrc.Doctor_Id
data.Status_Code = &inputSrc.Status_Code
} }
func setBulkData(input []ir.CreateDto, encounterId uint) []ir.InternalReference { func setBulkData(input []ir.CreateDto, encounterId uint) []ir.InternalReference {
var data []ir.InternalReference var data []ir.InternalReference
for _, v := range input { for _, v := range input {
statusCode := erc.DACNew
data = append(data, ir.InternalReference{ data = append(data, ir.InternalReference{
Encounter_Id: &encounterId, Encounter_Id: &encounterId,
Unit_Id: v.Unit_Id, Unit_Id: v.Unit_Id,
Doctor_Id: v.Doctor_Id, Doctor_Id: v.Doctor_Id,
Status_Code: &statusCode,
}) })
} }
@@ -18,6 +18,9 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Rehab) {
} }
data.Encounter_Id = inputSrc.Encounter_Id data.Encounter_Id = inputSrc.Encounter_Id
data.Doctor_Id = inputSrc.Doctor_Id data.Parent_Encounter_Id = inputSrc.Parent_Encounter_Id
data.AllocatedVisitCount = inputSrc.AllocatedVisitCount data.AllocatedVisitCount = inputSrc.AllocatedVisitCount
data.ExpiredAt = inputSrc.ExpiredAt
data.VisitMode_Code = &inputSrc.VisitMode_Code
data.Status_Code = &inputSrc.Status_Code
} }