From b8275094351c918d736f1ddf00fe1f7fcb96e393 Mon Sep 17 00:00:00 2001 From: vanilia Date: Tue, 21 Oct 2025 14:43:13 +0700 Subject: [PATCH 01/12] import cycle found --- internal/domain/main-entities/encounter/dto.go | 3 +++ .../main-entities/internal-reference/dto.go | 15 ++++++--------- .../interface/main-handler/encounter/handler.go | 6 ++++++ 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 67810d27..8962d7c2 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -8,6 +8,7 @@ import ( ea "simrs-vx/internal/domain/main-entities/appointment" ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/employee" + eir "simrs-vx/internal/domain/main-entities/internal-reference" ep "simrs-vx/internal/domain/main-entities/patient" es "simrs-vx/internal/domain/main-entities/specialist" ess "simrs-vx/internal/domain/main-entities/subspecialist" @@ -100,6 +101,8 @@ type DischargeDto struct { MedicalDischargeEducation *string `json:"medicalDischargeEducation"` AdmDischargeEducation *string `json:"admDischargeEducation"` DischargeReason *string `json:"dischargeReason"` + DeathCause *string `json:"deathCause"` + InternalReferences *[]eir.CreateDto `json:"internalReferences,omitempty"` } type ResponseDto struct { diff --git a/internal/domain/main-entities/internal-reference/dto.go b/internal/domain/main-entities/internal-reference/dto.go index 6c71e6f1..360c3193 100644 --- a/internal/domain/main-entities/internal-reference/dto.go +++ b/internal/domain/main-entities/internal-reference/dto.go @@ -3,12 +3,11 @@ package internal_reference import ( ecore "simrs-vx/internal/domain/base-entities/core" ed "simrs-vx/internal/domain/main-entities/doctor" - ee "simrs-vx/internal/domain/main-entities/encounter" eu "simrs-vx/internal/domain/main-entities/unit" ) type CreateDto struct { - Encounter_Id *uint `json:"encounter_id"` + Encounter_Id *uint `json:"-"` Unit_Id *uint16 `json:"unit_id"` Doctor_Id *uint `json:"doctor_Id"` } @@ -47,18 +46,16 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Encounter_Id *uint `json:"encounter_id"` - Encounter *ee.Encounter `json:"encounter,omitempty"` - Unit_Id *uint16 `json:"unit_id"` - Unit *eu.Unit `json:"unit,omitempty"` - Doctor_Id *uint `json:"doctor_id"` - Doctor *ed.Doctor `json:"doctor,omitempty"` + Encounter_Id *uint `json:"encounter_id"` + Unit_Id *uint16 `json:"unit_id"` + Unit *eu.Unit `json:"unit,omitempty"` + Doctor_Id *uint `json:"doctor_id"` + Doctor *ed.Doctor `json:"doctor,omitempty"` } func (d InternalReference) ToResponse() ResponseDto { resp := ResponseDto{ Encounter_Id: d.Encounter_Id, - Encounter: d.Encounter, Unit_Id: d.Unit_Id, Unit: d.Unit, Doctor_Id: d.Doctor_Id, diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index 96063298..acbb5e08 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -12,6 +12,7 @@ import ( u "simrs-vx/internal/use-case/main-use-case/encounter" erc "simrs-vx/internal/domain/references/common" + ere "simrs-vx/internal/domain/references/encounter" pa "simrs-vx/pkg/auth-helper" @@ -88,9 +89,14 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { if id <= 0 { return } + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { return } + + // validate request body + if dto.DischargeMethod_Code == ere. + dto.Id = uint(id) res, err := u.CheckOut(dto) rw.DataResponse(w, res, err) From a795ccd7470b9336c9b7a42583f5346471e03a2c Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 09:03:19 +0700 Subject: [PATCH 02/12] add adjustment checkout --- .../domain/main-entities/death-cause/dto.go | 62 +++++ .../domain/main-entities/encounter/dto.go | 2 +- .../main-handler/encounter/handler.go | 42 +++- .../ambulance-transport-req/case.go | 2 +- .../ambulance-transport-req/helper.go | 2 +- .../ambulance-transport-req/lib.go | 2 +- .../middleware-runner.go | 2 +- .../ambulance-transport-req/middleware.go | 2 +- .../ambulance-transport-req/tycovar.go | 2 +- .../main-use-case/death-cause/case.go | 228 ++++++++++++++++++ .../main-use-case/death-cause/helper.go | 22 ++ .../use-case/main-use-case/death-cause/lib.go | 140 +++++++++++ .../death-cause/middleware-runner.go | 103 ++++++++ .../main-use-case/death-cause/middleware.go | 9 + .../main-use-case/death-cause/tycovar.go | 44 ++++ .../use-case/main-use-case/encounter/case.go | 70 +++++- .../main-use-case/encounter/helper.go | 40 ++- .../main-use-case/internal-reference/case.go | 49 ---- .../internal-reference/helper.go | 14 ++ .../main-use-case/internal-reference/lib.go | 21 ++ 20 files changed, 797 insertions(+), 61 deletions(-) create mode 100644 internal/domain/main-entities/death-cause/dto.go create mode 100644 internal/use-case/main-use-case/death-cause/case.go create mode 100644 internal/use-case/main-use-case/death-cause/helper.go create mode 100644 internal/use-case/main-use-case/death-cause/lib.go create mode 100644 internal/use-case/main-use-case/death-cause/middleware-runner.go create mode 100644 internal/use-case/main-use-case/death-cause/middleware.go create mode 100644 internal/use-case/main-use-case/death-cause/tycovar.go diff --git a/internal/domain/main-entities/death-cause/dto.go b/internal/domain/main-entities/death-cause/dto.go new file mode 100644 index 00000000..8781ace8 --- /dev/null +++ b/internal/domain/main-entities/death-cause/dto.go @@ -0,0 +1,62 @@ +package death_cause + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" +) + +type CreateDto struct { + Encounter_Id *uint `json:"-"` + Value *string `json:"value"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Encounter_Id *uint `json:"encounter_id"` + Value *string `json:"value"` +} + +func (d DeathCause) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Value: d.Value, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []DeathCause) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 7fc15284..4a30281d 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -96,7 +96,7 @@ type MetaDto struct { } type DischargeDto struct { Id uint `json:"id"` - DischargeMethod_Code *ere.DischargeMethodCode `json:"dischargeMethod_code" validate:"maxLength=16"` + Discharge_Method_Code *ere.DischargeMethodCode `json:"discharge_method_code" validate:"maxLength=16"` EarlyEducation *string `json:"earlyEducation"` MedicalDischargeEducation *string `json:"medicalDischargeEducation"` AdmDischargeEducation *string `json:"admDischargeEducation"` diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index acbb5e08..6cf91832 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -1,6 +1,7 @@ package encounter import ( + "fmt" "net/http" rw "github.com/karincake/risoles" @@ -84,6 +85,8 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { } func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { + const dataValidationVail = "data-validation-fail" + dto := e.DischargeDto{} id := rw.ValidateInt(w, "id", r.PathValue("id")) if id <= 0 { @@ -95,7 +98,44 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { } // validate request body - if dto.DischargeMethod_Code == ere. + { + switch *dto.Discharge_Method_Code { + case ere.DMCDeath: + if dto.DeathCause == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationVail, + Message: "deathCause required if discharge_method_code is death", + }) + return + } + case ere.DMCConsulPoly, ere.DMCConsulExecutive: + if dto.InternalReferences == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationVail, + Message: fmt.Sprintf("internalReferences required if discharge_method_code is %s", *dto.Discharge_Method_Code), + }) + return + } + + for _, v := range *dto.InternalReferences { + if v.Unit_Id == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationVail, + Message: "internalReferences.unit_id required", + }) + return + } + + if v.Doctor_Id == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationVail, + Message: "internalReferences.doctor_id required", + }) + return + } + } + } + } dto.Id = uint(id) res, err := u.CheckOut(dto) diff --git a/internal/use-case/main-use-case/ambulance-transport-req/case.go b/internal/use-case/main-use-case/ambulance-transport-req/case.go index 712d6241..6810fe83 100644 --- a/internal/use-case/main-use-case/ambulance-transport-req/case.go +++ b/internal/use-case/main-use-case/ambulance-transport-req/case.go @@ -1,4 +1,4 @@ -package ambulancetransport +package ambulance_transport_req import ( "errors" diff --git a/internal/use-case/main-use-case/ambulance-transport-req/helper.go b/internal/use-case/main-use-case/ambulance-transport-req/helper.go index 3c739eb0..9ad4122d 100644 --- a/internal/use-case/main-use-case/ambulance-transport-req/helper.go +++ b/internal/use-case/main-use-case/ambulance-transport-req/helper.go @@ -2,7 +2,7 @@ DESCRIPTION: Any functions that are used internally by the use-case */ -package ambulancetransport +package ambulance_transport_req import ( "errors" diff --git a/internal/use-case/main-use-case/ambulance-transport-req/lib.go b/internal/use-case/main-use-case/ambulance-transport-req/lib.go index e8555a6f..eca1c5b9 100644 --- a/internal/use-case/main-use-case/ambulance-transport-req/lib.go +++ b/internal/use-case/main-use-case/ambulance-transport-req/lib.go @@ -1,4 +1,4 @@ -package ambulancetransport +package ambulance_transport_req import ( "errors" diff --git a/internal/use-case/main-use-case/ambulance-transport-req/middleware-runner.go b/internal/use-case/main-use-case/ambulance-transport-req/middleware-runner.go index b98c497d..18937f5c 100644 --- a/internal/use-case/main-use-case/ambulance-transport-req/middleware-runner.go +++ b/internal/use-case/main-use-case/ambulance-transport-req/middleware-runner.go @@ -1,4 +1,4 @@ -package ambulancetransport +package ambulance_transport_req import ( atr "simrs-vx/internal/domain/main-entities/ambulance-transport-req" diff --git a/internal/use-case/main-use-case/ambulance-transport-req/middleware.go b/internal/use-case/main-use-case/ambulance-transport-req/middleware.go index 847aa3dd..cc1f7c3e 100644 --- a/internal/use-case/main-use-case/ambulance-transport-req/middleware.go +++ b/internal/use-case/main-use-case/ambulance-transport-req/middleware.go @@ -1,4 +1,4 @@ -package ambulancetransport +package ambulance_transport_req // example of middleware // func init() { diff --git a/internal/use-case/main-use-case/ambulance-transport-req/tycovar.go b/internal/use-case/main-use-case/ambulance-transport-req/tycovar.go index 5d3ac027..54f8f604 100644 --- a/internal/use-case/main-use-case/ambulance-transport-req/tycovar.go +++ b/internal/use-case/main-use-case/ambulance-transport-req/tycovar.go @@ -6,7 +6,7 @@ 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 ambulancetransport +package ambulance_transport_req import ( "gorm.io/gorm" diff --git a/internal/use-case/main-use-case/death-cause/case.go b/internal/use-case/main-use-case/death-cause/case.go new file mode 100644 index 00000000..484c0f66 --- /dev/null +++ b/internal/use-case/main-use-case/death-cause/case.go @@ -0,0 +1,228 @@ +package deathcause + +import ( + + // main entities + edc "simrs-vx/internal/domain/main-entities/death-cause" + "strconv" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" +) + +const source = "death-cause" + +func Create(input edc.CreateDto) (*d.Data, error) { + data := edc.DeathCause{} + + 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 edc.ReadListDto) (*d.Data, error) { + var data *edc.DeathCause + var dataList []edc.DeathCause + var metaList *edc.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: edc.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input edc.ReadDetailDto) (*d.Data, error) { + var data *edc.DeathCause + 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 edc.UpdateDto) (*d.Data, error) { + rdDto := edc.ReadDetailDto{Id: input.Id} + var data *edc.DeathCause + 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 + } + + // Get Updated Data + if data, err = ReadDetailData(rdDto, &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 + +} diff --git a/internal/use-case/main-use-case/death-cause/helper.go b/internal/use-case/main-use-case/death-cause/helper.go new file mode 100644 index 00000000..c655a966 --- /dev/null +++ b/internal/use-case/main-use-case/death-cause/helper.go @@ -0,0 +1,22 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package deathcause + +import ( + e "simrs-vx/internal/domain/main-entities/death-cause" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.DeathCause) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Encounter_Id = inputSrc.Encounter_Id + data.Value = inputSrc.Value +} diff --git a/internal/use-case/main-use-case/death-cause/lib.go b/internal/use-case/main-use-case/death-cause/lib.go new file mode 100644 index 00000000..9f1d548b --- /dev/null +++ b/internal/use-case/main-use-case/death-cause/lib.go @@ -0,0 +1,140 @@ +package deathcause + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/death-cause" + 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.DeathCause, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.DeathCause{} + 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.DeathCause, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.DeathCause{} + 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.DeathCause{}). + 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.DeathCause, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.DeathCause{} + + 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.DeathCause, 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.DeathCause, 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/death-cause/middleware-runner.go b/internal/use-case/main-use-case/death-cause/middleware-runner.go new file mode 100644 index 00000000..3b0b1aa0 --- /dev/null +++ b/internal/use-case/main-use-case/death-cause/middleware-runner.go @@ -0,0 +1,103 @@ +package deathcause + +import ( + e "simrs-vx/internal/domain/main-entities/death-cause" + 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.DeathCause) 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.DeathCause) 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.DeathCause) 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.DeathCause) 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.DeathCause) 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/death-cause/middleware.go b/internal/use-case/main-use-case/death-cause/middleware.go new file mode 100644 index 00000000..845ec256 --- /dev/null +++ b/internal/use-case/main-use-case/death-cause/middleware.go @@ -0,0 +1,9 @@ +package deathcause + +// 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/death-cause/tycovar.go b/internal/use-case/main-use-case/death-cause/tycovar.go new file mode 100644 index 00000000..9cfe21a6 --- /dev/null +++ b/internal/use-case/main-use-case/death-cause/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 deathcause + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/death-cause" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.DeathCause, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.DeathCause, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.DeathCause, 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/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index fad183f6..43ce3cbf 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -2,10 +2,13 @@ package encounter import ( "errors" + "fmt" + uir "simrs-vx/internal/use-case/main-use-case/internal-reference" "strconv" ea "simrs-vx/internal/domain/main-entities/ambulatory" ec "simrs-vx/internal/domain/main-entities/chemo" + edc "simrs-vx/internal/domain/main-entities/death-cause" ee "simrs-vx/internal/domain/main-entities/emergency" eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" @@ -13,6 +16,7 @@ import ( ua "simrs-vx/internal/use-case/main-use-case/ambulatory" uc "simrs-vx/internal/use-case/main-use-case/chemo" + udc "simrs-vx/internal/use-case/main-use-case/death-cause" ue "simrs-vx/internal/use-case/main-use-case/emergency" uem "simrs-vx/internal/use-case/main-use-case/employee" ui "simrs-vx/internal/use-case/main-use-case/inpatient" @@ -20,12 +24,12 @@ import ( erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" - 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" + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" ) @@ -411,6 +415,66 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { } } + switch *input.Discharge_Method_Code { + case ere.DMCDeath: + // insert data death-cause + if _, err := udc.CreateData(edc.CreateDto{ + Encounter_Id: &input.Id, + Value: input.DeathCause, + }, &event, tx); err != nil { + return err + } + + case ere.DMCConsulPoly, ere.DMCConsulExecutive: + unitIDs := make(map[uint16]struct{}) + doctorIDs := make(map[uint]struct{}) + + for _, ref := range *input.InternalReferences { + if ref.Unit_Id != nil { + unitIDs[*ref.Unit_Id] = struct{}{} + } + if ref.Doctor_Id != nil { + doctorIDs[*ref.Doctor_Id] = struct{}{} + } + } + + // validate unitIds + if len(unitIDs) > 0 { + var ids []uint16 + for id := range unitIDs { + ids = append(ids, id) + } + + units, err := getUnits(ids, &event, tx) + if err != nil { + return fmt.Errorf("failed to fetch units: %w", err) + } + if len(units) != len(ids) { + return fmt.Errorf("some unit_id not found") + } + } + + // validate doctorIds + if len(doctorIDs) > 0 { + var ids []uint + for id := range doctorIDs { + ids = append(ids, id) + } + + doctors, err := getDoctors(ids, &event, tx) + if err != nil { + return fmt.Errorf("failed to fetch doctors: %w", err) + } + if len(doctors) != len(ids) { + return fmt.Errorf("some doctor_id not found") + } + } + + if err := uir.CreateBulkData(*input.InternalReferences, input.Id, &event, tx); err != nil { + return err + } + } + pl.SetLogInfo(&event, nil, "complete") return nil diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 7cb72938..36244f83 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "strings" + "time" "gorm.io/gorm" @@ -15,6 +16,7 @@ import ( pu "simrs-vx/pkg/use-case-helper" edo "simrs-vx/internal/domain/main-entities/device-order" + ed "simrs-vx/internal/domain/main-entities/doctor" emo "simrs-vx/internal/domain/main-entities/material-order" emco "simrs-vx/internal/domain/main-entities/mcu-order" em "simrs-vx/internal/domain/main-entities/medication" @@ -23,6 +25,7 @@ import ( emmi "simrs-vx/internal/domain/main-entities/medicine-mix-item" ep "simrs-vx/internal/domain/main-entities/prescription" epi "simrs-vx/internal/domain/main-entities/prescription-item" + eu "simrs-vx/internal/domain/main-entities/unit" // udo "simrs-vx/internal/use-case/main-use-case/device-order" es "simrs-vx/internal/domain/main-entities/soapi" @@ -77,12 +80,15 @@ func setDataUpdate(src e.UpdateDto, dst *e.Encounter) { } func setDataDischarge(src e.DischargeDto, dst *e.Encounter) { - dst.DischargeMethod_Code = src.DischargeMethod_Code + dst.Discharge_Method_Code = src.Discharge_Method_Code dst.EarlyEducation = src.EarlyEducation dst.MedicalDischargeEducation = src.MedicalDischargeEducation dst.AdmDischargeEducation = src.AdmDischargeEducation dst.DischargeReason = src.DischargeReason dst.Status_Code = erc.DSCDone + + now := time.Now() + dst.Discharge_Date = &now } func checkSoapiByDocExists(encounter_id uint, event *pl.Event, tx *gorm.DB) error { @@ -317,3 +323,35 @@ func getMcuOrders(encounter_id uint, event *pl.Event, tx *gorm.DB) error { func setDataUpdateStatus(src e.UpdateStatusDto, dst *e.Encounter) { dst.Status_Code = src.StatusCode } + +func getUnits(unitIds []uint16, event *pl.Event, tx *gorm.DB) ([]eu.Unit, error) { + pl.SetLogInfo(event, nil, "started", "getUnits") + var units []eu.Unit + err := tx.Where("\"Id\" IN ?", unitIds).Find(&units).Error + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-get-fail", + Detail: "get units", + Raw: err, + } + return nil, pl.SetLogError(event, nil) + } + return units, nil +} + +func getDoctors(doctorIds []uint, event *pl.Event, tx *gorm.DB) ([]ed.Doctor, error) { + pl.SetLogInfo(event, nil, "started", "getDoctors") + var doctors []ed.Doctor + err := tx.Where("\"Id\" IN ?", doctorIds).Find(&doctors).Error + if err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-get-fail", + Detail: "get doctors", + Raw: err, + } + return nil, pl.SetLogError(event, nil) + } + return doctors, nil +} diff --git a/internal/use-case/main-use-case/internal-reference/case.go b/internal/use-case/main-use-case/internal-reference/case.go index 1681bf26..bfd9550f 100644 --- a/internal/use-case/main-use-case/internal-reference/case.go +++ b/internal/use-case/main-use-case/internal-reference/case.go @@ -1,19 +1,9 @@ package internal_reference import ( - - // main entities - ed "simrs-vx/internal/domain/main-entities/doctor" - ee "simrs-vx/internal/domain/main-entities/encounter" eir "simrs-vx/internal/domain/main-entities/internal-reference" - eu "simrs-vx/internal/domain/main-entities/unit" "strconv" - // main use case - ud "simrs-vx/internal/use-case/main-use-case/doctor" - ue "simrs-vx/internal/use-case/main-use-case/encounter" - uu "simrs-vx/internal/use-case/main-use-case/unit" - pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -43,11 +33,6 @@ func Create(input eir.CreateDto) (*d.Data, error) { return err } - // Validate Request - if err := validateRequest(input); err != nil { - return err - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { @@ -179,29 +164,6 @@ func ReadDetail(input eir.ReadDetailDto) (*d.Data, error) { }, nil } -func validateRequest(input eir.CreateDto) (err error) { - - // Validate Encounter Id - _, err = ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Encounter_Id)}) - if err != nil { - return err - } - - // Validate Unit Id - _, err = uu.ReadDetail(eu.ReadDetailDto{Id: *input.Unit_Id}) - if err != nil { - return err - } - - // Validate Doctor Id - _, err = ud.ReadDetail(ed.ReadDetailDto{Id: uint16(*input.Doctor_Id)}) - if err != nil { - return err - } - - return -} - func Update(input eir.UpdateDto) (*d.Data, error) { rdDto := eir.ReadDetailDto{Id: input.Id} var data *eir.InternalReference @@ -228,21 +190,10 @@ func Update(input eir.UpdateDto) (*d.Data, error) { return err } - // Validate Request - if err := validateRequest(input.CreateDto); err != nil { - return err - } - if err := UpdateData(input, data, &event, tx); err != nil { return err } - // Get Updated Data - rdDto.Includes = "encounter,unit,doctor" - if data, err = ReadDetailData(rdDto, &event, tx); err != nil { - return err - } - pl.SetLogInfo(&event, nil, "complete") mwRunner.setMwType(pu.MWTPost) diff --git a/internal/use-case/main-use-case/internal-reference/helper.go b/internal/use-case/main-use-case/internal-reference/helper.go index aaef382a..dbd92f73 100644 --- a/internal/use-case/main-use-case/internal-reference/helper.go +++ b/internal/use-case/main-use-case/internal-reference/helper.go @@ -21,3 +21,17 @@ func setData[T *ir.CreateDto | *ir.UpdateDto](input T, data *ir.InternalReferenc data.Unit_Id = inputSrc.Unit_Id data.Doctor_Id = inputSrc.Doctor_Id } + +func setBulkData(input []ir.CreateDto, encounterId uint) []ir.InternalReference { + var data []ir.InternalReference + + for _, v := range input { + data = append(data, ir.InternalReference{ + Encounter_Id: &encounterId, + Unit_Id: v.Unit_Id, + Doctor_Id: v.Doctor_Id, + }) + } + + return data +} diff --git a/internal/use-case/main-use-case/internal-reference/lib.go b/internal/use-case/main-use-case/internal-reference/lib.go index a50d594c..18215078 100644 --- a/internal/use-case/main-use-case/internal-reference/lib.go +++ b/internal/use-case/main-use-case/internal-reference/lib.go @@ -142,3 +142,24 @@ func DeleteData(data *eir.InternalReference, event *pl.Event, dbx ...*gorm.DB) e pl.SetLogInfo(event, nil, "complete") return nil } + +func CreateBulkData(input []eir.CreateDto, encounterId uint, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := []eir.InternalReference{} + setBulkData(input, encounterId) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Create(&data).Error; err != nil { + return plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} From e290c585cfbb4743fa1c6a37bc98f2c9517f4e67 Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 09:34:33 +0700 Subject: [PATCH 03/12] adjustment checkout --- internal/use-case/main-use-case/encounter/case.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 43ce3cbf..2f7ae8c4 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -397,9 +397,9 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { } return pl.SetLogError(&event, input) } - if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { - return err - } + //if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { + // return err + //} if err := updateDischargeData(input, data, &event, tx); err != nil { return err From ea715ec9c529bfa468e25cd0f16d9749d6422a31 Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 10:49:28 +0700 Subject: [PATCH 04/12] update bulk insert --- internal/use-case/main-use-case/encounter/case.go | 11 ++++++----- .../use-case/main-use-case/internal-reference/lib.go | 3 +-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 2f7ae8c4..5f31e371 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -397,9 +397,10 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { } return pl.SetLogError(&event, input) } - //if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { - // return err - //} + + if err := checkSoapiByDocExists(data.Id, &event, tx); err != nil { + return err + } if err := updateDischargeData(input, data, &event, tx); err != nil { return err @@ -450,7 +451,7 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { return fmt.Errorf("failed to fetch units: %w", err) } if len(units) != len(ids) { - return fmt.Errorf("some unit_id not found") + return fmt.Errorf("unit_id not found") } } @@ -466,7 +467,7 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { return fmt.Errorf("failed to fetch doctors: %w", err) } if len(doctors) != len(ids) { - return fmt.Errorf("some doctor_id not found") + return fmt.Errorf("doctor_id not found") } } diff --git a/internal/use-case/main-use-case/internal-reference/lib.go b/internal/use-case/main-use-case/internal-reference/lib.go index 18215078..bf852dc6 100644 --- a/internal/use-case/main-use-case/internal-reference/lib.go +++ b/internal/use-case/main-use-case/internal-reference/lib.go @@ -146,8 +146,7 @@ func DeleteData(data *eir.InternalReference, event *pl.Event, dbx ...*gorm.DB) e func CreateBulkData(input []eir.CreateDto, encounterId uint, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "started", "DBCreate") - data := []eir.InternalReference{} - setBulkData(input, encounterId) + data := setBulkData(input, encounterId) var tx *gorm.DB if len(dbx) > 0 { From 69797f99557963eb3655b121f48cbc5f1b127474 Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 11:44:30 +0700 Subject: [PATCH 05/12] adjustment position_code --- internal/domain/main-entities/employee/dto.go | 30 ++++++++++--------- .../use-case/main-use-case/employee/helper.go | 1 + .../main-use-case/encounter/helper.go | 2 +- .../use-case/main-use-case/user/helper.go | 1 + 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/internal/domain/main-entities/employee/dto.go b/internal/domain/main-entities/employee/dto.go index faaea8a4..133260c7 100644 --- a/internal/domain/main-entities/employee/dto.go +++ b/internal/domain/main-entities/employee/dto.go @@ -11,11 +11,12 @@ import ( ) type CreateDto struct { - User_Id *uint `json:"user_id"` - Person_Id *uint `json:"person_id"` - Division_Code *string `json:"division_code"` - Number *string `json:"number" validate:"maxLength=20"` - Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` + User_Id *uint `json:"user_id"` + Person_Id *uint `json:"person_id"` + Division_Code *string `json:"division_code"` + Number *string `json:"number" validate:"maxLength=20"` + Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` + Position_Code *erg.EmployeePositionCode `json:"position_code"` } type ReadListDto struct { @@ -57,15 +58,15 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - User_Id *uint `json:"user_id"` - User *eu.User `json:"user,omitempty"` - Person_Id *uint `json:"person_id"` - Person *ep.Person `json:"person,omitempty"` - Division_Code *string `json:"division_code"` - Division *ed.Division `json:"division,omitempty"` - Position_Code *erg.EmployeePosisitionCode `json:"position_code"` - Number *string `json:"number"` - Status_Code erc.ActiveStatusCode `json:"status_code"` + User_Id *uint `json:"user_id"` + User *eu.User `json:"user,omitempty"` + Person_Id *uint `json:"person_id"` + Person *ep.Person `json:"person,omitempty"` + Division_Code *string `json:"division_code"` + Division *ed.Division `json:"division,omitempty"` + Position_Code *erg.EmployeePositionCode `json:"position_code"` + Number *string `json:"number"` + Status_Code erc.ActiveStatusCode `json:"status_code"` } func (d Employee) ToResponse() ResponseDto { @@ -78,6 +79,7 @@ func (d Employee) ToResponse() ResponseDto { Division: d.Division, Number: d.Number, Status_Code: d.Status_Code, + Position_Code: d.Position_Code, } resp.Main = d.Main return resp diff --git a/internal/use-case/main-use-case/employee/helper.go b/internal/use-case/main-use-case/employee/helper.go index 252d35eb..b0202c08 100644 --- a/internal/use-case/main-use-case/employee/helper.go +++ b/internal/use-case/main-use-case/employee/helper.go @@ -22,6 +22,7 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Employee) { data.Division_Code = inputSrc.Division_Code data.Number = inputSrc.Number data.Status_Code = inputSrc.Status_Code + data.Position_Code = inputSrc.Position_Code } // func createUser(input *e.CreateDto, event *pl.Event, tx *gorm.DB) error { diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index f81f9ab4..36244f83 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -80,7 +80,7 @@ func setDataUpdate(src e.UpdateDto, dst *e.Encounter) { } func setDataDischarge(src e.DischargeDto, dst *e.Encounter) { - dst.Discharge_Method_Code = src.DischargeMethod_Code + dst.Discharge_Method_Code = src.Discharge_Method_Code dst.EarlyEducation = src.EarlyEducation dst.MedicalDischargeEducation = src.MedicalDischargeEducation dst.AdmDischargeEducation = src.AdmDischargeEducation diff --git a/internal/use-case/main-use-case/user/helper.go b/internal/use-case/main-use-case/user/helper.go index 1663bb24..cb640dcb 100644 --- a/internal/use-case/main-use-case/user/helper.go +++ b/internal/use-case/main-use-case/user/helper.go @@ -49,6 +49,7 @@ func setDataEmployeeUpdate(src e.EmployeUpdateDto) ee.UpdateDto { Division_Code: src.Division_Code, Number: src.Number, Status_Code: src.Status_Code, + Position_Code: &src.Position_Code, }, } } From a9f3c5297d62da64ebca63dd12343a3ff77b1a9e Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 11:50:54 +0700 Subject: [PATCH 06/12] fix typo const --- internal/interface/main-handler/encounter/handler.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index 6cf91832..3493ab27 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -85,7 +85,7 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { } func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { - const dataValidationVail = "data-validation-fail" + const dataValidationFail = "data-validation-fail" dto := e.DischargeDto{} id := rw.ValidateInt(w, "id", r.PathValue("id")) @@ -103,7 +103,7 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { case ere.DMCDeath: if dto.DeathCause == nil { rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationVail, + Code: dataValidationFail, Message: "deathCause required if discharge_method_code is death", }) return @@ -111,7 +111,7 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { case ere.DMCConsulPoly, ere.DMCConsulExecutive: if dto.InternalReferences == nil { rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationVail, + Code: dataValidationFail, Message: fmt.Sprintf("internalReferences required if discharge_method_code is %s", *dto.Discharge_Method_Code), }) return @@ -120,7 +120,7 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { for _, v := range *dto.InternalReferences { if v.Unit_Id == nil { rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationVail, + Code: dataValidationFail, Message: "internalReferences.unit_id required", }) return @@ -128,7 +128,7 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { if v.Doctor_Id == nil { rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationVail, + Code: dataValidationFail, Message: "internalReferences.doctor_id required", }) return From a2dd87a1b2c11559c7acde53d71432a9b34387da Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 12:24:44 +0700 Subject: [PATCH 07/12] fix medicine mix preload --- internal/use-case/main-use-case/encounter/helper.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 36244f83..dda042e3 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -141,7 +141,7 @@ func createMedication(encounter_id uint, event *pl.Event, tx *gorm.DB) error { return err } - prescriptionItem, _, err := upi.ReadListData(epi.ReadListDto{FilterDto: epi.FilterDto{Prescription_Id: &prescription.Id}}, event, tx) + prescriptionItem, _, err := upi.ReadListData(epi.ReadListDto{FilterDto: epi.FilterDto{Prescription_Id: &prescription.Id}, Includes: "medicineMix"}, event, tx) if err != nil { return err } From 26fa5b8c8e7b8f1802016a148dc36ab90b5c0e4b Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 22 Oct 2025 15:01:13 +0700 Subject: [PATCH 08/12] initial config for simrs sync --- .vscode/launch.json | 14 +++++++ cmd/main-sync-api/config.yml-example | 41 +++++++++++++++++++ cmd/main-sync-api/main.go | 15 +++++++ cmd/simgos-sync-api/config.yml-example | 41 +++++++++++++++++++ cmd/simgos-sync-api/main.go | 15 +++++++ internal/domain/simgos-sync-entities/.keep | 0 internal/infra/sync-cfg/sync-cfg.go | 14 +++++++ internal/infra/sync-cfg/tycovar.go | 8 ++++ .../main-sync-handler/main-sync-handler.go | 36 ++++++++++++++++ .../simgos-sync-handler.go | 36 ++++++++++++++++ internal/use-case/main-sync-case/.keep | 0 internal/use-case/simgos-sync-case/.keep | 0 12 files changed, 220 insertions(+) create mode 100644 cmd/main-sync-api/config.yml-example create mode 100644 cmd/main-sync-api/main.go create mode 100644 cmd/simgos-sync-api/config.yml-example create mode 100644 cmd/simgos-sync-api/main.go create mode 100644 internal/domain/simgos-sync-entities/.keep create mode 100644 internal/infra/sync-cfg/sync-cfg.go create mode 100644 internal/infra/sync-cfg/tycovar.go create mode 100644 internal/interface/main-sync-handler/main-sync-handler.go create mode 100644 internal/interface/simgos-sync-handler/simgos-sync-handler.go create mode 100644 internal/use-case/main-sync-case/.keep create mode 100644 internal/use-case/simgos-sync-case/.keep diff --git a/.vscode/launch.json b/.vscode/launch.json index fddb0d29..6969b05e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,6 +14,20 @@ "mode": "auto", "program": "${workspaceFolder}/cmd/bpjs-api" }, + { + "name": "Launch Package simgos sync API", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/simgos-sync-api" + }, + { + "name": "Launch Package main sync API", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "${workspaceFolder}/cmd/main-sync-api" + }, { "name": "Launch Package migratioon", "type": "go", diff --git a/cmd/main-sync-api/config.yml-example b/cmd/main-sync-api/config.yml-example new file mode 100644 index 00000000..edb5c84b --- /dev/null +++ b/cmd/main-sync-api/config.yml-example @@ -0,0 +1,41 @@ +appCfg: + fullName: SIMRS Sync + codeName: simrs-sync + version: 0.1.0 + env: development + lang: en + +dbCfg: + dsn: + maxOpenConns: 5 + maxIdleConns: 5 + maxIdleTime: 100 + +multiDbCfg: + dbs : + - name: simrs_sync + dsn: + maxOpenConns: 5 + maxIdleConns: 5 + maxIdleTime: 100 + +httpCfg: + host: 127.0.0.1 + port: 8003 + +loggerCfg: + hideTime: true + hideLevel: true + +langCfg: + active: en + path: ../../assets/language/en + fileName: data.json + +corsCfg: + allowedOrigin: + allowedMethod: GET, POST, PUT, PATCH, DELETE, OPTIONS + +syncUrlCfg: + target: + host: \ No newline at end of file diff --git a/cmd/main-sync-api/main.go b/cmd/main-sync-api/main.go new file mode 100644 index 00000000..5bfdd36b --- /dev/null +++ b/cmd/main-sync-api/main.go @@ -0,0 +1,15 @@ +package main + +import ( + a "github.com/karincake/apem" + + h "simrs-vx/internal/interface/main-sync-handler" + + d "github.com/karincake/apem/db-gorm-pg" + + l "github.com/karincake/apem/logger-zerolog" +) + +func main() { + a.Run(h.SetRoutes(), &l.O, &d.O) +} diff --git a/cmd/simgos-sync-api/config.yml-example b/cmd/simgos-sync-api/config.yml-example new file mode 100644 index 00000000..153be50e --- /dev/null +++ b/cmd/simgos-sync-api/config.yml-example @@ -0,0 +1,41 @@ +appCfg: + fullName: SIMRS Sync + codeName: simrs-sync + version: 0.1.0 + env: development + lang: en + +dbCfg: + dsn: + maxOpenConns: 5 + maxIdleConns: 5 + maxIdleTime: 100 + +multiDbCfg: + dbs : + - name: simrs_sync + dsn: + maxOpenConns: 5 + maxIdleConns: 5 + maxIdleTime: 100 + +httpCfg: + host: 127.0.0.1 + port: 8002 + +loggerCfg: + hideTime: true + hideLevel: true + +langCfg: + active: en + path: ../../assets/language/en + fileName: data.json + +corsCfg: + allowedOrigin: + allowedMethod: GET, POST, PUT, PATCH, DELETE, OPTIONS + +syncUrlCfg: + target: + host: \ No newline at end of file diff --git a/cmd/simgos-sync-api/main.go b/cmd/simgos-sync-api/main.go new file mode 100644 index 00000000..ddb3897d --- /dev/null +++ b/cmd/simgos-sync-api/main.go @@ -0,0 +1,15 @@ +package main + +import ( + a "github.com/karincake/apem" + + h "simrs-vx/internal/interface/simgos-sync-handler" + + d "github.com/karincake/apem/db-gorm-pg" + + l "github.com/karincake/apem/logger-zerolog" +) + +func main() { + a.Run(h.SetRoutes(), &l.O, &d.O) +} diff --git a/internal/domain/simgos-sync-entities/.keep b/internal/domain/simgos-sync-entities/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/infra/sync-cfg/sync-cfg.go b/internal/infra/sync-cfg/sync-cfg.go new file mode 100644 index 00000000..0368a7c4 --- /dev/null +++ b/internal/infra/sync-cfg/sync-cfg.go @@ -0,0 +1,14 @@ +package synccfg + +import ( + a "github.com/karincake/apem" + lo "github.com/karincake/apem/loggero" +) + +func SetConfig() { + a.ParseSingleCfg(&O) + if O.Host == "" || O.Target == "" { + panic("sync url config host or target empty") + } + lo.I.Println("sync url config loaded, status: DONE!!") +} diff --git a/internal/infra/sync-cfg/tycovar.go b/internal/infra/sync-cfg/tycovar.go new file mode 100644 index 00000000..9bb59f3d --- /dev/null +++ b/internal/infra/sync-cfg/tycovar.go @@ -0,0 +1,8 @@ +package synccfg + +var O SyncUrlCfg = SyncUrlCfg{} + +type SyncUrlCfg struct { + Target string `yaml:"target"` + Host string `yaml:"host"` +} diff --git a/internal/interface/main-sync-handler/main-sync-handler.go b/internal/interface/main-sync-handler/main-sync-handler.go new file mode 100644 index 00000000..c8ba5144 --- /dev/null +++ b/internal/interface/main-sync-handler/main-sync-handler.go @@ -0,0 +1,36 @@ +package mainsynchandler + +import ( + "net/http" + + /******************** infra ********************/ + gs "simrs-vx/internal/infra/gorm-setting" + synccfg "simrs-vx/internal/infra/sync-cfg" + + /******************** pkg ********************/ + cmw "simrs-vx/pkg/cors-manager-mw" + lh "simrs-vx/pkg/lang-helper" + handlerlogger "simrs-vx/pkg/middleware/handler-logger" + zlc "simrs-vx/pkg/zerolog-ctx" + + /******************** external ********************/ + a "github.com/karincake/apem" + + /******************** internal ********************/ + "simrs-vx/internal/interface/main-handler/home" +) + +func SetRoutes() http.Handler { + /// + a.RegisterExtCall(gs.Adjust) + a.RegisterExtCall(zlc.Adjust) + a.RegisterExtCall(lh.Populate) + a.RegisterExtCall(synccfg.SetConfig) + + r := http.NewServeMux() + + /******************** Main ********************/ + r.HandleFunc("/", home.Home) + + return cmw.SetCors(handlerlogger.SetLog(r)) +} diff --git a/internal/interface/simgos-sync-handler/simgos-sync-handler.go b/internal/interface/simgos-sync-handler/simgos-sync-handler.go new file mode 100644 index 00000000..f51106a9 --- /dev/null +++ b/internal/interface/simgos-sync-handler/simgos-sync-handler.go @@ -0,0 +1,36 @@ +package simgossynchandler + +import ( + "net/http" + + /******************** infra ********************/ + gs "simrs-vx/internal/infra/gorm-setting" + synccfg "simrs-vx/internal/infra/sync-cfg" + + /******************** pkg ********************/ + cmw "simrs-vx/pkg/cors-manager-mw" + lh "simrs-vx/pkg/lang-helper" + handlerlogger "simrs-vx/pkg/middleware/handler-logger" + zlc "simrs-vx/pkg/zerolog-ctx" + + /******************** external ********************/ + a "github.com/karincake/apem" + + /******************** internal ********************/ + "simrs-vx/internal/interface/main-handler/home" +) + +func SetRoutes() http.Handler { + /// + a.RegisterExtCall(gs.Adjust) + a.RegisterExtCall(zlc.Adjust) + a.RegisterExtCall(lh.Populate) + a.RegisterExtCall(synccfg.SetConfig) + + r := http.NewServeMux() + + /******************** Main ********************/ + r.HandleFunc("/", home.Home) + + return cmw.SetCors(handlerlogger.SetLog(r)) +} diff --git a/internal/use-case/main-sync-case/.keep b/internal/use-case/main-sync-case/.keep new file mode 100644 index 00000000..e69de29b diff --git a/internal/use-case/simgos-sync-case/.keep b/internal/use-case/simgos-sync-case/.keep new file mode 100644 index 00000000..e69de29b From 73e4374366fd09a354080e7330ed638a18d655aa Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 15:28:26 +0700 Subject: [PATCH 09/12] move request validation to spesific folder --- .../main-handler/encounter/handler.go | 44 +----------- .../encounter/request-validation.go | 53 +++++++++++++++ .../use-case/main-use-case/encounter/case.go | 55 +-------------- .../main-use-case/encounter/helper.go | 67 ++++++++++++++++++- 4 files changed, 124 insertions(+), 95 deletions(-) create mode 100644 internal/interface/main-handler/encounter/request-validation.go diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index 3493ab27..df3320ef 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -1,7 +1,6 @@ package encounter import ( - "fmt" "net/http" rw "github.com/karincake/risoles" @@ -13,8 +12,6 @@ import ( u "simrs-vx/internal/use-case/main-use-case/encounter" erc "simrs-vx/internal/domain/references/common" - ere "simrs-vx/internal/domain/references/encounter" - pa "simrs-vx/pkg/auth-helper" d "github.com/karincake/dodol" @@ -85,8 +82,6 @@ func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { } func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { - const dataValidationFail = "data-validation-fail" - dto := e.DischargeDto{} id := rw.ValidateInt(w, "id", r.PathValue("id")) if id <= 0 { @@ -98,43 +93,8 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { } // validate request body - { - switch *dto.Discharge_Method_Code { - case ere.DMCDeath: - if dto.DeathCause == nil { - rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationFail, - Message: "deathCause required if discharge_method_code is death", - }) - return - } - case ere.DMCConsulPoly, ere.DMCConsulExecutive: - if dto.InternalReferences == nil { - rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationFail, - Message: fmt.Sprintf("internalReferences required if discharge_method_code is %s", *dto.Discharge_Method_Code), - }) - return - } - - for _, v := range *dto.InternalReferences { - if v.Unit_Id == nil { - rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationFail, - Message: "internalReferences.unit_id required", - }) - return - } - - if v.Doctor_Id == nil { - rw.DataResponse(w, nil, d.FieldError{ - Code: dataValidationFail, - Message: "internalReferences.doctor_id required", - }) - return - } - } - } + if valid := validateRequestCheckout(w, dto); !valid { + return } dto.Id = uint(id) diff --git a/internal/interface/main-handler/encounter/request-validation.go b/internal/interface/main-handler/encounter/request-validation.go new file mode 100644 index 00000000..182dd12e --- /dev/null +++ b/internal/interface/main-handler/encounter/request-validation.go @@ -0,0 +1,53 @@ +package encounter + +import ( + "fmt" + "net/http" + e "simrs-vx/internal/domain/main-entities/encounter" + ere "simrs-vx/internal/domain/references/encounter" + + d "github.com/karincake/dodol" + rw "github.com/karincake/risoles" +) + +func validateRequestCheckout(w http.ResponseWriter, i e.DischargeDto) (valid bool) { + const dataValidationFail = "" + + switch *i.Discharge_Method_Code { + case ere.DMCDeath: + if i.DeathCause == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: "deathCause required if discharge_method_code is death", + }) + return + } + case ere.DMCConsulPoly, ere.DMCConsulExecutive: + if i.InternalReferences == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: fmt.Sprintf("internalReferences required if discharge_method_code is %s", *i.Discharge_Method_Code), + }) + return + } + + for _, v := range *i.InternalReferences { + if v.Unit_Id == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: "internalReferences.unit_id required", + }) + return + } + + if v.Doctor_Id == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: "internalReferences.doctor_id required", + }) + return + } + } + } + return true +} diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 5f31e371..04eedfff 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -2,8 +2,6 @@ package encounter import ( "errors" - "fmt" - uir "simrs-vx/internal/use-case/main-use-case/internal-reference" "strconv" ea "simrs-vx/internal/domain/main-entities/ambulatory" @@ -419,59 +417,12 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { switch *input.Discharge_Method_Code { case ere.DMCDeath: // insert data death-cause - if _, err := udc.CreateData(edc.CreateDto{ - Encounter_Id: &input.Id, - Value: input.DeathCause, - }, &event, tx); err != nil { + if _, err = udc.CreateData(edc.CreateDto{Encounter_Id: &input.Id, Value: input.DeathCause}, &event, tx); err != nil { return err } - case ere.DMCConsulPoly, ere.DMCConsulExecutive: - unitIDs := make(map[uint16]struct{}) - doctorIDs := make(map[uint]struct{}) - - for _, ref := range *input.InternalReferences { - if ref.Unit_Id != nil { - unitIDs[*ref.Unit_Id] = struct{}{} - } - if ref.Doctor_Id != nil { - doctorIDs[*ref.Doctor_Id] = struct{}{} - } - } - - // validate unitIds - if len(unitIDs) > 0 { - var ids []uint16 - for id := range unitIDs { - ids = append(ids, id) - } - - units, err := getUnits(ids, &event, tx) - if err != nil { - return fmt.Errorf("failed to fetch units: %w", err) - } - if len(units) != len(ids) { - return fmt.Errorf("unit_id not found") - } - } - - // validate doctorIds - if len(doctorIDs) > 0 { - var ids []uint - for id := range doctorIDs { - ids = append(ids, id) - } - - doctors, err := getDoctors(ids, &event, tx) - if err != nil { - return fmt.Errorf("failed to fetch doctors: %w", err) - } - if len(doctors) != len(ids) { - return fmt.Errorf("doctor_id not found") - } - } - - if err := uir.CreateBulkData(*input.InternalReferences, input.Id, &event, tx); err != nil { + // bulk insert internal-references + if err = createInternalReferences(input, &event, tx); err != nil { return err } } diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index dda042e3..41fd6a51 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -7,6 +7,7 @@ package encounter import ( "errors" "fmt" + uir "simrs-vx/internal/use-case/main-use-case/internal-reference" "strings" "time" @@ -141,7 +142,9 @@ func createMedication(encounter_id uint, event *pl.Event, tx *gorm.DB) error { return err } - prescriptionItem, _, err := upi.ReadListData(epi.ReadListDto{FilterDto: epi.FilterDto{Prescription_Id: &prescription.Id}, Includes: "medicineMix"}, event, tx) + prescriptionItem, _, err := upi.ReadListData(epi.ReadListDto{ + FilterDto: epi.FilterDto{Prescription_Id: &prescription.Id}, + Includes: "medicineMix,medicineMix-MixItems"}, event, tx) if err != nil { return err } @@ -324,6 +327,68 @@ func setDataUpdateStatus(src e.UpdateStatusDto, dst *e.Encounter) { dst.Status_Code = src.StatusCode } +func createInternalReferences(input e.DischargeDto, event *pl.Event, tx *gorm.DB) error { + unitIDs := make(map[uint16]struct{}) + doctorIDs := make(map[uint]struct{}) + + for _, ref := range *input.InternalReferences { + if ref.Unit_Id != nil { + unitIDs[*ref.Unit_Id] = struct{}{} + } + if ref.Doctor_Id != nil { + doctorIDs[*ref.Doctor_Id] = struct{}{} + } + } + + // validate unitIds + if len(unitIDs) > 0 { + var ids []uint16 + for id := range unitIDs { + ids = append(ids, id) + } + + units, err := getUnits(ids, event, tx) + if err != nil { + return fmt.Errorf("failed to fetch units: %w", err) + } + if len(units) != len(ids) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-validation-fail", + Detail: "unit_id not found", + } + return pl.SetLogError(event, nil) + } + } + + // validate doctorIds + if len(doctorIDs) > 0 { + var ids []uint + for id := range doctorIDs { + ids = append(ids, id) + } + + doctors, err := getDoctors(ids, event, tx) + if err != nil { + return fmt.Errorf("failed to fetch doctors: %w", err) + } + if len(doctors) != len(ids) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-validation-fail", + Detail: "doctor_id not found", + } + return pl.SetLogError(event, nil) + } + } + + if err := uir.CreateBulkData(*input.InternalReferences, input.Id, event, tx); err != nil { + return err + } + + return nil +} + func getUnits(unitIds []uint16, event *pl.Event, tx *gorm.DB) ([]eu.Unit, error) { pl.SetLogInfo(event, nil, "started", "getUnits") var units []eu.Unit From 8f8053daf49ab2b3a9df955b6dde10f2aa13044c Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 16:31:55 +0700 Subject: [PATCH 10/12] crud edu-assessment --- .../main-entities/edu-assessment/dto.go | 75 +++++ .../main-handler/edu-assessment/handler.go | 74 +++++ .../interface/main-handler/main-handler.go | 2 + .../main-use-case/edu-assessment/case.go | 301 ++++++++++++++++++ .../main-use-case/edu-assessment/helper.go | 25 ++ .../main-use-case/edu-assessment/lib.go | 144 +++++++++ .../edu-assessment/middleware-runner.go | 103 ++++++ .../edu-assessment/middleware.go | 9 + .../main-use-case/edu-assessment/tycovar.go | 44 +++ 9 files changed, 777 insertions(+) create mode 100644 internal/domain/main-entities/edu-assessment/dto.go create mode 100644 internal/interface/main-handler/edu-assessment/handler.go create mode 100644 internal/use-case/main-use-case/edu-assessment/case.go create mode 100644 internal/use-case/main-use-case/edu-assessment/helper.go create mode 100644 internal/use-case/main-use-case/edu-assessment/lib.go create mode 100644 internal/use-case/main-use-case/edu-assessment/middleware-runner.go create mode 100644 internal/use-case/main-use-case/edu-assessment/middleware.go create mode 100644 internal/use-case/main-use-case/edu-assessment/tycovar.go diff --git a/internal/domain/main-entities/edu-assessment/dto.go b/internal/domain/main-entities/edu-assessment/dto.go new file mode 100644 index 00000000..d476dd1a --- /dev/null +++ b/internal/domain/main-entities/edu-assessment/dto.go @@ -0,0 +1,75 @@ +package edu_assessment + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/encounter" +) + +type CreateDto struct { + Encounter_Id *uint `json:"encounter_id" validate:"required"` + GeneralEdus *string `json:"generalEdus"` + SpecialEdus *string `json:"specialEdus"` + Assessments *string `json:"assessments" validate:"required"` + Plan *string `json:"plan" validate:"required"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Encounter_Id *uint `json:"encounter_id" gorm:"not null"` + Encounter *ee.Encounter `json:"encounter,omitempty" gorm:"foreignKey:Encounter_Id;references:Id"` + GeneralEdus *string `json:"generalEdus"` + SpecialEdus *string `json:"specialEdus"` + Assessments *string `json:"assessments"` + Plan *string `json:"plan"` +} + +func (d EduAssessment) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Encounter: d.Encounter, + GeneralEdus: d.GeneralEdus, + SpecialEdus: d.SpecialEdus, + Assessments: d.Assessments, + Plan: d.Plan, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []EduAssessment) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/interface/main-handler/edu-assessment/handler.go b/internal/interface/main-handler/edu-assessment/handler.go new file mode 100644 index 00000000..9d06d434 --- /dev/null +++ b/internal/interface/main-handler/edu-assessment/handler.go @@ -0,0 +1,74 @@ +package internal_reference + +import ( + "net/http" + + e "simrs-vx/internal/domain/main-entities/edu-assessment" + u "simrs-vx/internal/use-case/main-use-case/edu-assessment" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.ReadDetailDto{} + sf.UrlQueryParam(&dto, *r.URL) + + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + dto.Id = uint16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index c53e8795..94c16fc1 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -66,6 +66,7 @@ import ( division "simrs-vx/internal/interface/main-handler/division" divisionposition "simrs-vx/internal/interface/main-handler/division-position" doctorfee "simrs-vx/internal/interface/main-handler/doctor-fee" + eduassesment "simrs-vx/internal/interface/main-handler/edu-assessment" ethnic "simrs-vx/internal/interface/main-handler/ethnic" infra "simrs-vx/internal/interface/main-handler/infra" installation "simrs-vx/internal/interface/main-handler/installation" @@ -303,6 +304,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/mcu-sub-src", mcusubsrc.O) hc.RegCrud(r, "/v1/vehicle", vehicle.O) hc.RegCrud(r, "/v1/vehicle-hist", vehiclehist.O) + hc.RegCrud(r, "/v1/edu-assessment", eduassesment.O) hc.RegCrud(r, "/v1/village", village.O) hc.RegCrud(r, "/v1/district", district.O) diff --git a/internal/use-case/main-use-case/edu-assessment/case.go b/internal/use-case/main-use-case/edu-assessment/case.go new file mode 100644 index 00000000..bcceb21c --- /dev/null +++ b/internal/use-case/main-use-case/edu-assessment/case.go @@ -0,0 +1,301 @@ +package edu_assessment + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/edu-assessment" + ee "simrs-vx/internal/domain/main-entities/encounter" + "strconv" + + ue "simrs-vx/internal/use-case/main-use-case/encounter" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" +) + +const source = "edu-assessment" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.EduAssessment{} + + 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 + } + + // validate encounter_id + _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Encounter_Id)}) + if err != nil { + return err + } + + // Check if an education assessment record already exists + // for the specified encounter ID before creating a new one. + dataEduAssessment, err := ReadList(e.ReadListDto{FilterDto: e.FilterDto{Encounter_Id: input.Encounter_Id}}) + if err != nil { + return err + } + + if list, ok := dataEduAssessment.Data.([]e.ResponseDto); ok { + if len(list) > 0 { + event.ErrInfo.Code = "data-already-exists" + event.ErrInfo.Detail = "edu-assessment for this encounter already exists" + event.ErrInfo.Raw = errors.New("edu-assessment for this encounter already exists") + return pl.SetLogError(&event, input) + } + } + + 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.EduAssessment + var dataList []e.EduAssessment + 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.EduAssessment + 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.EduAssessment + 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.EduAssessment + 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/edu-assessment/helper.go b/internal/use-case/main-use-case/edu-assessment/helper.go new file mode 100644 index 00000000..9363a91a --- /dev/null +++ b/internal/use-case/main-use-case/edu-assessment/helper.go @@ -0,0 +1,25 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package edu_assessment + +import ( + e "simrs-vx/internal/domain/main-entities/edu-assessment" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.EduAssessment) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Encounter_Id = inputSrc.Encounter_Id + data.GeneralEdus = inputSrc.GeneralEdus + data.SpecialEdus = inputSrc.SpecialEdus + data.Assessments = inputSrc.Assessments + data.Plan = inputSrc.Plan +} diff --git a/internal/use-case/main-use-case/edu-assessment/lib.go b/internal/use-case/main-use-case/edu-assessment/lib.go new file mode 100644 index 00000000..fd2d0b22 --- /dev/null +++ b/internal/use-case/main-use-case/edu-assessment/lib.go @@ -0,0 +1,144 @@ +package edu_assessment + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/edu-assessment" + 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.EduAssessment, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.EduAssessment{} + 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.EduAssessment, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.EduAssessment{} + 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.EduAssessment{}). + 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.EduAssessment, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.EduAssessment{} + + 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.EduAssessment, 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.EduAssessment, 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/edu-assessment/middleware-runner.go b/internal/use-case/main-use-case/edu-assessment/middleware-runner.go new file mode 100644 index 00000000..8d92aaf9 --- /dev/null +++ b/internal/use-case/main-use-case/edu-assessment/middleware-runner.go @@ -0,0 +1,103 @@ +package edu_assessment + +import ( + e "simrs-vx/internal/domain/main-entities/edu-assessment" + 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.EduAssessment) 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.EduAssessment) 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.EduAssessment) 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.EduAssessment) 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.EduAssessment) 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/edu-assessment/middleware.go b/internal/use-case/main-use-case/edu-assessment/middleware.go new file mode 100644 index 00000000..dd5ea973 --- /dev/null +++ b/internal/use-case/main-use-case/edu-assessment/middleware.go @@ -0,0 +1,9 @@ +package edu_assessment + +// 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/edu-assessment/tycovar.go b/internal/use-case/main-use-case/edu-assessment/tycovar.go new file mode 100644 index 00000000..02f3b131 --- /dev/null +++ b/internal/use-case/main-use-case/edu-assessment/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 edu_assessment + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/edu-assessment" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.EduAssessment, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.EduAssessment, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.EduAssessment, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw From b3ed68fc1f20148596c6e3135f0deb6ae14bd93f Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 16:34:29 +0700 Subject: [PATCH 11/12] crud edu-assessment --- .../main-use-case/edu-assessment/case.go | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/internal/use-case/main-use-case/edu-assessment/case.go b/internal/use-case/main-use-case/edu-assessment/case.go index bcceb21c..230f0679 100644 --- a/internal/use-case/main-use-case/edu-assessment/case.go +++ b/internal/use-case/main-use-case/edu-assessment/case.go @@ -1,7 +1,6 @@ package edu_assessment import ( - "errors" e "simrs-vx/internal/domain/main-entities/edu-assessment" ee "simrs-vx/internal/domain/main-entities/encounter" "strconv" @@ -43,22 +42,6 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } - // Check if an education assessment record already exists - // for the specified encounter ID before creating a new one. - dataEduAssessment, err := ReadList(e.ReadListDto{FilterDto: e.FilterDto{Encounter_Id: input.Encounter_Id}}) - if err != nil { - return err - } - - if list, ok := dataEduAssessment.Data.([]e.ResponseDto); ok { - if len(list) > 0 { - event.ErrInfo.Code = "data-already-exists" - event.ErrInfo.Detail = "edu-assessment for this encounter already exists" - event.ErrInfo.Raw = errors.New("edu-assessment for this encounter already exists") - return pl.SetLogError(&event, input) - } - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { From 92be0adf43adbae1b96d93e5ad4e3a4e46161fd3 Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 23 Oct 2025 15:34:33 +0700 Subject: [PATCH 12/12] adjustment auth token with position --- .../installation-position/dto.go | 84 +++++ .../main-entities/specialist-position/dto.go | 84 +++++ .../subspecialist-position/dto.go | 84 +++++ .../domain/main-entities/unit-position/dto.go | 84 +++++ .../installation-position/handler.go | 71 ++++ .../interface/main-handler/main-handler.go | 8 + .../specialist-position/handler.go | 71 ++++ .../subspecialist-position/handler.go | 71 ++++ .../main-handler/unit-position/handler.go | 71 ++++ .../main-use-case/authentication/case.go | 34 +- .../main-use-case/authentication/helper.go | 143 ++++++++- .../installation-position/case.go | 302 ++++++++++++++++++ .../installation-position/helper.go | 25 ++ .../installation-position/lib.go | 156 +++++++++ .../middleware-runner.go | 103 ++++++ .../installation-position/middleware.go | 9 + .../installation-position/tycovar.go | 44 +++ .../main-use-case/specialist-position/case.go | 302 ++++++++++++++++++ .../specialist-position/helper.go | 25 ++ .../main-use-case/specialist-position/lib.go | 156 +++++++++ .../specialist-position/middleware-runner.go | 103 ++++++ .../specialist-position/middleware.go | 9 + .../specialist-position/tycovar.go | 44 +++ .../subspecialist-position/case.go | 302 ++++++++++++++++++ .../subspecialist-position/helper.go | 25 ++ .../subspecialist-position/lib.go | 156 +++++++++ .../middleware-runner.go | 103 ++++++ .../subspecialist-position/middleware.go | 9 + .../subspecialist-position/tycovar.go | 44 +++ .../main-use-case/unit-position/case.go | 302 ++++++++++++++++++ .../main-use-case/unit-position/helper.go | 25 ++ .../main-use-case/unit-position/lib.go | 156 +++++++++ .../unit-position/middleware-runner.go | 103 ++++++ .../main-use-case/unit-position/middleware.go | 9 + .../main-use-case/unit-position/tycovar.go | 44 +++ 35 files changed, 3347 insertions(+), 14 deletions(-) create mode 100644 internal/domain/main-entities/installation-position/dto.go create mode 100644 internal/domain/main-entities/specialist-position/dto.go create mode 100644 internal/domain/main-entities/subspecialist-position/dto.go create mode 100644 internal/domain/main-entities/unit-position/dto.go create mode 100644 internal/interface/main-handler/installation-position/handler.go create mode 100644 internal/interface/main-handler/specialist-position/handler.go create mode 100644 internal/interface/main-handler/subspecialist-position/handler.go create mode 100644 internal/interface/main-handler/unit-position/handler.go create mode 100644 internal/use-case/main-use-case/installation-position/case.go create mode 100644 internal/use-case/main-use-case/installation-position/helper.go create mode 100644 internal/use-case/main-use-case/installation-position/lib.go create mode 100644 internal/use-case/main-use-case/installation-position/middleware-runner.go create mode 100644 internal/use-case/main-use-case/installation-position/middleware.go create mode 100644 internal/use-case/main-use-case/installation-position/tycovar.go create mode 100644 internal/use-case/main-use-case/specialist-position/case.go create mode 100644 internal/use-case/main-use-case/specialist-position/helper.go create mode 100644 internal/use-case/main-use-case/specialist-position/lib.go create mode 100644 internal/use-case/main-use-case/specialist-position/middleware-runner.go create mode 100644 internal/use-case/main-use-case/specialist-position/middleware.go create mode 100644 internal/use-case/main-use-case/specialist-position/tycovar.go create mode 100644 internal/use-case/main-use-case/subspecialist-position/case.go create mode 100644 internal/use-case/main-use-case/subspecialist-position/helper.go create mode 100644 internal/use-case/main-use-case/subspecialist-position/lib.go create mode 100644 internal/use-case/main-use-case/subspecialist-position/middleware-runner.go create mode 100644 internal/use-case/main-use-case/subspecialist-position/middleware.go create mode 100644 internal/use-case/main-use-case/subspecialist-position/tycovar.go create mode 100644 internal/use-case/main-use-case/unit-position/case.go create mode 100644 internal/use-case/main-use-case/unit-position/helper.go create mode 100644 internal/use-case/main-use-case/unit-position/lib.go create mode 100644 internal/use-case/main-use-case/unit-position/middleware-runner.go create mode 100644 internal/use-case/main-use-case/unit-position/middleware.go create mode 100644 internal/use-case/main-use-case/unit-position/tycovar.go diff --git a/internal/domain/main-entities/installation-position/dto.go b/internal/domain/main-entities/installation-position/dto.go new file mode 100644 index 00000000..aada7443 --- /dev/null +++ b/internal/domain/main-entities/installation-position/dto.go @@ -0,0 +1,84 @@ +package installation_position + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/employee" + ei "simrs-vx/internal/domain/main-entities/installation" +) + +type CreateDto struct { + Installation_Id *uint16 `json:"installation_id" validate:"required"` + Code string `json:"code" validate:"maxLength=10;required"` + Name string `json:"name" validate:"maxLength=30;required"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Installation_Id *uint16 `json:"installation-id"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus *bool `json:"head-status"` + Employee_Id *uint `json:"employee-id"` + Search string `json:"search" gormhelper:"searchColumns=Code,Name"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Code *string `json:"code"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.SmallMain + Installation_Id *uint16 `json:"installation_id"` + Installation *ei.Installation `json:"installation,omitempty"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` + Employee *ee.Employee `json:"employee,omitempty"` +} + +func (d InstallationPosition) ToResponse() ResponseDto { + resp := ResponseDto{ + Installation_Id: d.Installation_Id, + Installation: d.Installation, + Code: d.Code, + Name: d.Name, + HeadStatus: d.HeadStatus, + Employee_Id: d.Employee_Id, + Employee: d.Employee, + } + resp.SmallMain = d.SmallMain + return resp +} + +func ToResponseList(data []InstallationPosition) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/specialist-position/dto.go b/internal/domain/main-entities/specialist-position/dto.go new file mode 100644 index 00000000..0641fb12 --- /dev/null +++ b/internal/domain/main-entities/specialist-position/dto.go @@ -0,0 +1,84 @@ +package specialist_position + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/employee" + es "simrs-vx/internal/domain/main-entities/specialist" +) + +type CreateDto struct { + Specialist_Id *uint16 `json:"specialist_id" validate:"required"` + Code string `json:"code" validate:"maxLength=10;required"` + Name string `json:"name" validate:"maxLength=30;required"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Specialist_Id *uint16 `json:"specialist-id"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus *bool `json:"head-status"` + Employee_Id *uint `json:"employee-id"` + Search string `json:"search" gormhelper:"searchColumns=Code,Name"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Code *string `json:"code"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.SmallMain + Specialist_Id *uint16 `json:"specialist_id"` + Specialist *es.Specialist `json:"specialist,omitempty"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` + Employee *ee.Employee `json:"employee,omitempty"` +} + +func (d SpecialistPosition) ToResponse() ResponseDto { + resp := ResponseDto{ + Specialist_Id: d.Specialist_Id, + Specialist: d.Specialist, + Code: d.Code, + Name: d.Name, + HeadStatus: d.HeadStatus, + Employee_Id: d.Employee_Id, + Employee: d.Employee, + } + resp.SmallMain = d.SmallMain + return resp +} + +func ToResponseList(data []SpecialistPosition) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/subspecialist-position/dto.go b/internal/domain/main-entities/subspecialist-position/dto.go new file mode 100644 index 00000000..3c65841e --- /dev/null +++ b/internal/domain/main-entities/subspecialist-position/dto.go @@ -0,0 +1,84 @@ +package subspecialist_position + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/employee" + es "simrs-vx/internal/domain/main-entities/subspecialist" +) + +type CreateDto struct { + Subspecialist_Id *uint16 `json:"subspecialist_id" validate:"required"` + Code string `json:"code" validate:"maxLength=10;required"` + Name string `json:"name" validate:"maxLength=30;required"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Subspecialist_Id *uint16 `json:"subspecialist-id"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus *bool `json:"head-status"` + Employee_Id *uint `json:"employee-id"` + Search string `json:"search" gormhelper:"searchColumns=Code,Name"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Code *string `json:"code"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.SmallMain + Subspecialist_Id *uint16 `json:"subspecialist_id"` + Subspecialist *es.Subspecialist `json:"subspecialist,omitempty"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` + Employee *ee.Employee `json:"employee,omitempty"` +} + +func (d SubspecialistPosition) ToResponse() ResponseDto { + resp := ResponseDto{ + Subspecialist_Id: d.Subspecialist_Id, + Subspecialist: d.Subspecialist, + Code: d.Code, + Name: d.Name, + HeadStatus: d.HeadStatus, + Employee_Id: d.Employee_Id, + Employee: d.Employee, + } + resp.SmallMain = d.SmallMain + return resp +} + +func ToResponseList(data []SubspecialistPosition) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/unit-position/dto.go b/internal/domain/main-entities/unit-position/dto.go new file mode 100644 index 00000000..cc52c493 --- /dev/null +++ b/internal/domain/main-entities/unit-position/dto.go @@ -0,0 +1,84 @@ +package unit_position + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/employee" + eu "simrs-vx/internal/domain/main-entities/unit" +) + +type CreateDto struct { + Unit_Id *uint16 `json:"unit_id" validate:"required"` + Code string `json:"code" validate:"maxLength=10;required"` + Name string `json:"name" validate:"maxLength=30;required"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Unit_Id *uint16 `json:"unit-id"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus *bool `json:"head-status"` + Employee_Id *uint `json:"employee-id"` + Search string `json:"search" gormhelper:"searchColumns=Code,Name"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Code *string `json:"code"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.SmallMain + Unit_Id *uint16 `json:"unit_id"` + Unit *eu.Unit `json:"unit,omitempty"` + Code string `json:"code"` + Name string `json:"name"` + HeadStatus bool `json:"headStatus"` + Employee_Id *uint `json:"employee_id"` + Employee *ee.Employee `json:"employee,omitempty"` +} + +func (d UnitPosition) ToResponse() ResponseDto { + resp := ResponseDto{ + Unit_Id: d.Unit_Id, + Unit: d.Unit, + Code: d.Code, + Name: d.Name, + HeadStatus: d.HeadStatus, + Employee_Id: d.Employee_Id, + Employee: d.Employee, + } + resp.SmallMain = d.SmallMain + return resp +} + +func ToResponseList(data []UnitPosition) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/interface/main-handler/installation-position/handler.go b/internal/interface/main-handler/installation-position/handler.go new file mode 100644 index 00000000..59f35340 --- /dev/null +++ b/internal/interface/main-handler/installation-position/handler.go @@ -0,0 +1,71 @@ +package installation_position + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/installation-position" + u "simrs-vx/internal/use-case/main-use-case/installation-position" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 94c16fc1..690a830a 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -70,6 +70,7 @@ import ( ethnic "simrs-vx/internal/interface/main-handler/ethnic" infra "simrs-vx/internal/interface/main-handler/infra" installation "simrs-vx/internal/interface/main-handler/installation" + installationposition "simrs-vx/internal/interface/main-handler/installation-position" insurancecompany "simrs-vx/internal/interface/main-handler/insurance-company" item "simrs-vx/internal/interface/main-handler/item" itemprice "simrs-vx/internal/interface/main-handler/item-price" @@ -86,8 +87,11 @@ import ( pharmacycompany "simrs-vx/internal/interface/main-handler/pharmacy-company" proceduresrc "simrs-vx/internal/interface/main-handler/procedure-src" specialist "simrs-vx/internal/interface/main-handler/specialist" + specialistposition "simrs-vx/internal/interface/main-handler/specialist-position" subspecialist "simrs-vx/internal/interface/main-handler/subspecialist" + subspecialistposition "simrs-vx/internal/interface/main-handler/subspecialist-position" unit "simrs-vx/internal/interface/main-handler/unit" + unitposition "simrs-vx/internal/interface/main-handler/unit-position" uom "simrs-vx/internal/interface/main-handler/uom" vehicle "simrs-vx/internal/interface/main-handler/vehicle" vehiclehist "simrs-vx/internal/interface/main-handler/vehicle-hist" @@ -305,6 +309,10 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/vehicle", vehicle.O) hc.RegCrud(r, "/v1/vehicle-hist", vehiclehist.O) hc.RegCrud(r, "/v1/edu-assessment", eduassesment.O) + hc.RegCrud(r, "/v1/installation-position", installationposition.O) + hc.RegCrud(r, "/v1/unit-position", unitposition.O) + hc.RegCrud(r, "/v1/specialist-position", specialistposition.O) + hc.RegCrud(r, "/v1/subspecialist-position", subspecialistposition.O) hc.RegCrud(r, "/v1/village", village.O) hc.RegCrud(r, "/v1/district", district.O) diff --git a/internal/interface/main-handler/specialist-position/handler.go b/internal/interface/main-handler/specialist-position/handler.go new file mode 100644 index 00000000..d16c2c7e --- /dev/null +++ b/internal/interface/main-handler/specialist-position/handler.go @@ -0,0 +1,71 @@ +package divisionposition + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/specialist-position" + u "simrs-vx/internal/use-case/main-use-case/specialist-position" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/subspecialist-position/handler.go b/internal/interface/main-handler/subspecialist-position/handler.go new file mode 100644 index 00000000..d8e27b14 --- /dev/null +++ b/internal/interface/main-handler/subspecialist-position/handler.go @@ -0,0 +1,71 @@ +package divisionposition + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/subspecialist-position" + u "simrs-vx/internal/use-case/main-use-case/subspecialist-position" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/interface/main-handler/unit-position/handler.go b/internal/interface/main-handler/unit-position/handler.go new file mode 100644 index 00000000..82b89465 --- /dev/null +++ b/internal/interface/main-handler/unit-position/handler.go @@ -0,0 +1,71 @@ +package unit_position + +import ( + "net/http" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" + + // ua "github.com/karincake/tumpeng/auth/svc" + + e "simrs-vx/internal/domain/main-entities/unit-position" + u "simrs-vx/internal/use-case/main-use-case/unit-position" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + dto := e.ReadDetailDto{} + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index ffefa7ef..da454333 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -155,12 +155,44 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { atClaims["nurse_id"] = nurse.Id outputData["nurse_id"] = nurse.Id } + + errorGetPosition := d.FieldErrors{"authentication": d.FieldError{Code: "auth-getData-failed", Message: el.GenMessage("auth-getData-failed")}} + // division position divsionPositions, err := getDivisionPosition(employee.Id) if err != nil { - return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-getData-failed", Message: el.GenMessage("auth-getData-failed")}} + return nil, errorGetPosition } + + // installation position + installationPositions, err := getInstallationPosition(employee.Id) + if err != nil { + return nil, errorGetPosition + } + + // unit position + unitPositions, err := getUnitPosition(employee.Id) + if err != nil { + return nil, errorGetPosition + } + + // specialist position + specialistPositions, err := getSpecialistPosition(employee.Id) + if err != nil { + return nil, errorGetPosition + } + + // subspecialist position + subspecialistPositions, err := getSubspecialistPosition(employee.Id) + if err != nil { + return nil, errorGetPosition + } + role = append(role, divsionPositions...) + role = append(role, installationPositions...) + role = append(role, unitPositions...) + role = append(role, specialistPositions...) + role = append(role, subspecialistPositions...) // atClaims["division_positions"] = divsionPositions // outputData["division_positions"] = divsionPositions } diff --git a/internal/use-case/main-use-case/authentication/helper.go b/internal/use-case/main-use-case/authentication/helper.go index c4e67200..87473386 100644 --- a/internal/use-case/main-use-case/authentication/helper.go +++ b/internal/use-case/main-use-case/authentication/helper.go @@ -2,9 +2,18 @@ package authentication import ( edp "simrs-vx/internal/domain/main-entities/division-position" + eip "simrs-vx/internal/domain/main-entities/installation-position" + esp "simrs-vx/internal/domain/main-entities/specialist-position" + essp "simrs-vx/internal/domain/main-entities/subspecialist-position" + eup "simrs-vx/internal/domain/main-entities/unit-position" + + udp "simrs-vx/internal/use-case/main-use-case/division-position" + uip "simrs-vx/internal/use-case/main-use-case/installation-position" + usp "simrs-vx/internal/use-case/main-use-case/specialist-position" + ussp "simrs-vx/internal/use-case/main-use-case/subspecialist-position" + uup "simrs-vx/internal/use-case/main-use-case/unit-position" dg "github.com/karincake/apem/db-gorm-pg" - "gorm.io/gorm" ) // just return the error code @@ -34,21 +43,129 @@ func getDivisionPosition(employee_id uint) ([]string, error) { // return result, errors.New("no employee found") // } - var divisionPositions []edp.DivisionPosition - err := dg.I. - Preload("Division"). - Where("\"Employee_Id\" = ?", employee_id). - Find(&divisionPositions).Error + //var divisionPositions []edp.DivisionPosition + //err := dg.I. + // Preload("Division"). + // Where("\"Employee_Id\" = ?", employee_id). + // Find(&divisionPositions).Error + //if err != nil { + // if err == gorm.ErrRecordNotFound { + // return result, nil + // } + // return result, err + //} + + // get data division_position based on employee_id + dataDivisionPosition, err := udp.ReadList(edp.ReadListDto{ + FilterDto: edp.FilterDto{Employee_Id: &employee_id}, + Includes: "division"}) if err != nil { - if err == gorm.ErrRecordNotFound { - return result, nil - } - return result, err + return nil, err } - for _, dp := range divisionPositions { - if dp.Division != nil { - result = append(result, "div-"+dp.Division.Code+"-"+dp.Code) + if list, ok := dataDivisionPosition.Data.([]edp.ResponseDto); ok { + if len(list) > 0 { + for _, dp := range list { + if dp.Division != nil { + result = append(result, "div-"+dp.Division.Code+"-"+dp.Code) + } + } + } + } + + return result, nil +} + +func getInstallationPosition(employeeId uint) ([]string, error) { + var result []string + + // get data unit_position based on employee_id + dataInstallationPosition, err := uip.ReadList(eip.ReadListDto{ + FilterDto: eip.FilterDto{Employee_Id: &employeeId}, + Includes: "installation"}) + if err != nil { + return nil, err + } + + if list, ok := dataInstallationPosition.Data.([]eip.ResponseDto); ok { + if len(list) > 0 { + for _, dp := range list { + if dp.Installation != nil { + result = append(result, "inst-"+dp.Installation.Code+"-"+dp.Code) + } + } + } + } + + return result, nil +} + +func getUnitPosition(employeeId uint) ([]string, error) { + var result []string + + // get data unit_position based on employee_id + dataUnitPosition, err := uup.ReadList(eup.ReadListDto{ + FilterDto: eup.FilterDto{Employee_Id: &employeeId}, + Includes: "unit"}) + if err != nil { + return nil, err + } + + if list, ok := dataUnitPosition.Data.([]eup.ResponseDto); ok { + if len(list) > 0 { + for _, dp := range list { + if dp.Unit != nil { + result = append(result, "unit-"+dp.Unit.Code+"-"+dp.Code) + } + } + } + } + + return result, nil +} + +func getSpecialistPosition(employeeId uint) ([]string, error) { + var result []string + + // get data unit_position based on employee_id + dataSpecialistPosition, err := usp.ReadList(esp.ReadListDto{ + FilterDto: esp.FilterDto{Employee_Id: &employeeId}, + Includes: "specialist"}) + if err != nil { + return nil, err + } + + if list, ok := dataSpecialistPosition.Data.([]esp.ResponseDto); ok { + if len(list) > 0 { + for _, dp := range list { + if dp.Specialist != nil { + result = append(result, "spec-"+dp.Specialist.Code+"-"+dp.Code) + } + } + } + } + + return result, nil +} + +func getSubspecialistPosition(employeeId uint) ([]string, error) { + var result []string + + // get data unit_position based on employee_id + dataSubspecialistPosition, err := ussp.ReadList(essp.ReadListDto{ + FilterDto: essp.FilterDto{Employee_Id: &employeeId}, + Includes: "subspecialist"}) + if err != nil { + return nil, err + } + + if list, ok := dataSubspecialistPosition.Data.([]essp.ResponseDto); ok { + if len(list) > 0 { + for _, dp := range list { + if dp.Subspecialist != nil { + result = append(result, "subspec-"+dp.Subspecialist.Code+"-"+dp.Code) + } + } } } diff --git a/internal/use-case/main-use-case/installation-position/case.go b/internal/use-case/main-use-case/installation-position/case.go new file mode 100644 index 00000000..f9458c0b --- /dev/null +++ b/internal/use-case/main-use-case/installation-position/case.go @@ -0,0 +1,302 @@ +package installation_position + +import ( + ee "simrs-vx/internal/domain/main-entities/employee" + ei "simrs-vx/internal/domain/main-entities/installation" + e "simrs-vx/internal/domain/main-entities/installation-position" + "strconv" + + ue "simrs-vx/internal/use-case/main-use-case/employee" + ui "simrs-vx/internal/use-case/main-use-case/installation" + + 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 = "installation-position" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.InstallationPosition{} + + 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 err := validateForeignKey(input); 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.InstallationPosition + var dataList []e.InstallationPosition + 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.InstallationPosition + 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.InstallationPosition + 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 := validateForeignKey(input.CreateDto); 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.InstallationPosition + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} + +func validateForeignKey(input e.CreateDto) error { + // validate installation_id + if _, err := ui.ReadDetail(ei.ReadDetailDto{Id: *input.Installation_Id}); err != nil { + return err + } + + // validate employee_Id + if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Employee_Id)}); err != nil { + return err + } + return nil +} diff --git a/internal/use-case/main-use-case/installation-position/helper.go b/internal/use-case/main-use-case/installation-position/helper.go new file mode 100644 index 00000000..640993c8 --- /dev/null +++ b/internal/use-case/main-use-case/installation-position/helper.go @@ -0,0 +1,25 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package installation_position + +import ( + e "simrs-vx/internal/domain/main-entities/installation-position" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.InstallationPosition) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Installation_Id = inputSrc.Installation_Id + data.Code = inputSrc.Code + data.Name = inputSrc.Name + data.HeadStatus = inputSrc.HeadStatus + data.Employee_Id = inputSrc.Employee_Id +} diff --git a/internal/use-case/main-use-case/installation-position/lib.go b/internal/use-case/main-use-case/installation-position/lib.go new file mode 100644 index 00000000..bb33356f --- /dev/null +++ b/internal/use-case/main-use-case/installation-position/lib.go @@ -0,0 +1,156 @@ +package installation_position + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/installation-position" + + 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.InstallationPosition, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.InstallationPosition{} + 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.InstallationPosition, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.InstallationPosition{} + 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.InstallationPosition{}). + 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 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.InstallationPosition, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.InstallationPosition{} + + var tx, getData *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + switch { + case input.Id != 0: + getData = tx.First(&data, input.Id) + case input.Code != nil && *input.Code != "": + getData = tx.Where("code = ?", *input.Code).First(&data) + default: + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-detail-fail", + Detail: "either Id or Code must be provided", + } + + return nil, pl.SetLogError(event, nil) + } + + if err := getData.Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.InstallationPosition, 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.InstallationPosition, 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/installation-position/middleware-runner.go b/internal/use-case/main-use-case/installation-position/middleware-runner.go new file mode 100644 index 00000000..f59aedd9 --- /dev/null +++ b/internal/use-case/main-use-case/installation-position/middleware-runner.go @@ -0,0 +1,103 @@ +package installation_position + +import ( + e "simrs-vx/internal/domain/main-entities/installation-position" + 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.InstallationPosition) 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.InstallationPosition) 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.InstallationPosition) 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.InstallationPosition) 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.InstallationPosition) 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/installation-position/middleware.go b/internal/use-case/main-use-case/installation-position/middleware.go new file mode 100644 index 00000000..ed9aac6e --- /dev/null +++ b/internal/use-case/main-use-case/installation-position/middleware.go @@ -0,0 +1,9 @@ +package installation_position + +// 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/installation-position/tycovar.go b/internal/use-case/main-use-case/installation-position/tycovar.go new file mode 100644 index 00000000..38807df3 --- /dev/null +++ b/internal/use-case/main-use-case/installation-position/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 installation_position + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/installation-position" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.InstallationPosition, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.InstallationPosition, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.InstallationPosition, 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/specialist-position/case.go b/internal/use-case/main-use-case/specialist-position/case.go new file mode 100644 index 00000000..c3c469e6 --- /dev/null +++ b/internal/use-case/main-use-case/specialist-position/case.go @@ -0,0 +1,302 @@ +package specialist_position + +import ( + ee "simrs-vx/internal/domain/main-entities/employee" + es "simrs-vx/internal/domain/main-entities/specialist" + e "simrs-vx/internal/domain/main-entities/specialist-position" + "strconv" + + ue "simrs-vx/internal/use-case/main-use-case/employee" + us "simrs-vx/internal/use-case/main-use-case/specialist" + + 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 = "specialist-position" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.SpecialistPosition{} + + 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 err := validateForeignKey(input); 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.SpecialistPosition + var dataList []e.SpecialistPosition + 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.SpecialistPosition + 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.SpecialistPosition + 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 := validateForeignKey(input.CreateDto); 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.SpecialistPosition + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} + +func validateForeignKey(input e.CreateDto) error { + // validate installation_id + if _, err := us.ReadDetail(es.ReadDetailDto{Id: *input.Specialist_Id}); err != nil { + return err + } + + // validate employee_Id + if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Employee_Id)}); err != nil { + return err + } + return nil +} diff --git a/internal/use-case/main-use-case/specialist-position/helper.go b/internal/use-case/main-use-case/specialist-position/helper.go new file mode 100644 index 00000000..6c3d6434 --- /dev/null +++ b/internal/use-case/main-use-case/specialist-position/helper.go @@ -0,0 +1,25 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package specialist_position + +import ( + e "simrs-vx/internal/domain/main-entities/specialist-position" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.SpecialistPosition) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Specialist_Id = inputSrc.Specialist_Id + data.Code = inputSrc.Code + data.Name = inputSrc.Name + data.HeadStatus = inputSrc.HeadStatus + data.Employee_Id = inputSrc.Employee_Id +} diff --git a/internal/use-case/main-use-case/specialist-position/lib.go b/internal/use-case/main-use-case/specialist-position/lib.go new file mode 100644 index 00000000..3ae54f50 --- /dev/null +++ b/internal/use-case/main-use-case/specialist-position/lib.go @@ -0,0 +1,156 @@ +package specialist_position + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/specialist-position" + + 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.SpecialistPosition, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.SpecialistPosition{} + 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.SpecialistPosition, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.SpecialistPosition{} + 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.SpecialistPosition{}). + 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 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.SpecialistPosition, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.SpecialistPosition{} + + var tx, getData *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + switch { + case input.Id != 0: + getData = tx.First(&data, input.Id) + case input.Code != nil && *input.Code != "": + getData = tx.Where("code = ?", *input.Code).First(&data) + default: + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-detail-fail", + Detail: "either Id or Code must be provided", + } + + return nil, pl.SetLogError(event, nil) + } + + if err := getData.Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.SpecialistPosition, 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.SpecialistPosition, 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/specialist-position/middleware-runner.go b/internal/use-case/main-use-case/specialist-position/middleware-runner.go new file mode 100644 index 00000000..cade8cf9 --- /dev/null +++ b/internal/use-case/main-use-case/specialist-position/middleware-runner.go @@ -0,0 +1,103 @@ +package specialist_position + +import ( + e "simrs-vx/internal/domain/main-entities/specialist-position" + 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.SpecialistPosition) 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.SpecialistPosition) 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.SpecialistPosition) 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.SpecialistPosition) 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.SpecialistPosition) 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/specialist-position/middleware.go b/internal/use-case/main-use-case/specialist-position/middleware.go new file mode 100644 index 00000000..9e3bc169 --- /dev/null +++ b/internal/use-case/main-use-case/specialist-position/middleware.go @@ -0,0 +1,9 @@ +package specialist_position + +// 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/specialist-position/tycovar.go b/internal/use-case/main-use-case/specialist-position/tycovar.go new file mode 100644 index 00000000..4cf93b3b --- /dev/null +++ b/internal/use-case/main-use-case/specialist-position/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 specialist_position + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/specialist-position" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.SpecialistPosition, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.SpecialistPosition, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.SpecialistPosition, 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/subspecialist-position/case.go b/internal/use-case/main-use-case/subspecialist-position/case.go new file mode 100644 index 00000000..d954427a --- /dev/null +++ b/internal/use-case/main-use-case/subspecialist-position/case.go @@ -0,0 +1,302 @@ +package subspecialist_position + +import ( + ee "simrs-vx/internal/domain/main-entities/employee" + es "simrs-vx/internal/domain/main-entities/subspecialist" + e "simrs-vx/internal/domain/main-entities/subspecialist-position" + "strconv" + + ue "simrs-vx/internal/use-case/main-use-case/employee" + us "simrs-vx/internal/use-case/main-use-case/subspecialist" + + 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 = "subspecialist-position" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.SubspecialistPosition{} + + 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 err := validateForeignKey(input); 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.SubspecialistPosition + var dataList []e.SubspecialistPosition + 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.SubspecialistPosition + 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.SubspecialistPosition + 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 := validateForeignKey(input.CreateDto); 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.SubspecialistPosition + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} + +func validateForeignKey(input e.CreateDto) error { + // validate installation_id + if _, err := us.ReadDetail(es.ReadDetailDto{Id: *input.Subspecialist_Id}); err != nil { + return err + } + + // validate employee_Id + if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Employee_Id)}); err != nil { + return err + } + return nil +} diff --git a/internal/use-case/main-use-case/subspecialist-position/helper.go b/internal/use-case/main-use-case/subspecialist-position/helper.go new file mode 100644 index 00000000..1f4c9b2b --- /dev/null +++ b/internal/use-case/main-use-case/subspecialist-position/helper.go @@ -0,0 +1,25 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package subspecialist_position + +import ( + e "simrs-vx/internal/domain/main-entities/subspecialist-position" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.SubspecialistPosition) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Subspecialist_Id = inputSrc.Subspecialist_Id + data.Code = inputSrc.Code + data.Name = inputSrc.Name + data.HeadStatus = inputSrc.HeadStatus + data.Employee_Id = inputSrc.Employee_Id +} diff --git a/internal/use-case/main-use-case/subspecialist-position/lib.go b/internal/use-case/main-use-case/subspecialist-position/lib.go new file mode 100644 index 00000000..136e982e --- /dev/null +++ b/internal/use-case/main-use-case/subspecialist-position/lib.go @@ -0,0 +1,156 @@ +package subspecialist_position + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/subspecialist-position" + + 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.SubspecialistPosition, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.SubspecialistPosition{} + 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.SubspecialistPosition, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.SubspecialistPosition{} + 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.SubspecialistPosition{}). + 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 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.SubspecialistPosition, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.SubspecialistPosition{} + + var tx, getData *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + switch { + case input.Id != 0: + getData = tx.First(&data, input.Id) + case input.Code != nil && *input.Code != "": + getData = tx.Where("code = ?", *input.Code).First(&data) + default: + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-detail-fail", + Detail: "either Id or Code must be provided", + } + + return nil, pl.SetLogError(event, nil) + } + + if err := getData.Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.SubspecialistPosition, 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.SubspecialistPosition, 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/subspecialist-position/middleware-runner.go b/internal/use-case/main-use-case/subspecialist-position/middleware-runner.go new file mode 100644 index 00000000..66c3d013 --- /dev/null +++ b/internal/use-case/main-use-case/subspecialist-position/middleware-runner.go @@ -0,0 +1,103 @@ +package subspecialist_position + +import ( + e "simrs-vx/internal/domain/main-entities/subspecialist-position" + 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.SubspecialistPosition) 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.SubspecialistPosition) 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.SubspecialistPosition) 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.SubspecialistPosition) 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.SubspecialistPosition) 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/subspecialist-position/middleware.go b/internal/use-case/main-use-case/subspecialist-position/middleware.go new file mode 100644 index 00000000..e7cafc86 --- /dev/null +++ b/internal/use-case/main-use-case/subspecialist-position/middleware.go @@ -0,0 +1,9 @@ +package subspecialist_position + +// 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/subspecialist-position/tycovar.go b/internal/use-case/main-use-case/subspecialist-position/tycovar.go new file mode 100644 index 00000000..e2cd8acd --- /dev/null +++ b/internal/use-case/main-use-case/subspecialist-position/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 subspecialist_position + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/subspecialist-position" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.SubspecialistPosition, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.SubspecialistPosition, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.SubspecialistPosition, 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/unit-position/case.go b/internal/use-case/main-use-case/unit-position/case.go new file mode 100644 index 00000000..f3bd2840 --- /dev/null +++ b/internal/use-case/main-use-case/unit-position/case.go @@ -0,0 +1,302 @@ +package unit_position + +import ( + ee "simrs-vx/internal/domain/main-entities/employee" + eu "simrs-vx/internal/domain/main-entities/unit" + e "simrs-vx/internal/domain/main-entities/unit-position" + "strconv" + + ue "simrs-vx/internal/use-case/main-use-case/employee" + uu "simrs-vx/internal/use-case/main-use-case/unit" + + 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 = "unit-position" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.UnitPosition{} + + 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 err := validateForeignKey(input); 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.UnitPosition + var dataList []e.UnitPosition + 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.UnitPosition + 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.UnitPosition + 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 := validateForeignKey(input.CreateDto); 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.UnitPosition + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} + +func validateForeignKey(input e.CreateDto) error { + // validate installation_id + if _, err := uu.ReadDetail(eu.ReadDetailDto{Id: *input.Unit_Id}); err != nil { + return err + } + + // validate employee_Id + if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Employee_Id)}); err != nil { + return err + } + return nil +} diff --git a/internal/use-case/main-use-case/unit-position/helper.go b/internal/use-case/main-use-case/unit-position/helper.go new file mode 100644 index 00000000..c1db758d --- /dev/null +++ b/internal/use-case/main-use-case/unit-position/helper.go @@ -0,0 +1,25 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package unit_position + +import ( + e "simrs-vx/internal/domain/main-entities/unit-position" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.UnitPosition) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Unit_Id = inputSrc.Unit_Id + data.Code = inputSrc.Code + data.Name = inputSrc.Name + data.HeadStatus = inputSrc.HeadStatus + data.Employee_Id = inputSrc.Employee_Id +} diff --git a/internal/use-case/main-use-case/unit-position/lib.go b/internal/use-case/main-use-case/unit-position/lib.go new file mode 100644 index 00000000..c58edd4e --- /dev/null +++ b/internal/use-case/main-use-case/unit-position/lib.go @@ -0,0 +1,156 @@ +package unit_position + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/unit-position" + + 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.UnitPosition, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.UnitPosition{} + 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.UnitPosition, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.UnitPosition{} + 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.UnitPosition{}). + 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 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.UnitPosition, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.UnitPosition{} + + var tx, getData *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + switch { + case input.Id != 0: + getData = tx.First(&data, input.Id) + case input.Code != nil && *input.Code != "": + getData = tx.Where("code = ?", *input.Code).First(&data) + default: + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-read-detail-fail", + Detail: "either Id or Code must be provided", + } + + return nil, pl.SetLogError(event, nil) + } + + if err := getData.Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.UnitPosition, 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.UnitPosition, 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/unit-position/middleware-runner.go b/internal/use-case/main-use-case/unit-position/middleware-runner.go new file mode 100644 index 00000000..73092025 --- /dev/null +++ b/internal/use-case/main-use-case/unit-position/middleware-runner.go @@ -0,0 +1,103 @@ +package unit_position + +import ( + e "simrs-vx/internal/domain/main-entities/unit-position" + 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.UnitPosition) 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.UnitPosition) 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.UnitPosition) 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.UnitPosition) 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.UnitPosition) 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/unit-position/middleware.go b/internal/use-case/main-use-case/unit-position/middleware.go new file mode 100644 index 00000000..44c1e396 --- /dev/null +++ b/internal/use-case/main-use-case/unit-position/middleware.go @@ -0,0 +1,9 @@ +package unit_position + +// 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/unit-position/tycovar.go b/internal/use-case/main-use-case/unit-position/tycovar.go new file mode 100644 index 00000000..451d6445 --- /dev/null +++ b/internal/use-case/main-use-case/unit-position/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 unit_position + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/unit-position" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.UnitPosition, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.UnitPosition, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.UnitPosition, 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