From 7e9914998f86fe7d813febfe7c2f556746ad566b Mon Sep 17 00:00:00 2001 From: vanilia Date: Sun, 2 Nov 2025 18:38:08 +0700 Subject: [PATCH 01/12] add rehab --- .../domain/main-entities/encounter/dto.go | 3 + internal/domain/main-entities/rehab/dto.go | 70 +++++ internal/use-case/main-use-case/rehab/case.go | 275 ++++++++++++++++++ .../use-case/main-use-case/rehab/helper.go | 23 ++ internal/use-case/main-use-case/rehab/lib.go | 144 +++++++++ .../main-use-case/rehab/middleware-runner.go | 103 +++++++ .../main-use-case/rehab/middleware.go | 9 + .../use-case/main-use-case/rehab/tycovar.go | 44 +++ 8 files changed, 671 insertions(+) create mode 100644 internal/domain/main-entities/rehab/dto.go create mode 100644 internal/use-case/main-use-case/rehab/case.go create mode 100644 internal/use-case/main-use-case/rehab/helper.go create mode 100644 internal/use-case/main-use-case/rehab/lib.go create mode 100644 internal/use-case/main-use-case/rehab/middleware-runner.go create mode 100644 internal/use-case/main-use-case/rehab/middleware.go create mode 100644 internal/use-case/main-use-case/rehab/tycovar.go diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 89f12caf..6a716d6e 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -16,6 +16,7 @@ import ( // internal - domain - main-entities evs "simrs-vx/internal/domain/bpjs-entities/vclaim-sep" + eam "simrs-vx/internal/domain/main-entities/ambulatory" ea "simrs-vx/internal/domain/main-entities/appointment" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/employee" @@ -48,6 +49,8 @@ type CreateDto struct { Appointment_Id *uint `json:"appointment_id"` RefTypeCode ere.RefTypeCode `json:"refTypeCode"` NewStatus bool `json:"newStatus"` + Ambulatory *eam.CreateDto `json:"ambulatory"` + Re pa.AuthInfo } diff --git a/internal/domain/main-entities/rehab/dto.go b/internal/domain/main-entities/rehab/dto.go new file mode 100644 index 00000000..0cec61d8 --- /dev/null +++ b/internal/domain/main-entities/rehab/dto.go @@ -0,0 +1,70 @@ +package rehab + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ed "simrs-vx/internal/domain/main-entities/doctor" +) + +type CreateDto struct { + Encounter_Id *uint `json:"encounter_id"` + Doctor_Id *uint `json:"doctor_id"` + AllocatedVisitCount *int `json:"allocatedVisitCount"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` + Doctor_Id *uint `json:"doctor-id"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Encounter_Id *uint `json:"encounter_id"` + Doctor_Id *uint `json:"doctor_id"` + Doctor *ed.Doctor `json:"doctor,omitempty"` + AllocatedVisitCount *int `json:"allocatedVisitCount"` +} + +func (d Rehab) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Doctor_Id: d.Doctor_Id, + Doctor: d.Doctor, + AllocatedVisitCount: d.AllocatedVisitCount, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []Rehab) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/use-case/main-use-case/rehab/case.go b/internal/use-case/main-use-case/rehab/case.go new file mode 100644 index 00000000..0d7a243e --- /dev/null +++ b/internal/use-case/main-use-case/rehab/case.go @@ -0,0 +1,275 @@ +package rehab + +import ( + e "simrs-vx/internal/domain/main-entities/rehab" + "strconv" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" +) + +const source = "rehab" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.Rehab{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.Rehab + var dataList []e.Rehab + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { + return err + } + + if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + "record_currentCount": strconv.Itoa(len(dataList)), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.Rehab + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { + return err + } + + if data, err = ReadDetailData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Rehab + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + 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 + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + 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 + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Rehab + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + 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 + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/rehab/helper.go b/internal/use-case/main-use-case/rehab/helper.go new file mode 100644 index 00000000..5c06ca0a --- /dev/null +++ b/internal/use-case/main-use-case/rehab/helper.go @@ -0,0 +1,23 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package rehab + +import ( + e "simrs-vx/internal/domain/main-entities/rehab" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Rehab) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Encounter_Id = inputSrc.Encounter_Id + data.Doctor_Id = inputSrc.Doctor_Id + data.AllocatedVisitCount = inputSrc.AllocatedVisitCount +} diff --git a/internal/use-case/main-use-case/rehab/lib.go b/internal/use-case/main-use-case/rehab/lib.go new file mode 100644 index 00000000..faee1b35 --- /dev/null +++ b/internal/use-case/main-use-case/rehab/lib.go @@ -0,0 +1,144 @@ +package rehab + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/rehab" + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.Rehab, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.Rehab{} + 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.Rehab, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.Rehab{} + 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.Rehab{}). + 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 errors.Is(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.Rehab, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.Rehab{} + + 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.Rehab, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setData(&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.Rehab, 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 +} diff --git a/internal/use-case/main-use-case/rehab/middleware-runner.go b/internal/use-case/main-use-case/rehab/middleware-runner.go new file mode 100644 index 00000000..8c0af77a --- /dev/null +++ b/internal/use-case/main-use-case/rehab/middleware-runner.go @@ -0,0 +1,103 @@ +package rehab + +import ( + e "simrs-vx/internal/domain/main-entities/rehab" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +type middlewareRunner struct { + Event *pl.Event + Tx *gorm.DB + MwType pu.MWType +} + +// NewMiddlewareExecutor creates a new middleware executor +func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { + return &middlewareRunner{ + Event: event, + Tx: tx, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.Rehab) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.Rehab) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Rehab) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Rehab) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.Rehab) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) setMwType(mwType pu.MWType) { + me.MwType = mwType +} diff --git a/internal/use-case/main-use-case/rehab/middleware.go b/internal/use-case/main-use-case/rehab/middleware.go new file mode 100644 index 00000000..dadf6b9b --- /dev/null +++ b/internal/use-case/main-use-case/rehab/middleware.go @@ -0,0 +1,9 @@ +package rehab + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/main-use-case/rehab/tycovar.go b/internal/use-case/main-use-case/rehab/tycovar.go new file mode 100644 index 00000000..4899ec44 --- /dev/null +++ b/internal/use-case/main-use-case/rehab/tycovar.go @@ -0,0 +1,44 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package rehab + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/rehab" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Rehab, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Rehab, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Rehab, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw From 616f90096865b24a0930b17c3e2ade01eb255150 Mon Sep 17 00:00:00 2001 From: vanilia Date: Sun, 2 Nov 2025 18:53:35 +0700 Subject: [PATCH 02/12] add rehab --- internal/domain/main-entities/ambulatory/dto.go | 17 ++++++++++------- internal/domain/main-entities/encounter/dto.go | 3 ++- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/internal/domain/main-entities/ambulatory/dto.go b/internal/domain/main-entities/ambulatory/dto.go index 455f7f02..42ece735 100644 --- a/internal/domain/main-entities/ambulatory/dto.go +++ b/internal/domain/main-entities/ambulatory/dto.go @@ -2,13 +2,13 @@ package ambulatory import ( ecore "simrs-vx/internal/domain/base-entities/core" - ee "simrs-vx/internal/domain/main-entities/encounter" ere "simrs-vx/internal/domain/references/encounter" ) type CreateDto struct { - Encounter_Id *uint `json:"encounter_id"` - Class_Code ere.AmbulatoryClassCode `json:"class_code" validate:"maxLength=10"` + Encounter_Id *uint `json:"encounter_id"` + Class_Code ere.AmbulatoryClassCode `json:"class_code" validate:"maxLength=10"` + VisitMode_Code ere.VisitModeCode `json:"visitMode_code"` } type ReadListDto struct { @@ -43,13 +43,16 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty"` - Class_Code ere.AmbulatoryClassCode `json:"class_code"` + Encounter_Id *uint `json:"encounter_id"` + Class_Code ere.AmbulatoryClassCode `json:"class_code"` + VisitMode_Code ere.VisitModeCode `json:"visitMode_code"` } func (d Ambulatory) ToResponse() ResponseDto { - resp := ResponseDto{} + resp := ResponseDto{ + Class_Code: d.Class_Code, + VisitMode_Code: d.VisitMode_Code, + } resp.Main = d.Main return resp } diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 6a716d6e..d20d53cf 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -22,6 +22,7 @@ import ( 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" + er "simrs-vx/internal/domain/main-entities/rehab/base" es "simrs-vx/internal/domain/main-entities/specialist" ess "simrs-vx/internal/domain/main-entities/subspecialist" eu "simrs-vx/internal/domain/main-entities/unit" @@ -50,7 +51,7 @@ type CreateDto struct { RefTypeCode ere.RefTypeCode `json:"refTypeCode"` NewStatus bool `json:"newStatus"` Ambulatory *eam.CreateDto `json:"ambulatory"` - Re + Rehab *er.Basic `json:"rehab"` pa.AuthInfo } From 16bb34b8b404a3b947f0513d0df7438cc804bfc3 Mon Sep 17 00:00:00 2001 From: vanilia Date: Sun, 2 Nov 2025 20:58:13 +0700 Subject: [PATCH 03/12] add validation for class_code ambulatory --- .../encounter/request-validation.go | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/internal/interface/main-handler/encounter/request-validation.go b/internal/interface/main-handler/encounter/request-validation.go index 025298fc..5963abcb 100644 --- a/internal/interface/main-handler/encounter/request-validation.go +++ b/internal/interface/main-handler/encounter/request-validation.go @@ -63,3 +63,26 @@ func validateRequestCheckIn(w http.ResponseWriter, i e.CheckinDto) (valid bool) return true } + +func validateRequestCreate(w http.ResponseWriter, i e.CreateDto) (valid bool) { + switch { + case i.Class_Code == ere.ECAmbulatory: + if i.Ambulatory == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: "ambulatory required", + }) + } + } + + if i.Class_Code == ere.ECAmbulatory && i.Ambulatory.Class_Code == ere.ACCRme && i.Ambulatory.VisitMode_Code == ere.VMCAdm { + if *i.Rehab.AllocatedVisitCount == 0 { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: "rehab.AllocatedVisitCode required", + }) + return + } + } + return +} From 79f5c2c7dbf9ccadf7283b4fc6c83b99584e3a5d Mon Sep 17 00:00:00 2001 From: vanilia Date: Sun, 2 Nov 2025 21:02:49 +0700 Subject: [PATCH 04/12] update dto encounter --- internal/domain/main-entities/encounter/dto.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index d20d53cf..e4b9a521 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -19,7 +19,9 @@ import ( eam "simrs-vx/internal/domain/main-entities/ambulatory" ea "simrs-vx/internal/domain/main-entities/appointment" ed "simrs-vx/internal/domain/main-entities/doctor" + eem "simrs-vx/internal/domain/main-entities/emergency" ee "simrs-vx/internal/domain/main-entities/employee" + ei "simrs-vx/internal/domain/main-entities/inpatient" eir "simrs-vx/internal/domain/main-entities/internal-reference" ep "simrs-vx/internal/domain/main-entities/patient" er "simrs-vx/internal/domain/main-entities/rehab/base" @@ -51,6 +53,8 @@ type CreateDto struct { RefTypeCode ere.RefTypeCode `json:"refTypeCode"` NewStatus bool `json:"newStatus"` Ambulatory *eam.CreateDto `json:"ambulatory"` + Emergency *eem.CreateDto `json:"emergency"` + Inpatient *ei.Inpatient `json:"inpatient"` Rehab *er.Basic `json:"rehab"` pa.AuthInfo From 8a3a140cdec7772c7925208be5e84b223b9edda3 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 4 Nov 2025 10:26:11 +0700 Subject: [PATCH 05/12] adjustment encounter --- .../domain/main-entities/encounter/dto.go | 10 +- .../main-handler/encounter/handler.go | 5 + .../encounter/request-validation.go | 25 +- .../main-use-case/ambulatory/helper.go | 1 + .../use-case/main-use-case/encounter/case.go | 148 +++++++---- .../use-case/main-use-case/encounter/lib.go | 245 ++++++++++++++++++ .../use-case/main-use-case/soapi/helper.go | 16 ++ internal/use-case/main-use-case/soapi/lib.go | 20 ++ 8 files changed, 403 insertions(+), 67 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index e4b9a521..0d5e1aae 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -16,15 +16,11 @@ import ( // internal - domain - main-entities evs "simrs-vx/internal/domain/bpjs-entities/vclaim-sep" - eam "simrs-vx/internal/domain/main-entities/ambulatory" ea "simrs-vx/internal/domain/main-entities/appointment" ed "simrs-vx/internal/domain/main-entities/doctor" - eem "simrs-vx/internal/domain/main-entities/emergency" ee "simrs-vx/internal/domain/main-entities/employee" - ei "simrs-vx/internal/domain/main-entities/inpatient" eir "simrs-vx/internal/domain/main-entities/internal-reference" ep "simrs-vx/internal/domain/main-entities/patient" - er "simrs-vx/internal/domain/main-entities/rehab/base" es "simrs-vx/internal/domain/main-entities/specialist" ess "simrs-vx/internal/domain/main-entities/subspecialist" eu "simrs-vx/internal/domain/main-entities/unit" @@ -52,10 +48,8 @@ type CreateDto struct { Appointment_Id *uint `json:"appointment_id"` RefTypeCode ere.RefTypeCode `json:"refTypeCode"` NewStatus bool `json:"newStatus"` - Ambulatory *eam.CreateDto `json:"ambulatory"` - Emergency *eem.CreateDto `json:"emergency"` - Inpatient *ei.Inpatient `json:"inpatient"` - Rehab *er.Basic `json:"rehab"` + VisitMode_Code *ere.VisitModeCode `json:"visitMode_code"` // if subClass_Code is rehab + AllocatedVisitCount *int `json:"allocatedVisitCount"` pa.AuthInfo } diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index ed8b1fbc..b66abe76 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -26,10 +26,15 @@ func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { if err != nil { rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) } + dto := e.CreateDto{} if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { return } + if valid := validateRequestCreate(w, dto); !valid { + return + } + dto.AuthInfo = *authInfo res, err := u.Create(dto) rw.DataResponse(w, res, err) diff --git a/internal/interface/main-handler/encounter/request-validation.go b/internal/interface/main-handler/encounter/request-validation.go index 5963abcb..d3a7afe4 100644 --- a/internal/interface/main-handler/encounter/request-validation.go +++ b/internal/interface/main-handler/encounter/request-validation.go @@ -67,22 +67,17 @@ func validateRequestCheckIn(w http.ResponseWriter, i e.CheckinDto) (valid bool) func validateRequestCreate(w http.ResponseWriter, i e.CreateDto) (valid bool) { switch { case i.Class_Code == ere.ECAmbulatory: - if i.Ambulatory == nil { - rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationFail, - Message: "ambulatory required", - }) + // field allocatedVisitCount required if ambulatory visitMode_Code is adm + if ere.AmbulatoryClassCode(*i.SubClass_Code) == ere.ACCRme && *i.VisitMode_Code == ere.VMCAdm { + if *i.AllocatedVisitCount == 0 { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: "allocatedVisitCount required", + }) + return + } } } - if i.Class_Code == ere.ECAmbulatory && i.Ambulatory.Class_Code == ere.ACCRme && i.Ambulatory.VisitMode_Code == ere.VMCAdm { - if *i.Rehab.AllocatedVisitCount == 0 { - rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationFail, - Message: "rehab.AllocatedVisitCode required", - }) - return - } - } - return + return true } diff --git a/internal/use-case/main-use-case/ambulatory/helper.go b/internal/use-case/main-use-case/ambulatory/helper.go index f78f0b65..bdd9b106 100644 --- a/internal/use-case/main-use-case/ambulatory/helper.go +++ b/internal/use-case/main-use-case/ambulatory/helper.go @@ -22,6 +22,7 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Ambulatory) { data.Encounter_Id = inputSrc.Encounter_Id data.Class_Code = inputSrc.Class_Code + data.VisitMode_Code = inputSrc.VisitMode_Code } func CheckClassCode(input *string) (ere.AmbulatoryClassCode, error) { diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 11b17e2b..cb886eee 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -25,6 +25,7 @@ import ( eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" ei "simrs-vx/internal/domain/main-entities/inpatient" + 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" @@ -36,6 +37,7 @@ import ( 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" + 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" ) @@ -46,6 +48,7 @@ var now = time.Now() func Create(input e.CreateDto) (*d.Data, error) { data := e.Encounter{} + createSoapi := []es.CreateDto{} event := pl.Event{ Feature: "Create", @@ -55,6 +58,33 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") + // validate SubClass + var subCode interface{} + subCode, err := verifyClassCode(input) + if err != nil { + return nil, err + } + + // verify whether the allocated visit count has not exceeded the limit + if input.Class_Code == ere.ECAmbulatory && subCode.(ere.AmbulatoryClassCode) == ere.ACCRme && + *input.VisitMode_Code == ere.VMCSeries { + + dataEncounter, valid, err := verifyAllocatedVisitCount(input, &event) + if err != nil { + return nil, err + } + + if !valid { + return nil, err + } + + // get data soapi + createSoapi, err = getSoapiEncounterAdm(dataEncounter, &event) + if err != nil { + return nil, err + } + } + // check if patient is new in the hospital dataPatient, err := ReadList(e.ReadListDto{ FilterDto: e.FilterDto{Patient_Id: input.Patient_Id}, @@ -62,7 +92,6 @@ func Create(input e.CreateDto) (*d.Data, error) { if err != nil { return nil, err } - if list, ok := dataPatient.Data.([]e.ResponseDto); ok { if len(list) < 1 { input.NewStatus = true @@ -83,6 +112,7 @@ func Create(input e.CreateDto) (*d.Data, error) { input.Adm_Employee_Id = &emp.Id } + // create encounter if resData, err := CreateData(input, &event, tx); err != nil { return err } else { @@ -91,52 +121,57 @@ func Create(input e.CreateDto) (*d.Data, error) { switch input.Class_Code { case ere.ECAmbulatory: - subCode, err := ua.CheckClassCode(input.SubClass_Code) - if err != nil { - return err - } + subCodeAmbulatory := subCode.(ere.AmbulatoryClassCode) ambCreate := ea.CreateDto{ - Encounter_Id: &data.Id, - Class_Code: subCode, + Encounter_Id: &data.Id, + Class_Code: subCodeAmbulatory, + VisitMode_Code: *input.VisitMode_Code, } _, err = ua.CreateData(ambCreate, &event, tx) if err != nil { return err } - if subCode == ere.ACCCac || subCode == ere.ACCCad { + if subCodeAmbulatory == ere.ACCCac || subCodeAmbulatory == ere.ACCCad { 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 } } - case ere.ECEmergency: - subCode, err := ue.CheckClassCode(input.SubClass_Code) - if err != nil { - return err + + if subCodeAmbulatory == ere.ACCRme && *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.ACCRme && *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, + Class_Code: subCode.(ere.EmergencyClassCode), } _, err = ue.CreateData(emerCreate, &event, tx) if err != nil { return err } case ere.ECInpatient: - subCode, err := ui.CheckClassCode(input.SubClass_Code) - if err != nil { - return err - } inpCreate := ei.CreateDto{ Encounter_Id: &data.Id, - Class_Code: subCode, + Class_Code: subCode.(ere.InpatientClassCode), Infra_Id: input.Infra_Id, } _, err = ui.CreateData(inpCreate, &event, tx) @@ -148,11 +183,10 @@ func Create(input e.CreateDto) (*d.Data, error) { } // insert adm_employee_hist - if _, err := uaeh.Create(eaeh.CreateDto{ + if _, err := uaeh.CreateData(eaeh.CreateDto{ Encounter_Id: &data.Main.Id, Employee_Id: data.Adm_Employee_Id, - StartedAt: &now, - }); err != nil { + StartedAt: &now}, &event, tx); err != nil { return err } @@ -430,8 +464,16 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { return pl.SetLogError(&event, input) } - if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { - return err + if data.Class_Code == ere.ECAmbulatory { + // validate if soapi exist + err = getSoapiByTypeCode(input.Id, &event, "check-out") + if err != nil { + return err + } + } else { + if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { + return err + } } if err := updateDischargeData(input, data, &event, tx); err != nil { @@ -557,7 +599,7 @@ func UpdateStatusCode(input e.UpdateStatusDto) (*d.Data, error) { } func CheckIn(input e.CheckinDto) (*d.Data, error) { - rdDto := e.ReadDetailDto{Id: uint16(input.Id)} + rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Rehab"} var data *e.Encounter var err error @@ -580,23 +622,11 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { } // validate if soapi exist - dataSoapi, err := us.ReadList(es.ReadListDto{FilterDto: es.FilterDto{Encounter_Id: &input.Id}}) + err = getSoapiByTypeCode(input.Id, &event, "check-in") if err != nil { return nil, err } - if list, ok := dataSoapi.Data.([]es.ResponseDto); ok { - if len(list) > 0 { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-state-mismatch", - Detail: "soapi already exist", - Raw: errors.New("soapi already exist"), - } - return nil, pl.SetLogError(&event, input) - } - } - err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { @@ -621,11 +651,10 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { } //insert responsible_doctor_hist - if _, err = urdh.Create(erdh.CreateDto{ + if _, err = urdh.CreateData(erdh.CreateDto{ Encounter_Id: &input.Id, Doctor_Id: input.Responsible_Doctor_Id, - StartedAt: input.StartedAt, - }); err != nil { + StartedAt: input.StartedAt}, &event, tx); err != nil { return err } } @@ -638,11 +667,10 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { } // insert responsible_doctor_hist - if _, err = uaeh.Create(eaeh.CreateDto{ + if _, err = uaeh.CreateData(eaeh.CreateDto{ Encounter_Id: &input.Id, Employee_Id: input.Adm_Employee_Id, - StartedAt: input.StartedAt, - }); err != nil { + StartedAt: input.StartedAt}, &event, tx); err != nil { return err } } @@ -652,6 +680,15 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { return err } + 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") return nil @@ -688,3 +725,26 @@ func validateForeignKey(input e.CheckinDto) error { return nil } + +func verifyClassCode(input e.CreateDto) (subCode interface{}, err error) { + switch input.Class_Code { + case ere.ECAmbulatory: + subCode, err = ua.CheckClassCode(input.SubClass_Code) + if err != nil { + return nil, err + } + case ere.ECEmergency: + 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 +} diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index 3327bcfa..ed41e15f 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -3,6 +3,12 @@ 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" @@ -15,8 +21,11 @@ import ( // 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) { @@ -333,3 +342,239 @@ func updateLatestAdmEmployeeHist(input e.CheckinDto, event *pl.Event, dbx ...*go 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. + 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.ECAmbulatory, ere.VMCSeries). + Where("\"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. + 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}) + 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.STCEarlyRehab, erc.STCFunc} + 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 +} diff --git a/internal/use-case/main-use-case/soapi/helper.go b/internal/use-case/main-use-case/soapi/helper.go index 5a94848a..ae396270 100644 --- a/internal/use-case/main-use-case/soapi/helper.go +++ b/internal/use-case/main-use-case/soapi/helper.go @@ -23,3 +23,19 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Soapi) { data.TypeCode = inputSrc.TypeCode data.Value = inputSrc.Value } + +func setBulkData(input []e.CreateDto, encounterId uint) []e.Soapi { + var data []e.Soapi + + for _, v := range input { + data = append(data, e.Soapi{ + Encounter_Id: &encounterId, + Employee_Id: v.Employee_Id, + Time: v.Time, + TypeCode: v.TypeCode, + Value: v.Value, + }) + } + + return data +} diff --git a/internal/use-case/main-use-case/soapi/lib.go b/internal/use-case/main-use-case/soapi/lib.go index 7e551505..57bb5d23 100644 --- a/internal/use-case/main-use-case/soapi/lib.go +++ b/internal/use-case/main-use-case/soapi/lib.go @@ -138,3 +138,23 @@ func DeleteData(data *e.Soapi, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "complete") return nil } + +func CreateBulkData(input []e.CreateDto, encounterId uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := setBulkData(input, encounterId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} From 0f9305427c0f2e5d57bf42797a9c676b98edaf1e Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 4 Nov 2025 11:11:24 +0700 Subject: [PATCH 06/12] encounter-adjustment-finish --- internal/use-case/main-use-case/encounter/case.go | 8 +++++++- internal/use-case/main-use-case/encounter/lib.go | 11 ++++++----- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index cb886eee..8cff1e21 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -75,7 +75,13 @@ func Create(input e.CreateDto) (*d.Data, error) { } if !valid { - return nil, err + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "visit-limit-exceeded", + Detail: "Encounter has exceeded the allowed number of visits", + Raw: errors.New("visit count exceeds allowed limit"), + } + return nil, pl.SetLogError(&event, input) } // get data soapi diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index ed41e15f..b840f6e1 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -370,11 +370,12 @@ func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, boo } 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.ECAmbulatory, ere.VMCSeries). - Where("\"CreatedAt\" > ?", recentEncounterAdm.CreatedAt). + 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" @@ -402,7 +403,7 @@ func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.Crea } } - err = dg.I. + err = dg.I.Debug(). Model(&es.Soapi{}). Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\""). Where("\"Encounter_Id\" = ?", enc.Id). @@ -478,7 +479,7 @@ func getSoapiByTypeCode(encounterId uint, event *pl.Event, mode string) (err err 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}) + 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) } @@ -503,7 +504,7 @@ func validateExistedSoapi(dataSoapi []es.Soapi, dataAmbulatory *ea.Ambulatory, e 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.STCEarlyRehab, erc.STCFunc} + 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} } From c6eaaa2c10c0c2ff798c1bc78fe877c23342818d Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 4 Nov 2025 14:05:56 +0700 Subject: [PATCH 07/12] finish adjustment encounter --- .../encounter/request-validation.go | 2 +- .../use-case/main-use-case/encounter/case.go | 66 ++- .../main-use-case/encounter/helper.go | 387 +++++++++++++++++- .../use-case/main-use-case/encounter/lib.go | 288 +------------ 4 files changed, 415 insertions(+), 328 deletions(-) diff --git a/internal/interface/main-handler/encounter/request-validation.go b/internal/interface/main-handler/encounter/request-validation.go index d3a7afe4..b7d31e71 100644 --- a/internal/interface/main-handler/encounter/request-validation.go +++ b/internal/interface/main-handler/encounter/request-validation.go @@ -68,7 +68,7 @@ func validateRequestCreate(w http.ResponseWriter, i e.CreateDto) (valid bool) { switch { case i.Class_Code == ere.ECAmbulatory: // field allocatedVisitCount required if ambulatory visitMode_Code is adm - if ere.AmbulatoryClassCode(*i.SubClass_Code) == ere.ACCRme && *i.VisitMode_Code == ere.VMCAdm { + if ere.AmbulatoryClassCode(*i.SubClass_Code) == ere.ACCRehab && *i.VisitMode_Code == ere.VMCAdm { if *i.AllocatedVisitCount == 0 { rw.DataResponse(w, nil, d.FieldError{ Code: dataValidationFail, diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index fa55f222..ef70d9c2 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -38,7 +38,6 @@ import ( uem "simrs-vx/internal/use-case/main-use-case/employee" ui "simrs-vx/internal/use-case/main-use-case/inpatient" 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" ) @@ -66,7 +65,7 @@ func Create(input e.CreateDto) (*d.Data, error) { } // verify whether the allocated visit count has not exceeded the limit - if input.Class_Code == ere.ECAmbulatory && subCode.(ere.AmbulatoryClassCode) == ere.ACCRme && + if input.Class_Code == ere.ECAmbulatory && subCode.(ere.AmbulatoryClassCode) == ere.ACCRehab && *input.VisitMode_Code == ere.VMCSeries { dataEncounter, valid, err := verifyAllocatedVisitCount(input, &event) @@ -150,7 +149,7 @@ func Create(input e.CreateDto) (*d.Data, error) { } } - if subCodeAmbulatory == ere.ACCRme && *input.VisitMode_Code == ere.VMCAdm { + if subCodeAmbulatory == ere.ACCRehab && *input.VisitMode_Code == ere.VMCAdm { // create data rehab if _, err = ur.CreateData(er.CreateDto{ Encounter_Id: &data.Id, @@ -158,7 +157,7 @@ func Create(input e.CreateDto) (*d.Data, error) { AllocatedVisitCount: input.AllocatedVisitCount}, &event, tx); err != nil { return err } - } else if subCodeAmbulatory == ere.ACCRme && *input.VisitMode_Code == ere.VMCSeries { + } 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 @@ -442,7 +441,7 @@ func Delete(input e.DeleteDto) (*d.Data, error) { } func CheckOut(input e.DischargeDto) (*d.Data, error) { - rdDto := e.ReadDetailDto{Id: uint16(input.Id)} + rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Ambulatory"} var data *e.Encounter var err error @@ -470,13 +469,14 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { return pl.SetLogError(&event, input) } - if data.Class_Code == ere.ECAmbulatory { + if data.Ambulatory != nil && (data.Ambulatory.Class_Code == ere.ACCReg || data.Ambulatory.Class_Code == ere.ACCRehab) { // validate if soapi exist - err = getSoapiByTypeCode(input.Id, &event, "check-out") + err = getSoapiByTypeCode(input.Id, *data.Ambulatory, &event, "check-out") if err != nil { return err } } else { + // chemo TBC if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { return err } @@ -605,7 +605,7 @@ func UpdateStatusCode(input e.UpdateStatusDto) (*d.Data, error) { } func CheckIn(input e.CheckinDto) (*d.Data, error) { - rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Rehab"} + rdDto := e.ReadDetailDto{Id: uint16(input.Id), Includes: "Rehab,Ambulatory"} var data *e.Encounter var err error @@ -627,12 +627,6 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { input.StartedAt = &now } - // validate if soapi exist - err = getSoapiByTypeCode(input.Id, &event, "check-in") - if 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 { @@ -649,34 +643,34 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { return pl.SetLogError(&event, input) } - // Insert responsible_doctor_hist if responsible_doctor_id has changed && update latest history - if data.Responsible_Doctor_Id == nil || *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { - // update finishedAt in latest responsible_doctor_hist - if err = updateLatestResponsibleDoctorHist(input, &event, tx); err != nil { + // validate if soapi exist + 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") + if err != nil { return err } + } - //insert responsible_doctor_hist - if _, err = urdh.CreateData(erdh.CreateDto{ - Encounter_Id: &input.Id, + // Insert responsible_doctor_hist if responsible_doctor_id has changed && update latest history + if data.Responsible_Doctor_Id == nil || *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { + // upsert responsibleDoctorHist + if err = upsertResponsibleDoctorHist(erdh.CreateDto{ + Encounter_Id: &data.Id, Doctor_Id: input.Responsible_Doctor_Id, - StartedAt: input.StartedAt}, &event, tx); err != nil { + StartedAt: input.StartedAt, + }, &event, tx); err != nil { return err } } // Insert adm_employee_hist if adm_employee_id has changed && update latest history if input.Adm_Employee_Id != nil && *input.Adm_Employee_Id != *data.Adm_Employee_Id { - // update finishedAt in latest adm_employee_hist - if err = updateLatestAdmEmployeeHist(input, &event, tx); err != nil { - return err - } - - // insert responsible_doctor_hist - if _, err = uaeh.CreateData(eaeh.CreateDto{ - Encounter_Id: &input.Id, + // upsert admEmployeeHist + if err = upsertAdmEmployeeHist(eaeh.CreateDto{ + Encounter_Id: &data.Id, Employee_Id: input.Adm_Employee_Id, - StartedAt: input.StartedAt}, &event, tx); err != nil { + StartedAt: input.StartedAt, + }, &event, tx); err != nil { return err } } @@ -686,13 +680,13 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { return err } - if err := updateRehabDoctor(er.UpdateDto{ - CreateDto: er.CreateDto{ + 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 + }}, &event, tx); err != nil { + return err + } } pl.SetLogInfo(&event, nil, "complete") diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index c5eb155e..4bf7be60 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -7,17 +7,25 @@ package encounter import ( "errors" "fmt" - uir "simrs-vx/internal/use-case/main-use-case/internal-reference" "strings" "time" + dg "github.com/karincake/apem/db-gorm-pg" "gorm.io/gorm" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" + ercl "simrs-vx/internal/domain/references/clinical" + erc "simrs-vx/internal/domain/references/common" + ere "simrs-vx/internal/domain/references/encounter" + erg "simrs-vx/internal/domain/references/organization" + + eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" + ea "simrs-vx/internal/domain/main-entities/ambulatory" edo "simrs-vx/internal/domain/main-entities/device-order" ed "simrs-vx/internal/domain/main-entities/doctor" + e "simrs-vx/internal/domain/main-entities/encounter" 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" @@ -26,20 +34,21 @@ import ( 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" + er "simrs-vx/internal/domain/main-entities/rehab" + erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" 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" + uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" + uir "simrs-vx/internal/use-case/main-use-case/internal-reference" 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" + urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" ) func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) { @@ -432,3 +441,371 @@ func getDoctors(doctorIds []uint, event *pl.Event, tx *gorm.DB) ([]ed.Doctor, er } return doctors, nil } + +func upsertResponsibleDoctorHist(input erdh.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + var latest erdh.ResponsibleDoctorHist + err := tx. + Where("\"Encounter_Id\" = ?", input.Encounter_Id). + Order("\"CreatedAt\" DESC"). + Limit(1). + First(&latest).Error + + switch { + case errors.Is(err, gorm.ErrRecordNotFound): + // Insert + if _, err = urdh.CreateData(input, event, tx); err != nil { + return err + } + case err != nil: + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "read-fail", + Detail: "Failed to read responsible doctor history", + Raw: err, + } + return pl.SetLogError(event, input) + default: + // Update + if err := tx.Model(&latest).Updates(map[string]interface{}{ + "Doctor_Id": input.Doctor_Id, + "StartedAt": input.StartedAt, + "UpdatedAt": time.Now(), + }).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "update-fail", + Detail: "Failed to update responsible doctor history", + Raw: err, + } + return pl.SetLogError(event, input) + } + } + pl.SetLogInfo(event, input, "complete") + return nil +} + +func upsertAdmEmployeeHist(input eaeh.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + var latest eaeh.AdmEmployeeHist + err := tx. + Where("\"Encounter_Id\" = ?", input.Encounter_Id). + Order("\"CreatedAt\" DESC"). + Limit(1). + First(&latest).Error + + switch { + case errors.Is(err, gorm.ErrRecordNotFound): + // Insert + if _, err = uaeh.CreateData(input, event, tx); err != nil { + return err + } + case err != nil: + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "read-fail", + Detail: "Failed to read responsible doctor history", + Raw: err, + } + return pl.SetLogError(event, input) + + default: + // Update + if err := tx.Model(&latest).Updates(map[string]interface{}{ + "Employee_Id": input.Employee_Id, + "StartedAt": input.StartedAt, + "UpdatedAt": time.Now(), + }).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "update-fail", + Detail: "Failed to update responsible doctor history", + Raw: err, + } + return pl.SetLogError(event, input) + } + } + pl.SetLogInfo(event, input, "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 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\" = ?", erg.EPCDoc). + Where("\"Soapi\".\"TypeCode\" IN ?", []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.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, dataAmbulatory ea.Ambulatory, event *pl.Event, mode string) (err error) { + pl.SetLogInfo(event, encounterId, "started", "DBReadList") + + var ( + dataSoapi []es.Soapi + ) + + // 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\" = ?", erg.EPCDoc) + + // Set Case + switch { + case dataAmbulatory.Class_Code == ere.ACCReg: + tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.STCEEarlyMedic) + case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCAdm: + 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: + tx = tx.Where("\"Soapi\".\"TypeCode\" = ?", ercl.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[ercl.SoapiTypeCode]bool) + for _, s := range dataSoapi { + typeExist[s.TypeCode] = true + } + + required := []ercl.SoapiTypeCode{} + + switch { + case dataAmbulatory.Class_Code == ere.ACCReg: + required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic} + case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCAdm: + required = []ercl.SoapiTypeCode{ercl.STCEEarlyMedic, ercl.STCFunc, ercl.STCEarlyRehab} + case dataAmbulatory.Class_Code == ere.ACCRehab && dataAmbulatory.VisitMode_Code == ere.VMCSeries: + required = []ercl.SoapiTypeCode{ercl.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 +} diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index b840f6e1..d80fd513 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -3,29 +3,17 @@ 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) { @@ -261,88 +249,6 @@ func updateCheckInData(input e.CheckinDto, data *e.Encounter, event *pl.Event, d 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") @@ -356,7 +262,7 @@ func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, boo 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). + Where("\"Ambulatory\".\"Class_Code\" = ? AND \"Ambulatory\".\"VisitMode_Code\" = ?", ere.ACCRehab, ere.VMCAdm). Order("\"CreatedAt\" DESC"). First(&recentEncounterAdm).Error if err != nil { @@ -374,7 +280,7 @@ func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, boo 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("\"Ambulatory\".\"Class_Code\" = ? AND \"Ambulatory\".\"VisitMode_Code\" = ?", ere.ACCRehab, ere.VMCSeries). Where("\"Encounter\".\"CreatedAt\" > ?", recentEncounterAdm.CreatedAt). Count(&countEncounterSeries).Error if err != nil { @@ -389,193 +295,3 @@ func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, boo 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 -} From 64879fced480b11c6e58c5df6e274ebc99cc8b96 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Tue, 4 Nov 2025 15:15:25 +0700 Subject: [PATCH 08/12] migration from server --- cmd/main-migration/migrations/atlas.sum | 140 ++++++++++++------------ 1 file changed, 70 insertions(+), 70 deletions(-) diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index 7be07b61..e0d46a35 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,70 +1,70 @@ -h1:2J5umJFoWClM/F8kKNyZFoDqrV1aL8PimwTG6Mx6syo= -20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= -20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= -20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= -20250908062323.sql h1:oXl6Z143tOpIl4EfP4B8JNU8LrMvVmHEtCgAfiB4gs8= -20250908073811.sql h1:m2aNXfnGxnLq1+rVWrh4f60q7fhyhV3gEwNu/OIqQlE= -20250908073839.sql h1:cPk54xjLdMs26uY8ZHjNWLuyfAMzV7Zb0/9oJQrsw04= -20250910055902.sql h1:5xwjAV6QbtZT9empTJKfhyAjdknbHzb15B0Ku5dzqtQ= -20250915123412.sql h1:D83xaU2YlDEd21HLup/YQpQ2easMToYCyy/oK6AFgQs= -20250916043819.sql h1:ekoTJsBqQZ8G8n0qJ03d13+eoNoc7sAUEQGA5D/CCxk= -20250917040616.sql h1:zoCnmcXuM7AVv85SmN7RmFglCgJnoDmpRWExH0LAc9Q= -20250917040751.sql h1:J1xyRrh32y1+lezwAyNwPcUQ6ABBSgbvzNLva4SVdQU= -20250917045138.sql h1:jKe1Z0uOLG4SGBYM+S/3P+/zMPztmgoderD5swnMuCg= -20250917093645.sql h1:cNI3Pbz1R3LxvIXLuexafJFCXUXrmuFCgXXJ2sG+FW0= -20250918073552.sql h1:RJ1SvMzP6aeWnoPVD3eVAmIQOkcp6Php8z3QRri6v4g= -20250918073742.sql h1:+cEsnJTJFybe2fR69ZoOiX2R6c6iITl4m6WTZ1hjyzY= -20250918074745.sql h1:2hNVQCXF/dVYXAh+T/7oBFgERGWxzVb2FXJjwkFWGCI= -20250923025134.sql h1:Ykz/qpHiGDXPsCsWTjydQFVSibZP2D+h2fIeb2h2JGA= -20250924051317.sql h1:yQuW6SwJxIOM5fcxeAaie5lSm1oLysU/C2hH2xNCVoQ= -20250929034321.sql h1:101FJ8VH12mrZWlt/X1gvKUGOhoiF8tFbjiapAjnHzg= -20250929034428.sql h1:i+pROD9p+g5dOmmZma6WF/0Hw5g3Ha28NN85iTo1K34= -20250930025550.sql h1:+F+CsCUXD/ql0tHGEow70GhPBX1ZybVn+bh/T4YMh7Y= -20250930140351.sql h1:9AAEG1AnOAH+o0+oHL5G7I8vqlWOhwRlCGyyCpT/y1Q= -20251002085604.sql h1:3xZ68eYp4urXRnvotNH1XvG2mYOSDV/j3zHEZ/txg5E= -20251003032030.sql h1:HB+mQ2lXMNomHDpaRhB/9IwYI9/YiDO5eOJ+nAQH/jw= -20251005060450.sql h1:LbtCE2b+8osM3CvnmQJH1uCPtn+d7WchsslBOz8bL3Q= -20251006041122.sql h1:MlS7f21z06sutnf9dIekt5fuHJr4lgcQ4uCuCXAGsfc= -20251006045658.sql h1:3FmGCPCzjgMPdWDRodZTsx3KVaodd9zB9ilib69aewk= -20251006045928.sql h1:Z5g31PmnzNwk/OKdODcxZGm8fjJQdMFK32Xfnt3bRHg= -20251007022859.sql h1:FO03zEfaNEk/aXwY81d5Lp3MoBB9kPQuXlXJ4BPiSR8= -20251008031337.sql h1:l+sxUAGvcTfj3I6kAFHo+T6AYodC9k9GkR+jaKO2xXc= -20251008031554.sql h1:AqrVfIhSzY3PCy8ZlP5W91wn2iznfIuj5qQfubp6/94= -20251008052346.sql h1:nxnXmooIJ6r1mmzwnw+6efxLfc/k9h2aE6RMptPRons= -20251008073620.sql h1:6YsJp1W4SmQJ1lxpqF27BBlDC1zqhw7Yhc7pLzQTY6M= -20251009042854.sql h1:nkBV+R6j0fg7/JY6wH3eb5Vv0asJLnXmb6lINfT/GLQ= -20251009052657.sql h1:EPvdsib5rzCGPryd10HShGKvFPwM/R5S2lIVwtYxpms= -20251010031743.sql h1:T8IZmx8/btRFKLzTe78MzcBsPJNodnLvB0tby9QkirQ= -20251010070721.sql h1:5NQUk/yOV6sABLCB7swx++YIOyJe6MnU+yt1nRzde5w= -20251010072711.sql h1:ZJNqR2piyu8xJhBvVABSlnGEoKSKae3wuEs+wshPe4k= -20251013044536.sql h1:0Xjw8fNILiT8nnfrJDZgQnPf3dntmIoilbapnih8AE4= -20251013051438.sql h1:lfSuw5mgJnePBJamvhZ81osFIouXeiIEiSZ/evdwo48= -20251013081808.sql h1:ijgjNX08G6GBjA/ks8EKtb7P7Y7Cg7zbhqEOruGnv6M= -20251014060047.sql h1:0jqj49WTtneEIMQDBoo4c095ZGi8sCrA8NnHBrPU6D8= -20251014063537.sql h1:VZLXol0PTsTW21Epg6vBPsztWkDtcxup9F/z88EGgIg= -20251014063720.sql h1:2HVUyCV0ud3BJJDH2GEKZN/+IWLFPCsN1KqhP6csO14= -20251015045455.sql h1:MeLWmMhAOAz8b15Dd7IAQnt6JxjSml02XCXK22C0Lpg= -20251016010845.sql h1:4BncQdDOasRZJkzVJrSJJA7091A9VPNVx/faUCUPhBM= -20251016011023.sql h1:9JB9eFZKURK5RoCVDKR6glSvdJ8NTXrN7K/4q51zkz4= -20251016062912.sql h1:ACNn0fe+EMqUt3hoY+Dr3uqAV/QICBa1+mIW7fUc9Fk= -20251017060617.sql h1:4T3t9ifWrEQTPMSM0XJ98pF7Qdt+UfgtMui17bhrnWI= -20251017082207.sql h1:8vLG1l/saRRMHXkyA4nelJyjaSddhZd6r7R+Uo4JS/c= -20251018032635.sql h1:2xey5gnO3y2XSOrU8MLlIfoylPKbRGDRtHDD07B3MbQ= -20251018040322.sql h1:k/pdNiSoT8zFPqNQ/avOD0vYkNh3BTD64IlHrfVXr7I= -20251019093915.sql h1:hFcQE0y+p5dZiVwePGsRGto9m/q6kJNiUZbVDd5Rnjk= -20251020062553.sql h1:Iw7hulcm5iRQlfW+ygA4iTPxLqkxx6h9vXMXEwUAHKs= -20251021041042.sql h1:wMgSivBV2A0NDcsLmKGIp0kMcVh2IODSG9b4dgzCaOM= -20251021075552.sql h1:8gfSMAglflNO6L0sSzxFNEubYN8/O4thT7OQT+WH+3M= -20251023044432.sql h1:MkvajJs3bfk9+wHvQ43/ccAluJEBARm1gWr1u92ccLA= -20251024034832.sql h1:x3s3VEVYLOSKLAFxJGb2+c1FyTMMvPE+9k4Ew7rKQaI= -20251024074315.sql h1:EjAjelgi5qAfcRq/8vPTlGGYHvAKxNTllm8f0SzZDns= -20251025013451.sql h1:6hnuIiwYiG+6nLhOY/+Yyn+I6ZCFNRZxrJNqBV6HLqE= -20251025013609.sql h1:evPJaTD8WxYRMOJZHkSr7ONLx9PYxT+ankzQt9c/sJ0= -20251027075128.sql h1:/iFQBM1sytjqpyQSOx61q33gnorMgxTiFVSuL6bQqsM= -20251027091406.sql h1:eCZGtUkxAzEAqpC9UsGpP8Df9mS0DEOqSl885LgqpvM= -20251102002037.sql h1:lFJbuoZ2LMQnUNGdcwHVY3Xlfslgzu9t2WByT8yfOZI= -20251102091932.sql h1:rmdhb5m+P+fU8jROBZNyeYgZKuQvucsuljXv4ZVzvks= -20251103081637.sql h1:tf3BcwTeIw+oxMEisKDDfyKnBfalTLs8b0PJA8JWYxY= -20251104042334.sql h1:7PDMWOhmJywolAPKFZ14XaDBeMvcxShaXFN2IemNtzk= -20251104043530.sql h1:qvYVp3ysPf27f1BcoRNCFGovxuVE12lg9d6Xzda6zWU= -20251104080952.sql h1:+SMRSBPjP0qeLlaiIdff7hUnW2+1l1GJOWjP7fWPiGA= +h1:fcpOnigzzVgIt+BimD58z3BfW5JzOXIltdMVGabIh/Q= +20250904105930.sql h1:Vv4vCurl7m7/ZB6TjRpkubHpQ4RYwSUn0QHdzfoGpzY= +20250904141448.sql h1:FYCHH9Os4KkrZMDu/jR8FMP+wLMRW+Mb0PkLU/9BRDg= +20250908062237.sql h1:oanBpKZd+akPu2I/xYhUSbd0G5tAFbXzKLER/Zs8ENI= +20250908062323.sql h1:miNG9COddXkD1jGTgaROMAZ618eT6oiLGiJhXWnQwhE= +20250908073811.sql h1:gOi5cnGG1htlpfizybYmUIT0vYjZTBfXiI0nPSYK2u8= +20250908073839.sql h1:cWNDA4YikjoOteAJuNLFILjQUJPFB6o8Wxreiek4QyI= +20250910055902.sql h1:nxxOGnU0BbH/v3IPgeIOXOwH8d3tKomw7h6FTeMnnBs= +20250915123412.sql h1:mz7SiWfrdf0qE1VTSAAnA/147d6gyp6ry5vZ2bR9SH0= +20250916043819.sql h1:RHXVtmMkB6wfv06HfPyHMBmUfIpFt1xveafNz0kwKnE= +20250917040616.sql h1:MYVDht+akBlzQGKNu2hTTTLPEcH1bxT/Q8MK6WEtuhs= +20250917040751.sql h1:J79YyS2JzWgh5oKXMTgh67uo3gLxKaAsxRiZmSIfjBs= +20250917045138.sql h1:/SM1N4O8X3yxpoJgMEARmS1uOkuLKsTOy4PLsRCOKaQ= +20250917093645.sql h1:PNBTGZ7s10e5b5+Tie8YfVQBN0zKtJ5T34oK1iOUEb4= +20250918073552.sql h1:jG7+g3i8ODYaJdcdZz12v3nbsZ5mB9wG6kWnGyTQIRI= +20250918073742.sql h1:j+rgw7puxE7s+phqPVZHmPk0af3rcaA56Itp86y1suY= +20250918074745.sql h1:rPmP4DXs6OnY4Vp+xO/z9jFpJt/RrJ52SJJjIIxeDvc= +20250923025134.sql h1:2r6pcwnBSU5Y9Czk1OHBoh4yZXiMtEca9X8843fTEX0= +20250924051317.sql h1:iUAk2gsGoEGIPQ0lEEUp8maMSId8emNbP+kP712ABIA= +20250929034321.sql h1:UlpALNVmdi95zOIT0yc6ZyTj9bBjQEIpZhvgrc52M+k= +20250929034428.sql h1:feF+H4nDyHh5bdx48Oiz0A1qecZfi6v3qTTdjzJ45Dg= +20250930025550.sql h1:6XT1kXI3Z3ZIxxmvT7poufZWWCW0QiejZPaFV5wBnjI= +20250930140351.sql h1:HxnmAbh9gCy8jwl/9ycGktiByaUripsjFFvohofY2CY= +20251002085604.sql h1:SjLPi+ZN6qDccK3DaEQCgNsZpPwr5kynWXwbwEsziCI= +20251003032030.sql h1:oHfxNSuqTxU8Zaf9H+h8TuUb1Da03wcyc6hZjDrUQ2s= +20251005060450.sql h1:GIuCcrd4MwjmXpvbzDzPYL18BV3QaZZ+Y2FmEzjvi0E= +20251006041122.sql h1:uNDQbSw0M08lYoMvUNlQtS3iDzpPM1ixT13ugSAoWjE= +20251006045658.sql h1:z+t7yCK54Q4SSiF9kUyUhkYB2F+kzSW9TB7ogxd9wzw= +20251006045928.sql h1:1lATLFLp4BWwGZqAjZdP0Dc6ypNXiYcwjoNkqGa8NFE= +20251007022859.sql h1:HXXwWrkyvzJzJGAt9mGskCRBBV/c1JfPmfjDocmJhQ4= +20251008031337.sql h1:Ln5pCF3Hxa5foHZLcds+z/us2eH6VAhhEj3w0TAGlVs= +20251008031554.sql h1:aB4MUS2lmqG0//4HKUWorcPSpWya0VC4QItvGyskEVI= +20251008052346.sql h1:MI3AZgU5XcwZT2OvvlWAxdRtL0eJ3jjRwt56IY1+pRU= +20251008073620.sql h1:sztWXuSNYwpEraaSapSsYwno75LO5H/N7ob7OJQ8X/A= +20251009042854.sql h1:TnPXj+dCJls3IU//cuqJZymyBzZMKs7ayazfgtAFRxM= +20251009052657.sql h1:leXbs0CP8r5dRilmYyLRk1MICqak3ea1/LWMtFrijqQ= +20251010031743.sql h1:SgHNY/lQ88G2F4nZyMfiOkDntb+gtOR+nEQLqXBTwv4= +20251010070721.sql h1:AnJnhXsMzDvK4AFgYw6B16Kpo/hljrZtcpc9m2VOSHQ= +20251010072711.sql h1:aXPTtNwLcTuw8C/yAxwxvqs0ayEjNzI1uuE0vE3ERa8= +20251013044536.sql h1:7Pq6JcvTpPBYDCW2dz3HdgUwY65HlhEVWy9TiG8iONE= +20251013051438.sql h1:X6t8bkqpUYYokBunSufMQUe5vCg0VyO6dxbm7ngosUc= +20251013081808.sql h1:495pLguXL2Ozh+ycn4UYZgZbn6WbjXNbyZUc3JU8qhI= +20251014060047.sql h1:nCgImMRGHhziiW57O1ofWaXCAPGaCOHN7PldQ3OSmM4= +20251014063537.sql h1:2cLmID79jP6cuQ1YJaWTtghFiM1GtHMC0ZQl30Hpy1M= +20251014063720.sql h1:bzLKKVAjSHgDFoiI/glj7t1ETlSAKx+AlsIAaP0ru2g= +20251015045455.sql h1:S547+UugQhlTRcn1Lm1IfqT5RNPttIWIiD+RTx69YaE= +20251016010845.sql h1:c9DUvxl17pUkf0azdYGM/YDzYxIJkLcfZOcMI4rL+R0= +20251016011023.sql h1:u3ivg83bXgYHBbojbWpemLxPE9Dmmj53B6LXo664jxw= +20251016062912.sql h1:W9n1hWchfYkqNX9LO9uxFxEXAb/iY+Pexjnhmp6PbgI= +20251017060617.sql h1:VU6yZ2+LfHpDZ3+TIH40t3F5YXPCpTppuF9+uSqa4b8= +20251017082207.sql h1:QshZslfedckz7iDpSGmPyY9sP6dy6ckHbs8L1TuXIA4= +20251018032635.sql h1:M1U/9W/F9wlW5YDmVAmHFfUJU7FWFaX0DblpfZcYWrE= +20251018040322.sql h1:Zk/vw0e6AzWFO2ElLOzB+OrSz6k+h1Ynxp0TImAzxwY= +20251019093915.sql h1:3Q0kPiZwJnHn5rAvdh0w1LBdiA7W2xBmZWncoPXb044= +20251020062553.sql h1:mJwC/J8GzPAIXckNMvy1f/Nguk2VVf8roD/Raclhbao= +20251021041042.sql h1:d1BAOGAQhqr+oOwcpAVozUsTh457VSDEk2qQFavGG58= +20251021075552.sql h1:TNChGQ1Zlr/1iQ6qvK4iDTAJpe6L/z/M6e/O0SkQVaM= +20251023044432.sql h1:GA2AdJk2ULyjr6igtu9C/CEi4YUIks8r9jXGGaCvPsk= +20251024034832.sql h1:RXmbEhMkOLK5g1QL6up8iRPcwYfo89oLP26ZHvrUK9o= +20251024074315.sql h1:3GnPQSbuAAfMa8oWDyBjhXqn1j1zunY/w0ydX0IGPrA= +20251025013451.sql h1:5eNrA9LDxA4i5CCP7wSyOgFZ6t6jBWVil+oGzJpkJ2E= +20251025013609.sql h1:+N6EHc1kv3hqf16CUhXe+UITPmoxOPfi4MECLmJXNrc= +20251027075128.sql h1:PQflgsjce/p2ClbybLtdehdPNDcMZ9Lb1vd98xd0K8E= +20251027091406.sql h1:bYXV57GvodCMjg0/ox+XKGIAGhrDlVuJ1wO4foNEKtQ= +20251102002037.sql h1:ZcwULsJU2lI9t5pX7ukmfAHSx4ZxdxLUX6jzLDc2SrU= +20251102091932.sql h1:A6y9j4wAqm4/HfMDxhpy/ChDn3UNRVN99KGgSCX+e18= +20251103081637.sql h1:RFqJNhJItSwJQaMP5IcQ14mL7eQ3EJjGZua9zLWzwMU= +20251104042334.sql h1:BbjLAwImggvI6489FQgD+S/AaafeiZut8k19TdH7XUE= +20251104043530.sql h1:Y0sOQqgnHxd5vKPA4+4fAGGdWCv6duKAYgHnjRJZUIc= +20251104080952.sql h1:hDHzFs/k+NhPYJ64ifbVp+nTJLMCG5MIMJU1zBMP0V0= From 66ee63a9b65e6b71554d81e63086c2d1f1c82d13 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 4 Nov 2025 15:29:57 +0700 Subject: [PATCH 09/12] add therapy protocol --- internal/domain/main-entities/chemo/dto.go | 2 + .../main-entities/therapy-protocol/dto.go | 107 ++++++ .../interface/main-handler/chemo/handler.go | 5 + .../interface/main-handler/main-handler.go | 2 + .../main-handler/therapy-protocol/handler.go | 73 +++++ internal/use-case/main-use-case/chemo/case.go | 2 + .../main-use-case/therapy-protocol/case.go | 310 ++++++++++++++++++ .../main-use-case/therapy-protocol/helper.go | 34 ++ .../main-use-case/therapy-protocol/lib.go | 145 ++++++++ .../therapy-protocol/middleware-runner.go | 103 ++++++ .../therapy-protocol/middleware.go | 9 + .../main-use-case/therapy-protocol/tycovar.go | 44 +++ 12 files changed, 836 insertions(+) create mode 100644 internal/domain/main-entities/therapy-protocol/dto.go create mode 100644 internal/interface/main-handler/therapy-protocol/handler.go create mode 100644 internal/use-case/main-use-case/therapy-protocol/case.go create mode 100644 internal/use-case/main-use-case/therapy-protocol/helper.go create mode 100644 internal/use-case/main-use-case/therapy-protocol/lib.go create mode 100644 internal/use-case/main-use-case/therapy-protocol/middleware-runner.go create mode 100644 internal/use-case/main-use-case/therapy-protocol/middleware.go create mode 100644 internal/use-case/main-use-case/therapy-protocol/tycovar.go diff --git a/internal/domain/main-entities/chemo/dto.go b/internal/domain/main-entities/chemo/dto.go index 8317b259..653b5409 100644 --- a/internal/domain/main-entities/chemo/dto.go +++ b/internal/domain/main-entities/chemo/dto.go @@ -52,6 +52,8 @@ type DeleteDto struct { type VerifyDto struct { Id uint16 `json:"id"` Status_Code erc.DataVerifiedCode `json:"status_code"` + Bed *string `json:"bed" validate:"required"` + Needs *string `json:"needs" validate:"required"` pa.AuthInfo } diff --git a/internal/domain/main-entities/therapy-protocol/dto.go b/internal/domain/main-entities/therapy-protocol/dto.go new file mode 100644 index 00000000..f7a5f2e4 --- /dev/null +++ b/internal/domain/main-entities/therapy-protocol/dto.go @@ -0,0 +1,107 @@ +package therapy_protocol + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + "simrs-vx/internal/domain/references/common" +) + +type CreateDto struct { + Encounter_Id *uint `json:"encounter_id"` + Doctor_Id *uint `json:"doctor_id"` + Anamnesis *string `json:"anamnesis" validate:"maxLength=2048"` + MedicalDiagnoses *string `json:"medicalDiagnoses"` + FunctionDiagnoses *string `json:"functionDiagnoses"` + Procedures *string `json:"procedures"` + SupportingExams *string `json:"supportingExams" validate:"maxLength=2048"` + Instruction *string `json:"instruction" validate:"maxLength=2048"` + Evaluation *string `json:"evaluation" validate:"maxLength=2048"` + WorkCauseStatus *string `json:"workCauseStatus"` + Frequency *uint `json:"frequency"` + IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_code" validate:"maxLength=10"` + Duration *uint `json:"duration"` + DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code" validate:"maxLength=10"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` + Doctor_Id *uint `json:"doctor-id"` +} + +type ReadDetailDto struct { + Id uint `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty"` + Doctor_Id *uint `json:"doctor_id"` + Doctor *ed.Doctor `json:"doctor,omitempty"` + Anamnesis *string `json:"anamnesis"` + MedicalDiagnoses *string `json:"medicalDiagnoses"` + FunctionDiagnoses *string `json:"functionDiagnoses"` + Procedures *string `json:"procedures"` + SupportingExams *string `json:"supportingExams"` + Instruction *string `json:"instruction"` + Evaluation *string `json:"evaluation"` + WorkCauseStatus *string `json:"workCauseStatus"` + Frequency *uint `json:"frequency"` + IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_code"` + Duration *uint `json:"duration"` + DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code"` +} + +func (d TherapyProtocol) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Encounter: d.Encounter, + Doctor_Id: d.Doctor_Id, + Doctor: d.Doctor, + Anamnesis: d.Anamnesis, + MedicalDiagnoses: d.MedicalDiagnoses, + FunctionDiagnoses: d.FunctionDiagnoses, + Procedures: d.Procedures, + SupportingExams: d.SupportingExams, + Instruction: d.Instruction, + Evaluation: d.Evaluation, + WorkCauseStatus: d.WorkCauseStatus, + Frequency: d.Frequency, + IntervalUnit_Code: d.IntervalUnit_Code, + Duration: d.Duration, + DurationUnit_Code: d.DurationUnit_Code, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []TherapyProtocol) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/interface/main-handler/chemo/handler.go b/internal/interface/main-handler/chemo/handler.go index 90552020..18435479 100644 --- a/internal/interface/main-handler/chemo/handler.go +++ b/internal/interface/main-handler/chemo/handler.go @@ -84,11 +84,16 @@ func (obj myBase) Verify(w http.ResponseWriter, r *http.Request) { } dto := e.VerifyDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) authInfo, err := pa.GetAuthInfo(r) if err != nil { rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) } + dto.AuthInfo = *authInfo dto.Status_Code = erc.DVCVerified res, err := u.Verify(dto) diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index baea92da..554de761 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -94,6 +94,7 @@ import ( specialistposition "simrs-vx/internal/interface/main-handler/specialist-position" subspecialist "simrs-vx/internal/interface/main-handler/subspecialist" subspecialistposition "simrs-vx/internal/interface/main-handler/subspecialist-position" + therapyprotocol "simrs-vx/internal/interface/main-handler/therapy-protocol" unit "simrs-vx/internal/interface/main-handler/unit" unitposition "simrs-vx/internal/interface/main-handler/unit-position" uom "simrs-vx/internal/interface/main-handler/uom" @@ -257,6 +258,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/ambulance-transport-req", ambulancetransportrequest.O) hc.RegCrud(r, "/v1/responsible-doctor-hist", responsibledoctorhist.O) hc.RegCrud(r, "/v1/adm-employee-hist", admemployeehist.O) + hc.RegCrud(r, "/v1/therapy-protocol", therapyprotocol.O) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/interface/main-handler/therapy-protocol/handler.go b/internal/interface/main-handler/therapy-protocol/handler.go new file mode 100644 index 00000000..2d9efc3b --- /dev/null +++ b/internal/interface/main-handler/therapy-protocol/handler.go @@ -0,0 +1,73 @@ +package therapy_protocol + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/therapy-protocol" + u "simrs-vx/internal/use-case/main-use-case/therapy-protocol" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + + sf.UrlQueryParam(&dto, *r.URL) + dto.Id = uint(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/use-case/main-use-case/chemo/case.go b/internal/use-case/main-use-case/chemo/case.go index 541faa04..f7b3c7d3 100644 --- a/internal/use-case/main-use-case/chemo/case.go +++ b/internal/use-case/main-use-case/chemo/case.go @@ -311,6 +311,8 @@ func Verify(input e.VerifyDto) (*d.Data, error) { data.VerifiedAt = pu.GetTimeNow() data.Status_Code = input.Status_Code data.VerifiedBy_User_Id = &input.AuthInfo.User_Id + data.Bed = input.Bed + data.Needs = input.Needs err = tx.Save(&data).Error if err != nil { diff --git a/internal/use-case/main-use-case/therapy-protocol/case.go b/internal/use-case/main-use-case/therapy-protocol/case.go new file mode 100644 index 00000000..93cf05a4 --- /dev/null +++ b/internal/use-case/main-use-case/therapy-protocol/case.go @@ -0,0 +1,310 @@ +package therapy_protocol + +import ( + "strconv" + + "gorm.io/gorm" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + e "simrs-vx/internal/domain/main-entities/therapy-protocol" + + ud "simrs-vx/internal/use-case/main-use-case/doctor" + ue "simrs-vx/internal/use-case/main-use-case/encounter" +) + +const source = "therapy-protocol" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.TherapyProtocol{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + // validate foreign key + if err := validateForeignKey(input); err != nil { + return nil, err + } + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.TherapyProtocol + var dataList []e.TherapyProtocol + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { + return err + } + + if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + "record_currentCount": strconv.Itoa(len(dataList)), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.TherapyProtocol + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { + return err + } + + if data, err = ReadDetailData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.TherapyProtocol + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + // validate foreign key + if err := validateForeignKey(input.CreateDto); 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 + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + 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 + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.TherapyProtocol + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + 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 + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} + +func validateForeignKey(input e.CreateDto) error { + // validate encounter + if input.Encounter_Id != nil { + if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Encounter_Id)}); err != nil { + return err + } + } + + // validate doctor_id + if input.Doctor_Id != nil { + if _, err := ud.ReadDetail(ed.ReadDetailDto{Id: uint16(*input.Doctor_Id)}); err != nil { + return err + } + } + + return nil +} diff --git a/internal/use-case/main-use-case/therapy-protocol/helper.go b/internal/use-case/main-use-case/therapy-protocol/helper.go new file mode 100644 index 00000000..12e48747 --- /dev/null +++ b/internal/use-case/main-use-case/therapy-protocol/helper.go @@ -0,0 +1,34 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package therapy_protocol + +import ( + e "simrs-vx/internal/domain/main-entities/therapy-protocol" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.TherapyProtocol) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Encounter_Id = inputSrc.Encounter_Id + data.Doctor_Id = inputSrc.Doctor_Id + data.Anamnesis = inputSrc.Anamnesis + data.MedicalDiagnoses = inputSrc.MedicalDiagnoses + data.FunctionDiagnoses = inputSrc.FunctionDiagnoses + data.Procedures = inputSrc.Procedures + data.SupportingExams = inputSrc.SupportingExams + data.Instruction = inputSrc.Instruction + data.Evaluation = inputSrc.Evaluation + data.WorkCauseStatus = inputSrc.WorkCauseStatus + data.Frequency = inputSrc.Frequency + data.IntervalUnit_Code = inputSrc.IntervalUnit_Code + data.Duration = inputSrc.Duration + data.DurationUnit_Code = inputSrc.DurationUnit_Code +} diff --git a/internal/use-case/main-use-case/therapy-protocol/lib.go b/internal/use-case/main-use-case/therapy-protocol/lib.go new file mode 100644 index 00000000..550ef327 --- /dev/null +++ b/internal/use-case/main-use-case/therapy-protocol/lib.go @@ -0,0 +1,145 @@ +package therapy_protocol + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/therapy-protocol" + + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.TherapyProtocol, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.TherapyProtocol{} + 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.TherapyProtocol, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.TherapyProtocol{} + 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.TherapyProtocol{}). + 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 errors.Is(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.TherapyProtocol, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.TherapyProtocol{} + + 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.TherapyProtocol, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setData(&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.TherapyProtocol, 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 +} diff --git a/internal/use-case/main-use-case/therapy-protocol/middleware-runner.go b/internal/use-case/main-use-case/therapy-protocol/middleware-runner.go new file mode 100644 index 00000000..4391b63a --- /dev/null +++ b/internal/use-case/main-use-case/therapy-protocol/middleware-runner.go @@ -0,0 +1,103 @@ +package therapy_protocol + +import ( + e "simrs-vx/internal/domain/main-entities/therapy-protocol" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +type middlewareRunner struct { + Event *pl.Event + Tx *gorm.DB + MwType pu.MWType +} + +// NewMiddlewareExecutor creates a new middleware executor +func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { + return &middlewareRunner{ + Event: event, + Tx: tx, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.TherapyProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.TherapyProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.TherapyProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.TherapyProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.TherapyProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) setMwType(mwType pu.MWType) { + me.MwType = mwType +} diff --git a/internal/use-case/main-use-case/therapy-protocol/middleware.go b/internal/use-case/main-use-case/therapy-protocol/middleware.go new file mode 100644 index 00000000..a2f3626f --- /dev/null +++ b/internal/use-case/main-use-case/therapy-protocol/middleware.go @@ -0,0 +1,9 @@ +package therapy_protocol + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/main-use-case/therapy-protocol/tycovar.go b/internal/use-case/main-use-case/therapy-protocol/tycovar.go new file mode 100644 index 00000000..a027e334 --- /dev/null +++ b/internal/use-case/main-use-case/therapy-protocol/tycovar.go @@ -0,0 +1,44 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package therapy_protocol + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/therapy-protocol" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.TherapyProtocol, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.TherapyProtocol, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.TherapyProtocol, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw From c53082fd260c1031191f318c3b9953090db911a5 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 4 Nov 2025 15:43:10 +0700 Subject: [PATCH 10/12] add chemo protocol entity --- .../migrations/20251104084135.sql | 17 +++ cmd/main-migration/migrations/atlas.sum | 141 +++++++++--------- .../main-entities/chemo-protocol/entity.go | 21 +++ .../main-entities/therapy-protocol/entity.go | 4 +- internal/interface/migration/main-entities.go | 2 + 5 files changed, 113 insertions(+), 72 deletions(-) create mode 100644 cmd/main-migration/migrations/20251104084135.sql create mode 100644 internal/domain/main-entities/chemo-protocol/entity.go diff --git a/cmd/main-migration/migrations/20251104084135.sql b/cmd/main-migration/migrations/20251104084135.sql new file mode 100644 index 00000000..8c50d914 --- /dev/null +++ b/cmd/main-migration/migrations/20251104084135.sql @@ -0,0 +1,17 @@ +-- Create "ChemoProtocol" table +CREATE TABLE "public"."ChemoProtocol" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Encounter_Id" bigint NULL, + "Patient_Weight" numeric NULL, + "Patient_Height" numeric NULL, + "Diagnoses" text NULL, + "Duration" bigint NULL, + "DurationUnit_Code" character varying(10) NULL, + "StartDate" timestamptz NULL, + "EndDate" timestamptz NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "fk_ChemoProtocol_Encounter" FOREIGN KEY ("Encounter_Id") REFERENCES "public"."Encounter" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index e0d46a35..40b66b02 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,70 +1,71 @@ -h1:fcpOnigzzVgIt+BimD58z3BfW5JzOXIltdMVGabIh/Q= -20250904105930.sql h1:Vv4vCurl7m7/ZB6TjRpkubHpQ4RYwSUn0QHdzfoGpzY= -20250904141448.sql h1:FYCHH9Os4KkrZMDu/jR8FMP+wLMRW+Mb0PkLU/9BRDg= -20250908062237.sql h1:oanBpKZd+akPu2I/xYhUSbd0G5tAFbXzKLER/Zs8ENI= -20250908062323.sql h1:miNG9COddXkD1jGTgaROMAZ618eT6oiLGiJhXWnQwhE= -20250908073811.sql h1:gOi5cnGG1htlpfizybYmUIT0vYjZTBfXiI0nPSYK2u8= -20250908073839.sql h1:cWNDA4YikjoOteAJuNLFILjQUJPFB6o8Wxreiek4QyI= -20250910055902.sql h1:nxxOGnU0BbH/v3IPgeIOXOwH8d3tKomw7h6FTeMnnBs= -20250915123412.sql h1:mz7SiWfrdf0qE1VTSAAnA/147d6gyp6ry5vZ2bR9SH0= -20250916043819.sql h1:RHXVtmMkB6wfv06HfPyHMBmUfIpFt1xveafNz0kwKnE= -20250917040616.sql h1:MYVDht+akBlzQGKNu2hTTTLPEcH1bxT/Q8MK6WEtuhs= -20250917040751.sql h1:J79YyS2JzWgh5oKXMTgh67uo3gLxKaAsxRiZmSIfjBs= -20250917045138.sql h1:/SM1N4O8X3yxpoJgMEARmS1uOkuLKsTOy4PLsRCOKaQ= -20250917093645.sql h1:PNBTGZ7s10e5b5+Tie8YfVQBN0zKtJ5T34oK1iOUEb4= -20250918073552.sql h1:jG7+g3i8ODYaJdcdZz12v3nbsZ5mB9wG6kWnGyTQIRI= -20250918073742.sql h1:j+rgw7puxE7s+phqPVZHmPk0af3rcaA56Itp86y1suY= -20250918074745.sql h1:rPmP4DXs6OnY4Vp+xO/z9jFpJt/RrJ52SJJjIIxeDvc= -20250923025134.sql h1:2r6pcwnBSU5Y9Czk1OHBoh4yZXiMtEca9X8843fTEX0= -20250924051317.sql h1:iUAk2gsGoEGIPQ0lEEUp8maMSId8emNbP+kP712ABIA= -20250929034321.sql h1:UlpALNVmdi95zOIT0yc6ZyTj9bBjQEIpZhvgrc52M+k= -20250929034428.sql h1:feF+H4nDyHh5bdx48Oiz0A1qecZfi6v3qTTdjzJ45Dg= -20250930025550.sql h1:6XT1kXI3Z3ZIxxmvT7poufZWWCW0QiejZPaFV5wBnjI= -20250930140351.sql h1:HxnmAbh9gCy8jwl/9ycGktiByaUripsjFFvohofY2CY= -20251002085604.sql h1:SjLPi+ZN6qDccK3DaEQCgNsZpPwr5kynWXwbwEsziCI= -20251003032030.sql h1:oHfxNSuqTxU8Zaf9H+h8TuUb1Da03wcyc6hZjDrUQ2s= -20251005060450.sql h1:GIuCcrd4MwjmXpvbzDzPYL18BV3QaZZ+Y2FmEzjvi0E= -20251006041122.sql h1:uNDQbSw0M08lYoMvUNlQtS3iDzpPM1ixT13ugSAoWjE= -20251006045658.sql h1:z+t7yCK54Q4SSiF9kUyUhkYB2F+kzSW9TB7ogxd9wzw= -20251006045928.sql h1:1lATLFLp4BWwGZqAjZdP0Dc6ypNXiYcwjoNkqGa8NFE= -20251007022859.sql h1:HXXwWrkyvzJzJGAt9mGskCRBBV/c1JfPmfjDocmJhQ4= -20251008031337.sql h1:Ln5pCF3Hxa5foHZLcds+z/us2eH6VAhhEj3w0TAGlVs= -20251008031554.sql h1:aB4MUS2lmqG0//4HKUWorcPSpWya0VC4QItvGyskEVI= -20251008052346.sql h1:MI3AZgU5XcwZT2OvvlWAxdRtL0eJ3jjRwt56IY1+pRU= -20251008073620.sql h1:sztWXuSNYwpEraaSapSsYwno75LO5H/N7ob7OJQ8X/A= -20251009042854.sql h1:TnPXj+dCJls3IU//cuqJZymyBzZMKs7ayazfgtAFRxM= -20251009052657.sql h1:leXbs0CP8r5dRilmYyLRk1MICqak3ea1/LWMtFrijqQ= -20251010031743.sql h1:SgHNY/lQ88G2F4nZyMfiOkDntb+gtOR+nEQLqXBTwv4= -20251010070721.sql h1:AnJnhXsMzDvK4AFgYw6B16Kpo/hljrZtcpc9m2VOSHQ= -20251010072711.sql h1:aXPTtNwLcTuw8C/yAxwxvqs0ayEjNzI1uuE0vE3ERa8= -20251013044536.sql h1:7Pq6JcvTpPBYDCW2dz3HdgUwY65HlhEVWy9TiG8iONE= -20251013051438.sql h1:X6t8bkqpUYYokBunSufMQUe5vCg0VyO6dxbm7ngosUc= -20251013081808.sql h1:495pLguXL2Ozh+ycn4UYZgZbn6WbjXNbyZUc3JU8qhI= -20251014060047.sql h1:nCgImMRGHhziiW57O1ofWaXCAPGaCOHN7PldQ3OSmM4= -20251014063537.sql h1:2cLmID79jP6cuQ1YJaWTtghFiM1GtHMC0ZQl30Hpy1M= -20251014063720.sql h1:bzLKKVAjSHgDFoiI/glj7t1ETlSAKx+AlsIAaP0ru2g= -20251015045455.sql h1:S547+UugQhlTRcn1Lm1IfqT5RNPttIWIiD+RTx69YaE= -20251016010845.sql h1:c9DUvxl17pUkf0azdYGM/YDzYxIJkLcfZOcMI4rL+R0= -20251016011023.sql h1:u3ivg83bXgYHBbojbWpemLxPE9Dmmj53B6LXo664jxw= -20251016062912.sql h1:W9n1hWchfYkqNX9LO9uxFxEXAb/iY+Pexjnhmp6PbgI= -20251017060617.sql h1:VU6yZ2+LfHpDZ3+TIH40t3F5YXPCpTppuF9+uSqa4b8= -20251017082207.sql h1:QshZslfedckz7iDpSGmPyY9sP6dy6ckHbs8L1TuXIA4= -20251018032635.sql h1:M1U/9W/F9wlW5YDmVAmHFfUJU7FWFaX0DblpfZcYWrE= -20251018040322.sql h1:Zk/vw0e6AzWFO2ElLOzB+OrSz6k+h1Ynxp0TImAzxwY= -20251019093915.sql h1:3Q0kPiZwJnHn5rAvdh0w1LBdiA7W2xBmZWncoPXb044= -20251020062553.sql h1:mJwC/J8GzPAIXckNMvy1f/Nguk2VVf8roD/Raclhbao= -20251021041042.sql h1:d1BAOGAQhqr+oOwcpAVozUsTh457VSDEk2qQFavGG58= -20251021075552.sql h1:TNChGQ1Zlr/1iQ6qvK4iDTAJpe6L/z/M6e/O0SkQVaM= -20251023044432.sql h1:GA2AdJk2ULyjr6igtu9C/CEi4YUIks8r9jXGGaCvPsk= -20251024034832.sql h1:RXmbEhMkOLK5g1QL6up8iRPcwYfo89oLP26ZHvrUK9o= -20251024074315.sql h1:3GnPQSbuAAfMa8oWDyBjhXqn1j1zunY/w0ydX0IGPrA= -20251025013451.sql h1:5eNrA9LDxA4i5CCP7wSyOgFZ6t6jBWVil+oGzJpkJ2E= -20251025013609.sql h1:+N6EHc1kv3hqf16CUhXe+UITPmoxOPfi4MECLmJXNrc= -20251027075128.sql h1:PQflgsjce/p2ClbybLtdehdPNDcMZ9Lb1vd98xd0K8E= -20251027091406.sql h1:bYXV57GvodCMjg0/ox+XKGIAGhrDlVuJ1wO4foNEKtQ= -20251102002037.sql h1:ZcwULsJU2lI9t5pX7ukmfAHSx4ZxdxLUX6jzLDc2SrU= -20251102091932.sql h1:A6y9j4wAqm4/HfMDxhpy/ChDn3UNRVN99KGgSCX+e18= -20251103081637.sql h1:RFqJNhJItSwJQaMP5IcQ14mL7eQ3EJjGZua9zLWzwMU= -20251104042334.sql h1:BbjLAwImggvI6489FQgD+S/AaafeiZut8k19TdH7XUE= -20251104043530.sql h1:Y0sOQqgnHxd5vKPA4+4fAGGdWCv6duKAYgHnjRJZUIc= -20251104080952.sql h1:hDHzFs/k+NhPYJ64ifbVp+nTJLMCG5MIMJU1zBMP0V0= +h1:drtrRtMhlNYK0c9wV3CUkJvXwWgrD8xGPPJy9wlcvNA= +20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= +20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= +20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= +20250908062323.sql h1:oXl6Z143tOpIl4EfP4B8JNU8LrMvVmHEtCgAfiB4gs8= +20250908073811.sql h1:m2aNXfnGxnLq1+rVWrh4f60q7fhyhV3gEwNu/OIqQlE= +20250908073839.sql h1:cPk54xjLdMs26uY8ZHjNWLuyfAMzV7Zb0/9oJQrsw04= +20250910055902.sql h1:5xwjAV6QbtZT9empTJKfhyAjdknbHzb15B0Ku5dzqtQ= +20250915123412.sql h1:D83xaU2YlDEd21HLup/YQpQ2easMToYCyy/oK6AFgQs= +20250916043819.sql h1:ekoTJsBqQZ8G8n0qJ03d13+eoNoc7sAUEQGA5D/CCxk= +20250917040616.sql h1:zoCnmcXuM7AVv85SmN7RmFglCgJnoDmpRWExH0LAc9Q= +20250917040751.sql h1:J1xyRrh32y1+lezwAyNwPcUQ6ABBSgbvzNLva4SVdQU= +20250917045138.sql h1:jKe1Z0uOLG4SGBYM+S/3P+/zMPztmgoderD5swnMuCg= +20250917093645.sql h1:cNI3Pbz1R3LxvIXLuexafJFCXUXrmuFCgXXJ2sG+FW0= +20250918073552.sql h1:RJ1SvMzP6aeWnoPVD3eVAmIQOkcp6Php8z3QRri6v4g= +20250918073742.sql h1:+cEsnJTJFybe2fR69ZoOiX2R6c6iITl4m6WTZ1hjyzY= +20250918074745.sql h1:2hNVQCXF/dVYXAh+T/7oBFgERGWxzVb2FXJjwkFWGCI= +20250923025134.sql h1:Ykz/qpHiGDXPsCsWTjydQFVSibZP2D+h2fIeb2h2JGA= +20250924051317.sql h1:yQuW6SwJxIOM5fcxeAaie5lSm1oLysU/C2hH2xNCVoQ= +20250929034321.sql h1:101FJ8VH12mrZWlt/X1gvKUGOhoiF8tFbjiapAjnHzg= +20250929034428.sql h1:i+pROD9p+g5dOmmZma6WF/0Hw5g3Ha28NN85iTo1K34= +20250930025550.sql h1:+F+CsCUXD/ql0tHGEow70GhPBX1ZybVn+bh/T4YMh7Y= +20250930140351.sql h1:9AAEG1AnOAH+o0+oHL5G7I8vqlWOhwRlCGyyCpT/y1Q= +20251002085604.sql h1:3xZ68eYp4urXRnvotNH1XvG2mYOSDV/j3zHEZ/txg5E= +20251003032030.sql h1:HB+mQ2lXMNomHDpaRhB/9IwYI9/YiDO5eOJ+nAQH/jw= +20251005060450.sql h1:LbtCE2b+8osM3CvnmQJH1uCPtn+d7WchsslBOz8bL3Q= +20251006041122.sql h1:MlS7f21z06sutnf9dIekt5fuHJr4lgcQ4uCuCXAGsfc= +20251006045658.sql h1:3FmGCPCzjgMPdWDRodZTsx3KVaodd9zB9ilib69aewk= +20251006045928.sql h1:Z5g31PmnzNwk/OKdODcxZGm8fjJQdMFK32Xfnt3bRHg= +20251007022859.sql h1:FO03zEfaNEk/aXwY81d5Lp3MoBB9kPQuXlXJ4BPiSR8= +20251008031337.sql h1:l+sxUAGvcTfj3I6kAFHo+T6AYodC9k9GkR+jaKO2xXc= +20251008031554.sql h1:AqrVfIhSzY3PCy8ZlP5W91wn2iznfIuj5qQfubp6/94= +20251008052346.sql h1:nxnXmooIJ6r1mmzwnw+6efxLfc/k9h2aE6RMptPRons= +20251008073620.sql h1:6YsJp1W4SmQJ1lxpqF27BBlDC1zqhw7Yhc7pLzQTY6M= +20251009042854.sql h1:nkBV+R6j0fg7/JY6wH3eb5Vv0asJLnXmb6lINfT/GLQ= +20251009052657.sql h1:EPvdsib5rzCGPryd10HShGKvFPwM/R5S2lIVwtYxpms= +20251010031743.sql h1:T8IZmx8/btRFKLzTe78MzcBsPJNodnLvB0tby9QkirQ= +20251010070721.sql h1:5NQUk/yOV6sABLCB7swx++YIOyJe6MnU+yt1nRzde5w= +20251010072711.sql h1:ZJNqR2piyu8xJhBvVABSlnGEoKSKae3wuEs+wshPe4k= +20251013044536.sql h1:0Xjw8fNILiT8nnfrJDZgQnPf3dntmIoilbapnih8AE4= +20251013051438.sql h1:lfSuw5mgJnePBJamvhZ81osFIouXeiIEiSZ/evdwo48= +20251013081808.sql h1:ijgjNX08G6GBjA/ks8EKtb7P7Y7Cg7zbhqEOruGnv6M= +20251014060047.sql h1:0jqj49WTtneEIMQDBoo4c095ZGi8sCrA8NnHBrPU6D8= +20251014063537.sql h1:VZLXol0PTsTW21Epg6vBPsztWkDtcxup9F/z88EGgIg= +20251014063720.sql h1:2HVUyCV0ud3BJJDH2GEKZN/+IWLFPCsN1KqhP6csO14= +20251015045455.sql h1:MeLWmMhAOAz8b15Dd7IAQnt6JxjSml02XCXK22C0Lpg= +20251016010845.sql h1:4BncQdDOasRZJkzVJrSJJA7091A9VPNVx/faUCUPhBM= +20251016011023.sql h1:9JB9eFZKURK5RoCVDKR6glSvdJ8NTXrN7K/4q51zkz4= +20251016062912.sql h1:ACNn0fe+EMqUt3hoY+Dr3uqAV/QICBa1+mIW7fUc9Fk= +20251017060617.sql h1:4T3t9ifWrEQTPMSM0XJ98pF7Qdt+UfgtMui17bhrnWI= +20251017082207.sql h1:8vLG1l/saRRMHXkyA4nelJyjaSddhZd6r7R+Uo4JS/c= +20251018032635.sql h1:2xey5gnO3y2XSOrU8MLlIfoylPKbRGDRtHDD07B3MbQ= +20251018040322.sql h1:k/pdNiSoT8zFPqNQ/avOD0vYkNh3BTD64IlHrfVXr7I= +20251019093915.sql h1:hFcQE0y+p5dZiVwePGsRGto9m/q6kJNiUZbVDd5Rnjk= +20251020062553.sql h1:Iw7hulcm5iRQlfW+ygA4iTPxLqkxx6h9vXMXEwUAHKs= +20251021041042.sql h1:wMgSivBV2A0NDcsLmKGIp0kMcVh2IODSG9b4dgzCaOM= +20251021075552.sql h1:8gfSMAglflNO6L0sSzxFNEubYN8/O4thT7OQT+WH+3M= +20251023044432.sql h1:MkvajJs3bfk9+wHvQ43/ccAluJEBARm1gWr1u92ccLA= +20251024034832.sql h1:x3s3VEVYLOSKLAFxJGb2+c1FyTMMvPE+9k4Ew7rKQaI= +20251024074315.sql h1:EjAjelgi5qAfcRq/8vPTlGGYHvAKxNTllm8f0SzZDns= +20251025013451.sql h1:6hnuIiwYiG+6nLhOY/+Yyn+I6ZCFNRZxrJNqBV6HLqE= +20251025013609.sql h1:evPJaTD8WxYRMOJZHkSr7ONLx9PYxT+ankzQt9c/sJ0= +20251027075128.sql h1:/iFQBM1sytjqpyQSOx61q33gnorMgxTiFVSuL6bQqsM= +20251027091406.sql h1:eCZGtUkxAzEAqpC9UsGpP8Df9mS0DEOqSl885LgqpvM= +20251102002037.sql h1:lFJbuoZ2LMQnUNGdcwHVY3Xlfslgzu9t2WByT8yfOZI= +20251102091932.sql h1:rmdhb5m+P+fU8jROBZNyeYgZKuQvucsuljXv4ZVzvks= +20251103081637.sql h1:tf3BcwTeIw+oxMEisKDDfyKnBfalTLs8b0PJA8JWYxY= +20251104042334.sql h1:7PDMWOhmJywolAPKFZ14XaDBeMvcxShaXFN2IemNtzk= +20251104043530.sql h1:qvYVp3ysPf27f1BcoRNCFGovxuVE12lg9d6Xzda6zWU= +20251104080952.sql h1:avghpv1n3yaCDR/TA0X+hgxDGoLBQGu/GJUwj4VT/Ic= +20251104084135.sql h1:Y4coFrHgDXd/DM8ihEy+qMkOSrO8M4SI4shRCJIiBBA= diff --git a/internal/domain/main-entities/chemo-protocol/entity.go b/internal/domain/main-entities/chemo-protocol/entity.go new file mode 100644 index 00000000..e1508e91 --- /dev/null +++ b/internal/domain/main-entities/chemo-protocol/entity.go @@ -0,0 +1,21 @@ +package chemo_protocol + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/encounter" + "simrs-vx/internal/domain/references/common" + "time" +) + +type ChemoProtocol struct { + ecore.Main + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Patient_Weight *float32 `json:"patient_weight"` + Patient_Height *float32 `json:"patient_height"` + Diagnoses *string `json:"diagnoses"` + Duration *uint `json:"duration"` + DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code" gorm:"size:10"` + StartDate *time.Time `json:"startDate"` + EndDate *time.Time `json:"endDate"` +} diff --git a/internal/domain/main-entities/therapy-protocol/entity.go b/internal/domain/main-entities/therapy-protocol/entity.go index 99b5b59d..f5ec318a 100644 --- a/internal/domain/main-entities/therapy-protocol/entity.go +++ b/internal/domain/main-entities/therapy-protocol/entity.go @@ -25,7 +25,7 @@ type TherapyProtocol struct { Evaluation *string `json:"evaluation" gorm:"size:2048"` WorkCauseStatus *string `json:"workCauseStatus" gorm:"size:2048"` Frequency *uint `json:"frequency"` - IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_Code" gorm:"size:10"` + IntervalUnit_Code *common.TimeUnitCode `json:"intervalUnit_code" gorm:"size:10"` Duration *uint `json:"duration"` - DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_Code" gorm:"size:10"` + DurationUnit_Code *common.TimeUnitCode `json:"durationUnit_code" gorm:"size:10"` } diff --git a/internal/interface/migration/main-entities.go b/internal/interface/migration/main-entities.go index fac22ea6..0b3a9970 100644 --- a/internal/interface/migration/main-entities.go +++ b/internal/interface/migration/main-entities.go @@ -7,6 +7,7 @@ import ( ambulatory "simrs-vx/internal/domain/main-entities/ambulatory" appointment "simrs-vx/internal/domain/main-entities/appointment" chemo "simrs-vx/internal/domain/main-entities/chemo" + chemoprotocol "simrs-vx/internal/domain/main-entities/chemo-protocol" consultation "simrs-vx/internal/domain/main-entities/consultation" controlletter "simrs-vx/internal/domain/main-entities/control-letter" counter "simrs-vx/internal/domain/main-entities/counter" @@ -197,5 +198,6 @@ func getMainEntities() []any { &vclaimmember.VclaimMember{}, &controlletter.ControlLetter{}, &rehab.Rehab{}, + &chemoprotocol.ChemoProtocol{}, } } From 28927d4c8b3d5d979ef78edf053d14bc1b60824b Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 4 Nov 2025 16:13:19 +0700 Subject: [PATCH 11/12] add chemo protocol --- .../main-entities/adm-employee-hist/dto.go | 5 +- .../main-entities/chemo-protocol/dto.go | 91 ++++++ .../responsible-doctor-hist/dto.go | 5 +- .../main-handler/chemo-protocol/handler.go | 72 +++++ .../interface/main-handler/main-handler.go | 2 + .../main-use-case/adm-employee-hist/lib.go | 21 +- .../main-use-case/chemo-protocol/case.go | 277 ++++++++++++++++++ .../main-use-case/chemo-protocol/helper.go | 28 ++ .../main-use-case/chemo-protocol/lib.go | 147 ++++++++++ .../chemo-protocol/middleware-runner.go | 103 +++++++ .../chemo-protocol/middleware.go | 9 + .../main-use-case/chemo-protocol/tycovar.go | 44 +++ .../responsible-doctor-hist/lib.go | 21 +- 13 files changed, 787 insertions(+), 38 deletions(-) create mode 100644 internal/domain/main-entities/chemo-protocol/dto.go create mode 100644 internal/interface/main-handler/chemo-protocol/handler.go create mode 100644 internal/use-case/main-use-case/chemo-protocol/case.go create mode 100644 internal/use-case/main-use-case/chemo-protocol/helper.go create mode 100644 internal/use-case/main-use-case/chemo-protocol/lib.go create mode 100644 internal/use-case/main-use-case/chemo-protocol/middleware-runner.go create mode 100644 internal/use-case/main-use-case/chemo-protocol/middleware.go create mode 100644 internal/use-case/main-use-case/chemo-protocol/tycovar.go diff --git a/internal/domain/main-entities/adm-employee-hist/dto.go b/internal/domain/main-entities/adm-employee-hist/dto.go index 9a5d838f..a3500060 100644 --- a/internal/domain/main-entities/adm-employee-hist/dto.go +++ b/internal/domain/main-entities/adm-employee-hist/dto.go @@ -28,9 +28,8 @@ type FilterDto struct { } type ReadDetailDto struct { - Id uint16 `json:"id"` - Code *string `json:"code"` - Includes string `json:"includes"` + Id uint16 `json:"id"` + Includes string `json:"includes"` } type UpdateDto struct { diff --git a/internal/domain/main-entities/chemo-protocol/dto.go b/internal/domain/main-entities/chemo-protocol/dto.go new file mode 100644 index 00000000..04ea0023 --- /dev/null +++ b/internal/domain/main-entities/chemo-protocol/dto.go @@ -0,0 +1,91 @@ +package chemo_protocol + +import ( + // std + "time" + + // internal - domain - references + erc "simrs-vx/internal/domain/references/common" + + // internal - domain - main-entities + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/encounter" +) + +type CreateDto struct { + Encounter_Id *uint `json:"encounter_id"` + Patient_Weight *float32 `json:"patient_weight"` + Patient_Height *float32 `json:"patient_height"` + Diagnoses *string `json:"diagnoses"` + Duration *uint `json:"duration"` + DurationUnit_Code *erc.TimeUnitCode `json:"durationUnit_code"` + StartDate *time.Time `json:"startDate"` + EndDate *time.Time `json:"endDate"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty"` + Patient_Weight *float32 `json:"patient_weight"` + Patient_Height *float32 `json:"patient_height"` + Diagnoses *string `json:"diagnoses"` + Duration *uint `json:"duration"` + DurationUnit_Code *erc.TimeUnitCode `json:"durationUnit_code"` + StartDate *time.Time `json:"startDate"` + EndDate *time.Time `json:"endDate"` +} + +func (d ChemoProtocol) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Encounter: d.Encounter, + Patient_Weight: d.Patient_Weight, + Patient_Height: d.Patient_Height, + Diagnoses: d.Diagnoses, + Duration: d.Duration, + DurationUnit_Code: d.DurationUnit_Code, + StartDate: d.StartDate, + EndDate: d.EndDate, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []ChemoProtocol) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/responsible-doctor-hist/dto.go b/internal/domain/main-entities/responsible-doctor-hist/dto.go index 342faffa..68c8dd21 100644 --- a/internal/domain/main-entities/responsible-doctor-hist/dto.go +++ b/internal/domain/main-entities/responsible-doctor-hist/dto.go @@ -28,9 +28,8 @@ type FilterDto struct { } type ReadDetailDto struct { - Id uint16 `json:"id"` - Code *string `json:"code"` - Includes string `json:"includes"` + Id uint16 `json:"id"` + Includes string `json:"includes"` } type UpdateDto struct { diff --git a/internal/interface/main-handler/chemo-protocol/handler.go b/internal/interface/main-handler/chemo-protocol/handler.go new file mode 100644 index 00000000..60d9db4d --- /dev/null +++ b/internal/interface/main-handler/chemo-protocol/handler.go @@ -0,0 +1,72 @@ +package chemo_protocol + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + e "simrs-vx/internal/domain/main-entities/chemo-protocol" + u "simrs-vx/internal/use-case/main-use-case/chemo-protocol" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + sf.UrlQueryParam(&dto, *r.URL) + + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 554de761..1c703b64 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -9,6 +9,7 @@ import ( ambulancetransportrequest "simrs-vx/internal/interface/main-handler/ambulance-transport-req" auth "simrs-vx/internal/interface/main-handler/authentication" chemo "simrs-vx/internal/interface/main-handler/chemo" + chemoprotocol "simrs-vx/internal/interface/main-handler/chemo-protocol" consultation "simrs-vx/internal/interface/main-handler/consultation" controlletter "simrs-vx/internal/interface/main-handler/control-letter" counter "simrs-vx/internal/interface/main-handler/counter" @@ -259,6 +260,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/responsible-doctor-hist", responsibledoctorhist.O) hc.RegCrud(r, "/v1/adm-employee-hist", admemployeehist.O) hc.RegCrud(r, "/v1/therapy-protocol", therapyprotocol.O) + hc.RegCrud(r, "/v1/chemo-protocol", chemoprotocol.O) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/use-case/main-use-case/adm-employee-hist/lib.go b/internal/use-case/main-use-case/adm-employee-hist/lib.go index dc03b28a..6bdcbe22 100644 --- a/internal/use-case/main-use-case/adm-employee-hist/lib.go +++ b/internal/use-case/main-use-case/adm-employee-hist/lib.go @@ -75,29 +75,18 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e pl.SetLogInfo(event, input, "started", "DBReadDetail") data := e.AdmEmployeeHist{} - var tx, getData *gorm.DB + var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { tx = dg.I } - switch { - case input.Id != 0: - getData = tx.First(&data, input.Id) - case input.Code != nil && *input.Code != "": - getData = tx.Where("code = ?", *input.Code).First(&data) - default: - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "either Id or Code must be provided", - } + if err := tx. + Scopes(gh.Preload(input.Includes)). + First(&data, input.Id). + Error; err != nil { - return nil, pl.SetLogError(event, nil) - } - - if err := getData.Error; err != nil { if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { return nil, processedErr } diff --git a/internal/use-case/main-use-case/chemo-protocol/case.go b/internal/use-case/main-use-case/chemo-protocol/case.go new file mode 100644 index 00000000..4e007d28 --- /dev/null +++ b/internal/use-case/main-use-case/chemo-protocol/case.go @@ -0,0 +1,277 @@ +package chemo_protocol + +import ( + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/chemo-protocol" +) + +const source = "chemo-protocol" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.ChemoProtocol{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.ChemoProtocol + var dataList []e.ChemoProtocol + var metaList *e.MetaDto + var err error + + event := pl.Event{ + Feature: "ReadList", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readList") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadListMiddleware(readListPreMw, &input, data); err != nil { + return err + } + + if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadListMiddleware(readListPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "list-data", + "status": "fetched", + "page_number": strconv.Itoa(metaList.PageNumber), + "page_size": strconv.Itoa(metaList.PageSize), + "record_totalCount": strconv.Itoa(metaList.Count), + "record_currentCount": strconv.Itoa(len(dataList)), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.ChemoProtocol + var err error + + event := pl.Event{ + Feature: "ReadDetail", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "readDetail") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPreMw, &input, data); err != nil { + return err + } + + if data, err = ReadDetailData(input, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunReadDetailMiddleware(readDetailPostMw, &input, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "fetched", + }, + Data: data.ToResponse(), + }, nil +} + +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.ChemoProtocol + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + 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 + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + 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 + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.ChemoProtocol + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + 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 + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/chemo-protocol/helper.go b/internal/use-case/main-use-case/chemo-protocol/helper.go new file mode 100644 index 00000000..885e6fbb --- /dev/null +++ b/internal/use-case/main-use-case/chemo-protocol/helper.go @@ -0,0 +1,28 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package chemo_protocol + +import ( + e "simrs-vx/internal/domain/main-entities/chemo-protocol" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.ChemoProtocol) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Encounter_Id = inputSrc.Encounter_Id + data.Patient_Weight = inputSrc.Patient_Weight + data.Patient_Height = inputSrc.Patient_Height + data.Diagnoses = inputSrc.Diagnoses + data.Duration = inputSrc.Duration + data.DurationUnit_Code = inputSrc.DurationUnit_Code + data.StartDate = inputSrc.StartDate + data.EndDate = inputSrc.EndDate +} diff --git a/internal/use-case/main-use-case/chemo-protocol/lib.go b/internal/use-case/main-use-case/chemo-protocol/lib.go new file mode 100644 index 00000000..22e8808c --- /dev/null +++ b/internal/use-case/main-use-case/chemo-protocol/lib.go @@ -0,0 +1,147 @@ +package chemo_protocol + +import ( + "errors" + + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + + e "simrs-vx/internal/domain/main-entities/chemo-protocol" +) + +func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.ChemoProtocol, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.ChemoProtocol{} + 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.ChemoProtocol, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.ChemoProtocol{} + 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.ChemoProtocol{}). + 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 errors.Is(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.ChemoProtocol, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.ChemoProtocol{} + + 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.ChemoProtocol, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setData(&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.ChemoProtocol, 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 +} diff --git a/internal/use-case/main-use-case/chemo-protocol/middleware-runner.go b/internal/use-case/main-use-case/chemo-protocol/middleware-runner.go new file mode 100644 index 00000000..7c932d7b --- /dev/null +++ b/internal/use-case/main-use-case/chemo-protocol/middleware-runner.go @@ -0,0 +1,103 @@ +package chemo_protocol + +import ( + e "simrs-vx/internal/domain/main-entities/chemo-protocol" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +type middlewareRunner struct { + Event *pl.Event + Tx *gorm.DB + MwType pu.MWType +} + +// NewMiddlewareExecutor creates a new middleware executor +func newMiddlewareRunner(event *pl.Event, tx *gorm.DB) *middlewareRunner { + return &middlewareRunner{ + Event: event, + Tx: tx, + } +} + +// ExecuteCreateMiddleware executes create middleware +func (me *middlewareRunner) RunCreateMiddleware(middlewares []createMw, input *e.CreateDto, data *e.ChemoProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadListMiddleware(middlewares []readListMw, input *e.ReadListDto, data *e.ChemoProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunReadDetailMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.ChemoProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunUpdateMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.ChemoProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) RunDeleteMiddleware(middlewares []readDetailMw, input *e.ReadDetailDto, data *e.ChemoProtocol) error { + for _, middleware := range middlewares { + logData := pu.GetLogData(input, data) + + pl.SetLogInfo(me.Event, logData, "started", middleware.Name) + + if err := middleware.Func(input, data, me.Tx); err != nil { + return pu.HandleMiddlewareError(me.Event, string(me.MwType), middleware.Name, logData, err) + } + + pl.SetLogInfo(me.Event, nil, "complete") + } + return nil +} + +func (me *middlewareRunner) setMwType(mwType pu.MWType) { + me.MwType = mwType +} diff --git a/internal/use-case/main-use-case/chemo-protocol/middleware.go b/internal/use-case/main-use-case/chemo-protocol/middleware.go new file mode 100644 index 00000000..a438fd1c --- /dev/null +++ b/internal/use-case/main-use-case/chemo-protocol/middleware.go @@ -0,0 +1,9 @@ +package chemo_protocol + +// example of middleware +// func init() { +// createPreMw = append(createPreMw, +// CreateMw{Name: "modif-input", Func: pm.ModifInput}, +// CreateMw{Name: "check-data", Func: pm.CheckData}, +// ) +// } diff --git a/internal/use-case/main-use-case/chemo-protocol/tycovar.go b/internal/use-case/main-use-case/chemo-protocol/tycovar.go new file mode 100644 index 00000000..f4822996 --- /dev/null +++ b/internal/use-case/main-use-case/chemo-protocol/tycovar.go @@ -0,0 +1,44 @@ +/* +DESCRIPTION: +A sample, part of the package that contains type, constants, and/or variables. + +In this sample it also provides type and variable regarding the needs of the +middleware to separate from main use-case which has the basic CRUD +functionality. The purpose of this is to make the code more maintainable. +*/ +package chemo_protocol + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/chemo-protocol" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.ChemoProtocol, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.ChemoProtocol, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.ChemoProtocol, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/responsible-doctor-hist/lib.go b/internal/use-case/main-use-case/responsible-doctor-hist/lib.go index d91294fe..3f035d5e 100644 --- a/internal/use-case/main-use-case/responsible-doctor-hist/lib.go +++ b/internal/use-case/main-use-case/responsible-doctor-hist/lib.go @@ -75,29 +75,18 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e pl.SetLogInfo(event, input, "started", "DBReadDetail") data := e.ResponsibleDoctorHist{} - var tx, getData *gorm.DB + var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] } else { tx = dg.I } - switch { - case input.Id != 0: - getData = tx.First(&data, input.Id) - case input.Code != nil && *input.Code != "": - getData = tx.Where("code = ?", *input.Code).First(&data) - default: - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-read-detail-fail", - Detail: "either Id or Code must be provided", - } + if err := tx. + Scopes(gh.Preload(input.Includes)). + First(&data, input.Id). + Error; err != nil { - return nil, pl.SetLogError(event, nil) - } - - if err := getData.Error; err != nil { if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { return nil, processedErr } From 4173b20165db8f1377f9e75023c71201452a351d Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 4 Nov 2025 16:27:47 +0700 Subject: [PATCH 12/12] delete debug --- internal/domain/main-entities/ambulatory/dto.go | 1 + internal/domain/main-entities/encounter/dto.go | 4 ++-- internal/use-case/main-use-case/encounter/helper.go | 2 +- internal/use-case/main-use-case/encounter/lib.go | 1 - 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/domain/main-entities/ambulatory/dto.go b/internal/domain/main-entities/ambulatory/dto.go index 42ece735..f580467a 100644 --- a/internal/domain/main-entities/ambulatory/dto.go +++ b/internal/domain/main-entities/ambulatory/dto.go @@ -50,6 +50,7 @@ type ResponseDto struct { func (d Ambulatory) ToResponse() ResponseDto { resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, Class_Code: d.Class_Code, VisitMode_Code: d.VisitMode_Code, } diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 0d5e1aae..6782b18c 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -48,8 +48,8 @@ type CreateDto struct { Appointment_Id *uint `json:"appointment_id"` RefTypeCode ere.RefTypeCode `json:"refTypeCode"` NewStatus bool `json:"newStatus"` - VisitMode_Code *ere.VisitModeCode `json:"visitMode_code"` // if subClass_Code is rehab - AllocatedVisitCount *int `json:"allocatedVisitCount"` + 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" pa.AuthInfo } diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 4bf7be60..67cef58b 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -641,7 +641,7 @@ func getSoapiEncounterAdm(enc e.Encounter, event *pl.Event) (dataSoapi []es.Crea } } - err = dg.I.Debug(). + err = dg.I. Model(&es.Soapi{}). Joins("JOIN \"Employee\" ON \"Employee\".\"Id\" = \"Soapi\".\"Employee_Id\""). Where("\"Encounter_Id\" = ?", enc.Id). diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index d80fd513..0c51a8d6 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -276,7 +276,6 @@ func verifyAllocatedVisitCount(i e.CreateDto, event *pl.Event) (e.Encounter, boo } err = tx. - Debug(). Model(&e.Encounter{}). Joins("JOIN \"Ambulatory\" ON \"Ambulatory\".\"Encounter_Id\" = \"Encounter\".\"Id\""). Where("\"Patient_Id\" = ?", i.Patient_Id).