From b454f52039301decbdc8da3b4c4d11ade97f5a71 Mon Sep 17 00:00:00 2001 From: vanilia Date: Sat, 6 Dec 2025 04:40:13 +0700 Subject: [PATCH 1/9] on going --- internal/domain/main-entities/chemo/dto.go | 1 + .../main-use-case/chemo-protocol/helper.go | 2 +- internal/use-case/main-use-case/chemo/case.go | 29 ++++++++++++++++--- internal/use-case/main-use-case/chemo/lib.go | 3 ++ 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/internal/domain/main-entities/chemo/dto.go b/internal/domain/main-entities/chemo/dto.go index 602bb711..8fc65ef2 100644 --- a/internal/domain/main-entities/chemo/dto.go +++ b/internal/domain/main-entities/chemo/dto.go @@ -35,6 +35,7 @@ type FilterDto struct { Status_Code *erc.DataVerifiedCode `json:"status-code"` VerifiedBy_User_Id *uint `json:"verifiedBy-user-id"` SrcUnit_Code *string `json:"srcUnit-code"` + Patient_Id *uint `json:"patient-id"` } type ReadDetailDto struct { diff --git a/internal/use-case/main-use-case/chemo-protocol/helper.go b/internal/use-case/main-use-case/chemo-protocol/helper.go index 351bf872..fd70130b 100644 --- a/internal/use-case/main-use-case/chemo-protocol/helper.go +++ b/internal/use-case/main-use-case/chemo-protocol/helper.go @@ -46,7 +46,7 @@ func getChemoEncounterReg(event *pl.Event) (*ec.Chemo, error) { if err := tx.Model(&ec.Chemo{}). Joins(`LEFT JOIN "Encounter" "e" ON "e"."Id" = "Chemo"."Encounter_Id"`). Joins(`LEFT JOIN "Ambulatory" "a" ON "a"."Encounter_Id" = "e"."Id"`). - Where(`"Chemo"."Status_Code" = ? AND a"."Class_Code" = ?`, erc.DVCVerified, ere.ACCReg). + Where(`"Chemo"."Status_Code" = ? AND "a"."Class_Code" = ?`, erc.DVCVerified, ere.ACCReg). Order("\"CreatedAt\" DESC"). First(&data). Error; err != nil { diff --git a/internal/use-case/main-use-case/chemo/case.go b/internal/use-case/main-use-case/chemo/case.go index d466feef..8e456dc1 100644 --- a/internal/use-case/main-use-case/chemo/case.go +++ b/internal/use-case/main-use-case/chemo/case.go @@ -4,10 +4,6 @@ import ( "errors" "strconv" - e "simrs-vx/internal/domain/main-entities/chemo" - - erc "simrs-vx/internal/domain/references/common" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -15,6 +11,13 @@ import ( pu "simrs-vx/pkg/use-case-helper" "gorm.io/gorm" + + erc "simrs-vx/internal/domain/references/common" + + e "simrs-vx/internal/domain/main-entities/chemo" + ee "simrs-vx/internal/domain/main-entities/encounter" + + ue "simrs-vx/internal/use-case/main-use-case/encounter" ) const source = "chemo" @@ -83,6 +86,15 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "readList") + if input.Encounter_Id == nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-validation-fail", + Detail: "Encounter-Id is required", + } + return nil, pl.SetLogError(&event, input) + } + err = dg.I.Transaction(func(tx *gorm.DB) error { mwRunner := newMiddlewareRunner(&event, tx) mwRunner.setMwType(pu.MWTPre) @@ -91,6 +103,15 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { return err } + // Get Encounter + dataEncounter, err := ue.ReadDetailData(ee.ReadDetailDto{Id: *input.Encounter_Id}, &event) + if err != nil { + return err + } + + input.Patient_Id = dataEncounter.Patient_Id + input.Encounter_Id = nil + if dataList, metaList, err = ReadListData(input, &event, tx); err != nil { return err } diff --git a/internal/use-case/main-use-case/chemo/lib.go b/internal/use-case/main-use-case/chemo/lib.go index 4a7accbb..4ebdb321 100644 --- a/internal/use-case/main-use-case/chemo/lib.go +++ b/internal/use-case/main-use-case/chemo/lib.go @@ -49,6 +49,9 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Ch tx = tx. Model(&e.Chemo{}). + Joins(`LEFT JOIN "Encounter" "e" ON "e"."Id" = "Chemo"."Encounter_Id"`). + Joins(`LEFT JOIN "Patient" "p" ON "e"."Patient_Id" = "p"."Id"`). + Where(`"p"."Id" = ?`, input.Patient_Id). Scopes(gh.Preload(input.Includes)). Scopes(gh.Filter(input.FilterDto)). Count(&count). From cb950e63e925c114bb073a031aaae03b72b957ef Mon Sep 17 00:00:00 2001 From: vanilia Date: Sat, 6 Dec 2025 05:06:58 +0700 Subject: [PATCH 2/9] ongoing --- .../main-entities/chemo-protocol/dto.go | 3 +- internal/domain/main-entities/chemo/dto.go | 1 + .../use-case/main-use-case/chemo-plan/case.go | 359 ++++++++++++++++++ .../main-use-case/chemo-plan/helper.go | 104 +++++ .../use-case/main-use-case/chemo-plan/lib.go | 147 +++++++ .../chemo-plan/middleware-runner.go | 103 +++++ .../main-use-case/chemo-plan/middleware.go | 9 + .../main-use-case/chemo-plan/tycovar.go | 44 +++ .../main-use-case/chemo-protocol/case.go | 37 +- .../main-use-case/chemo-protocol/helper.go | 1 + internal/use-case/main-use-case/chemo/case.go | 18 +- internal/use-case/main-use-case/chemo/lib.go | 1 + 12 files changed, 805 insertions(+), 22 deletions(-) create mode 100644 internal/use-case/main-use-case/chemo-plan/case.go create mode 100644 internal/use-case/main-use-case/chemo-plan/helper.go create mode 100644 internal/use-case/main-use-case/chemo-plan/lib.go create mode 100644 internal/use-case/main-use-case/chemo-plan/middleware-runner.go create mode 100644 internal/use-case/main-use-case/chemo-plan/middleware.go create mode 100644 internal/use-case/main-use-case/chemo-plan/tycovar.go diff --git a/internal/domain/main-entities/chemo-protocol/dto.go b/internal/domain/main-entities/chemo-protocol/dto.go index 8964f451..e329cf67 100644 --- a/internal/domain/main-entities/chemo-protocol/dto.go +++ b/internal/domain/main-entities/chemo-protocol/dto.go @@ -16,7 +16,7 @@ import ( type CreateDto struct { Encounter_Id *uint `json:"encounter_id" validate:"required"` - Chemo_Id *uint `json:"chemo_id"` + Chemo_Id *uint `json:"chemo_id" validate:"required"` Patient_Weight *float32 `json:"patient_weight"` Patient_Height *float32 `json:"patient_height"` Diagnoses *string `json:"diagnoses" validate:"required"` @@ -26,6 +26,7 @@ type CreateDto struct { StartDate *time.Time `json:"startDate"` EndDate *time.Time `json:"endDate"` Status_Code erc.DataVerifiedCode `json:"-"` + Patient_Id *uint `json:"patient_id"` } type ReadListDto struct { diff --git a/internal/domain/main-entities/chemo/dto.go b/internal/domain/main-entities/chemo/dto.go index 8fc65ef2..abe82dde 100644 --- a/internal/domain/main-entities/chemo/dto.go +++ b/internal/domain/main-entities/chemo/dto.go @@ -41,6 +41,7 @@ type FilterDto struct { type ReadDetailDto struct { Id uint16 `json:"id"` Includes string `json:"includes"` + FilterDto } type UpdateDto struct { diff --git a/internal/use-case/main-use-case/chemo-plan/case.go b/internal/use-case/main-use-case/chemo-plan/case.go new file mode 100644 index 00000000..7b1ffeb9 --- /dev/null +++ b/internal/use-case/main-use-case/chemo-plan/case.go @@ -0,0 +1,359 @@ +package chemo_plan + +import ( + "errors" + erc "simrs-vx/internal/domain/references/common" + "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 + } + + //// get chemo data from encounter + //dataChemo, err := getChemoEncounter(input.Encounter_Id, &event) + //if err != nil { + // return err + //} + // + //input.Chemo_Id = &dataChemo.Id + + // determine status code chemo-protocol + //switch dataChemo.Encounter.Ambulatory.Class_Code { + //case ere.ACCChemo: + // input.Status_Code = erc.DVCVerified + //default: + // input.Status_Code = erc.DVCNew + //} + + input.Status_Code = erc.DVCNew + + 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 + +} + +func Verify(input e.VerifyDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.ChemoProtocol + var err error + + event := pl.Event{ + Feature: "Verify", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "verify") + + 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 + } + + if data.Status_Code != erc.DVCNew { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-state-mismatch", + Detail: "data is not new", + Raw: errors.New("data is not new"), + } + return pl.SetLogError(&event, input) + } + + data.VerifiedAt = pu.GetTimeNow() + data.Status_Code = erc.DVCVerified + data.VerifiedBy_User_Id = &input.AuthInfo.User_Id + + err = tx.Save(&data).Error + if 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 + }) + + if err != nil { + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "verify", + }, + Data: data.ToResponse(), + }, nil +} diff --git a/internal/use-case/main-use-case/chemo-plan/helper.go b/internal/use-case/main-use-case/chemo-plan/helper.go new file mode 100644 index 00000000..a06f5f20 --- /dev/null +++ b/internal/use-case/main-use-case/chemo-plan/helper.go @@ -0,0 +1,104 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package chemo_plan + +import ( + ec "simrs-vx/internal/domain/main-entities/chemo" + e "simrs-vx/internal/domain/main-entities/chemo-plan" + erc "simrs-vx/internal/domain/references/common" + ere "simrs-vx/internal/domain/references/encounter" + 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" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.ChemoPlan) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Chemo_Id = inputSrc.Chemo_Id + data.Patient_Weight = inputSrc.Patient_Weight + data.Patient_Height = inputSrc.Patient_Height + data.Diagnoses = inputSrc.Diagnoses + data.Interval = inputSrc.Interval + data.Cycle = inputSrc.Cycle + data.Series = inputSrc.Series + data.StartDate = inputSrc.StartDate + data.EndDate = inputSrc.EndDate + data.Status_Code = inputSrc.Status_Code +} + +func getChemoEncounterReg(event *pl.Event) (*ec.Chemo, error) { + pl.SetLogInfo(event, nil, "started", "DBReadChemoEncounterReg") + data := ec.Chemo{} + + var tx = dg.I + + if err := tx.Model(&ec.Chemo{}). + Joins(`LEFT JOIN "Encounter" "e" ON "e"."Id" = "Chemo"."Encounter_Id"`). + Joins(`LEFT JOIN "Ambulatory" "a" ON "a"."Encounter_Id" = "e"."Id"`). + Where(`"Chemo"."Status_Code" = ? AND "a"."Class_Code" = ?`, erc.DVCVerified, ere.ACCReg). + Order("\"CreatedAt\" DESC"). + First(&data). + Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, nil, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func getChemoFromLatestEncounter(event *pl.Event) (*ec.Chemo, error) { + pl.SetLogInfo(event, nil, "started", "DBReadChemoFromLatestEncounter") + data := ec.Chemo{} + + var tx = dg.I + + if err := tx.Model(&ec.Chemo{}). + Joins(`LEFT JOIN "Encounter" "e" ON "e"."Id" = "Chemo"."Encounter_Id"`). + Joins(`LEFT JOIN "Ambulatory" "a" ON "a"."Encounter_Id" = "e"."Id"`). + Where(`"a"."Class_Code" IN (?)"`, []string{string(ere.ACCReg), string(ere.ACCChemo)}). + Where(`"Chemo"."Status_Code" = ?`, erc.DVCVerified). + Scopes(gh.Preload("Encounter.Ambulatory")). + Order("\"Chemo\".\"CreatedAt\" DESC"). + First(&data). + Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, nil, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func getChemoEncounter(encounterId *uint, event *pl.Event) (*ec.Chemo, error) { + pl.SetLogInfo(event, encounterId, "started", "DBReadChemoEncounter") + data := ec.Chemo{} + + var tx = dg.I + + if err := tx.Model(&ec.Chemo{}). + Where(`"Encounter_Id" = ? AND "Status_Code"`, encounterId, erc.DVCVerified). + Scopes(gh.Preload("Encounter.Ambulatory")). + First(&data). + Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, nil, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} diff --git a/internal/use-case/main-use-case/chemo-plan/lib.go b/internal/use-case/main-use-case/chemo-plan/lib.go new file mode 100644 index 00000000..1b05a81b --- /dev/null +++ b/internal/use-case/main-use-case/chemo-plan/lib.go @@ -0,0 +1,147 @@ +package chemo_plan + +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-plan/middleware-runner.go b/internal/use-case/main-use-case/chemo-plan/middleware-runner.go new file mode 100644 index 00000000..406b109d --- /dev/null +++ b/internal/use-case/main-use-case/chemo-plan/middleware-runner.go @@ -0,0 +1,103 @@ +package chemo_plan + +import ( + e "simrs-vx/internal/domain/main-entities/chemo-plan" + 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.ChemoPlan) 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.ChemoPlan) 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.ChemoPlan) 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.ChemoPlan) 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.ChemoPlan) 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-plan/middleware.go b/internal/use-case/main-use-case/chemo-plan/middleware.go new file mode 100644 index 00000000..10e658f5 --- /dev/null +++ b/internal/use-case/main-use-case/chemo-plan/middleware.go @@ -0,0 +1,9 @@ +package chemo_plan + +// 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-plan/tycovar.go b/internal/use-case/main-use-case/chemo-plan/tycovar.go new file mode 100644 index 00000000..c438e2de --- /dev/null +++ b/internal/use-case/main-use-case/chemo-plan/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_plan + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/chemo-plan" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.ChemoPlan, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.ChemoPlan, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.ChemoPlan, 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/chemo-protocol/case.go b/internal/use-case/main-use-case/chemo-protocol/case.go index f434b900..ab481b34 100644 --- a/internal/use-case/main-use-case/chemo-protocol/case.go +++ b/internal/use-case/main-use-case/chemo-protocol/case.go @@ -3,7 +3,6 @@ package chemo_protocol import ( "errors" erc "simrs-vx/internal/domain/references/common" - ere "simrs-vx/internal/domain/references/encounter" "strconv" dg "github.com/karincake/apem/db-gorm-pg" @@ -15,6 +14,9 @@ import ( "gorm.io/gorm" e "simrs-vx/internal/domain/main-entities/chemo-protocol" + ee "simrs-vx/internal/domain/main-entities/encounter" + + ue "simrs-vx/internal/use-case/main-use-case/encounter" ) const source = "chemo-protocol" @@ -38,22 +40,33 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } - // get chemo data from latest encounter - dataChemo, err := getChemoEncounter(input.Encounter_Id, &event) - if err != nil { - return err + if input.Patient_Id == nil { + // get encounter + dataEncounter, err := ue.ReadDetailData(ee.ReadDetailDto{Id: *input.Encounter_Id}, &event) + if err != nil { + return err + } + + input.Patient_Id = dataEncounter.Patient_Id } - input.Chemo_Id = &dataChemo.Id + // get chemo data from encounter + //dataChemo, err := getChemoEncounter(input.Encounter_Id, &event) + //if err != nil { + // return err + //} + + //input.Chemo_Id = &dataChemo.Id // determine status code chemo-protocol - switch dataChemo.Encounter.Ambulatory.Class_Code { - case ere.ACCChemo: - input.Status_Code = erc.DVCVerified - default: - input.Status_Code = erc.DVCNew + //switch dataChemo.Encounter.Ambulatory.Class_Code { + //case ere.ACCChemo: + // input.Status_Code = erc.DVCVerified + //default: + // input.Status_Code = erc.DVCNew + //} - } + input.Status_Code = erc.DVCNew if resData, err := CreateData(input, &event, tx); err != nil { return err diff --git a/internal/use-case/main-use-case/chemo-protocol/helper.go b/internal/use-case/main-use-case/chemo-protocol/helper.go index fd70130b..49711bed 100644 --- a/internal/use-case/main-use-case/chemo-protocol/helper.go +++ b/internal/use-case/main-use-case/chemo-protocol/helper.go @@ -35,6 +35,7 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.ChemoProtocol) { data.StartDate = inputSrc.StartDate data.EndDate = inputSrc.EndDate data.Status_Code = inputSrc.Status_Code + data } func getChemoEncounterReg(event *pl.Event) (*ec.Chemo, error) { diff --git a/internal/use-case/main-use-case/chemo/case.go b/internal/use-case/main-use-case/chemo/case.go index 8e456dc1..1a0851ff 100644 --- a/internal/use-case/main-use-case/chemo/case.go +++ b/internal/use-case/main-use-case/chemo/case.go @@ -86,15 +86,6 @@ func ReadList(input e.ReadListDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "readList") - if input.Encounter_Id == nil { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-validation-fail", - Detail: "Encounter-Id is required", - } - return nil, pl.SetLogError(&event, input) - } - err = dg.I.Transaction(func(tx *gorm.DB) error { mwRunner := newMiddlewareRunner(&event, tx) mwRunner.setMwType(pu.MWTPre) @@ -155,6 +146,15 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "readDetail") + if input.Encounter_Id == nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-validation-fail", + Detail: "Encounter-Id is required", + } + return nil, pl.SetLogError(&event, input) + } + err = dg.I.Transaction(func(tx *gorm.DB) error { mwRunner := newMiddlewareRunner(&event, tx) mwRunner.setMwType(pu.MWTPre) diff --git a/internal/use-case/main-use-case/chemo/lib.go b/internal/use-case/main-use-case/chemo/lib.go index 4ebdb321..4b049c13 100644 --- a/internal/use-case/main-use-case/chemo/lib.go +++ b/internal/use-case/main-use-case/chemo/lib.go @@ -86,6 +86,7 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e if err := tx. Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). First(&data, input.Id).Error; err != nil { if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { return nil, processedErr From befc121563c760cec743cd77e1498e0fd894702b Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 9 Dec 2025 09:04:51 +0700 Subject: [PATCH 3/9] on going chemo therapy --- .../migrations/20251207020537.sql | 22 ++ .../migrations/20251207212015.sql | 99 +++++++ .../migrations/20251207221222.sql | 8 + cmd/main-migration/migrations/atlas.sum | 8 +- .../main-entities/ap-mcu-order/base/dto.go | 23 ++ .../main-entities/ap-mcu-order/base/entity.go | 25 ++ .../domain/main-entities/ap-mcu-order/dto.go | 96 ++++++ .../main-entities/ap-mcu-order/entity.go | 26 ++ .../main-entities/chemo-protocol/dto.go | 55 ++-- internal/domain/main-entities/chemo/dto.go | 8 +- .../cp-mcu-order-item/base/dto.go | 6 + .../cp-mcu-order-item/base/entity.go | 17 ++ .../main-entities/cp-mcu-order-item/dto.go | 80 +++++ .../main-entities/cp-mcu-order-item/entity.go | 17 ++ .../main-entities/cp-mcu-order/base/entity.go | 21 ++ .../domain/main-entities/cp-mcu-order/dto.go | 84 ++++++ .../main-entities/cp-mcu-order/entity.go | 28 ++ .../micro-mcu-order-item/base/dto.go | 6 + .../micro-mcu-order-item/base/entity.go | 17 ++ .../main-entities/micro-mcu-order-item/dto.go | 80 +++++ .../micro-mcu-order-item/entity.go | 18 ++ .../micro-mcu-order/base/entity.go | 18 ++ .../main-entities/micro-mcu-order/dto.go | 86 ++++++ .../main-entities/micro-mcu-order/entity.go | 28 ++ .../radiology-mcu-order-item/base/dto.go | 6 + .../radiology-mcu-order-item/base/entity.go | 17 ++ .../radiology-mcu-order-item/dto.go | 80 +++++ .../radiology-mcu-order-item/entity.go | 17 ++ .../radiology-mcu-order/base/entity.go | 17 ++ .../main-entities/radiology-mcu-order/dto.go | 88 ++++++ .../radiology-mcu-order/entity.go | 28 ++ .../domain/references/clinical/clinical.go | 4 + .../interface/main-handler/chemo/handler.go | 10 +- internal/interface/migration/main-entities.go | 14 + .../main-use-case/ap-mcu-order/case.go | 276 ++++++++++++++++++ .../main-use-case/ap-mcu-order/helper.go | 27 ++ .../main-use-case/ap-mcu-order/lib.go | 140 +++++++++ .../ap-mcu-order/middleware-runner.go | 103 +++++++ .../main-use-case/ap-mcu-order/middleware.go | 9 + .../main-use-case/ap-mcu-order/tycovar.go | 44 +++ .../use-case/main-use-case/chemo-plan/case.go | 10 +- .../main-use-case/chemo-plan/helper.go | 35 +-- .../use-case/main-use-case/chemo-plan/lib.go | 7 +- .../main-use-case/chemo-protocol/case.go | 18 ++ .../main-use-case/chemo-protocol/helper.go | 24 -- .../main-use-case/chemo-protocol/lib.go | 3 +- internal/use-case/main-use-case/soapi/case.go | 31 +- .../use-case/main-use-case/soapi/helper.go | 108 +++++++ 48 files changed, 1897 insertions(+), 95 deletions(-) create mode 100644 cmd/main-migration/migrations/20251207020537.sql create mode 100644 cmd/main-migration/migrations/20251207212015.sql create mode 100644 cmd/main-migration/migrations/20251207221222.sql create mode 100644 internal/domain/main-entities/ap-mcu-order/base/dto.go create mode 100644 internal/domain/main-entities/ap-mcu-order/base/entity.go create mode 100644 internal/domain/main-entities/ap-mcu-order/dto.go create mode 100644 internal/domain/main-entities/ap-mcu-order/entity.go create mode 100644 internal/domain/main-entities/cp-mcu-order-item/base/dto.go create mode 100644 internal/domain/main-entities/cp-mcu-order-item/base/entity.go create mode 100644 internal/domain/main-entities/cp-mcu-order-item/dto.go create mode 100644 internal/domain/main-entities/cp-mcu-order-item/entity.go create mode 100644 internal/domain/main-entities/cp-mcu-order/base/entity.go create mode 100644 internal/domain/main-entities/cp-mcu-order/dto.go create mode 100644 internal/domain/main-entities/cp-mcu-order/entity.go create mode 100644 internal/domain/main-entities/micro-mcu-order-item/base/dto.go create mode 100644 internal/domain/main-entities/micro-mcu-order-item/base/entity.go create mode 100644 internal/domain/main-entities/micro-mcu-order-item/dto.go create mode 100644 internal/domain/main-entities/micro-mcu-order-item/entity.go create mode 100644 internal/domain/main-entities/micro-mcu-order/base/entity.go create mode 100644 internal/domain/main-entities/micro-mcu-order/dto.go create mode 100644 internal/domain/main-entities/micro-mcu-order/entity.go create mode 100644 internal/domain/main-entities/radiology-mcu-order-item/base/dto.go create mode 100644 internal/domain/main-entities/radiology-mcu-order-item/base/entity.go create mode 100644 internal/domain/main-entities/radiology-mcu-order-item/dto.go create mode 100644 internal/domain/main-entities/radiology-mcu-order-item/entity.go create mode 100644 internal/domain/main-entities/radiology-mcu-order/base/entity.go create mode 100644 internal/domain/main-entities/radiology-mcu-order/dto.go create mode 100644 internal/domain/main-entities/radiology-mcu-order/entity.go create mode 100644 internal/use-case/main-use-case/ap-mcu-order/case.go create mode 100644 internal/use-case/main-use-case/ap-mcu-order/helper.go create mode 100644 internal/use-case/main-use-case/ap-mcu-order/lib.go create mode 100644 internal/use-case/main-use-case/ap-mcu-order/middleware-runner.go create mode 100644 internal/use-case/main-use-case/ap-mcu-order/middleware.go create mode 100644 internal/use-case/main-use-case/ap-mcu-order/tycovar.go diff --git a/cmd/main-migration/migrations/20251207020537.sql b/cmd/main-migration/migrations/20251207020537.sql new file mode 100644 index 00000000..14142cb0 --- /dev/null +++ b/cmd/main-migration/migrations/20251207020537.sql @@ -0,0 +1,22 @@ +-- Create "ApMcuOrder" table +CREATE TABLE "public"."ApMcuOrder" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "McuOrder_Id" smallint NULL, + "Substances" text NULL, + "Fictations" text NULL, + "Localization" text NULL, + "ClinicalDiagnoses" text NULL, + "Stadium" text NULL, + "ClinicalNotes" text NULL, + "PastHistory" text NULL, + "CurrentHistory" text NULL, + "PrevApMcu" text NULL, + "PrevApMcuNotes" text NULL, + "SupportingExams" text NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_ApMcuOrder_McuOrder_Id" UNIQUE ("McuOrder_Id"), + CONSTRAINT "fk_ApMcuOrder_McuOrder" FOREIGN KEY ("McuOrder_Id") REFERENCES "public"."McuOrder" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); diff --git a/cmd/main-migration/migrations/20251207212015.sql b/cmd/main-migration/migrations/20251207212015.sql new file mode 100644 index 00000000..c551aa8c --- /dev/null +++ b/cmd/main-migration/migrations/20251207212015.sql @@ -0,0 +1,99 @@ +-- Modify "ApMcuOrder" table +ALTER TABLE "public"."ApMcuOrder" DROP COLUMN "McuOrder_Id", ADD COLUMN "Encounter_Id" bigint NULL, ADD COLUMN "Number" bigint NULL, ADD COLUMN "Doctor_Code" character varying(20) NULL, ADD CONSTRAINT "uni_ApMcuOrder_Doctor_Code" UNIQUE ("Doctor_Code"), ADD CONSTRAINT "fk_ApMcuOrder_Doctor" FOREIGN KEY ("Doctor_Code") REFERENCES "public"."Doctor" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION, ADD CONSTRAINT "fk_ApMcuOrder_Encounter" FOREIGN KEY ("Encounter_Id") REFERENCES "public"."Encounter" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION; +-- Create "CpMcuOrder" table +CREATE TABLE "public"."CpMcuOrder" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Encounter_Id" bigint NULL, + "Number" bigint NULL, + "Doctor_Code" character varying(20) NULL, + "UrgencyLevel_Code" character varying(15) NOT NULL, + "OtherNotes" text NULL, + "ExamScheduleDate" timestamptz NULL, + "Resume" text NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_CpMcuOrder_Doctor_Code" UNIQUE ("Doctor_Code"), + CONSTRAINT "fk_CpMcuOrder_Doctor" FOREIGN KEY ("Doctor_Code") REFERENCES "public"."Doctor" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT "fk_CpMcuOrder_Encounter" FOREIGN KEY ("Encounter_Id") REFERENCES "public"."Encounter" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "CpMcuOrderItem" table +CREATE TABLE "public"."CpMcuOrderItem" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "CpMcuOrder_Id" bigint NULL, + "McuSrc_Code" character varying(20) NULL, + "Note" character varying(1024) NULL, + "Result" text NULL, + "Status_Code" text NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "fk_CpMcuOrderItem_CpMcuOrder" FOREIGN KEY ("CpMcuOrder_Id") REFERENCES "public"."CpMcuOrder" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT "fk_CpMcuOrderItem_McuSrc" FOREIGN KEY ("McuSrc_Code") REFERENCES "public"."McuSrc" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "MicroMcuOrder" table +CREATE TABLE "public"."MicroMcuOrder" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Encounter_Id" bigint NULL, + "Number" bigint NULL, + "Doctor_Code" character varying(20) NULL, + "Stage_Code" character varying(10) NOT NULL, + "AxillaryTemp" numeric NULL, + "OtherNotes" text NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_MicroMcuOrder_Doctor_Code" UNIQUE ("Doctor_Code"), + CONSTRAINT "fk_MicroMcuOrder_Doctor" FOREIGN KEY ("Doctor_Code") REFERENCES "public"."Doctor" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT "fk_MicroMcuOrder_Encounter" FOREIGN KEY ("Encounter_Id") REFERENCES "public"."Encounter" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "MicroMcuOrderItem" table +CREATE TABLE "public"."MicroMcuOrderItem" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "MicroMcuOrder_Id" bigint NULL, + "McuSrc_Code" character varying(20) NULL, + "Note" character varying(1024) NULL, + "Result" text NULL, + "Status_Code" text NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "fk_MicroMcuOrderItem_McuSrc" FOREIGN KEY ("McuSrc_Code") REFERENCES "public"."McuSrc" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT "fk_MicroMcuOrderItem_MicroMcuOrder" FOREIGN KEY ("MicroMcuOrder_Id") REFERENCES "public"."MicroMcuOrder" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "RadiologyMcuOrder" table +CREATE TABLE "public"."RadiologyMcuOrder" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Encounter_Id" bigint NULL, + "Number" bigint NULL, + "Doctor_Code" character varying(20) NULL, + "ClinicalNotes" text NULL, + "OtherNotes" text NULL, + "Resume" text NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "uni_RadiologyMcuOrder_Doctor_Code" UNIQUE ("Doctor_Code"), + CONSTRAINT "fk_RadiologyMcuOrder_Doctor" FOREIGN KEY ("Doctor_Code") REFERENCES "public"."Doctor" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT "fk_RadiologyMcuOrder_Encounter" FOREIGN KEY ("Encounter_Id") REFERENCES "public"."Encounter" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); +-- Create "RadiologyMcuOrderItem" table +CREATE TABLE "public"."RadiologyMcuOrderItem" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "RadiologyMcuOrder_Id" bigint NULL, + "McuSrc_Code" character varying(20) NULL, + "Note" character varying(1024) NULL, + "Result" text NULL, + "Status_Code" text NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "fk_RadiologyMcuOrderItem_McuSrc" FOREIGN KEY ("McuSrc_Code") REFERENCES "public"."McuSrc" ("Code") ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT "fk_RadiologyMcuOrderItem_RadiologyMcuOrder" FOREIGN KEY ("RadiologyMcuOrder_Id") REFERENCES "public"."RadiologyMcuOrder" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); diff --git a/cmd/main-migration/migrations/20251207221222.sql b/cmd/main-migration/migrations/20251207221222.sql new file mode 100644 index 00000000..3c806128 --- /dev/null +++ b/cmd/main-migration/migrations/20251207221222.sql @@ -0,0 +1,8 @@ +-- Modify "ApMcuOrder" table +ALTER TABLE "public"."ApMcuOrder" ADD COLUMN "Status_Code" character varying(10) NOT NULL; +-- Modify "CpMcuOrder" table +ALTER TABLE "public"."CpMcuOrder" ADD COLUMN "Status_Code" character varying(10) NOT NULL; +-- Modify "MicroMcuOrder" table +ALTER TABLE "public"."MicroMcuOrder" ADD COLUMN "Status_Code" character varying(10) NOT NULL; +-- Modify "RadiologyMcuOrder" table +ALTER TABLE "public"."RadiologyMcuOrder" ADD COLUMN "Status_Code" character varying(10) NOT NULL; diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index d1f6820b..3880e01e 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:BsDkwn1AzNdKnOPVb6PkonyiXM7nTdVcjRdw62i/NlQ= +h1:o4jOACV/QY64oiqrbnZvGei0J65px0JBwAXakl8h+Oc= 20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= 20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= 20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= @@ -144,8 +144,10 @@ h1:BsDkwn1AzNdKnOPVb6PkonyiXM7nTdVcjRdw62i/NlQ= 20251202160848.sql h1:Kd2/TziKSMezrt4XgbjQcYvY/Lo9rX0qw7/Lz0/oyKk= 20251202180207.sql h1:IHmSMIO3ia+YV5GULixbdlV1joaUAWtnjQHPd8+HKiM= 20251202231005.sql h1:lua0KKoeBptSfs/6ehZE6Azo6YUlNkOJwGFyb1HQWkY= -20251203205052.sql h1:az/hGpk7u4YKT7gU+UuEw9guqB9AqdckPF1cYavQ3CA= -20251205073858.sql h1:GD692c2jC2mFr6esv3eQmxEpWOeIA860TGctwHAldfM= +20251203205052.sql h1:nk0VK2Uv4bHE3SBfHo/aevVZxtHzr7zAzvmMU8TCCtk= +20251207020537.sql h1:uqqIQaBoZGoOXVQKkalNXwF2awcA+rhjNVevWEzIIok= +20251207212015.sql h1:muRdWqRKfJQFMfezj2B4n0E6bbjIz1GrzHEyhoxy2mk= +20251207221222.sql h1:+mU/25n3hQLRU6MK8B+Uwzl9YBRezX3xWDtIKnweN78= 20251205073858.sql h1:Ep03jQ9hJAJILDJfKVcmi3Z6fqmTHSq9gKQtwf070PE= 20251205211957.sql h1:zRHcvLY+nfLdUyMcfpgI8vYQcPzJUWbfjkc4cK0QGHU= 20251205214433.sql h1:tgHB/fNy9XwAOWIqNI6GCqGXeJgSuZs2aHiCEj3BHZA= diff --git a/internal/domain/main-entities/ap-mcu-order/base/dto.go b/internal/domain/main-entities/ap-mcu-order/base/dto.go new file mode 100644 index 00000000..447bc771 --- /dev/null +++ b/internal/domain/main-entities/ap-mcu-order/base/dto.go @@ -0,0 +1,23 @@ +package apmcuorder + +type CreateDto struct { + Encounter_Id uint `json:"encounter_id"` + Number uint64 `json:"number"` + Doctor_Code string `json:"-"` + Substances string `json:"substances"` + Fictations string `json:"fictations"` + Localization string `json:"localization"` + ClinicalDiagnoses string `json:"clinicalDiagnoses"` + Stadium string `json:"stadium"` + ClinicalNotes string `json:"clinicalNotes"` + CurrentHistory string `json:"currentHistory"` + PastHistory string `json:"pastHistory"` + PrevApMcu string `json:"prevApMcu"` + PrevApMcuNotes string `json:"prevApMcuNotes"` + SupportingExams string `json:"supportingExams"` +} + +type UpdateDto struct { + Id uint64 `json:"id"` + CreateDto +} diff --git a/internal/domain/main-entities/ap-mcu-order/base/entity.go b/internal/domain/main-entities/ap-mcu-order/base/entity.go new file mode 100644 index 00000000..d098fcfd --- /dev/null +++ b/internal/domain/main-entities/ap-mcu-order/base/entity.go @@ -0,0 +1,25 @@ +package apmcuorder + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + erc "simrs-vx/internal/domain/references/common" +) + +type ApMcuOrder struct { + ecore.BigMain + Encounter_Id uint `json:"encounter_id"` + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code" gorm:"unique;size:20"` + Substances string `json:"substances"` + Fictations string `json:"fictations"` + Localization string `json:"localization"` + ClinicalDiagnoses string `json:"clinicalDiagnoses"` + Stadium string `json:"stadium"` + ClinicalNotes *string `json:"clinicalNotes"` + CurrentHistory string `json:"currentHistory"` + PastHistory string `json:"pastHistory"` + PrevApMcu *string `json:"prevApMcu"` + PrevApMcuNotes *string `json:"prevApMcuNotes"` + SupportingExams *string `json:"supportingExams"` + Status_Code erc.DataStatusCode `json:"status_code" gorm:"not null;size:10"` +} diff --git a/internal/domain/main-entities/ap-mcu-order/dto.go b/internal/domain/main-entities/ap-mcu-order/dto.go new file mode 100644 index 00000000..80b0b447 --- /dev/null +++ b/internal/domain/main-entities/ap-mcu-order/dto.go @@ -0,0 +1,96 @@ +package apmcuorder + +import ( + la "simrs-vx/internal/lib/auth" + + ecore "simrs-vx/internal/domain/base-entities/core" + eamob "simrs-vx/internal/domain/main-entities/ap-mcu-order/base" + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" +) + +type CreateDto struct { + eamob.CreateDto + la.AuthInfo +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + McuOrder_Id string `json:"mcu-order-id"` +} + +type ReadDetailDto struct { + Id uint64 `json:"id"` +} + +type UpdateDto struct { + Id uint64 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint64 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.BigMain + Encounter_Id uint `json:"encounter_id"` + Encounter *ee.Encounter + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code"` + Doctor *ed.Doctor + Substances string `json:"substances"` + Fictations string `json:"fictations"` + Localization string `json:"localization"` + ClinicalDiagnoses string `json:"clinicalDiagnoses"` + Stadium string `json:"stadium"` + ClinicalNotes *string `json:"clinicalNotes"` + PastHistory string `json:"pastHistory"` + CurrentHistory string `json:"currentHistory"` + PrevApMcu *string `json:"prevApMcu"` + PrevApMcuNotes *string `json:"prevApMcuNotes"` + SupportingExams *string `json:"supportingExams"` +} + +func (d ApMcuOrder) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Encounter: d.Encounter, + Number: d.Number, + Doctor_Code: d.Doctor_Code, + Doctor: d.Doctor, + Substances: d.Substances, + Fictations: d.Fictations, + Localization: d.Localization, + ClinicalDiagnoses: d.ClinicalDiagnoses, + Stadium: d.Stadium, + ClinicalNotes: d.ClinicalNotes, + PastHistory: d.PastHistory, + CurrentHistory: d.CurrentHistory, + PrevApMcu: d.PrevApMcu, + PrevApMcuNotes: d.PrevApMcuNotes, + SupportingExams: d.SupportingExams, + } + resp.BigMain = d.BigMain + return resp +} + +func ToResponseList(data []ApMcuOrder) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/ap-mcu-order/entity.go b/internal/domain/main-entities/ap-mcu-order/entity.go new file mode 100644 index 00000000..361d0948 --- /dev/null +++ b/internal/domain/main-entities/ap-mcu-order/entity.go @@ -0,0 +1,26 @@ +package apmcuorder + +import ( + eamob "simrs-vx/internal/domain/main-entities/ap-mcu-order/base" + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + erc "simrs-vx/internal/domain/references/common" +) + +type ApMcuOrder struct { + eamob.ApMcuOrder + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` +} + +func (d ApMcuOrder) IsNotNew() bool { + return d.Status_Code != erc.DSCNew +} + +func (d ApMcuOrder) IsCompleted() bool { + return d.Status_Code == erc.DSCDone +} + +func (d ApMcuOrder) IsSameDoctor(doctor_code *string) bool { + return d.Doctor_Code == *doctor_code +} diff --git a/internal/domain/main-entities/chemo-protocol/dto.go b/internal/domain/main-entities/chemo-protocol/dto.go index e485770a..7156b846 100644 --- a/internal/domain/main-entities/chemo-protocol/dto.go +++ b/internal/domain/main-entities/chemo-protocol/dto.go @@ -1,6 +1,7 @@ package chemo_protocol import ( + eus "simrs-vx/internal/domain/main-entities/user" pa "simrs-vx/internal/lib/auth" // std "time" @@ -73,32 +74,42 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Chemo_Id *uint `json:"chemo_id"` - Chemo *ec.Chemo `json:"chemo,omitempty"` - Patient_Weight *float32 `json:"patient_weight"` - Patient_Height *float32 `json:"patient_height"` - Diagnoses *string `json:"diagnoses"` - Interval *uint `json:"interval"` - Cycle *uint `json:"cycle"` - Series *uint16 `json:"series"` - StartDate *time.Time `json:"startDate"` - EndDate *time.Time `json:"endDate"` - Status_Code erc.DataVerifiedCode `json:"status_code"` + Chemo_Id *uint `json:"chemo_id"` + Chemo *ec.Chemo `json:"chemo,omitempty"` + Patient_Weight *float32 `json:"patient_weight"` + Patient_Height *float32 `json:"patient_height"` + Diagnoses *string `json:"diagnoses"` + Interval *uint `json:"interval"` + Cycle *uint `json:"cycle"` + Series *uint16 `json:"series"` + StartDate *time.Time `json:"startDate"` + EndDate *time.Time `json:"endDate"` + Status_Code erc.DataVerifiedCode `json:"status_code"` + VerifiedAt *time.Time `json:"verifiedAt"` + VerifiedBy_User_Id *uint `json:"verifiedBy_user_id"` + VerifiedBy *eus.User `json:"verifiedBy,omitempty"` + ChemoPlans *[]ep.ChemoPlan `json:"chemoPlans,omitempty"` + Patient_Id *uint `json:"patient_id"` } func (d ChemoProtocol) ToResponse() ResponseDto { resp := ResponseDto{ - Chemo_Id: d.Chemo_Id, - Chemo: d.Chemo, - Patient_Weight: d.Patient_Weight, - Patient_Height: d.Patient_Height, - Diagnoses: d.Diagnoses, - Interval: d.Interval, - Cycle: d.Cycle, - Series: d.Series, - StartDate: d.StartDate, - EndDate: d.EndDate, - Status_Code: d.Status_Code, + Chemo_Id: d.Chemo_Id, + Chemo: d.Chemo, + Patient_Weight: d.Patient_Weight, + Patient_Height: d.Patient_Height, + Diagnoses: d.Diagnoses, + Interval: d.Interval, + Cycle: d.Cycle, + Series: d.Series, + StartDate: d.StartDate, + EndDate: d.EndDate, + Status_Code: d.Status_Code, + VerifiedAt: d.VerifiedAt, + VerifiedBy_User_Id: d.VerifiedBy_User_Id, + VerifiedBy: d.VerifiedBy, + ChemoPlans: d.ChemoPlans, + Patient_Id: d.Patient_Id, } resp.Main = d.Main return resp diff --git a/internal/domain/main-entities/chemo/dto.go b/internal/domain/main-entities/chemo/dto.go index abe82dde..695c1ca7 100644 --- a/internal/domain/main-entities/chemo/dto.go +++ b/internal/domain/main-entities/chemo/dto.go @@ -39,22 +39,22 @@ type FilterDto struct { } type ReadDetailDto struct { - Id uint16 `json:"id"` + Id uint `json:"id"` Includes string `json:"includes"` FilterDto } type UpdateDto struct { - Id uint16 `json:"id"` + Id uint `json:"id"` CreateDto } type DeleteDto struct { - Id uint16 `json:"id"` + Id uint `json:"id"` } type VerifyDto struct { - Id uint16 `json:"id"` + Id uint `json:"id"` Status_Code erc.DataVerifiedCode `json:"status_code"` Bed *string `json:"bed"` Needs *string `json:"needs"` diff --git a/internal/domain/main-entities/cp-mcu-order-item/base/dto.go b/internal/domain/main-entities/cp-mcu-order-item/base/dto.go new file mode 100644 index 00000000..2510f9a0 --- /dev/null +++ b/internal/domain/main-entities/cp-mcu-order-item/base/dto.go @@ -0,0 +1,6 @@ +package cpmcuorderitem + +type SubCreateDto struct { + McuSrc_Code string `json:"mcuSrc_code" validate:"required"` + Note string `json:"note"` +} diff --git a/internal/domain/main-entities/cp-mcu-order-item/base/entity.go b/internal/domain/main-entities/cp-mcu-order-item/base/entity.go new file mode 100644 index 00000000..5abb8494 --- /dev/null +++ b/internal/domain/main-entities/cp-mcu-order-item/base/entity.go @@ -0,0 +1,17 @@ +package cpmcuorderitem + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ems "simrs-vx/internal/domain/main-entities/mcu-src" + erc "simrs-vx/internal/domain/references/common" +) + +type CpMcuOrderItem struct { + ecore.BigMain // adjust this according to the needs + CpMcuOrder_Id uint64 `json:"cpMcuOrder_id" gorm:"uniqueIndex:idx_order_src"` + McuSrc_Code string `json:"mcuSrc_code" gorm:"uniqueIndex:idx_order_src"` + McuSrc *ems.McuSrc `json:"mcuSrc,omitempty" gorm:"foreignKey:McuSrc_Code;references:Code"` + Note *string `json:"note" gorm:"size:1024"` + Result *string `json:"result"` + Status_Code erc.DataStatusCode `json:"status_code"` +} diff --git a/internal/domain/main-entities/cp-mcu-order-item/dto.go b/internal/domain/main-entities/cp-mcu-order-item/dto.go new file mode 100644 index 00000000..a0979186 --- /dev/null +++ b/internal/domain/main-entities/cp-mcu-order-item/dto.go @@ -0,0 +1,80 @@ +package cpmcuorderitem + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + emro "simrs-vx/internal/domain/main-entities/cp-mcu-order" + ems "simrs-vx/internal/domain/main-entities/mcu-src" + erc "simrs-vx/internal/domain/references/common" + "time" +) + +type CreateDto struct { + CpMcuOrder_Id uint64 `json:"cpMcuOrder_id"` + McuSrc_Code string `json:"mcuSrc_code"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + CpMcuOrder_Id *uint64 `json:"cp-mcu-order-id"` + McuSrc_Code *string `json:"mcu-src-code"` + Status_Code erc.DataStatusCode `json:"status-code"` +} +type ReadDetailDto struct { + Id uint `json:"id"` +} + +type UpdateDto struct { + Id uint `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint `json:"id"` +} + +type SetScheduleDto struct { + Id uint `json:"id"` + ExaminationDate *time.Time `json:"examinationDate"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.BigMain + CpMcuOrder_Id uint64 `json:"cpMcuOrder_id"` + CpMcuOrder *emro.CpMcuOrder `json:"cpMcuOrder,omitempty"` + McuSrc_Code string `json:"mcuSrc_code"` + McuSrc *ems.McuSrc `json:"mcuSrc,omitempty"` + Result *string `json:"result"` + Status_Code erc.DataStatusCode `json:"status_code"` +} + +func (d CpMcuOrderItem) ToResponse() ResponseDto { + resp := ResponseDto{ + CpMcuOrder_Id: d.CpMcuOrder_Id, + CpMcuOrder: d.CpMcuOrder, + McuSrc_Code: d.McuSrc_Code, + McuSrc: d.McuSrc, + Result: d.Result, + Status_Code: d.Status_Code, + } + resp.BigMain = d.BigMain + return resp +} + +func ToResponseList(data []CpMcuOrderItem) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/cp-mcu-order-item/entity.go b/internal/domain/main-entities/cp-mcu-order-item/entity.go new file mode 100644 index 00000000..5858ab58 --- /dev/null +++ b/internal/domain/main-entities/cp-mcu-order-item/entity.go @@ -0,0 +1,17 @@ +package cpmcuorderitem + +import ( + emo "simrs-vx/internal/domain/main-entities/cp-mcu-order" + emoib "simrs-vx/internal/domain/main-entities/cp-mcu-order-item/base" + + erc "simrs-vx/internal/domain/references/common" +) + +type CpMcuOrderItem struct { + emoib.CpMcuOrderItem + CpMcuOrder *emo.CpMcuOrder `json:"cpMcuOrder,omitempty" gorm:"foreignKey:CpMcuOrder_Id;references:Id"` +} + +func (d CpMcuOrderItem) IsCompleted() bool { + return d.Status_Code == erc.DSCDone +} diff --git a/internal/domain/main-entities/cp-mcu-order/base/entity.go b/internal/domain/main-entities/cp-mcu-order/base/entity.go new file mode 100644 index 00000000..e9531e22 --- /dev/null +++ b/internal/domain/main-entities/cp-mcu-order/base/entity.go @@ -0,0 +1,21 @@ +package cpmcuorder + +import ( + "time" + + ecore "simrs-vx/internal/domain/base-entities/core" + ercl "simrs-vx/internal/domain/references/clinical" + erc "simrs-vx/internal/domain/references/common" +) + +type CpMcuOrder struct { + ecore.BigMain + Encounter_Id uint `json:"encounter_id"` + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code" gorm:"unique;size:20"` + UrgencyLevel_Code ercl.McuUrgencyLevelCode `json:"urgencyLevel_code" gorm:"not null;size:15"` + OtherNotes *string `json:"otherNotes"` + ExamScheduleDate *time.Time `json:"examinationDate"` + Resume *string `json:"resume"` + Status_Code erc.DataStatusCode `json:"status_code" gorm:"not null;size:10"` +} diff --git a/internal/domain/main-entities/cp-mcu-order/dto.go b/internal/domain/main-entities/cp-mcu-order/dto.go new file mode 100644 index 00000000..afccfb20 --- /dev/null +++ b/internal/domain/main-entities/cp-mcu-order/dto.go @@ -0,0 +1,84 @@ +package cpmcuorder + +import ( + la "simrs-vx/internal/lib/auth" + + ecore "simrs-vx/internal/domain/base-entities/core" + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" +) + +type CreateDto struct { + Encounter_Id uint `json:"encounter_id" validate:"required"` + Number uint64 `json:"number"` // SHOULD BE AUTOMATIC WITHOUT SYNC + UrgencyLevel_Code string `json:"urgencyLevel_code" validate:"required"` + OtherNotes string `json:"otherNotes"` + la.AuthInfo +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id uint `json:"encounter-id"` + Doctor_Code string `json:"doctor-code"` +} + +type ReadDetailDto struct { + Id uint64 `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint64 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint64 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.BigMain + Encounter_Id uint + Encounter *ee.Encounter + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code"` + Doctor *ed.Doctor + UrgencyLevel_Code string `json:"urgencyLevel_code" gorm:"not null;size:15"` + OtherNotes *string `json:"otherNotes"` + Resume *string `json:"resume"` +} + +func (d CpMcuOrder) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Encounter: d.Encounter, + Number: d.Number, + Doctor_Code: d.Doctor_Code, + Doctor: d.Doctor, + UrgencyLevel_Code: string(d.UrgencyLevel_Code), + OtherNotes: d.OtherNotes, + Resume: d.Resume, + } + resp.BigMain = d.BigMain + return resp +} + +func ToResponseList(data []CpMcuOrder) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/cp-mcu-order/entity.go b/internal/domain/main-entities/cp-mcu-order/entity.go new file mode 100644 index 00000000..08a3d927 --- /dev/null +++ b/internal/domain/main-entities/cp-mcu-order/entity.go @@ -0,0 +1,28 @@ +package cpmcuorder + +import ( + ecmoi "simrs-vx/internal/domain/main-entities/cp-mcu-order-item/base" + eamob "simrs-vx/internal/domain/main-entities/cp-mcu-order/base" + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + erc "simrs-vx/internal/domain/references/common" +) + +type CpMcuOrder struct { + eamob.CpMcuOrder + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` + Items []*ecmoi.CpMcuOrderItem `json:"items" gorm:"foreignKey:CpMcuOrder_Id;references:Id"` +} + +func (d CpMcuOrder) IsNotNew() bool { + return d.Status_Code != erc.DSCNew +} + +func (d CpMcuOrder) IsCompleted() bool { + return d.Status_Code == erc.DSCDone +} + +func (d CpMcuOrder) IsSameDoctor(doctor_code *string) bool { + return d.Doctor_Code == *doctor_code +} diff --git a/internal/domain/main-entities/micro-mcu-order-item/base/dto.go b/internal/domain/main-entities/micro-mcu-order-item/base/dto.go new file mode 100644 index 00000000..56827131 --- /dev/null +++ b/internal/domain/main-entities/micro-mcu-order-item/base/dto.go @@ -0,0 +1,6 @@ +package micromcuorderitem + +type SubCreateDto struct { + McuSrc_Code string `json:"mcuSrc_code" validate:"required"` + Note string `json:"note"` +} diff --git a/internal/domain/main-entities/micro-mcu-order-item/base/entity.go b/internal/domain/main-entities/micro-mcu-order-item/base/entity.go new file mode 100644 index 00000000..c9d91561 --- /dev/null +++ b/internal/domain/main-entities/micro-mcu-order-item/base/entity.go @@ -0,0 +1,17 @@ +package micromcuorderitem + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ems "simrs-vx/internal/domain/main-entities/mcu-src" + erc "simrs-vx/internal/domain/references/common" +) + +type MicroMcuOrderItem struct { + ecore.BigMain // adjust this according to the needs + MicroMcuOrder_Id uint64 `json:"microMcuOrder_id" gorm:"uniqueIndex:idx_order_src"` + McuSrc_Code string `json:"mcuSrc_code" gorm:"uniqueIndex:idx_order_src"` + McuSrc *ems.McuSrc `json:"mcuSrc,omitempty" gorm:"foreignKey:McuSrc_Code;references:Code"` + Note *string `json:"note" gorm:"size:1024"` + Result *string `json:"result"` + Status_Code erc.DataStatusCode `json:"status_code"` +} diff --git a/internal/domain/main-entities/micro-mcu-order-item/dto.go b/internal/domain/main-entities/micro-mcu-order-item/dto.go new file mode 100644 index 00000000..1d2ceaeb --- /dev/null +++ b/internal/domain/main-entities/micro-mcu-order-item/dto.go @@ -0,0 +1,80 @@ +package micromcuorderitem + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ems "simrs-vx/internal/domain/main-entities/mcu-src" + emro "simrs-vx/internal/domain/main-entities/micro-mcu-order" + erc "simrs-vx/internal/domain/references/common" + "time" +) + +type CreateDto struct { + MicroMcuOrder_Id uint64 `json:"microMcuOrder_id"` + McuSrc_Code string `json:"mcuSrc_code"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + MicroMcuOrder_Id *uint64 `json:"micro-mcu-order-id"` + McuSrc_Code *string `json:"mcu-src-code"` + Status_Code erc.DataStatusCode `json:"status-code"` +} +type ReadDetailDto struct { + Id uint `json:"id"` +} + +type UpdateDto struct { + Id uint `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint `json:"id"` +} + +type SetScheduleDto struct { + Id uint `json:"id"` + ExaminationDate *time.Time `json:"examinationDate"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.BigMain + MicroMcuOrder_Id uint64 `json:"microMcuOrder_id"` + MicroMcuOrder *emro.MicroMcuOrder `json:"microMcuOrder,omitempty"` + McuSrc_Code string `json:"mcuSrc_code"` + McuSrc *ems.McuSrc `json:"mcuSrc,omitempty"` + Result *string `json:"result"` + Status_Code erc.DataStatusCode `json:"status_code"` +} + +func (d MicroMcuOrderItem) ToResponse() ResponseDto { + resp := ResponseDto{ + MicroMcuOrder_Id: d.MicroMcuOrder_Id, + MicroMcuOrder: d.MicroMcuOrder, + McuSrc_Code: d.McuSrc_Code, + McuSrc: d.McuSrc, + Result: d.Result, + Status_Code: d.Status_Code, + } + resp.BigMain = d.BigMain + return resp +} + +func ToResponseList(data []MicroMcuOrderItem) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/micro-mcu-order-item/entity.go b/internal/domain/main-entities/micro-mcu-order-item/entity.go new file mode 100644 index 00000000..64d701f7 --- /dev/null +++ b/internal/domain/main-entities/micro-mcu-order-item/entity.go @@ -0,0 +1,18 @@ +package micromcuorderitem + +import ( + ems "simrs-vx/internal/domain/main-entities/mcu-src" + emo "simrs-vx/internal/domain/main-entities/micro-mcu-order" + emoib "simrs-vx/internal/domain/main-entities/micro-mcu-order-item/base" + erc "simrs-vx/internal/domain/references/common" +) + +type MicroMcuOrderItem struct { + emoib.MicroMcuOrderItem + MicroMcuOrder *emo.MicroMcuOrder `json:"microMcuOrder,omitempty" gorm:"foreignKey:MicroMcuOrder_Id;references:Id"` + McuSrc *ems.McuSrc `json:"mcuSrc,omitempty" gorm:"foreignKey:McuSrc_Code;references:Code"` +} + +func (d MicroMcuOrderItem) IsCompleted() bool { + return d.Status_Code == erc.DSCDone +} diff --git a/internal/domain/main-entities/micro-mcu-order/base/entity.go b/internal/domain/main-entities/micro-mcu-order/base/entity.go new file mode 100644 index 00000000..3e611d81 --- /dev/null +++ b/internal/domain/main-entities/micro-mcu-order/base/entity.go @@ -0,0 +1,18 @@ +package micromcuorder + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ercl "simrs-vx/internal/domain/references/clinical" + erc "simrs-vx/internal/domain/references/common" +) + +type MicroMcuOrder struct { + ecore.BigMain + Encounter_Id uint `json:"encounter_id"` + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code" gorm:"unique;size:20"` + Stage_Code ercl.McuOrderStageCode `json:"stage_code" gorm:"not null;size:10"` + AxillaryTemp float64 `json:"axillaryTemp"` + OtherNotes *string `json:"otherNotes"` + Status_Code erc.DataStatusCode `json:"status_code" gorm:"not null;size:10"` +} diff --git a/internal/domain/main-entities/micro-mcu-order/dto.go b/internal/domain/main-entities/micro-mcu-order/dto.go new file mode 100644 index 00000000..008f219f --- /dev/null +++ b/internal/domain/main-entities/micro-mcu-order/dto.go @@ -0,0 +1,86 @@ +package micromcuorder + +import ( + la "simrs-vx/internal/lib/auth" + + ecore "simrs-vx/internal/domain/base-entities/core" + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + ercl "simrs-vx/internal/domain/references/clinical" +) + +type CreateDto struct { + Encounter_Id uint `json:"encounter_id" validate:"required"` + Number uint64 `json:"number"` // SHOULD BE AUTOMATIC WITHOUT SYNC + OrderStage_Code string `json:"orderStage_code" gorm:"not null;size:10"` + AxillaryTemp float64 `json:"axillaryTemp"` + OtherNotes string `json:"otherNotes"` + la.AuthInfo +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id uint `json:"encounter-id"` + Doctor_Code string `json:"doctor-code"` +} + +type ReadDetailDto struct { + Id uint64 `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint64 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint64 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.BigMain + Encounter_Id uint + Encounter *ee.Encounter + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code"` + Doctor *ed.Doctor + Stage_Code ercl.McuOrderStageCode `json:"stage_code" gorm:"not null;size:10"` + AxillaryTemp float64 `json:"axillaryTemp"` + OtherNotes *string `json:"otherNotes"` +} + +func (d MicroMcuOrder) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Encounter: d.Encounter, + Number: d.Number, + Doctor_Code: d.Doctor_Code, + Doctor: d.Doctor, + Stage_Code: d.Stage_Code, + AxillaryTemp: d.AxillaryTemp, + OtherNotes: d.OtherNotes, + } + resp.BigMain = d.BigMain + return resp +} + +func ToResponseList(data []MicroMcuOrder) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/micro-mcu-order/entity.go b/internal/domain/main-entities/micro-mcu-order/entity.go new file mode 100644 index 00000000..57550c84 --- /dev/null +++ b/internal/domain/main-entities/micro-mcu-order/entity.go @@ -0,0 +1,28 @@ +package micromcuorder + +import ( + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + emmoi "simrs-vx/internal/domain/main-entities/micro-mcu-order-item/base" + eamob "simrs-vx/internal/domain/main-entities/micro-mcu-order/base" + erc "simrs-vx/internal/domain/references/common" +) + +type MicroMcuOrder struct { + eamob.MicroMcuOrder + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` + Items []*emmoi.MicroMcuOrderItem `json:"items" gorm:"foreignKey:MicroMcuOrder_Id;references:Id"` +} + +func (d MicroMcuOrder) IsNotNew() bool { + return d.Status_Code != erc.DSCNew +} + +func (d MicroMcuOrder) IsCompleted() bool { + return d.Status_Code == erc.DSCDone +} + +func (d MicroMcuOrder) IsSameDoctor(doctor_code *string) bool { + return d.Doctor_Code == *doctor_code +} diff --git a/internal/domain/main-entities/radiology-mcu-order-item/base/dto.go b/internal/domain/main-entities/radiology-mcu-order-item/base/dto.go new file mode 100644 index 00000000..8ca6d02f --- /dev/null +++ b/internal/domain/main-entities/radiology-mcu-order-item/base/dto.go @@ -0,0 +1,6 @@ +package radiologymcuorderitem + +type SubCreateDto struct { + McuSrc_Code string `json:"mcuSrc_code" validate:"required"` + Note string `json:"note"` +} diff --git a/internal/domain/main-entities/radiology-mcu-order-item/base/entity.go b/internal/domain/main-entities/radiology-mcu-order-item/base/entity.go new file mode 100644 index 00000000..1f3ff5a5 --- /dev/null +++ b/internal/domain/main-entities/radiology-mcu-order-item/base/entity.go @@ -0,0 +1,17 @@ +package radiologymcuorderitem + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ems "simrs-vx/internal/domain/main-entities/mcu-src" + erc "simrs-vx/internal/domain/references/common" +) + +type RadiologyMcuOrderItem struct { + ecore.BigMain // adjust this according to the needs + RadiologyMcuOrder_Id uint64 `json:"radiologyMcuOrder_id" gorm:"uniqueIndex:idx_order_src"` + McuSrc_Code string `json:"mcuSrc_code" gorm:"uniqueIndex:idx_order_src"` + McuSrc *ems.McuSrc `json:"mcuSrc,omitempty" gorm:"foreignKey:McuSrc_Code;references:Code"` + Note *string `json:"note" gorm:"size:1024"` + Result *string `json:"result"` + Status_Code erc.DataStatusCode `json:"status_code"` +} diff --git a/internal/domain/main-entities/radiology-mcu-order-item/dto.go b/internal/domain/main-entities/radiology-mcu-order-item/dto.go new file mode 100644 index 00000000..99c65df3 --- /dev/null +++ b/internal/domain/main-entities/radiology-mcu-order-item/dto.go @@ -0,0 +1,80 @@ +package radiologymcuorderitem + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ems "simrs-vx/internal/domain/main-entities/mcu-src" + emro "simrs-vx/internal/domain/main-entities/radiology-mcu-order" + erc "simrs-vx/internal/domain/references/common" + "time" +) + +type CreateDto struct { + RadiologyMcuOrder_Id uint64 `json:"radiologyMcuOrder_id"` + McuSrc_Code string `json:"mcuSrc_code"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + RadiologyMcuOrder_Id *uint64 `json:"radiology-mcu-order-id"` + McuSrc_Code *string `json:"mcu-src-code"` + Status_Code erc.DataStatusCode `json:"status-code"` +} +type ReadDetailDto struct { + Id uint `json:"id"` +} + +type UpdateDto struct { + Id uint `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint `json:"id"` +} + +type SetScheduleDto struct { + Id uint `json:"id"` + ExaminationDate *time.Time `json:"examinationDate"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.BigMain + RadiologyMcuOrder_Id uint64 `json:"radiologyMcuOrder_id"` + RadiologyMcuOrder *emro.RadiologyMcuOrder `json:"radiologyMcuOrder,omitempty"` + McuSrc_Code string `json:"mcuSrc_code"` + McuSrc *ems.McuSrc `json:"mcuSrc,omitempty"` + Result *string `json:"result"` + Status_Code erc.DataStatusCode `json:"status_code"` +} + +func (d RadiologyMcuOrderItem) ToResponse() ResponseDto { + resp := ResponseDto{ + RadiologyMcuOrder_Id: d.RadiologyMcuOrder_Id, + RadiologyMcuOrder: d.RadiologyMcuOrder, + McuSrc_Code: d.McuSrc_Code, + McuSrc: d.McuSrc, + Result: d.Result, + Status_Code: d.Status_Code, + } + resp.BigMain = d.BigMain + return resp +} + +func ToResponseList(data []RadiologyMcuOrderItem) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/radiology-mcu-order-item/entity.go b/internal/domain/main-entities/radiology-mcu-order-item/entity.go new file mode 100644 index 00000000..da20a56a --- /dev/null +++ b/internal/domain/main-entities/radiology-mcu-order-item/entity.go @@ -0,0 +1,17 @@ +package radiologymcuorderitem + +import ( + emo "simrs-vx/internal/domain/main-entities/radiology-mcu-order" + emoib "simrs-vx/internal/domain/main-entities/radiology-mcu-order-item/base" + + erc "simrs-vx/internal/domain/references/common" +) + +type RadiologyMcuOrderItem struct { + emoib.RadiologyMcuOrderItem + RadiologyMcuOrder *emo.RadiologyMcuOrder `json:"radiologyMcuOrder,omitempty" gorm:"foreignKey:RadiologyMcuOrder_Id;references:Id"` +} + +func (d RadiologyMcuOrderItem) IsCompleted() bool { + return d.Status_Code == erc.DSCDone +} diff --git a/internal/domain/main-entities/radiology-mcu-order/base/entity.go b/internal/domain/main-entities/radiology-mcu-order/base/entity.go new file mode 100644 index 00000000..6871e689 --- /dev/null +++ b/internal/domain/main-entities/radiology-mcu-order/base/entity.go @@ -0,0 +1,17 @@ +package radiologymcuorder + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + erc "simrs-vx/internal/domain/references/common" +) + +type RadiologyMcuOrder struct { + ecore.BigMain + Encounter_Id uint `json:"encounter_id"` + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code" gorm:"unique;size:20"` + ClinicalNotes *string `json:"clinicalNotes"` + OtherNotes *string `json:"otherNotes"` + Resume *string `json:"resume"` + Status_Code erc.DataStatusCode `json:"status_code" gorm:"not null;size:10"` +} diff --git a/internal/domain/main-entities/radiology-mcu-order/dto.go b/internal/domain/main-entities/radiology-mcu-order/dto.go new file mode 100644 index 00000000..4250b4b0 --- /dev/null +++ b/internal/domain/main-entities/radiology-mcu-order/dto.go @@ -0,0 +1,88 @@ +package radiologymcuorder + +import ( + la "simrs-vx/internal/lib/auth" + + ecore "simrs-vx/internal/domain/base-entities/core" + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + ermoi "simrs-vx/internal/domain/main-entities/radiology-mcu-order-item/base" +) + +type CreateDto struct { + Encounter_Id uint `json:"encounter_id"` + Number *uint64 `json:"number"` // SHOULD BE AUTOMATIC WITHOUT SYNC + Doctor_Code string `json:"-" validate:"required"` + ClinicalNotes *string `json:"clinicalNotes"` + OtherNotes *string `json:"otherNotes"` + Items []ermoi.SubCreateDto `json:"items" validate:"required"` + la.AuthInfo +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id uint `json:"encounter-id"` + Doctor_Code string `json:"doctor-code"` +} + +type ReadDetailDto struct { + Id uint64 `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint64 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint64 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.BigMain + Encounter_Id uint `json:"encounter_id"` + Encounter *ee.Encounter + Number uint64 `json:"number"` + Doctor_Code string `json:"doctor_code"` + Doctor *ed.Doctor + ClinicalNotes *string `json:"clinicalNotes"` + OtherNotes *string `json:"otherNotes"` + Resume *string `json:"resume"` + Items []*ermoi.RadiologyMcuOrderItem +} + +func (d RadiologyMcuOrder) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Encounter: d.Encounter, + Number: d.Number, + Doctor_Code: d.Doctor_Code, + Doctor: d.Doctor, + ClinicalNotes: d.ClinicalNotes, + OtherNotes: d.OtherNotes, + Resume: d.Resume, + } + resp.BigMain = d.BigMain + return resp +} + +func ToResponseList(data []RadiologyMcuOrder) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/radiology-mcu-order/entity.go b/internal/domain/main-entities/radiology-mcu-order/entity.go new file mode 100644 index 00000000..1ef8882d --- /dev/null +++ b/internal/domain/main-entities/radiology-mcu-order/entity.go @@ -0,0 +1,28 @@ +package radiologymcuorder + +import ( + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + ermoi "simrs-vx/internal/domain/main-entities/radiology-mcu-order-item/base" + eamob "simrs-vx/internal/domain/main-entities/radiology-mcu-order/base" + erc "simrs-vx/internal/domain/references/common" +) + +type RadiologyMcuOrder struct { + eamob.RadiologyMcuOrder + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Doctor *ed.Doctor `json:"doctor,omitempty" gorm:"foreignKey:Doctor_Code;references:Code"` + Items []*ermoi.RadiologyMcuOrderItem `json:"items" gorm:"foreignKey:RadiologyMcuOrder_Id;references:Id"` +} + +func (d RadiologyMcuOrder) IsNotNew() bool { + return d.Status_Code != erc.DSCNew +} + +func (d RadiologyMcuOrder) IsCompleted() bool { + return d.Status_Code == erc.DSCDone +} + +func (d RadiologyMcuOrder) IsSameDoctor(doctor_code *string) bool { + return d.Doctor_Code == *doctor_code +} diff --git a/internal/domain/references/clinical/clinical.go b/internal/domain/references/clinical/clinical.go index 2cfdb3a5..a41f459d 100644 --- a/internal/domain/references/clinical/clinical.go +++ b/internal/domain/references/clinical/clinical.go @@ -9,6 +9,7 @@ type ( InstructionCode string HeadToToeCode string McuUrgencyLevelCode string + McuOrderStageCode string McuScopeCode string SoapiTypeCode string MedicalActionTypeCode string @@ -125,6 +126,9 @@ const ( MULCPF McuUrgencyLevelCode = "priority-form" // Form Prioritas MULCRT McuUrgencyLevelCode = "routine" // Pemeriksaan Rutin + MOSFirst McuOrderStageCode = "first" // Stage 1 + MOSSecond McuOrderStageCode = "repeat" // Stage 2 + STCEarlyNurse SoapiTypeCode = "early-nursery" // Kajian Awal Keperawatan STCEEarlyMedic SoapiTypeCode = "early-medic" // Kajian Awal Rehab Medis STCEarlyRehab SoapiTypeCode = "early-rehab" // Kajian Awal Rehab Medik diff --git a/internal/interface/main-handler/chemo/handler.go b/internal/interface/main-handler/chemo/handler.go index 5aa2be44..30535ac4 100644 --- a/internal/interface/main-handler/chemo/handler.go +++ b/internal/interface/main-handler/chemo/handler.go @@ -42,7 +42,7 @@ func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { } dto := e.ReadDetailDto{} sf.UrlQueryParam(&dto, *r.URL) - dto.Id = uint16(id) + dto.Id = uint(id) res, err := u.ReadDetail(dto) rw.DataResponse(w, res, err) } @@ -57,7 +57,7 @@ func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { return } - dto.Id = uint16(id) + dto.Id = uint(id) res, err := u.Update(dto) rw.DataResponse(w, res, err) } @@ -69,7 +69,7 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { } dto := e.DeleteDto{} - dto.Id = uint16(id) + dto.Id = uint(id) res, err := u.Delete(dto) rw.DataResponse(w, res, err) } @@ -85,7 +85,7 @@ func (obj myBase) Verify(w http.ResponseWriter, r *http.Request) { return } - dto.Id = uint16(id) + dto.Id = uint(id) authInfo, err := pa.GetAuthInfo(r) if err != nil { rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) @@ -104,7 +104,7 @@ func (obj myBase) Reject(w http.ResponseWriter, r *http.Request) { } dto := e.VerifyDto{} - dto.Id = uint16(id) + dto.Id = uint(id) authInfo, err := pa.GetAuthInfo(r) if err != nil { rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) diff --git a/internal/interface/migration/main-entities.go b/internal/interface/migration/main-entities.go index 4c182d22..745710a0 100644 --- a/internal/interface/migration/main-entities.go +++ b/internal/interface/migration/main-entities.go @@ -8,6 +8,7 @@ import ( ambulatory "simrs-vx/internal/domain/main-entities/ambulatory" antibioticinuse "simrs-vx/internal/domain/main-entities/antibiotic-in-use" antibioticsrccategory "simrs-vx/internal/domain/main-entities/antibiotic-src-category" + apmcuorder "simrs-vx/internal/domain/main-entities/ap-mcu-order" appointment "simrs-vx/internal/domain/main-entities/appointment" authpartner "simrs-vx/internal/domain/main-entities/auth-partner" chemo "simrs-vx/internal/domain/main-entities/chemo" @@ -16,6 +17,8 @@ import ( 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" + cpmcuorder "simrs-vx/internal/domain/main-entities/cp-mcu-order" + cpmcuorderitem "simrs-vx/internal/domain/main-entities/cp-mcu-order-item" deathcause "simrs-vx/internal/domain/main-entities/death-cause" device "simrs-vx/internal/domain/main-entities/device" deviceorder "simrs-vx/internal/domain/main-entities/device-order" @@ -68,6 +71,8 @@ import ( medicinemethod "simrs-vx/internal/domain/main-entities/medicine-method" medicinemix "simrs-vx/internal/domain/main-entities/medicine-mix" medicinemixitem "simrs-vx/internal/domain/main-entities/medicine-mix-item" + micromcuorder "simrs-vx/internal/domain/main-entities/micro-mcu-order" + micromcuorderitem "simrs-vx/internal/domain/main-entities/micro-mcu-order-item" midwife "simrs-vx/internal/domain/main-entities/midwife" nurse "simrs-vx/internal/domain/main-entities/nurse" nutritionist "simrs-vx/internal/domain/main-entities/nutritionist" @@ -88,6 +93,8 @@ import ( procedureroomorderitem "simrs-vx/internal/domain/main-entities/procedure-room-order-item" proceduresrc "simrs-vx/internal/domain/main-entities/procedure-src" province "simrs-vx/internal/domain/main-entities/province" + radiologymcuorder "simrs-vx/internal/domain/main-entities/radiology-mcu-order" + radiologymcuorderitem "simrs-vx/internal/domain/main-entities/radiology-mcu-order-item" regency "simrs-vx/internal/domain/main-entities/regency" rehab "simrs-vx/internal/domain/main-entities/rehab" responsibledoctorhist "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" @@ -201,6 +208,13 @@ func getMainEntities() []any { &mcuorderitem.McuOrderItem{}, &mcusubsrc.McuSubSrc{}, &mcuordersubitem.McuOrderSubItem{}, + &apmcuorder.ApMcuOrder{}, + &radiologymcuorder.RadiologyMcuOrder{}, + &radiologymcuorderitem.RadiologyMcuOrderItem{}, + &cpmcuorder.CpMcuOrder{}, + &cpmcuorderitem.CpMcuOrderItem{}, + µmcuorder.MicroMcuOrder{}, + µmcuorderitem.MicroMcuOrderItem{}, &antibioticsrccategory.AntibioticSrcCategory{}, &antibioticinuse.AntibioticInUse{}, &consultation.Consultation{}, diff --git a/internal/use-case/main-use-case/ap-mcu-order/case.go b/internal/use-case/main-use-case/ap-mcu-order/case.go new file mode 100644 index 00000000..845b8eeb --- /dev/null +++ b/internal/use-case/main-use-case/ap-mcu-order/case.go @@ -0,0 +1,276 @@ +package apmcuorder + +import ( + e "simrs-vx/internal/domain/main-entities/ap-mcu-order" + "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" +) + +const source = "device" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.ApMcuOrder{} + + 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.ApMcuOrder + var dataList []e.ApMcuOrder + 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.ApMcuOrder + 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.ApMcuOrder + 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.ApMcuOrder + 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/ap-mcu-order/helper.go b/internal/use-case/main-use-case/ap-mcu-order/helper.go new file mode 100644 index 00000000..4b91b7aa --- /dev/null +++ b/internal/use-case/main-use-case/ap-mcu-order/helper.go @@ -0,0 +1,27 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package apmcuorder + +import ( + e "simrs-vx/internal/domain/main-entities/ap-mcu-order" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.ApMcuOrder) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Substances = inputSrc.Substances + data.Fictations = inputSrc.Fictations + data.Localization = inputSrc.Localization + data.ClinicalDiagnoses = inputSrc.ClinicalDiagnoses + data.Stadium = inputSrc.Stadium + data.PastHistory = inputSrc.PastHistory + data.CurrentHistory = inputSrc.CurrentHistory +} diff --git a/internal/use-case/main-use-case/ap-mcu-order/lib.go b/internal/use-case/main-use-case/ap-mcu-order/lib.go new file mode 100644 index 00000000..8b6d8a74 --- /dev/null +++ b/internal/use-case/main-use-case/ap-mcu-order/lib.go @@ -0,0 +1,140 @@ +package apmcuorder + +import ( + e "simrs-vx/internal/domain/main-entities/ap-mcu-order" + + 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.ApMcuOrder, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.ApMcuOrder{} + 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.ApMcuOrder, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.ApMcuOrder{} + 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.ApMcuOrder{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Scopes(gh.Sort(input.Sort)) + + if err := tx.Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e.ApMcuOrder, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.ApMcuOrder{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.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.ApMcuOrder, 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.ApMcuOrder, 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/ap-mcu-order/middleware-runner.go b/internal/use-case/main-use-case/ap-mcu-order/middleware-runner.go new file mode 100644 index 00000000..dc7f2495 --- /dev/null +++ b/internal/use-case/main-use-case/ap-mcu-order/middleware-runner.go @@ -0,0 +1,103 @@ +package apmcuorder + +import ( + e "simrs-vx/internal/domain/main-entities/ap-mcu-order" + 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.ApMcuOrder) 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.ApMcuOrder) 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.ApMcuOrder) 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.ApMcuOrder) 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.ApMcuOrder) 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/ap-mcu-order/middleware.go b/internal/use-case/main-use-case/ap-mcu-order/middleware.go new file mode 100644 index 00000000..38161afe --- /dev/null +++ b/internal/use-case/main-use-case/ap-mcu-order/middleware.go @@ -0,0 +1,9 @@ +package apmcuorder + +// 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/ap-mcu-order/tycovar.go b/internal/use-case/main-use-case/ap-mcu-order/tycovar.go new file mode 100644 index 00000000..7f301ff1 --- /dev/null +++ b/internal/use-case/main-use-case/ap-mcu-order/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 apmcuorder + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/ap-mcu-order" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.ApMcuOrder, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.ApMcuOrder, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.ApMcuOrder, 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/chemo-plan/case.go b/internal/use-case/main-use-case/chemo-plan/case.go index eda5a526..f58f19ca 100644 --- a/internal/use-case/main-use-case/chemo-plan/case.go +++ b/internal/use-case/main-use-case/chemo-plan/case.go @@ -157,9 +157,9 @@ func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { }, nil } -func Update(input ep.UpdateDto) (*d.Data, error) { - rdDto := e.ReadListDto{FilterDto: e.FilterDto{Parent_Id: &input.Id}} - var data []e.ChemoPlan +func Update(input e.UpdateDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.ChemoPlan var err error event := pl.Event{ @@ -172,7 +172,7 @@ func Update(input ep.UpdateDto) (*d.Data, error) { err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") - if data, _, err = ReadListData(rdDto, &event, tx); err != nil { + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { return err } @@ -194,7 +194,7 @@ func Update(input ep.UpdateDto) (*d.Data, error) { "structure": "single-data", "status": "updated", }, - Data: e.ToResponseList(data), + Data: data.ToResponse(), }, nil } diff --git a/internal/use-case/main-use-case/chemo-plan/helper.go b/internal/use-case/main-use-case/chemo-plan/helper.go index b9dcca60..6a072ea3 100644 --- a/internal/use-case/main-use-case/chemo-plan/helper.go +++ b/internal/use-case/main-use-case/chemo-plan/helper.go @@ -5,31 +5,32 @@ Any functions that are used internally by the use-case package chemo_plan import ( - ere "simrs-vx/internal/domain/references/encounter" - e "simrs-vx/internal/domain/main-entities/chemo-plan" ep "simrs-vx/internal/domain/main-entities/chemo-protocol" + ere "simrs-vx/internal/domain/references/encounter" + "time" ) -func setData[T *ep.CreateDto | *ep.UpdateDto](input T, data []e.ChemoPlan) { - var inputSrc *ep.CreateDto - if inputT, ok := any(input).(*ep.CreateDto); ok { - inputSrc = inputT - } else { - inputTemp := any(input).(*ep.UpdateDto) - inputSrc = &inputTemp.CreateDto - } - - for _, cp := range *inputSrc.ChemoPlans { +func setDataCreate(input *ep.CreateDto) (data []e.ChemoPlan) { + for _, c := range *input.ChemoPlans { data = append(data, e.ChemoPlan{ - Parent_Id: inputSrc.Chemo_Id, - Protocol_Id: inputSrc.Id, - SeriesNumber: cp.SeriesNumber, - CycleNumber: cp.CycleNumber, - PlanDate: cp.PlanDate, + Parent_Id: input.Chemo_Id, + Protocol_Id: input.Id, + SeriesNumber: c.SeriesNumber, + CycleNumber: c.CycleNumber, + PlanDate: c.PlanDate, }) } status := ere.SPCPlanned data[0].Status = &status + return +} + +func setDataUpdate(data *e.ChemoPlan) { + now := time.Now() + data.RealizationDate = &now + + status := ere.SPCComplete + data.Status = &status } diff --git a/internal/use-case/main-use-case/chemo-plan/lib.go b/internal/use-case/main-use-case/chemo-plan/lib.go index 3b40325a..3dee0130 100644 --- a/internal/use-case/main-use-case/chemo-plan/lib.go +++ b/internal/use-case/main-use-case/chemo-plan/lib.go @@ -19,8 +19,7 @@ import ( func CreateData(input *ep.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*[]e.ChemoPlan, error) { pl.SetLogInfo(event, nil, "started", "DBCreate") - data := []e.ChemoPlan{} - setData(input, data) + data := setDataCreate(input) var tx *gorm.DB if len(dbx) > 0 { @@ -99,9 +98,9 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e return &data, nil } -func UpdateData(input *ep.UpdateDto, data []e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { +func UpdateData(data *e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBUpdate") - setData(input, data) + setDataUpdate(data) var tx *gorm.DB if len(dbx) > 0 { diff --git a/internal/use-case/main-use-case/chemo-protocol/case.go b/internal/use-case/main-use-case/chemo-protocol/case.go index 793ab06b..38e6ba4b 100644 --- a/internal/use-case/main-use-case/chemo-protocol/case.go +++ b/internal/use-case/main-use-case/chemo-protocol/case.go @@ -14,9 +14,11 @@ import ( erc "simrs-vx/internal/domain/references/common" + ec "simrs-vx/internal/domain/main-entities/chemo" e "simrs-vx/internal/domain/main-entities/chemo-protocol" ee "simrs-vx/internal/domain/main-entities/encounter" + uc "simrs-vx/internal/use-case/main-use-case/chemo" ucp "simrs-vx/internal/use-case/main-use-case/chemo-plan" ue "simrs-vx/internal/use-case/main-use-case/encounter" ) @@ -46,6 +48,21 @@ func Create(input e.CreateDto) (*d.Data, error) { input.Status_Code = erc.DVCNew } + // get chemo + dataChemo, err := uc.ReadDetailData(ec.ReadDetailDto{Id: *input.Chemo_Id}, &event) + if err != nil { + return nil, err + } + + if dataChemo.Status_Code != erc.DVCVerified { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-validation-fail", + Detail: "chemo must be verified", + } + return nil, pl.SetLogError(&event, input) + } + err = dg.I.Transaction(func(tx *gorm.DB) error { // Insert Chemo-Protocol if resData, err := CreateData(input, &event, tx); err != nil { @@ -55,6 +72,7 @@ func Create(input e.CreateDto) (*d.Data, error) { } // Insert Chemo-Plans + input.Id = &data.Id if data.ChemoPlans, err = ucp.CreateData(&input, &event, tx); err != nil { return err } diff --git a/internal/use-case/main-use-case/chemo-protocol/helper.go b/internal/use-case/main-use-case/chemo-protocol/helper.go index 28082ba9..b3989d98 100644 --- a/internal/use-case/main-use-case/chemo-protocol/helper.go +++ b/internal/use-case/main-use-case/chemo-protocol/helper.go @@ -60,30 +60,6 @@ func getChemoEncounterReg(event *pl.Event) (*ec.Chemo, error) { return &data, nil } -func getChemoFromLatestEncounter(event *pl.Event) (*ec.Chemo, error) { - pl.SetLogInfo(event, nil, "started", "DBReadChemoFromLatestEncounter") - data := ec.Chemo{} - - var tx = dg.I - - if err := tx.Model(&ec.Chemo{}). - Joins(`LEFT JOIN "Encounter" "e" ON "e"."Id" = "Chemo"."Encounter_Id"`). - Joins(`LEFT JOIN "Ambulatory" "a" ON "a"."Encounter_Id" = "e"."Id"`). - Where(`"a"."Class_Code" IN (?)"`, []string{string(ere.ACCReg), string(ere.ACCChemo)}). - Where(`"Chemo"."Status_Code" = ?`, erc.DVCVerified). - Scopes(gh.Preload("Encounter.Ambulatory")). - Order("\"Chemo\".\"CreatedAt\" DESC"). - First(&data). - Error; err != nil { - if processedErr := pu.HandleReadError(err, event, source, nil, data); processedErr != nil { - return nil, processedErr - } - } - - pl.SetLogInfo(event, nil, "complete") - return &data, nil -} - func getChemoEncounter(encounterId *uint, event *pl.Event) (*ec.Chemo, error) { pl.SetLogInfo(event, encounterId, "started", "DBReadChemoEncounter") data := ec.Chemo{} diff --git a/internal/use-case/main-use-case/chemo-protocol/lib.go b/internal/use-case/main-use-case/chemo-protocol/lib.go index 22e8808c..abb19224 100644 --- a/internal/use-case/main-use-case/chemo-protocol/lib.go +++ b/internal/use-case/main-use-case/chemo-protocol/lib.go @@ -2,7 +2,6 @@ package chemo_protocol import ( "errors" - plh "simrs-vx/pkg/lib-helper" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -58,7 +57,7 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Ch Scopes(gh.Paginate(input, &pagination)). Order("\"CreatedAt\" DESC") - if err := tx.Find(&data).Error; err != nil { + if err := tx.Debug().Find(&data).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, &meta, nil } diff --git a/internal/use-case/main-use-case/soapi/case.go b/internal/use-case/main-use-case/soapi/case.go index ebf1c7d8..3bf68d7c 100644 --- a/internal/use-case/main-use-case/soapi/case.go +++ b/internal/use-case/main-use-case/soapi/case.go @@ -2,19 +2,20 @@ package soapi import ( "errors" - erc "simrs-vx/internal/domain/references/common" - esync "simrs-vx/internal/domain/sync-entities/log" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "strconv" - e "simrs-vx/internal/domain/main-entities/soapi" - 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" + + erc "simrs-vx/internal/domain/references/common" + + e "simrs-vx/internal/domain/main-entities/soapi" + esync "simrs-vx/internal/domain/sync-entities/log" + + ucp "simrs-vx/internal/use-case/main-use-case/chemo-plan" ) const source = "soapi" @@ -41,13 +42,25 @@ func Create(input e.CreateDto) (*d.Data, error) { return nil, pl.SetLogError(&event, input) } - err := dg.I.Transaction(func(tx *gorm.DB) error { + chemoPlan, err := validateIfEncounterIsChemo(*input.Encounter_Id, &event) + if err != nil { + return nil, err + } + + err = dg.I.Transaction(func(tx *gorm.DB) error { if resData, err := CreateData(input, &event, tx); err != nil { return err } else { data = *resData } + // update chemoPlan + if chemoPlan != nil { + if err = ucp.UpdateData(chemoPlan, &event, tx); err != nil { + return err + } + } + // get detail for sync soapiData, err := ReadDetailData(e.ReadDetailDto{ Id: data.Id, diff --git a/internal/use-case/main-use-case/soapi/helper.go b/internal/use-case/main-use-case/soapi/helper.go index c42b95d0..dc251882 100644 --- a/internal/use-case/main-use-case/soapi/helper.go +++ b/internal/use-case/main-use-case/soapi/helper.go @@ -5,6 +5,21 @@ Any functions that are used internally by the use-case package soapi import ( + "errors" + "fmt" + erc "simrs-vx/internal/domain/references/common" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + "time" + + dg "github.com/karincake/apem/db-gorm-pg" + "gorm.io/gorm" + + ere "simrs-vx/internal/domain/references/encounter" + + ecpl "simrs-vx/internal/domain/main-entities/chemo-plan" + ecp "simrs-vx/internal/domain/main-entities/chemo-protocol" + ee "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/soapi" ) @@ -39,3 +54,96 @@ func setBulkData(input []e.CreateDto, encounterId uint) []e.Soapi { return data } + +func validateIfEncounterIsChemo(encounterId uint, event *pl.Event) (*ecpl.ChemoPlan, error) { + // get encounter + enc, err := getEncounter(encounterId, event) + if err != nil { + return nil, err + } + + // Encounter must be Ambulatory and NOT Rehab + a := enc.Ambulatory + if enc.Class_Code != ere.ECAmbulatory || a == nil || a.Class_Code == ere.ACCRehab { + return nil, nil + } + + // get chemo protocol + chemo, err := getChemo(*enc.Patient_Id, event) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, pl.SetLogError(event, nil) + } + + if chemo.Status_Code != erc.DVCVerified { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-not-match", + Detail: fmt.Sprintf("protocol chemo not yet verified"), + } + } + + now := time.Now() + if now.Before(*chemo.StartDate) || now.After(*chemo.EndDate) { + return nil, nil + } + + // --- 3. Must have at least one ChemoPlan --- + if chemo.ChemoPlans == nil || len(*chemo.ChemoPlans) == 0 { + return nil, nil + } + + // Return the first chemo plan + return &(*chemo.ChemoPlans)[0], nil +} + +func getEncounter(encounterId uint, event *pl.Event) (*ee.Encounter, error) { + pl.SetLogInfo(event, nil, "started", "getEncounter") + data := ee.Encounter{} + + var tx = dg.I + + if err := tx.Model(&ee.Encounter{}). + Where(`"Encounter_Id" = ?`, encounterId). + Scopes(withConditionalAmbulatory()). + First(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, nil, encounterId); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func withConditionalAmbulatory() func(db *gorm.DB) *gorm.DB { + return func(db *gorm.DB) *gorm.DB { + return db.Preload(`"Ambulatory"`, func(db *gorm.DB) *gorm.DB { + return db.Joins(`JOIN "Encounter" e ON "e"."Id" = "Ambulatory"."Encounter_Id"`) + }) + } +} + +func getChemo(patientId uint, event *pl.Event) (*ecp.ChemoProtocol, error) { + pl.SetLogInfo(event, nil, "started", "getChemoFromEncounter") + data := ecp.ChemoProtocol{} + + var tx = dg.I + + tx = tx.Model(&ecp.ChemoProtocol{}). + Joins(`"ChemoPlan" cp ON cp."Protocol_Id" = "ChemoProtocol"."Id"`). + Where(`"Patient.Id" = ? AND (cp.Status = ? OR cp.Status IS NULL)`, patientId, ere.SPCPlanned). + Preload("ChemoPlans", func(db *gorm.DB) *gorm.DB { + return tx.Order(`"Id" ASC`).Limit(1) + }). + First(&data) + + if err := tx.Error; err != nil { + return nil, err + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} From 45d883e0b748dcc0809417899135e52e187609d1 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 9 Dec 2025 12:11:05 +0700 Subject: [PATCH 4/9] fail chemo ongoing --- .../domain/main-entities/chemo-plan/dto.go | 8 +- internal/domain/main-entities/chemo/dto.go | 3 + .../domain/main-entities/encounter/dto.go | 2 +- .../use-case/main-use-case/chemo-plan/case.go | 4 +- .../main-use-case/chemo-plan/helper.go | 4 +- .../use-case/main-use-case/chemo-plan/lib.go | 2 +- .../main-use-case/encounter/helper.go | 231 +++--------------- .../use-case/main-use-case/encounter/lib.go | 8 +- internal/use-case/main-use-case/soapi/case.go | 3 +- .../use-case/main-use-case/soapi/helper.go | 2 +- 10 files changed, 62 insertions(+), 205 deletions(-) diff --git a/internal/domain/main-entities/chemo-plan/dto.go b/internal/domain/main-entities/chemo-plan/dto.go index 04be0ebc..d83af082 100644 --- a/internal/domain/main-entities/chemo-plan/dto.go +++ b/internal/domain/main-entities/chemo-plan/dto.go @@ -18,6 +18,7 @@ type CreateDto struct { RealizationDate *time.Time `json:"realizationDate"` Notes *string `json:"notes"` Status ere.StatusProtocolChemo `json:"status"` + Encounter_Id *uint `json:"encounter_id"` } type ReadListDto struct { @@ -45,6 +46,11 @@ type DeleteDto struct { Id uint `json:"id"` } +type FailDto struct { + Id uint `json:"id"` + Reasons *string `json:"reason"` +} + type MetaDto struct { PageNumber int `json:"page_number"` PageSize int `json:"page_size"` @@ -71,7 +77,7 @@ func (d ChemoPlan) ToResponse() ResponseDto { PlanDate: d.PlanDate, RealizationDate: d.RealizationDate, Notes: d.Notes, - Status: *d.Status, + Status: d.Status, } resp.Main = d.Main return resp diff --git a/internal/domain/main-entities/chemo/dto.go b/internal/domain/main-entities/chemo/dto.go index 695c1ca7..a6cab3d7 100644 --- a/internal/domain/main-entities/chemo/dto.go +++ b/internal/domain/main-entities/chemo/dto.go @@ -2,6 +2,8 @@ package chemo import ( ed "simrs-vx/internal/domain/main-entities/doctor" + ere "simrs-vx/internal/domain/references/encounter" + // std "time" @@ -22,6 +24,7 @@ type CreateDto struct { Encounter_Id *uint `json:"encounter_id"` Status_Code erc.DataVerifiedCode `json:"status_code"` SrcUnit_Code *string `json:"srcUnit_code"` + Class_Code ere.ChemoClassCode `json:"class_code"` } type ReadListDto struct { diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 1a9aa2e6..35dc50dc 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -118,7 +118,7 @@ type FilterDto struct { MedicalDischargeEducation *string `json:"medicalDischargeEducation"` AdmDischargeEducation *string `json:"admDischargeEducation"` DischargeReason *string `json:"dischargeReason"` - SubProcess *string `json:"subProcess"` + ChemoClass *ere.ChemoClassCode `json:"chemo-class"` } type ReadDetailDto struct { diff --git a/internal/use-case/main-use-case/chemo-plan/case.go b/internal/use-case/main-use-case/chemo-plan/case.go index f58f19ca..99ef3a63 100644 --- a/internal/use-case/main-use-case/chemo-plan/case.go +++ b/internal/use-case/main-use-case/chemo-plan/case.go @@ -176,7 +176,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - if err := UpdateData(&input, data, &event, tx); err != nil { + if err := UpdateData(data, &event, tx); err != nil { return err } @@ -252,3 +252,5 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } + +func Fail(i) diff --git a/internal/use-case/main-use-case/chemo-plan/helper.go b/internal/use-case/main-use-case/chemo-plan/helper.go index 6a072ea3..dedacfd8 100644 --- a/internal/use-case/main-use-case/chemo-plan/helper.go +++ b/internal/use-case/main-use-case/chemo-plan/helper.go @@ -23,7 +23,7 @@ func setDataCreate(input *ep.CreateDto) (data []e.ChemoPlan) { } status := ere.SPCPlanned - data[0].Status = &status + data[0].Status = status return } @@ -32,5 +32,5 @@ func setDataUpdate(data *e.ChemoPlan) { data.RealizationDate = &now status := ere.SPCComplete - data.Status = &status + data.Status = status } diff --git a/internal/use-case/main-use-case/chemo-plan/lib.go b/internal/use-case/main-use-case/chemo-plan/lib.go index 3dee0130..b90430cd 100644 --- a/internal/use-case/main-use-case/chemo-plan/lib.go +++ b/internal/use-case/main-use-case/chemo-plan/lib.go @@ -116,7 +116,7 @@ func UpdateData(data *e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { Detail: "Database update failed", Raw: err, } - return pl.SetLogError(event, input) + return pl.SetLogError(event, data) } 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 7f263bfe..6fc28632 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -7,7 +7,7 @@ package encounter import ( "errors" "fmt" - un "simrs-vx/internal/use-case/main-use-case/nurse" + "strings" "time" @@ -24,9 +24,9 @@ import ( 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" ec "simrs-vx/internal/domain/main-entities/chemo" + ecpl "simrs-vx/internal/domain/main-entities/chemo-plan" edo "simrs-vx/internal/domain/main-entities/device-order" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/emergency" @@ -42,12 +42,9 @@ import ( 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" es "simrs-vx/internal/domain/main-entities/soapi" eu "simrs-vx/internal/domain/main-entities/unit" - // udo "simrs-vx/internal/use-case/main-use-case/device-order" - uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" ua "simrs-vx/internal/use-case/main-use-case/ambulatory" uc "simrs-vx/internal/use-case/main-use-case/chemo" ud "simrs-vx/internal/use-case/main-use-case/doctor" @@ -58,10 +55,10 @@ import ( umi "simrs-vx/internal/use-case/main-use-case/medicine-mix" ummi "simrs-vx/internal/use-case/main-use-case/medicine-mix-item" _ "simrs-vx/internal/use-case/main-use-case/nurse" + un "simrs-vx/internal/use-case/main-use-case/nurse" up "simrs-vx/internal/use-case/main-use-case/prescription" upi "simrs-vx/internal/use-case/main-use-case/prescription-item" ur "simrs-vx/internal/use-case/main-use-case/rehab" - urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" us "simrs-vx/internal/use-case/main-use-case/soapi" ) @@ -396,192 +393,6 @@ func getMcuOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error { return 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_Code": input.Doctor_Code, - "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.FinishedAt) - - 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.FinishedAt) - - 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 getSoapiByResponsibleDoctor(enc e.Encounter, event *pl.Event) (data []es.Soapi, err error) { pl.SetLogInfo(event, enc, "started", "DBReadList") @@ -856,6 +667,7 @@ func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, e Encounter_Id: &input.Id, Status_Code: erc.DVCVerified, SrcUnit_Code: input.Unit_Code, + Class_Code: ere.CCCAct, } // create data chemo @@ -864,6 +676,12 @@ func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, e return err } + // set chemo-plan to planned + _, err = updateChemoPlan(*input.Patient_Id, event, tx) + if err != nil { + return err + } + case subCode == ere.ACCRehab: rehabData := er.CreateDto{ Encounter_Id: &input.Id, @@ -1056,3 +874,32 @@ func validateDoctorCodes(doctorCodes map[string]struct{}, event *pl.Event) error return nil } + +func updateChemoPlan(patientId uint, event *pl.Event, dbx ...*gorm.DB) (*ecpl.ChemoPlan, error) { + pl.SetLogInfo(event, nil, "started", "getChemoFromEncounter") + data := ecpl.ChemoPlan{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx.Model(&ecpl.ChemoPlan{}). + Joins(`"ChemoProtocol" cp ON cp."Id" = "ChemoPlan"."Protocol_Id"`). + Where(`cp."Patient_Id" = ? AND ("ChemoPlan"."Status" IS NULL OR "ChemoPlan"."Status" = ?)`, patientId, ere.SPCSchedule). + Order(`"Id" ASC`). + Limit(1).First(&data) + + if err := tx.Error; err != nil { + return nil, err + } + + if err := tx.Model(&data).Update(`"Status"`, ere.SPCPlanned).Error; err != nil { + return nil, err + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index 674a84a4..a54c2f03 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -93,11 +93,9 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.En tx = tx.Where("\"Encounter\".\"PaymentMethod_Code\" = ?", *input.PaymentMethod_Code) } - if input.SubProcess != nil { - switch *input.SubProcess { - case "chemo": - tx = tx.Joins(`RIGHT JOIN "Chemo" "c" ON "c"."Encounter_Id" = "Encounter"."Id"`) - } + if input.ChemoClass != nil { + tx = tx.Joins(`RIGHT JOIN "Chemo" "c" ON "c"."Encounter_Id" = "Encounter"."Id"`). + Where(`"c"."Class_Code" = ?`, *input.ChemoClass) } // count diff --git a/internal/use-case/main-use-case/soapi/case.go b/internal/use-case/main-use-case/soapi/case.go index 3bf68d7c..6b93fd8a 100644 --- a/internal/use-case/main-use-case/soapi/case.go +++ b/internal/use-case/main-use-case/soapi/case.go @@ -56,11 +56,12 @@ func Create(input e.CreateDto) (*d.Data, error) { // update chemoPlan if chemoPlan != nil { + chemoPlan.Encounter_Id = input.Encounter_Id if err = ucp.UpdateData(chemoPlan, &event, tx); err != nil { return err } } - + // get detail for sync soapiData, err := ReadDetailData(e.ReadDetailDto{ Id: data.Id, diff --git a/internal/use-case/main-use-case/soapi/helper.go b/internal/use-case/main-use-case/soapi/helper.go index dc251882..7d5e7825 100644 --- a/internal/use-case/main-use-case/soapi/helper.go +++ b/internal/use-case/main-use-case/soapi/helper.go @@ -134,7 +134,7 @@ func getChemo(patientId uint, event *pl.Event) (*ecp.ChemoProtocol, error) { tx = tx.Model(&ecp.ChemoProtocol{}). Joins(`"ChemoPlan" cp ON cp."Protocol_Id" = "ChemoProtocol"."Id"`). - Where(`"Patient.Id" = ? AND (cp.Status = ? OR cp.Status IS NULL)`, patientId, ere.SPCPlanned). + Where(`"Patient.Id" = ? AND cp."Status" = ?`, patientId, ere.SPCPlanned). Preload("ChemoPlans", func(db *gorm.DB) *gorm.DB { return tx.Order(`"Id" ASC`).Limit(1) }). From ecdf270738b0eaf98694ed736bbb66888bcb8ce2 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 9 Dec 2025 12:27:47 +0700 Subject: [PATCH 5/9] ongoing --- .../domain/main-entities/chemo-plan/dto.go | 4 ++ .../use-case/main-use-case/chemo-plan/case.go | 42 ++++++++++++++++++- .../main-use-case/chemo-plan/helper.go | 10 +++++ .../use-case/main-use-case/chemo-plan/lib.go | 25 +++++++++++ 4 files changed, 80 insertions(+), 1 deletion(-) diff --git a/internal/domain/main-entities/chemo-plan/dto.go b/internal/domain/main-entities/chemo-plan/dto.go index d83af082..1e8d4bb9 100644 --- a/internal/domain/main-entities/chemo-plan/dto.go +++ b/internal/domain/main-entities/chemo-plan/dto.go @@ -50,6 +50,10 @@ type FailDto struct { Id uint `json:"id"` Reasons *string `json:"reason"` } +type FailReason struct { + Date *time.Time `json:"date"` + FailReason *string `json:"failReason"` +} type MetaDto struct { PageNumber int `json:"page_number"` diff --git a/internal/use-case/main-use-case/chemo-plan/case.go b/internal/use-case/main-use-case/chemo-plan/case.go index 99ef3a63..6a4c426e 100644 --- a/internal/use-case/main-use-case/chemo-plan/case.go +++ b/internal/use-case/main-use-case/chemo-plan/case.go @@ -253,4 +253,44 @@ func Delete(input e.DeleteDto) (*d.Data, error) { } -func Fail(i) +func Fail(input e.FailDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.ChemoPlan + var err error + + event := pl.Event{ + Feature: "Chemo-Fail", + 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 + } + + if err := UpdateData(data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/chemo-plan/helper.go b/internal/use-case/main-use-case/chemo-plan/helper.go index dedacfd8..f1b6179d 100644 --- a/internal/use-case/main-use-case/chemo-plan/helper.go +++ b/internal/use-case/main-use-case/chemo-plan/helper.go @@ -5,6 +5,7 @@ Any functions that are used internally by the use-case package chemo_plan import ( + "encoding/json" e "simrs-vx/internal/domain/main-entities/chemo-plan" ep "simrs-vx/internal/domain/main-entities/chemo-protocol" ere "simrs-vx/internal/domain/references/encounter" @@ -34,3 +35,12 @@ func setDataUpdate(data *e.ChemoPlan) { status := ere.SPCComplete data.Status = status } + +func setDatafail(input e.FailDto, data *e.ChemoPlan) { + data.RealizationDate = nil + data.Status = ere.SPCSchedule + + var reasons []e.FailReason + + _ := json.Unmarshal(data.Re) +} diff --git a/internal/use-case/main-use-case/chemo-plan/lib.go b/internal/use-case/main-use-case/chemo-plan/lib.go index b90430cd..92d7c8eb 100644 --- a/internal/use-case/main-use-case/chemo-plan/lib.go +++ b/internal/use-case/main-use-case/chemo-plan/lib.go @@ -145,3 +145,28 @@ func DeleteData(data *e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "complete") return nil } + +func UpdateFailData(input e.FailDto, data *e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setDataUpdate(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, data) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} From be34c66ffd643a9e628407e34421379fe3fb2a82 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 9 Dec 2025 15:07:01 +0700 Subject: [PATCH 6/9] on going chemo plan --- .../use-case/main-use-case/chemo-plan/case.go | 41 ++++++--- .../main-use-case/chemo-plan/helper.go | 50 +++++++++-- .../main-use-case/encounter/helper.go | 85 ++++++++++++++++--- .../use-case/main-use-case/soapi/helper.go | 39 ++++++--- 4 files changed, 172 insertions(+), 43 deletions(-) diff --git a/internal/use-case/main-use-case/chemo-plan/case.go b/internal/use-case/main-use-case/chemo-plan/case.go index 6a4c426e..7f74b6f7 100644 --- a/internal/use-case/main-use-case/chemo-plan/case.go +++ b/internal/use-case/main-use-case/chemo-plan/case.go @@ -1,17 +1,17 @@ package chemo_plan import ( - erc "simrs-vx/internal/domain/references/common" + ere "simrs-vx/internal/domain/references/encounter" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" "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" + erc "simrs-vx/internal/domain/references/common" + e "simrs-vx/internal/domain/main-entities/chemo-plan" ep "simrs-vx/internal/domain/main-entities/chemo-protocol" ) @@ -266,12 +266,33 @@ func Fail(input e.FailDto) (*d.Data, error) { // 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 - } + if data, err = ReadDetailData(rdDto, &event); err != nil { + return nil, err + } + dataSoapi, err := getSoapiByEncounterId(*data.Encounter_Id, &event) + if err != nil { + return nil, err + } + + if dataSoapi != nil && len(*dataSoapi) > 0 { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "soapi-exist", + Detail: "cancel soapi to proceed", + } + return nil, pl.SetLogError(&event, data) + } + + if data.Status != ere.SPCPlanned { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "invalid-chemo-status", + Detail: "only chemo plans with status 'planned' can be deleted.", + } + } + + err = dg.I.Transaction(func(tx *gorm.DB) error { if err := UpdateData(data, &event, tx); err != nil { return err } diff --git a/internal/use-case/main-use-case/chemo-plan/helper.go b/internal/use-case/main-use-case/chemo-plan/helper.go index f1b6179d..5cf7ffd5 100644 --- a/internal/use-case/main-use-case/chemo-plan/helper.go +++ b/internal/use-case/main-use-case/chemo-plan/helper.go @@ -6,12 +6,21 @@ package chemo_plan import ( "encoding/json" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + "time" + + dg "github.com/karincake/apem/db-gorm-pg" + + ere "simrs-vx/internal/domain/references/encounter" + e "simrs-vx/internal/domain/main-entities/chemo-plan" ep "simrs-vx/internal/domain/main-entities/chemo-protocol" - ere "simrs-vx/internal/domain/references/encounter" - "time" + es "simrs-vx/internal/domain/main-entities/soapi" ) +var now = time.Now() + func setDataCreate(input *ep.CreateDto) (data []e.ChemoPlan) { for _, c := range *input.ChemoPlans { data = append(data, e.ChemoPlan{ @@ -22,14 +31,10 @@ func setDataCreate(input *ep.CreateDto) (data []e.ChemoPlan) { PlanDate: c.PlanDate, }) } - - status := ere.SPCPlanned - data[0].Status = status return } func setDataUpdate(data *e.ChemoPlan) { - now := time.Now() data.RealizationDate = &now status := ere.SPCComplete @@ -42,5 +47,36 @@ func setDatafail(input e.FailDto, data *e.ChemoPlan) { var reasons []e.FailReason - _ := json.Unmarshal(data.Re) + if data.Reasons != nil { + json.Unmarshal([]byte(*data.Reasons), &reasons) + } + + reasons = append(reasons, e.FailReason{ + FailReason: input.Reasons, + Date: &now, + }) + + if b, err := json.Marshal(reasons); err == nil { + jsonStr := string(b) + data.Reasons = &jsonStr + } +} + +func getSoapiByEncounterId(encounterId uint, event *pl.Event) (*[]es.Soapi, error) { + pl.SetLogInfo(event, encounterId, "started", "DBReadDetail") + var data []es.Soapi + + var tx = dg.I + + if err := tx. + Model(&es.Soapi{}). + Where(`"Encounter_Id" = ?`, encounterId). + Find(&data).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, encounterId, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil } diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 6fc28632..a4632296 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -7,7 +7,6 @@ package encounter import ( "errors" "fmt" - "strings" "time" @@ -27,6 +26,7 @@ import ( ea "simrs-vx/internal/domain/main-entities/ambulatory" ec "simrs-vx/internal/domain/main-entities/chemo" ecpl "simrs-vx/internal/domain/main-entities/chemo-plan" + ecp "simrs-vx/internal/domain/main-entities/chemo-protocol" edo "simrs-vx/internal/domain/main-entities/device-order" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/emergency" @@ -663,6 +663,20 @@ func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, e switch { case subCode == ere.ACCChemo: + // validate if encounter Chemo valid + chemoProtoc, err := validateChemoAdm(*input.Patient_Id, event) + if err != nil { + return err + } + + if chemoProtoc == nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-not-found", + Detail: "chemo plan not found", + } + } + chemoCreate := ec.CreateDto{ Encounter_Id: &input.Id, Status_Code: erc.DVCVerified, @@ -677,7 +691,7 @@ func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, e } // set chemo-plan to planned - _, err = updateChemoPlan(*input.Patient_Id, event, tx) + err = updateChemoPlan(chemoProtoc, event, tx) if err != nil { return err } @@ -875,10 +889,8 @@ func validateDoctorCodes(doctorCodes map[string]struct{}, event *pl.Event) error return nil } -func updateChemoPlan(patientId uint, event *pl.Event, dbx ...*gorm.DB) (*ecpl.ChemoPlan, error) { +func updateChemoPlan(data *ecpl.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "started", "getChemoFromEncounter") - data := ecpl.ChemoPlan{} - var tx *gorm.DB if len(dbx) > 0 { tx = dbx[0] @@ -886,20 +898,65 @@ func updateChemoPlan(patientId uint, event *pl.Event, dbx ...*gorm.DB) (*ecpl.Ch tx = dg.I } - tx = tx.Model(&ecpl.ChemoPlan{}). - Joins(`"ChemoProtocol" cp ON cp."Id" = "ChemoPlan"."Protocol_Id"`). - Where(`cp."Patient_Id" = ? AND ("ChemoPlan"."Status" IS NULL OR "ChemoPlan"."Status" = ?)`, patientId, ere.SPCSchedule). - Order(`"Id" ASC`). - Limit(1).First(&data) - - if err := tx.Error; err != nil { - return nil, err + if err := tx.Model(&data).Update(`"Status"`, ere.SPCPlanned).Error; err != nil { + return err } - if err := tx.Model(&data).Update(`"Status"`, ere.SPCPlanned).Error; err != nil { + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func getChemoProtocol(patientId uint, event *pl.Event) (*ecp.ChemoProtocol, error) { + pl.SetLogInfo(event, nil, "started", "getChemoProtocol") + data := ecp.ChemoProtocol{} + + var tx = dg.I + + tx = tx.Model(&ecp.ChemoProtocol{}). + Joins(`"ChemoPlan" cp ON cp."Protocol_Id" = "ChemoProtocol"."Id"`). + Where(`"Patient.Id" = ? AND cp."Status" IS NULL`, patientId). + Preload("ChemoPlans", func(db *gorm.DB) *gorm.DB { + return tx.Order(`"Id" ASC`).Limit(1) + }). + First(&data) + + if err := tx.Error; err != nil { return nil, err } pl.SetLogInfo(event, nil, "complete") return &data, nil } + +func validateChemoAdm(patientId uint, event *pl.Event) (*ecpl.ChemoPlan, error) { + // get chemo protocol + chemo, err := getChemoProtocol(patientId, event) + if err != nil { + return nil, err + } + + if chemo.Status_Code != erc.DVCVerified { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-not-match", + Detail: fmt.Sprintf("protocol chemo not yet verified"), + } + return nil, pl.SetLogError(event, chemo) + } + + if now.Before(*chemo.StartDate) || now.After(*chemo.EndDate) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "invalid-date-range", + Detail: "chemo cannot be performed because the current date is outside the allowed treatment window.", + } + return nil, pl.SetLogError(event, chemo) + } + + if chemo.ChemoPlans == nil || len(*chemo.ChemoPlans) == 0 { + return nil, nil + } + + // Return the first chemo plan + return &(*chemo.ChemoPlans)[0], nil +} diff --git a/internal/use-case/main-use-case/soapi/helper.go b/internal/use-case/main-use-case/soapi/helper.go index 7d5e7825..24f87465 100644 --- a/internal/use-case/main-use-case/soapi/helper.go +++ b/internal/use-case/main-use-case/soapi/helper.go @@ -77,20 +77,10 @@ func validateIfEncounterIsChemo(encounterId uint, event *pl.Event) (*ecpl.ChemoP return nil, pl.SetLogError(event, nil) } - if chemo.Status_Code != erc.DVCVerified { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-not-match", - Detail: fmt.Sprintf("protocol chemo not yet verified"), - } + if err = chemoProtocoValidation(chemo, event); err != nil { + } - now := time.Now() - if now.Before(*chemo.StartDate) || now.After(*chemo.EndDate) { - return nil, nil - } - - // --- 3. Must have at least one ChemoPlan --- if chemo.ChemoPlans == nil || len(*chemo.ChemoPlans) == 0 { return nil, nil } @@ -99,6 +89,31 @@ func validateIfEncounterIsChemo(encounterId uint, event *pl.Event) (*ecpl.ChemoP return &(*chemo.ChemoPlans)[0], nil } +func chemoProtocoValidation(chemo *ecp.ChemoProtocol, event *pl.Event) error { + if chemo.Status_Code != erc.DVCVerified { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-not-match", + Detail: fmt.Sprintf("protocol chemo not yet verified"), + } + + return pl.SetLogError(event, chemo) + } + + now := time.Now() + if now.Before(*chemo.StartDate) || now.After(*chemo.EndDate) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "invalid-date-range", + Detail: "chemo cannot be performed because the current date is outside the allowed treatment window.", + } + + return pl.SetLogError(event, chemo) + } + + return nil +} + func getEncounter(encounterId uint, event *pl.Event) (*ee.Encounter, error) { pl.SetLogInfo(event, nil, "started", "getEncounter") data := ee.Encounter{} From 6daa97459bf24195fe8d9c2879d35bf5226010cd Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 12 Dec 2025 10:12:53 +0700 Subject: [PATCH 7/9] on going chemo plan --- .../main-handler/encounter/handler.go | 26 ------------------- internal/use-case/main-use-case/chemo/case.go | 1 + 2 files changed, 1 insertion(+), 26 deletions(-) diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index 70231efb..8b8babc9 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -338,29 +338,3 @@ func (obj myBase) CreateWithPatient(w http.ResponseWriter, r *http.Request) { res, err := u.CreateWithPatient(dto) rw.DataResponse(w, res, err) } - -func (obj myBase) CreateWithPatient(w http.ResponseWriter, r *http.Request) { - authInfo, err := pa.GetAuthInfo(r) - if err != nil { - rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) - } - - dto := e.CreateWithPatientDto{} - if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { - return - } - - // validate SubClass - if err := verifyClassCode(dto.Encounter); err != nil { - rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationFail, - Message: err.Error(), - }) - return - } - - dto.Encounter.AuthInfo = *authInfo - dto.Patient.AuthInfo = *authInfo - res, err := u.CreateWithPatient(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 b940fa37..a922c74a 100644 --- a/internal/use-case/main-use-case/chemo/case.go +++ b/internal/use-case/main-use-case/chemo/case.go @@ -330,6 +330,7 @@ func Verify(input e.VerifyDto) (*d.Data, error) { }) if err != nil { + return nil, err } return &d.Data{ From cfdfac223f9b9c6b08769abc745daa8975e544e8 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 12 Dec 2025 13:48:34 +0700 Subject: [PATCH 8/9] protocol chemo finish --- .../domain/main-entities/chemo-plan/dto.go | 43 ++++----- .../domain/main-entities/chemo-plan/entity.go | 22 ++--- internal/domain/main-entities/chemo/dto.go | 1 + .../main-handler/chemo-plan/handler.go | 88 +++++++++++++++++++ .../main-handler/chemo-protocol/handler.go | 5 +- .../interface/main-handler/main-handler.go | 4 + .../use-case/main-use-case/chemo-plan/case.go | 6 +- .../main-use-case/chemo-plan/helper.go | 18 ++-- .../use-case/main-use-case/chemo-plan/lib.go | 12 ++- .../use-case/main-use-case/chemo/helper.go | 1 + internal/use-case/main-use-case/chemo/lib.go | 5 ++ .../main-use-case/encounter/helper.go | 86 ++++++++++++++---- internal/use-case/main-use-case/soapi/case.go | 16 +++- .../use-case/main-use-case/soapi/helper.go | 74 ++++------------ 14 files changed, 261 insertions(+), 120 deletions(-) create mode 100644 internal/interface/main-handler/chemo-plan/handler.go diff --git a/internal/domain/main-entities/chemo-plan/dto.go b/internal/domain/main-entities/chemo-plan/dto.go index c5e9cb11..29f47a70 100644 --- a/internal/domain/main-entities/chemo-plan/dto.go +++ b/internal/domain/main-entities/chemo-plan/dto.go @@ -11,14 +11,14 @@ import ( ) type CreateDto struct { - Parent_Id *uint `json:"parent_id"` - SeriesNumber *uint16 `json:"seriesNumber"` - CycleNumber *uint `json:"cycleNumber"` - PlanDate *time.Time `json:"planDate"` - RealizationDate *time.Time `json:"realizationDate"` - Notes *string `json:"notes"` - Status ere.StatusProtocolChemo `json:"status"` - Encounter_Id *uint `json:"encounter_id"` + Parent_Id *uint `json:"parent_id"` + SeriesNumber *uint16 `json:"seriesNumber"` + CycleNumber *uint `json:"cycleNumber"` + PlanDate *time.Time `json:"planDate"` + RealizationDate *time.Time `json:"realizationDate"` + Notes *string `json:"notes"` + Status *ere.StatusProtocolChemo `json:"status"` + Encounter_Id *uint `json:"encounter_id"` } type ReadListDto struct { @@ -47,12 +47,13 @@ type DeleteDto struct { } type FailDto struct { - Id uint `json:"id"` - Reasons *string `json:"reason"` + Id uint `json:"id"` + Reasons string `json:"reasons" validate:"required"` } type FailReason struct { - Date *time.Time `json:"date"` - FailReason *string `json:"failReason"` + Date *time.Time `json:"date"` + FailReason string `json:"failReason"` + Encounter_Id *uint `json:"encounter_id"` } type MetaDto struct { @@ -63,15 +64,15 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Parent_Id *uint `json:"parent_id"` // chemo.Id - Protocol_Id *uint `json:"protocol_id"` - SeriesNumber *uint16 `json:"seriesNumber"` // series ke - - CycleNumber *uint `json:"cycleNumber"` // cycle ke - - PlanDate *time.Time `json:"planDate"` - RealizationDate *time.Time `json:"realizationDate"` - Notes *string `json:"notes"` - Status ere.StatusProtocolChemo `json:"status"` - Reasons *string `json:"reasons"` + Parent_Id *uint `json:"parent_id"` // chemo.Id + Protocol_Id *uint `json:"protocol_id"` + SeriesNumber *uint16 `json:"seriesNumber"` // series ke - + CycleNumber *uint `json:"cycleNumber"` // cycle ke - + PlanDate *time.Time `json:"planDate"` + RealizationDate *time.Time `json:"realizationDate"` + Notes *string `json:"notes"` + Status *ere.StatusProtocolChemo `json:"status"` + Reasons *string `json:"reasons"` } func (d ChemoPlan) ToResponse() ResponseDto { diff --git a/internal/domain/main-entities/chemo-plan/entity.go b/internal/domain/main-entities/chemo-plan/entity.go index 1fef099e..3698039f 100644 --- a/internal/domain/main-entities/chemo-plan/entity.go +++ b/internal/domain/main-entities/chemo-plan/entity.go @@ -11,15 +11,15 @@ import ( type ChemoPlan struct { ecore.Main - Parent_Id *uint `json:"parent_id"` // chemo.Id - Protocol_Id *uint `json:"protocol_id"` - SeriesNumber *uint16 `json:"seriesNumber"` // series ke - - CycleNumber *uint `json:"cycleNumber"` // cycle ke - - PlanDate *time.Time `json:"planDate"` - RealizationDate *time.Time `json:"realizationDate"` - Notes *string `json:"notes"` - Status ere.StatusProtocolChemo `json:"status"` - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` - Reasons *string `json:"reasons"` // json + Parent_Id *uint `json:"parent_id"` // chemo.Id + Protocol_Id *uint `json:"protocol_id"` + SeriesNumber *uint16 `json:"seriesNumber"` // series ke - + CycleNumber *uint `json:"cycleNumber"` // cycle ke - + PlanDate *time.Time `json:"planDate"` + RealizationDate *time.Time `json:"realizationDate"` + Notes *string `json:"notes"` + Status *ere.StatusProtocolChemo `json:"status"` + Encounter_Id *uint `json:"encounter_id"` + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + Reasons *string `json:"reasons"` // json } diff --git a/internal/domain/main-entities/chemo/dto.go b/internal/domain/main-entities/chemo/dto.go index 737027fb..7a74acf6 100644 --- a/internal/domain/main-entities/chemo/dto.go +++ b/internal/domain/main-entities/chemo/dto.go @@ -39,6 +39,7 @@ type FilterDto struct { VerifiedBy_User_Id *uint `json:"verifiedBy-user-id"` Specialist_Code *string `json:"specialist-code"` Patient_Id *uint `json:"patient-id"` + Class_Code ere.ChemoClassCode `json:"class-code"` } type ReadDetailDto struct { diff --git a/internal/interface/main-handler/chemo-plan/handler.go b/internal/interface/main-handler/chemo-plan/handler.go new file mode 100644 index 00000000..215797b0 --- /dev/null +++ b/internal/interface/main-handler/chemo-plan/handler.go @@ -0,0 +1,88 @@ +package chemo_protocol + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + e "simrs-vx/internal/domain/main-entities/chemo-plan" + ep "simrs-vx/internal/domain/main-entities/chemo-protocol" + u "simrs-vx/internal/use-case/main-use-case/chemo-plan" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := ep.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) +} + +func (obj myBase) Fail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.FailDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + dto.Id = uint(id) + res, err := u.Fail(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/chemo-protocol/handler.go b/internal/interface/main-handler/chemo-protocol/handler.go index 92410188..4ac8d90f 100644 --- a/internal/interface/main-handler/chemo-protocol/handler.go +++ b/internal/interface/main-handler/chemo-protocol/handler.go @@ -81,11 +81,8 @@ 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 = uint(id) + authInfo, err := pa.GetAuthInfo(r) if err != nil { rw.WriteJSON(w, http.StatusUnauthorized, d.IS{"message": err.Error()}, nil) diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 937246fb..be90705d 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -81,6 +81,7 @@ import ( /******************** sources ********************/ antibioticsrc "simrs-vx/internal/interface/main-handler/antibiotic-src" antibioticsrccat "simrs-vx/internal/interface/main-handler/antibiotic-src-category" + chemoplan "simrs-vx/internal/interface/main-handler/chemo-plan" chemoprotocol "simrs-vx/internal/interface/main-handler/chemo-protocol" device "simrs-vx/internal/interface/main-handler/device" diagnosesrc "simrs-vx/internal/interface/main-handler/diagnose-src" @@ -323,6 +324,9 @@ func SetRoutes() http.Handler { "PATCH /{id}/verify": chemoprotocol.O.Verify, "PATCH /{id}/reject": chemoprotocol.O.Reject, }) + hk.GroupRoutes("/v1/chemo-plan", r, auth.GuardMW, hk.MapHandlerFunc{ + "PATCH /{id}/fail": chemoplan.O.Fail, + }) hc.RegCrud(r, "/v1/upload-file", uploadfile.O) hc.RegCrud(r, "/v1/encounter-document", encounterdocument.O) hc.RegCrud(r, "/v1/general-consent", generalconsent.O) diff --git a/internal/use-case/main-use-case/chemo-plan/case.go b/internal/use-case/main-use-case/chemo-plan/case.go index 7f74b6f7..eff45b46 100644 --- a/internal/use-case/main-use-case/chemo-plan/case.go +++ b/internal/use-case/main-use-case/chemo-plan/case.go @@ -176,7 +176,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - if err := UpdateData(data, &event, tx); err != nil { + if err := UpdateData(data, "", &event, tx); err != nil { return err } @@ -284,7 +284,7 @@ func Fail(input e.FailDto) (*d.Data, error) { return nil, pl.SetLogError(&event, data) } - if data.Status != ere.SPCPlanned { + if *data.Status != ere.SPCPlanned { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "invalid-chemo-status", @@ -293,7 +293,7 @@ func Fail(input e.FailDto) (*d.Data, error) { } err = dg.I.Transaction(func(tx *gorm.DB) error { - if err := UpdateData(data, &event, tx); err != nil { + if err := UpdateFailData(input, data, &event, tx); err != nil { return err } diff --git a/internal/use-case/main-use-case/chemo-plan/helper.go b/internal/use-case/main-use-case/chemo-plan/helper.go index 5cf7ffd5..4d62f819 100644 --- a/internal/use-case/main-use-case/chemo-plan/helper.go +++ b/internal/use-case/main-use-case/chemo-plan/helper.go @@ -34,16 +34,23 @@ func setDataCreate(input *ep.CreateDto) (data []e.ChemoPlan) { return } -func setDataUpdate(data *e.ChemoPlan) { +func setDataCreateSoapi(data *e.ChemoPlan) { data.RealizationDate = &now status := ere.SPCComplete - data.Status = status + data.Status = &status +} + +func setDataDeleteSoapi(data *e.ChemoPlan) { + status := ere.SPCPlanned + data.Status = &status } func setDatafail(input e.FailDto, data *e.ChemoPlan) { data.RealizationDate = nil - data.Status = ere.SPCSchedule + + status := ere.SPCSchedule + data.Status = &status var reasons []e.FailReason @@ -52,8 +59,9 @@ func setDatafail(input e.FailDto, data *e.ChemoPlan) { } reasons = append(reasons, e.FailReason{ - FailReason: input.Reasons, - Date: &now, + FailReason: input.Reasons, + Date: &now, + Encounter_Id: data.Encounter_Id, }) if b, err := json.Marshal(reasons); err == nil { diff --git a/internal/use-case/main-use-case/chemo-plan/lib.go b/internal/use-case/main-use-case/chemo-plan/lib.go index 92d7c8eb..22d6259b 100644 --- a/internal/use-case/main-use-case/chemo-plan/lib.go +++ b/internal/use-case/main-use-case/chemo-plan/lib.go @@ -98,9 +98,15 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e return &data, nil } -func UpdateData(data *e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { +func UpdateData(data *e.ChemoPlan, method string, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBUpdate") - setDataUpdate(data) + + switch method { + case "c": + setDataCreateSoapi(data) + case "d": + setDataDeleteSoapi(data) + } var tx *gorm.DB if len(dbx) > 0 { @@ -148,7 +154,7 @@ func DeleteData(data *e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { func UpdateFailData(input e.FailDto, data *e.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, data, "started", "DBUpdate") - setDataUpdate(data) + setDatafail(input, data) var tx *gorm.DB if len(dbx) > 0 { diff --git a/internal/use-case/main-use-case/chemo/helper.go b/internal/use-case/main-use-case/chemo/helper.go index c241bce3..96012228 100644 --- a/internal/use-case/main-use-case/chemo/helper.go +++ b/internal/use-case/main-use-case/chemo/helper.go @@ -20,4 +20,5 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Chemo) { data.Encounter_Id = inputSrc.Encounter_Id data.Status_Code = inputSrc.Status_Code data.Specialist_Code = inputSrc.Specialist_Code + data.Class_Code = inputSrc.Class_Code } diff --git a/internal/use-case/main-use-case/chemo/lib.go b/internal/use-case/main-use-case/chemo/lib.go index 4b049c13..8b848d8f 100644 --- a/internal/use-case/main-use-case/chemo/lib.go +++ b/internal/use-case/main-use-case/chemo/lib.go @@ -47,6 +47,11 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Ch tx = dg.I } + if input.Class_Code != "" { + tx = tx.Where(`"Chemo"."Class_Code" = ?`, input.Class_Code) + input.Class_Code = "" + } + tx = tx. Model(&e.Chemo{}). Joins(`LEFT JOIN "Encounter" "e" ON "e"."Id" = "Chemo"."Encounter_Id"`). diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 0d822768..29d7fa9b 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -660,21 +660,24 @@ func insertdataClassCode(input e.CreateDto, soapiData []es.CreateDto, event *pl. func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, event *pl.Event, tx *gorm.DB) (err error) { subCode := ere.AmbulatoryClassCode(*input.SubClass_Code) + var chemoPlan *ecpl.ChemoPlan switch { case subCode == ere.ACCChemo: // validate if encounter Chemo valid - chemoProtoc, err := validateChemoAdm(*input.Patient_Id, event) + chemoPlan, err = validateChemo(*input.Patient_Id, event) if err != nil { return err } - if chemoProtoc == nil { + if chemoPlan == nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-not-found", Detail: "chemo plan not found", } + + return pl.SetLogError(event, input) } chemoCreate := ec.CreateDto{ @@ -691,7 +694,8 @@ func insertDataSubClassAmbulatory(input e.CreateDto, soapiData []es.CreateDto, e } // set chemo-plan to planned - err = updateChemoPlan(chemoProtoc, event, tx) + chemoPlan.Encounter_Id = &input.Id + err = updateChemoPlan(chemoPlan, event, tx) if err != nil { return err } @@ -898,7 +902,10 @@ func updateChemoPlan(data *ecpl.ChemoPlan, event *pl.Event, dbx ...*gorm.DB) err tx = dg.I } - if err := tx.Model(&data).Update(`"Status"`, ere.SPCPlanned).Error; err != nil { + err := tx.Model(&data).Updates(map[string]interface{}{ + `"Status"`: ere.SPCPlanned, + `"Encounter_Id"`: data.Encounter_Id}).Error + if err != nil { return err } @@ -913,11 +920,15 @@ func getChemoProtocol(patientId uint, event *pl.Event) (*ecp.ChemoProtocol, erro var tx = dg.I tx = tx.Model(&ecp.ChemoProtocol{}). - Joins(`"ChemoPlan" cp ON cp."Protocol_Id" = "ChemoProtocol"."Id"`). - Where(`"Patient.Id" = ? AND cp."Status" IS NULL`, patientId). + Joins(`LEFT JOIN "ChemoPlan" cp ON cp."Protocol_Id" = "ChemoProtocol"."Id"`). + Where(`"ChemoProtocol"."Patient_Id" = ?`, patientId). Preload("ChemoPlans", func(db *gorm.DB) *gorm.DB { - return tx.Order(`"Id" ASC`).Limit(1) + return db. + Where(`"Status" IS NULL OR "Status" = ?`, ere.SPCSchedule). + Order(`"Id" ASC`). + Limit(1) }). + Order(`"CreatedAt" DESC`). First(&data) if err := tx.Error; err != nil { @@ -928,35 +939,80 @@ func getChemoProtocol(patientId uint, event *pl.Event) (*ecp.ChemoProtocol, erro return &data, nil } -func validateChemoAdm(patientId uint, event *pl.Event) (*ecpl.ChemoPlan, error) { +func validateChemo(patientId uint, event *pl.Event) (*ecpl.ChemoPlan, error) { + // get chemo adm + chemoAdm, err := getChemoAdm(patientId, event) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-not-found", + Detail: "patient doesn't have active chemo", + } + + return nil, pl.SetLogError(event, patientId) + } + return nil, err + } + + // validate is chemo verified + if chemoAdm.Status_Code != erc.DVCVerified { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-not-match", + Detail: fmt.Sprintf("chemo not yet verified"), + } + return nil, pl.SetLogError(event, chemoAdm) + } + // get chemo protocol - chemo, err := getChemoProtocol(patientId, event) + chemoProtocol, err := getChemoProtocol(patientId, event) if err != nil { return nil, err } - if chemo.Status_Code != erc.DVCVerified { + if chemoProtocol.Status_Code != erc.DVCVerified { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-not-match", Detail: fmt.Sprintf("protocol chemo not yet verified"), } - return nil, pl.SetLogError(event, chemo) + return nil, pl.SetLogError(event, chemoProtocol) } - if now.Before(*chemo.StartDate) || now.After(*chemo.EndDate) { + if now.Before(*chemoProtocol.StartDate) || now.After(*chemoProtocol.EndDate) { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "invalid-date-range", Detail: "chemo cannot be performed because the current date is outside the allowed treatment window.", } - return nil, pl.SetLogError(event, chemo) + return nil, pl.SetLogError(event, chemoProtocol) } - if chemo.ChemoPlans == nil || len(*chemo.ChemoPlans) == 0 { + if chemoProtocol.ChemoPlans == nil || len(*chemoProtocol.ChemoPlans) == 0 { return nil, nil } // Return the first chemo plan - return &(*chemo.ChemoPlans)[0], nil + return &(*chemoProtocol.ChemoPlans)[0], nil +} + +func getChemoAdm(patientId uint, event *pl.Event) (*ec.Chemo, error) { + pl.SetLogInfo(event, nil, "started", "getChemoProtocol") + data := ec.Chemo{} + + var tx = dg.I + + tx = tx.Model(&ec.Chemo{}). + Joins(`LEFT JOIN "Encounter" e ON e."Id" = "Chemo"."Encounter_Id"`). + Where(`e."Patient_Id" = ? AND "Chemo"."Class_Code" = ?`, patientId, ere.CCCAdm). + Order(`"CreatedAt" DESC`). + First(&data) + + if err := tx.Error; err != nil { + return nil, err + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil } diff --git a/internal/use-case/main-use-case/soapi/case.go b/internal/use-case/main-use-case/soapi/case.go index 6b93fd8a..f74ef594 100644 --- a/internal/use-case/main-use-case/soapi/case.go +++ b/internal/use-case/main-use-case/soapi/case.go @@ -42,7 +42,7 @@ func Create(input e.CreateDto) (*d.Data, error) { return nil, pl.SetLogError(&event, input) } - chemoPlan, err := validateIfEncounterIsChemo(*input.Encounter_Id, &event) + chemoPlan, err := validateIfEncounterIsChemo(*input.Encounter_Id, "c", &event) if err != nil { return nil, err } @@ -57,7 +57,7 @@ func Create(input e.CreateDto) (*d.Data, error) { // update chemoPlan if chemoPlan != nil { chemoPlan.Encounter_Id = input.Encounter_Id - if err = ucp.UpdateData(chemoPlan, &event, tx); err != nil { + if err = ucp.UpdateData(chemoPlan, "c", &event, tx); err != nil { return err } } @@ -252,6 +252,18 @@ func Delete(input e.DeleteDto) (*d.Data, error) { return err } + // update chemoPlan + chemoPlan, err := validateIfEncounterIsChemo(*data.Encounter_Id, "d", &event) + if err != nil { + return err + } + + if chemoPlan != nil { + if err = ucp.UpdateData(chemoPlan, "d", &event, tx); err != nil { + return err + } + } + mwRunner.setMwType(pu.MWTPre) // Run pre-middleware if err = mwRunner.ExecuteIfSyncOn(func() error { diff --git a/internal/use-case/main-use-case/soapi/helper.go b/internal/use-case/main-use-case/soapi/helper.go index 24f87465..74a63b80 100644 --- a/internal/use-case/main-use-case/soapi/helper.go +++ b/internal/use-case/main-use-case/soapi/helper.go @@ -6,11 +6,7 @@ package soapi import ( "errors" - "fmt" - erc "simrs-vx/internal/domain/references/common" pl "simrs-vx/pkg/logger" - pu "simrs-vx/pkg/use-case-helper" - "time" dg "github.com/karincake/apem/db-gorm-pg" "gorm.io/gorm" @@ -55,7 +51,7 @@ func setBulkData(input []e.CreateDto, encounterId uint) []e.Soapi { return data } -func validateIfEncounterIsChemo(encounterId uint, event *pl.Event) (*ecpl.ChemoPlan, error) { +func validateIfEncounterIsChemo(encounterId uint, method string, event *pl.Event) (*ecpl.ChemoPlan, error) { // get encounter enc, err := getEncounter(encounterId, event) if err != nil { @@ -64,12 +60,12 @@ func validateIfEncounterIsChemo(encounterId uint, event *pl.Event) (*ecpl.ChemoP // Encounter must be Ambulatory and NOT Rehab a := enc.Ambulatory - if enc.Class_Code != ere.ECAmbulatory || a == nil || a.Class_Code == ere.ACCRehab { + if a == nil || a.Class_Code == ere.ACCRehab { return nil, nil } // get chemo protocol - chemo, err := getChemo(*enc.Patient_Id, event) + chemo, err := getChemo(*enc.Patient_Id, enc.Id, event) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, nil @@ -77,10 +73,6 @@ func validateIfEncounterIsChemo(encounterId uint, event *pl.Event) (*ecpl.ChemoP return nil, pl.SetLogError(event, nil) } - if err = chemoProtocoValidation(chemo, event); err != nil { - - } - if chemo.ChemoPlans == nil || len(*chemo.ChemoPlans) == 0 { return nil, nil } @@ -89,69 +81,39 @@ func validateIfEncounterIsChemo(encounterId uint, event *pl.Event) (*ecpl.ChemoP return &(*chemo.ChemoPlans)[0], nil } -func chemoProtocoValidation(chemo *ecp.ChemoProtocol, event *pl.Event) error { - if chemo.Status_Code != erc.DVCVerified { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "data-not-match", - Detail: fmt.Sprintf("protocol chemo not yet verified"), - } - - return pl.SetLogError(event, chemo) - } - - now := time.Now() - if now.Before(*chemo.StartDate) || now.After(*chemo.EndDate) { - event.Status = "failed" - event.ErrInfo = pl.ErrorInfo{ - Code: "invalid-date-range", - Detail: "chemo cannot be performed because the current date is outside the allowed treatment window.", - } - - return pl.SetLogError(event, chemo) - } - - return nil -} - func getEncounter(encounterId uint, event *pl.Event) (*ee.Encounter, error) { pl.SetLogInfo(event, nil, "started", "getEncounter") data := ee.Encounter{} var tx = dg.I - if err := tx.Model(&ee.Encounter{}). - Where(`"Encounter_Id" = ?`, encounterId). - Scopes(withConditionalAmbulatory()). - First(&data).Error; err != nil { - if processedErr := pu.HandleReadError(err, event, source, nil, encounterId); processedErr != nil { - return nil, processedErr - } + err := tx.Model(&ee.Encounter{}). + Joins(`LEFT JOIN "Ambulatory" a ON a."Encounter_Id" = "Encounter"."Id"`). + Where(`"Encounter"."Id" = ?`, encounterId). + Where(`"Encounter"."Class_Code" = ?`, ere.ECAmbulatory). + Preload("Ambulatory"). + First(&data).Error + + if err != nil { + return nil, err } pl.SetLogInfo(event, nil, "complete") return &data, nil } -func withConditionalAmbulatory() func(db *gorm.DB) *gorm.DB { - return func(db *gorm.DB) *gorm.DB { - return db.Preload(`"Ambulatory"`, func(db *gorm.DB) *gorm.DB { - return db.Joins(`JOIN "Encounter" e ON "e"."Id" = "Ambulatory"."Encounter_Id"`) - }) - } -} - -func getChemo(patientId uint, event *pl.Event) (*ecp.ChemoProtocol, error) { - pl.SetLogInfo(event, nil, "started", "getChemoFromEncounter") +func getChemo(patientId, encounterId uint, event *pl.Event) (*ecp.ChemoProtocol, error) { + pl.SetLogInfo(event, nil, "started", "getChemo") data := ecp.ChemoProtocol{} var tx = dg.I tx = tx.Model(&ecp.ChemoProtocol{}). - Joins(`"ChemoPlan" cp ON cp."Protocol_Id" = "ChemoProtocol"."Id"`). - Where(`"Patient.Id" = ? AND cp."Status" = ?`, patientId, ere.SPCPlanned). + Joins(`LEFT JOIN "ChemoPlan" cp ON cp."Protocol_Id" = "ChemoProtocol"."Id"`). + Where(`"ChemoProtocol"."Patient_Id" = ?`, patientId). Preload("ChemoPlans", func(db *gorm.DB) *gorm.DB { - return tx.Order(`"Id" ASC`).Limit(1) + return db. + Where(`"Encounter_Id" = ?`, encounterId) }). First(&data) From 10ae092334ca6f7a5a7af56cc2deb407bd0b9648 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 12 Dec 2025 13:56:56 +0700 Subject: [PATCH 9/9] delete unnessasry code --- .../main-use-case/chemo-protocol/helper.go | 52 ------------------- .../main-use-case/chemo-protocol/lib.go | 2 +- .../use-case/main-use-case/patient/lib.go | 1 - .../main-use-case/prescription-item/lib.go | 2 +- .../new/specialist/lib.go | 2 +- 5 files changed, 3 insertions(+), 56 deletions(-) diff --git a/internal/use-case/main-use-case/chemo-protocol/helper.go b/internal/use-case/main-use-case/chemo-protocol/helper.go index 36ff70a6..73e487f5 100644 --- a/internal/use-case/main-use-case/chemo-protocol/helper.go +++ b/internal/use-case/main-use-case/chemo-protocol/helper.go @@ -5,16 +5,6 @@ Any functions that are used internally by the use-case package chemo_protocol import ( - 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" - - erc "simrs-vx/internal/domain/references/common" - ere "simrs-vx/internal/domain/references/encounter" - - ec "simrs-vx/internal/domain/main-entities/chemo" e "simrs-vx/internal/domain/main-entities/chemo-protocol" ) @@ -39,45 +29,3 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.ChemoProtocol) { data.Status_Code = inputSrc.Status_Code data.Patient_Id = inputSrc.Patient_Id } - -func getChemoEncounterReg(event *pl.Event) (*ec.Chemo, error) { - pl.SetLogInfo(event, nil, "started", "DBReadChemoEncounterReg") - data := ec.Chemo{} - - var tx = dg.I - - if err := tx.Model(&ec.Chemo{}). - Joins(`LEFT JOIN "Encounter" "e" ON "e"."Id" = "Chemo"."Encounter_Id"`). - Joins(`LEFT JOIN "Ambulatory" "a" ON "a"."Encounter_Id" = "e"."Id"`). - Where(`"Chemo"."Status_Code" = ? AND "a"."Class_Code" = ?`, erc.DVCVerified, ere.ACCReg). - Order("\"CreatedAt\" DESC"). - First(&data). - Error; err != nil { - if processedErr := pu.HandleReadError(err, event, source, nil, data); processedErr != nil { - return nil, processedErr - } - } - - pl.SetLogInfo(event, nil, "complete") - return &data, nil -} - -func getChemoEncounter(encounterId *uint, event *pl.Event) (*ec.Chemo, error) { - pl.SetLogInfo(event, encounterId, "started", "DBReadChemoEncounter") - data := ec.Chemo{} - - var tx = dg.I - - if err := tx.Model(&ec.Chemo{}). - Where(`"Encounter_Id" = ? AND "Status_Code"`, encounterId, erc.DVCVerified). - Scopes(gh.Preload("Encounter.Ambulatory")). - First(&data). - Error; err != nil { - if processedErr := pu.HandleReadError(err, event, source, nil, data); processedErr != nil { - return nil, processedErr - } - } - - pl.SetLogInfo(event, nil, "complete") - return &data, nil -} diff --git a/internal/use-case/main-use-case/chemo-protocol/lib.go b/internal/use-case/main-use-case/chemo-protocol/lib.go index abb19224..44a0bc80 100644 --- a/internal/use-case/main-use-case/chemo-protocol/lib.go +++ b/internal/use-case/main-use-case/chemo-protocol/lib.go @@ -57,7 +57,7 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Ch Scopes(gh.Paginate(input, &pagination)). Order("\"CreatedAt\" DESC") - if err := tx.Debug().Find(&data).Error; err != nil { + if err := tx.Find(&data).Error; err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { return nil, &meta, nil } diff --git a/internal/use-case/main-use-case/patient/lib.go b/internal/use-case/main-use-case/patient/lib.go index b4e5d8b9..e1a5fc42 100644 --- a/internal/use-case/main-use-case/patient/lib.go +++ b/internal/use-case/main-use-case/patient/lib.go @@ -51,7 +51,6 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Pa } tx = tx. - Debug(). Model(&e.Patient{}). Scopes(gh.Preload(input.Includes)). Scopes(gh.Filter(input.FilterDto)). diff --git a/internal/use-case/main-use-case/prescription-item/lib.go b/internal/use-case/main-use-case/prescription-item/lib.go index aa5c9e2b..1c21304b 100644 --- a/internal/use-case/main-use-case/prescription-item/lib.go +++ b/internal/use-case/main-use-case/prescription-item/lib.go @@ -47,7 +47,7 @@ func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Pr tx = dg.I } - tx = tx.Debug(). + tx = tx. Model(&e.PrescriptionItem{}). Scopes(gh.Preload(input.Includes)). Scopes(gh.Filter(input.FilterDto)). diff --git a/internal/use-case/simgos-sync-use-case/new/specialist/lib.go b/internal/use-case/simgos-sync-use-case/new/specialist/lib.go index 787c0039..26cc8efc 100644 --- a/internal/use-case/simgos-sync-use-case/new/specialist/lib.go +++ b/internal/use-case/simgos-sync-use-case/new/specialist/lib.go @@ -31,7 +31,7 @@ func CreateSimgosData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*esi tx = dg.IS["simrs"] } - if err := tx.Debug().Create(&data).Error; err != nil { + if err := tx.Create(&data).Error; err != nil { return nil, plh.HandleCreateError(input, event, err) }