From a7ce13c76e74ace50747d8a187f913e11544d4a7 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 24 Oct 2025 16:45:00 +0700 Subject: [PATCH 01/14] add crud responsible doctor hist and adm employee hist --- .../main-entities/adm-employee-hist/dto.go | 78 +++++ .../responsible-doctor-hist/dto.go | 78 +++++ .../main-handler/adm-employee-hist/handler.go | 71 ++++ .../interface/main-handler/main-handler.go | 4 + .../responsible-doctor-hist/handler.go | 71 ++++ .../main-use-case/adm-employee-hist/case.go | 303 ++++++++++++++++++ .../main-use-case/adm-employee-hist/helper.go | 24 ++ .../main-use-case/adm-employee-hist/lib.go | 156 +++++++++ .../adm-employee-hist/middleware-runner.go | 103 ++++++ .../adm-employee-hist/middleware.go | 9 + .../adm-employee-hist/tycovar.go | 44 +++ .../responsible-doctor-hist/case.go | 303 ++++++++++++++++++ .../responsible-doctor-hist/helper.go | 24 ++ .../responsible-doctor-hist/lib.go | 156 +++++++++ .../middleware-runner.go | 103 ++++++ .../responsible-doctor-hist/middleware.go | 9 + .../responsible-doctor-hist/tycovar.go | 44 +++ 17 files changed, 1580 insertions(+) create mode 100644 internal/domain/main-entities/adm-employee-hist/dto.go create mode 100644 internal/domain/main-entities/responsible-doctor-hist/dto.go create mode 100644 internal/interface/main-handler/adm-employee-hist/handler.go create mode 100644 internal/interface/main-handler/responsible-doctor-hist/handler.go create mode 100644 internal/use-case/main-use-case/adm-employee-hist/case.go create mode 100644 internal/use-case/main-use-case/adm-employee-hist/helper.go create mode 100644 internal/use-case/main-use-case/adm-employee-hist/lib.go create mode 100644 internal/use-case/main-use-case/adm-employee-hist/middleware-runner.go create mode 100644 internal/use-case/main-use-case/adm-employee-hist/middleware.go create mode 100644 internal/use-case/main-use-case/adm-employee-hist/tycovar.go create mode 100644 internal/use-case/main-use-case/responsible-doctor-hist/case.go create mode 100644 internal/use-case/main-use-case/responsible-doctor-hist/helper.go create mode 100644 internal/use-case/main-use-case/responsible-doctor-hist/lib.go create mode 100644 internal/use-case/main-use-case/responsible-doctor-hist/middleware-runner.go create mode 100644 internal/use-case/main-use-case/responsible-doctor-hist/middleware.go create mode 100644 internal/use-case/main-use-case/responsible-doctor-hist/tycovar.go diff --git a/internal/domain/main-entities/adm-employee-hist/dto.go b/internal/domain/main-entities/adm-employee-hist/dto.go new file mode 100644 index 00000000..9a5d838f --- /dev/null +++ b/internal/domain/main-entities/adm-employee-hist/dto.go @@ -0,0 +1,78 @@ +package adm_employee_hist + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ee "simrs-vx/internal/domain/main-entities/employee" + "time" +) + +type CreateDto struct { + Encounter_Id *uint `json:"encounter_id"` + Employee_Id *uint `json:"employee_id"` + StartedAt *time.Time `json:"startedAt"` + FinishedAt *time.Time `json:"finishedAt"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` + Employee_Id *uint `json:"employee-id"` + StartedAt *time.Time `json:"startedAt"` + FinishedAt *time.Time `json:"finishedAt"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Code *string `json:"code"` + 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 // adjust this according to the needs + Encounter_Id *uint `json:"encounter_id"` + Employee_Id *uint `json:"employee_id"` + Employee *ee.Employee `json:"employee,omitempty"` + StartedAt *time.Time `json:"startedAt"` + FinishedAt *time.Time `json:"finishedAt"` +} + +func (d AdmEmployeeHist) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Employee_Id: d.Employee_Id, + Employee: d.Employee, + StartedAt: d.StartedAt, + FinishedAt: d.FinishedAt, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []AdmEmployeeHist) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/responsible-doctor-hist/dto.go b/internal/domain/main-entities/responsible-doctor-hist/dto.go new file mode 100644 index 00000000..342faffa --- /dev/null +++ b/internal/domain/main-entities/responsible-doctor-hist/dto.go @@ -0,0 +1,78 @@ +package responsible_doctor_hist + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ed "simrs-vx/internal/domain/main-entities/doctor" + "time" +) + +type CreateDto struct { + Encounter_Id *uint `json:"encounter_id"` + Doctor_Id *uint `json:"doctor_id"` + StartedAt *time.Time `json:"startedAt"` + FinishedAt *time.Time `json:"finishedAt"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Encounter_Id *uint `json:"encounter-id"` + Doctor_Id *uint `json:"doctor-id"` + StartedAt *time.Time `json:"startedAt"` + FinishedAt *time.Time `json:"finishedAt"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Code *string `json:"code"` + 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 // adjust this according to the needs + Encounter_Id *uint `json:"encounter_id"` + Doctor_Id *uint `json:"doctor_id"` + Doctor *ed.Doctor `json:"doctor,omitempty"` + StartedAt *time.Time `json:"startedAt"` + FinishedAt *time.Time `json:"finishedAt"` +} + +func (d ResponsibleDoctorHist) ToResponse() ResponseDto { + resp := ResponseDto{ + Encounter_Id: d.Encounter_Id, + Doctor_Id: d.Doctor_Id, + Doctor: d.Doctor, + StartedAt: d.StartedAt, + FinishedAt: d.FinishedAt, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []ResponsibleDoctorHist) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/interface/main-handler/adm-employee-hist/handler.go b/internal/interface/main-handler/adm-employee-hist/handler.go new file mode 100644 index 00000000..176d8a68 --- /dev/null +++ b/internal/interface/main-handler/adm-employee-hist/handler.go @@ -0,0 +1,71 @@ +package adm_employee_hist + +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/adm-employee-hist" + u "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" +) + +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 690a830a..89b55f26 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -31,6 +31,7 @@ import ( soapi "simrs-vx/internal/interface/main-handler/soapi" /******************** actor ********************/ + admemployeehist "simrs-vx/internal/interface/main-handler/adm-employee-hist" doctor "simrs-vx/internal/interface/main-handler/doctor" employee "simrs-vx/internal/interface/main-handler/employee" nurse "simrs-vx/internal/interface/main-handler/nurse" @@ -41,6 +42,7 @@ import ( personcontact "simrs-vx/internal/interface/main-handler/person-contact" personinsurance "simrs-vx/internal/interface/main-handler/person-insurance" pharmacist "simrs-vx/internal/interface/main-handler/pharmacist" + responsibledoctorhist "simrs-vx/internal/interface/main-handler/responsible-doctor-hist" user "simrs-vx/internal/interface/main-handler/user" /******************** external ********************/ @@ -247,6 +249,8 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/internal-reference", internalreference.O) hc.RegCrud(r, "/v1/ambulance-transport-req", ambulancetransportrequest.O) + hc.RegCrud(r, "/v1/responsible-doctor-hist", responsibledoctorhist.O) + hc.RegCrud(r, "/v1/adm-employee-hist", admemployeehist.O) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/interface/main-handler/responsible-doctor-hist/handler.go b/internal/interface/main-handler/responsible-doctor-hist/handler.go new file mode 100644 index 00000000..4b07df8a --- /dev/null +++ b/internal/interface/main-handler/responsible-doctor-hist/handler.go @@ -0,0 +1,71 @@ +package responsible_doctor_hist + +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/responsible-doctor-hist" + u "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" +) + +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/adm-employee-hist/case.go b/internal/use-case/main-use-case/adm-employee-hist/case.go new file mode 100644 index 00000000..13e308bf --- /dev/null +++ b/internal/use-case/main-use-case/adm-employee-hist/case.go @@ -0,0 +1,303 @@ +package adm_employee_hist + +import ( + e "simrs-vx/internal/domain/main-entities/adm-employee-hist" + eem "simrs-vx/internal/domain/main-entities/employee" + ee "simrs-vx/internal/domain/main-entities/encounter" + "strconv" + + uem "simrs-vx/internal/use-case/main-use-case/employee" + ue "simrs-vx/internal/use-case/main-use-case/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" + + "gorm.io/gorm" +) + +const source = "adm-employee-hist" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.AdmEmployeeHist{} + + 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.AdmEmployeeHist + var dataList []e.AdmEmployeeHist + 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.AdmEmployeeHist + 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.AdmEmployeeHist + 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.AdmEmployeeHist + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} + +func validateForeignKey(input e.CreateDto) error { + // validate encounter_id + if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Encounter_Id)}); err != nil { + return err + } + + // validate doctor_id + if _, err := uem.ReadDetail(eem.ReadDetailDto{Id: uint16(*input.Employee_Id)}); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/adm-employee-hist/helper.go b/internal/use-case/main-use-case/adm-employee-hist/helper.go new file mode 100644 index 00000000..19363f0e --- /dev/null +++ b/internal/use-case/main-use-case/adm-employee-hist/helper.go @@ -0,0 +1,24 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package adm_employee_hist + +import ( + e "simrs-vx/internal/domain/main-entities/adm-employee-hist" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.AdmEmployeeHist) { + 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.Employee_Id = inputSrc.Employee_Id + data.StartedAt = inputSrc.StartedAt + data.FinishedAt = inputSrc.FinishedAt +} diff --git a/internal/use-case/main-use-case/adm-employee-hist/lib.go b/internal/use-case/main-use-case/adm-employee-hist/lib.go new file mode 100644 index 00000000..dc03b28a --- /dev/null +++ b/internal/use-case/main-use-case/adm-employee-hist/lib.go @@ -0,0 +1,156 @@ +package adm_employee_hist + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/adm-employee-hist" + + 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.AdmEmployeeHist, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.AdmEmployeeHist{} + 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.AdmEmployeeHist, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.AdmEmployeeHist{} + 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.AdmEmployeeHist{}). + 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.AdmEmployeeHist, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.AdmEmployeeHist{} + + 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.AdmEmployeeHist, 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.AdmEmployeeHist, 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/adm-employee-hist/middleware-runner.go b/internal/use-case/main-use-case/adm-employee-hist/middleware-runner.go new file mode 100644 index 00000000..45f736b5 --- /dev/null +++ b/internal/use-case/main-use-case/adm-employee-hist/middleware-runner.go @@ -0,0 +1,103 @@ +package adm_employee_hist + +import ( + e "simrs-vx/internal/domain/main-entities/adm-employee-hist" + 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.AdmEmployeeHist) 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.AdmEmployeeHist) 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.AdmEmployeeHist) 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.AdmEmployeeHist) 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.AdmEmployeeHist) 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/adm-employee-hist/middleware.go b/internal/use-case/main-use-case/adm-employee-hist/middleware.go new file mode 100644 index 00000000..ad6ae298 --- /dev/null +++ b/internal/use-case/main-use-case/adm-employee-hist/middleware.go @@ -0,0 +1,9 @@ +package adm_employee_hist + +// 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/adm-employee-hist/tycovar.go b/internal/use-case/main-use-case/adm-employee-hist/tycovar.go new file mode 100644 index 00000000..0827026d --- /dev/null +++ b/internal/use-case/main-use-case/adm-employee-hist/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 adm_employee_hist + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/adm-employee-hist" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.AdmEmployeeHist, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.AdmEmployeeHist, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.AdmEmployeeHist, tx *gorm.DB) error +} + +type UpdateMw = readDetailMw +type DeleteMw = readDetailMw + +var createPreMw []createMw // preprocess middleware +var createPostMw []createMw // postprocess middleware +var readListPreMw []readListMw // .. +var readListPostMw []readListMw // .. +var readDetailPreMw []readDetailMw +var readDetailPostMw []readDetailMw +var updatePreMw []readDetailMw +var updatePostMw []readDetailMw +var deletePreMw []readDetailMw +var deletePostMw []readDetailMw diff --git a/internal/use-case/main-use-case/responsible-doctor-hist/case.go b/internal/use-case/main-use-case/responsible-doctor-hist/case.go new file mode 100644 index 00000000..591c6dd7 --- /dev/null +++ b/internal/use-case/main-use-case/responsible-doctor-hist/case.go @@ -0,0 +1,303 @@ +package responsible_doctor_hist + +import ( + ed "simrs-vx/internal/domain/main-entities/doctor" + ee "simrs-vx/internal/domain/main-entities/encounter" + e "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" + "strconv" + + ud "simrs-vx/internal/use-case/main-use-case/doctor" + ue "simrs-vx/internal/use-case/main-use-case/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" + + "gorm.io/gorm" +) + +const source = "responsible-doctor-hist" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.ResponsibleDoctorHist{} + + 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.ResponsibleDoctorHist + var dataList []e.ResponsibleDoctorHist + 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.ResponsibleDoctorHist + 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.ResponsibleDoctorHist + 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.ResponsibleDoctorHist + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} + +func validateForeignKey(input e.CreateDto) error { + // validate encounter_id + if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Encounter_Id)}); err != nil { + return err + } + + // validate doctor_id + if _, err := ud.ReadDetail(ed.ReadDetailDto{Id: uint16(*input.Doctor_Id)}); err != nil { + return err + } + + return nil +} diff --git a/internal/use-case/main-use-case/responsible-doctor-hist/helper.go b/internal/use-case/main-use-case/responsible-doctor-hist/helper.go new file mode 100644 index 00000000..6efd1097 --- /dev/null +++ b/internal/use-case/main-use-case/responsible-doctor-hist/helper.go @@ -0,0 +1,24 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package responsible_doctor_hist + +import ( + e "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.ResponsibleDoctorHist) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Encounter_Id = inputSrc.Encounter_Id + data.Doctor_Id = inputSrc.Doctor_Id + data.StartedAt = inputSrc.StartedAt + data.FinishedAt = inputSrc.FinishedAt +} diff --git a/internal/use-case/main-use-case/responsible-doctor-hist/lib.go b/internal/use-case/main-use-case/responsible-doctor-hist/lib.go new file mode 100644 index 00000000..d91294fe --- /dev/null +++ b/internal/use-case/main-use-case/responsible-doctor-hist/lib.go @@ -0,0 +1,156 @@ +package responsible_doctor_hist + +import ( + "errors" + e "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" + + 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.ResponsibleDoctorHist, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.ResponsibleDoctorHist{} + 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.ResponsibleDoctorHist, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.ResponsibleDoctorHist{} + 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.ResponsibleDoctorHist{}). + 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.ResponsibleDoctorHist, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.ResponsibleDoctorHist{} + + 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.ResponsibleDoctorHist, 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.ResponsibleDoctorHist, 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/responsible-doctor-hist/middleware-runner.go b/internal/use-case/main-use-case/responsible-doctor-hist/middleware-runner.go new file mode 100644 index 00000000..0b107557 --- /dev/null +++ b/internal/use-case/main-use-case/responsible-doctor-hist/middleware-runner.go @@ -0,0 +1,103 @@ +package responsible_doctor_hist + +import ( + e "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" + 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.ResponsibleDoctorHist) 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.ResponsibleDoctorHist) 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.ResponsibleDoctorHist) 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.ResponsibleDoctorHist) 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.ResponsibleDoctorHist) 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/responsible-doctor-hist/middleware.go b/internal/use-case/main-use-case/responsible-doctor-hist/middleware.go new file mode 100644 index 00000000..324938e8 --- /dev/null +++ b/internal/use-case/main-use-case/responsible-doctor-hist/middleware.go @@ -0,0 +1,9 @@ +package responsible_doctor_hist + +// 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/responsible-doctor-hist/tycovar.go b/internal/use-case/main-use-case/responsible-doctor-hist/tycovar.go new file mode 100644 index 00000000..aac1d18c --- /dev/null +++ b/internal/use-case/main-use-case/responsible-doctor-hist/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 responsible_doctor_hist + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.ResponsibleDoctorHist, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.ResponsibleDoctorHist, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.ResponsibleDoctorHist, 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 de42ab3203033b480524a6f5788331bbea4fbaf1 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Sat, 25 Oct 2025 09:03:00 +0700 Subject: [PATCH 02/14] wip --- internal/domain/bpjs-entities/member/dto.go | 1 - .../domain/bpjs-entities/vclaim-member/dto.go | 60 ++++ internal/domain/main-entities/patient/dto.go | 23 +- internal/domain/main-entities/person/dto.go | 100 +++--- .../bpjs-use-case/vclaim-member/case.go | 334 ++++++++++++++++++ .../bpjs-use-case/vclaim-member/helper.go | 22 ++ .../bpjs-use-case/vclaim-member/lib.go | 147 ++++++++ .../vclaim-member/middleware-runner.go | 103 ++++++ .../bpjs-use-case/vclaim-member/middleware.go | 13 + .../bpjs-use-case/vclaim-member/tycovar.go | 44 +++ .../use-case/main-use-case/person/helper.go | 2 + 11 files changed, 794 insertions(+), 55 deletions(-) create mode 100644 internal/use-case/bpjs-use-case/vclaim-member/case.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-member/helper.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-member/lib.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-member/middleware-runner.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-member/middleware.go create mode 100644 internal/use-case/bpjs-use-case/vclaim-member/tycovar.go diff --git a/internal/domain/bpjs-entities/member/dto.go b/internal/domain/bpjs-entities/member/dto.go index f9c95655..e61a9f74 100644 --- a/internal/domain/bpjs-entities/member/dto.go +++ b/internal/domain/bpjs-entities/member/dto.go @@ -4,7 +4,6 @@ type ReadListDto struct { ReferenceType ReferenceType `json:"-"` PathValue1 string `json:"-"` PathValue2 string `json:"-"` - PathValue3 string `json:"-"` } type ReferenceType string diff --git a/internal/domain/bpjs-entities/vclaim-member/dto.go b/internal/domain/bpjs-entities/vclaim-member/dto.go index 5914deb6..34638abb 100644 --- a/internal/domain/bpjs-entities/vclaim-member/dto.go +++ b/internal/domain/bpjs-entities/vclaim-member/dto.go @@ -1 +1,61 @@ package vclaimmember + +import ecore "simrs-vx/internal/domain/base-entities/core" + +type CreateDto struct { + CardNumber *string `json:"cardNumber" validate:"maxLength=20"` + Person_Id *uint `json:"person_id"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Sort string `json:"sort"` + Pagination ecore.Pagination +} + +type FilterDto struct { + CardNumber *string `json:"cardNumber"` +} + +type ReadDetailDto struct { + Id uint `json:"id"` + CardNumber *string `json:"cardNumber"` +} + +type UpdateDto struct { + Id uint `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint `json:"id"` + CardNumber *string `json:"cardNumber"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + CardNumber *string `json:"cardNumber"` +} + +func (d VclaimMember) ToResponse() ResponseDto { + resp := ResponseDto{ + CardNumber: d.CardNumber, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []VclaimMember) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/patient/dto.go b/internal/domain/main-entities/patient/dto.go index 4927a50e..abe3b46b 100644 --- a/internal/domain/main-entities/patient/dto.go +++ b/internal/domain/main-entities/patient/dto.go @@ -13,18 +13,23 @@ import ( erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" + + pa "simrs-vx/pkg/auth-helper" ) type CreateDto struct { - Person_Id *uint `json:"-"` - Person *ep.UpdateDto `json:"person"` - NewBornStatus bool `json:"newBornStatus"` - PersonAddresses []epa.UpdateDto `json:"personAddresses"` - PersonContacts []epc.UpdateDto `json:"personContacts"` - PersonRelatives []epr.UpdateDto `json:"personRelatives"` - PersonInsurances []epi.UpdateDto `json:"personInsurances"` - RegisteredAt *time.Time `json:"registeredAt"` - Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` + Person_Id *uint `json:"-"` + Person *ep.UpdateDto `json:"person"` + NewBornStatus bool `json:"newBornStatus"` + PersonAddresses []epa.UpdateDto `json:"personAddresses"` + PersonContacts []epc.UpdateDto `json:"personContacts"` + PersonRelatives []epr.UpdateDto `json:"personRelatives"` + PersonInsurances []epi.UpdateDto `json:"personInsurances"` + RegisteredAt *time.Time `json:"registeredAt"` + RegisteredBy_User_Name *string `json:"registeredBy_user_name" validate:"maxLength=100"` + Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` + + pa.AuthInfo } type ReadListDto struct { diff --git a/internal/domain/main-entities/person/dto.go b/internal/domain/main-entities/person/dto.go index 7fb6a235..588379df 100644 --- a/internal/domain/main-entities/person/dto.go +++ b/internal/domain/main-entities/person/dto.go @@ -4,6 +4,7 @@ import ( "time" ecore "simrs-vx/internal/domain/base-entities/core" + evm "simrs-vx/internal/domain/bpjs-entities/vclaim-member" ee "simrs-vx/internal/domain/main-entities/ethnic" epa "simrs-vx/internal/domain/main-entities/person-address" epc "simrs-vx/internal/domain/main-entities/person-contact" @@ -14,24 +15,27 @@ import ( ) type CreateDto struct { - Name string `json:"name" validate:"maxLength=150"` - FrontTitle *string `json:"frontTitle" validate:"maxLength=50"` - EndTitle *string `json:"endTitle" validate:"maxLength=50"` - BirthDate *time.Time `json:"birthDate,omitempty"` - BirthRegency_Code *string `json:"birthRegency_code" validate:"maxLength=4"` - Gender_Code *erp.GenderCode `json:"gender_code"` - ResidentIdentityNumber *string `json:"residentIdentityNumber" validate:"nik;maxLength=16"` - PassportNumber *string `json:"passportNumber" validate:"maxLength=20"` - DrivingLicenseNumber *string `json:"drivingLicenseNumber" validate:"maxLength=20"` - Religion_Code *erp.ReligionCode `json:"religion_code" validate:"maxLength=10"` - Education_Code *erp.EducationCode `json:"education_code" validate:"maxLength=10"` - Ocupation_Code *erp.OcupationCode `json:"occupation_code" validate:"maxLength=15"` - Ocupation_Name *string `json:"occupation_name" validate:"maxLength=50"` - Nationality *string `json:"nationality" validate:"maxLength=50"` - Ethnic_Code *string `json:"ethnic_code" validate:"maxLength=20"` - Language_Code *string `json:"language_code" validate:"maxLength=10"` - CommunicationIssueStatus bool `json:"communicationIssueStatus"` - Disability *string `json:"disability" validate:"maxLength=100"` + Name string `json:"name" validate:"maxLength=150"` + FrontTitle *string `json:"frontTitle" validate:"maxLength=50"` + EndTitle *string `json:"endTitle" validate:"maxLength=50"` + BirthDate *time.Time `json:"birthDate,omitempty"` + BirthRegency_Code *string `json:"birthRegency_code" validate:"maxLength=4"` + Gender_Code *erp.GenderCode `json:"gender_code"` + ResidentIdentityNumber *string `json:"residentIdentityNumber" validate:"nik;maxLength=16"` + PassportNumber *string `json:"passportNumber" validate:"maxLength=20"` + DrivingLicenseNumber *string `json:"drivingLicenseNumber" validate:"maxLength=20"` + Religion_Code *erp.ReligionCode `json:"religion_code" validate:"maxLength=10"` + Education_Code *erp.EducationCode `json:"education_code" validate:"maxLength=10"` + Ocupation_Code *erp.OcupationCode `json:"occupation_code" validate:"maxLength=15"` + Ocupation_Name *string `json:"occupation_name" validate:"maxLength=50"` + MaritalStatus_Code *erp.MaritalStatusCode `json:"maritalStatus_code" validate:"maxLength=10"` + Confidence *string `json:"confidence" validate:"maxLength=512"` + Nationality *string `json:"nationality" validate:"maxLength=50"` + Ethnic_Code *string `json:"ethnic_code" validate:"maxLength=20"` + Language_Code *string `json:"language_code" validate:"maxLength=10"` + CommunicationIssueStatus bool `json:"communicationIssueStatus"` + Disability *string `json:"disability" validate:"maxLength=100"` + VclaimMember_CardNumber *string `json:"vclaimMember_cardNumber" validate:"maxLength=20"` } type ReadListDto struct { @@ -86,33 +90,36 @@ type MetaDto struct { type ResponseDto struct { ecore.Main - Name string `json:"name"` - FrontTitle *string `json:"frontTitle"` - EndTitle *string `json:"endTitle"` - BirthDate *time.Time `json:"birthDate,omitempty"` - BirthRegency_Code *string `json:"birthRegency_code"` - BirthRegency *er.Regency `json:"birthRegency,omitempty"` - Gender_Code *erp.GenderCode `json:"gender_code"` - ResidentIdentityNumber *string `json:"residentIdentityNumber"` - PassportNumber *string `json:"passportNumber"` - DrivingLicenseNumber *string `json:"drivingLicenseNumber"` - Religion_Code *erp.ReligionCode `json:"religion_code"` - Education_Code *erp.EducationCode `json:"education_code"` - Ocupation_Code *erp.OcupationCode `json:"occupation_code"` - Ocupation_Name *string `json:"occupation_name"` - Nationality *string `json:"nationality"` - Ethnic_Code *string `json:"ethnic_code"` - Ethnic *ee.Ethnic `json:"ethnic,omitempty"` - Addresses *[]epa.PersonAddress `json:"addresses,omitempty"` - Contacts *[]epc.PersonContact `json:"contacts,omitempty"` - Relatives *[]epr.PersonRelative `json:"relatives,omitempty"` - Language_Code *string `json:"language_code"` - CommunicationIssueStatus bool `json:"communicationIssueStatus"` - Disability *string `json:"disability"` - ResidentIdentityFileUrl *string `json:"residentIdentityFileUrl"` - PassportFileUrl *string `json:"passportFileUrl"` - DrivingLicenseFileUrl *string `json:"drivingLicenseFileUrl"` - FamilyIdentityFileUrl *string `json:"familyIdentityFileUrl"` + Name string `json:"name"` + FrontTitle *string `json:"frontTitle"` + EndTitle *string `json:"endTitle"` + BirthDate *time.Time `json:"birthDate,omitempty"` + BirthRegency_Code *string `json:"birthRegency_code"` + BirthRegency *er.Regency `json:"birthRegency,omitempty"` + Gender_Code *erp.GenderCode `json:"gender_code"` + ResidentIdentityNumber *string `json:"residentIdentityNumber"` + PassportNumber *string `json:"passportNumber"` + DrivingLicenseNumber *string `json:"drivingLicenseNumber"` + Religion_Code *erp.ReligionCode `json:"religion_code"` + Education_Code *erp.EducationCode `json:"education_code"` + Ocupation_Code *erp.OcupationCode `json:"occupation_code"` + Ocupation_Name *string `json:"occupation_name"` + MaritalStatus_Code *erp.MaritalStatusCode `json:"maritalStatus_code"` + Confidence *string `json:"confidence"` + Nationality *string `json:"nationality"` + Ethnic_Code *string `json:"ethnic_code"` + Ethnic *ee.Ethnic `json:"ethnic,omitempty"` + Addresses *[]epa.PersonAddress `json:"addresses,omitempty"` + Contacts *[]epc.PersonContact `json:"contacts,omitempty"` + Relatives *[]epr.PersonRelative `json:"relatives,omitempty"` + Language_Code *string `json:"language_code"` + CommunicationIssueStatus bool `json:"communicationIssueStatus"` + Disability *string `json:"disability"` + ResidentIdentityFileUrl *string `json:"residentIdentityFileUrl"` + PassportFileUrl *string `json:"passportFileUrl"` + DrivingLicenseFileUrl *string `json:"drivingLicenseFileUrl"` + FamilyIdentityFileUrl *string `json:"familyIdentityFileUrl"` + VclaimMember *evm.VclaimMember `json:"vclaimMember,omitempty"` } func (d *Person) ToResponse() ResponseDto { @@ -131,6 +138,8 @@ func (d *Person) ToResponse() ResponseDto { Education_Code: d.Education_Code, Ocupation_Code: d.Ocupation_Code, Ocupation_Name: d.Ocupation_Name, + MaritalStatus_Code: d.MaritalStatus_Code, + Confidence: d.Confidence, Nationality: d.Nationality, Ethnic_Code: d.Ethnic_Code, Ethnic: d.Ethnic, @@ -144,6 +153,7 @@ func (d *Person) ToResponse() ResponseDto { PassportFileUrl: d.PassportFileUrl, DrivingLicenseFileUrl: d.DrivingLicenseFileUrl, FamilyIdentityFileUrl: d.FamilyIdentityFileUrl, + VclaimMember: d.VclaimMember, } resp.Main = d.Main return resp diff --git a/internal/use-case/bpjs-use-case/vclaim-member/case.go b/internal/use-case/bpjs-use-case/vclaim-member/case.go new file mode 100644 index 00000000..7c572f5e --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-member/case.go @@ -0,0 +1,334 @@ +package vclaimmember + +import ( + "strconv" + + e "simrs-vx/internal/domain/bpjs-entities/vclaim-member" + // evsh "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-hist" + + 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 = "vclaim-member" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.VclaimMember{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func ReadList(input e.ReadListDto) (*d.Data, error) { + var data *e.VclaimMember + var dataList []e.VclaimMember + 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.VclaimMember + 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{CardNumber: input.CardNumber} + var data *e.VclaimMember + 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{CardNumber: input.CardNumber} + var data *e.VclaimMember + 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 CreateOrUpdate(input e.CreateDto) (*d.Data, error) { +// rdDto := e.ReadDetailDto{CardNumber: input.CardNumber} +// var data *e.VclaimMember +// var err error + +// event := pl.Event{ +// Feature: "CreateOrUpdate", +// Source: source, +// } + +// // Start log +// pl.SetLogInfo(&event, input, "started", "createOrUpdate") + +// 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.RunCreateMiddleware(createPreMw, &input, data); err != nil { +// return err +// } + +// if err := CreateOrUpdateData(input, data, &event, tx); err != nil { +// return err +// } + +// pl.SetLogInfo(&event, nil, "complete") + +// mwRunner.setMwType(pu.MWTPost) +// // Run post-middleware +// if err := mwRunner.RunCreateMiddleware(createPostMw, &input, data); err != nil { +// return err +// } + +// 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 + +// } diff --git a/internal/use-case/bpjs-use-case/vclaim-member/helper.go b/internal/use-case/bpjs-use-case/vclaim-member/helper.go new file mode 100644 index 00000000..c2789e58 --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-member/helper.go @@ -0,0 +1,22 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package vclaimmember + +import ( + e "simrs-vx/internal/domain/bpjs-entities/vclaim-member" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.VclaimMember) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.CardNumber = inputSrc.CardNumber + data.Person_Id = inputSrc.Person_Id +} diff --git a/internal/use-case/bpjs-use-case/vclaim-member/lib.go b/internal/use-case/bpjs-use-case/vclaim-member/lib.go new file mode 100644 index 00000000..9044664d --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-member/lib.go @@ -0,0 +1,147 @@ +package vclaimmember + +import ( + e "simrs-vx/internal/domain/bpjs-entities/vclaim-member" + + 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.VclaimMember, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.VclaimMember{} + 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.VclaimMember, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.VclaimMember{} + 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.VclaimMember{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Scopes(gh.Sort(input.Sort)) + + if err := tx.Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e.VclaimMember, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.VclaimMember{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if input.CardNumber != nil { + tx = tx.Where("\"CardNumber\" = ?", *input.CardNumber) + } + if input.Id != 0 { + tx = tx.Where("\"Id\" = ?", input.Id) + } + + if err := tx.First(&data).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.VclaimMember, 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.VclaimMember, 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/bpjs-use-case/vclaim-member/middleware-runner.go b/internal/use-case/bpjs-use-case/vclaim-member/middleware-runner.go new file mode 100644 index 00000000..452a06e0 --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-member/middleware-runner.go @@ -0,0 +1,103 @@ +package vclaimmember + +import ( + e "simrs-vx/internal/domain/bpjs-entities/vclaim-member" + 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.VclaimMember) 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.VclaimMember) 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.VclaimMember) 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.VclaimMember) 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.VclaimMember) 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/bpjs-use-case/vclaim-member/middleware.go b/internal/use-case/bpjs-use-case/vclaim-member/middleware.go new file mode 100644 index 00000000..56404d79 --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-member/middleware.go @@ -0,0 +1,13 @@ +package vclaimmember + +// import ( +// pvs "simrs-vx/internal/use-case/bpjs-plugin/vclaim-member" +// ) + +// // example of middleware +// func init() { +// createPreMw = append(createPreMw, +// createMw{Name: "create-sep", Func: pvs.CreateSep}, +// ) + +// } diff --git a/internal/use-case/bpjs-use-case/vclaim-member/tycovar.go b/internal/use-case/bpjs-use-case/vclaim-member/tycovar.go new file mode 100644 index 00000000..dafb1227 --- /dev/null +++ b/internal/use-case/bpjs-use-case/vclaim-member/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 vclaimmember + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/bpjs-entities/vclaim-member" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.VclaimMember, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.VclaimMember, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.VclaimMember, 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/person/helper.go b/internal/use-case/main-use-case/person/helper.go index 61a2f18c..3760f3b2 100644 --- a/internal/use-case/main-use-case/person/helper.go +++ b/internal/use-case/main-use-case/person/helper.go @@ -30,6 +30,8 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Person) { data.Education_Code = inputSrc.Education_Code data.Ocupation_Code = inputSrc.Ocupation_Code data.Ocupation_Name = inputSrc.Ocupation_Name + data.MaritalStatus_Code = inputSrc.MaritalStatus_Code + data.Confidence = inputSrc.Confidence data.Nationality = inputSrc.Nationality data.Ethnic_Code = inputSrc.Ethnic_Code data.Language_Code = inputSrc.Language_Code From a2ce9d9357c0fa0a4caafbf183e03ef8ae2b4f52 Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 27 Oct 2025 06:25:12 +0700 Subject: [PATCH 03/14] add encounter-checkin --- .../domain/main-entities/encounter/dto.go | 11 ++ .../main-handler/encounter/handler.go | 21 ++++ .../encounter/request-validation.go | 16 ++- .../interface/main-handler/main-handler.go | 21 ++-- .../main-use-case/adm-employee-hist/case.go | 27 ----- .../use-case/main-use-case/encounter/case.go | 105 ++++++++++++++++++ .../main-use-case/encounter/helper.go | 5 + .../use-case/main-use-case/encounter/lib.go | 25 +++++ .../responsible-doctor-hist/case.go | 27 ----- 9 files changed, 192 insertions(+), 66 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 4a30281d..ae8d1c5f 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -105,6 +105,17 @@ type DischargeDto struct { InternalReferences *[]eir.CreateDto `json:"internalReferences,omitempty"` } +type CheckinDto struct { + Id uint `json:"id"` + Responsible_Doctor_Id *uint `json:"responsible_doctor_id"` + Responsible_Doctor_StartedAt *time.Time `json:"responsible_doctor_startedAt"` + Responsible_Doctor_FinishedAt *time.Time `json:"responsible_doctor_finishedAt"` + + Adm_Employee_Id *uint `json:"adm_employee_id"` + Adm_Employee_StartedAt *time.Time `json:"adm_employee_startedAt"` + Adm_Employee_FinishedAt *time.Time `json:"adm_employee_finishedAt"` +} + type ResponseDto struct { ecore.Main Patient_Id *uint `json:"patient_id"` diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index df3320ef..85b8746d 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -102,6 +102,27 @@ func (obj myBase) CheckOut(w http.ResponseWriter, r *http.Request) { rw.DataResponse(w, res, err) } +func (obj myBase) CheckIn(w http.ResponseWriter, r *http.Request) { + dto := e.CheckinDto{} + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + // validate request body + if valid := validateRequestCheckIn(w, dto); !valid { + return + } + + dto.Id = uint(id) + res, err := u.CheckIn(dto) + rw.DataResponse(w, res, err) +} + func (obj myBase) Process(w http.ResponseWriter, r *http.Request) { id := rw.ValidateInt(w, "id", r.PathValue("id")) if id <= 0 { diff --git a/internal/interface/main-handler/encounter/request-validation.go b/internal/interface/main-handler/encounter/request-validation.go index 182dd12e..9e92ea88 100644 --- a/internal/interface/main-handler/encounter/request-validation.go +++ b/internal/interface/main-handler/encounter/request-validation.go @@ -10,9 +10,9 @@ import ( rw "github.com/karincake/risoles" ) -func validateRequestCheckout(w http.ResponseWriter, i e.DischargeDto) (valid bool) { - const dataValidationFail = "" +const dataValidationFail = "data-validation-fail" +func validateRequestCheckout(w http.ResponseWriter, i e.DischargeDto) (valid bool) { switch *i.Discharge_Method_Code { case ere.DMCDeath: if i.DeathCause == nil { @@ -51,3 +51,15 @@ func validateRequestCheckout(w http.ResponseWriter, i e.DischargeDto) (valid boo } return true } + +func validateRequestCheckIn(w http.ResponseWriter, i e.CheckinDto) (valid bool) { + if i.Responsible_Doctor_Id == nil && i.Adm_Employee_Id == nil { + rw.DataResponse(w, nil, d.FieldError{ + Code: dataValidationFail, + Message: "responsible_doctor_id or adm_employee_id required", + }) + return + } + + return true +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 89b55f26..23b532c2 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -172,16 +172,17 @@ func SetRoutes() http.Handler { "PATCH /{id}/complete": mcuordersubitem.O.Complete, }) hk.GroupRoutes("/v1/encounter", r, auth.GuardMW, hk.MapHandlerFunc{ - "GET /": encounter.O.GetList, - "GET /{id}": encounter.O.GetDetail, - "POST /": encounter.O.Create, - "PATCH /{id}": encounter.O.Update, - "DELETE /{id}": encounter.O.Delete, - "PATCH /{id}/checkout": encounter.O.CheckOut, - "PATCH /{id}/proccess": encounter.O.Process, - "PATCH /{id}/cancel": encounter.O.Cancel, - "PATCH /{id}/reject": encounter.O.Reject, - "PATCH /{id}/skip": encounter.O.Skip, + "GET /": encounter.O.GetList, + "GET /{id}": encounter.O.GetDetail, + "POST /": encounter.O.Create, + "PATCH /{id}": encounter.O.Update, + "DELETE /{id}": encounter.O.Delete, + "PATCH /{id}/check-out": encounter.O.CheckOut, + "PATCH /{id}/check-in": encounter.O.CheckIn, + "PATCH /{id}/proccess": encounter.O.Process, + "PATCH /{id}/cancel": encounter.O.Cancel, + "PATCH /{id}/reject": encounter.O.Reject, + "PATCH /{id}/skip": encounter.O.Skip, }) hk.GroupRoutes("/v1/medication", r, auth.GuardMW, hk.MapHandlerFunc{ "GET /": medication.O.GetList, diff --git a/internal/use-case/main-use-case/adm-employee-hist/case.go b/internal/use-case/main-use-case/adm-employee-hist/case.go index 13e308bf..51851584 100644 --- a/internal/use-case/main-use-case/adm-employee-hist/case.go +++ b/internal/use-case/main-use-case/adm-employee-hist/case.go @@ -2,13 +2,8 @@ package adm_employee_hist import ( e "simrs-vx/internal/domain/main-entities/adm-employee-hist" - eem "simrs-vx/internal/domain/main-entities/employee" - ee "simrs-vx/internal/domain/main-entities/encounter" "strconv" - uem "simrs-vx/internal/use-case/main-use-case/employee" - ue "simrs-vx/internal/use-case/main-use-case/encounter" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -39,10 +34,6 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } - if err := validateForeignKey(input); err != nil { - return err - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { @@ -200,10 +191,6 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - if err := validateForeignKey(input.CreateDto); err != nil { - return err - } - if err := UpdateData(input, data, &event, tx); err != nil { return err } @@ -287,17 +274,3 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } - -func validateForeignKey(input e.CreateDto) error { - // validate encounter_id - if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Encounter_Id)}); err != nil { - return err - } - - // validate doctor_id - if _, err := uem.ReadDetail(eem.ReadDetailDto{Id: uint16(*input.Employee_Id)}); err != nil { - return err - } - - return nil -} diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 04eedfff..4e41954e 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -4,20 +4,26 @@ import ( "errors" "strconv" + eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" ea "simrs-vx/internal/domain/main-entities/ambulatory" ec "simrs-vx/internal/domain/main-entities/chemo" edc "simrs-vx/internal/domain/main-entities/death-cause" + ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/emergency" eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" ei "simrs-vx/internal/domain/main-entities/inpatient" + erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" + uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" ua "simrs-vx/internal/use-case/main-use-case/ambulatory" uc "simrs-vx/internal/use-case/main-use-case/chemo" udc "simrs-vx/internal/use-case/main-use-case/death-cause" + ud "simrs-vx/internal/use-case/main-use-case/doctor" ue "simrs-vx/internal/use-case/main-use-case/emergency" uem "simrs-vx/internal/use-case/main-use-case/employee" ui "simrs-vx/internal/use-case/main-use-case/inpatient" + urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" @@ -511,3 +517,102 @@ func UpdateStatusCode(input e.UpdateStatusDto) (*d.Data, error) { }, nil } + +func CheckIn(input e.CheckinDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: uint16(input.Id)} + var data *e.Encounter + var err error + + event := pl.Event{ + Feature: "CheckIn", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "checkIn") + + // validate foreign key + if err := validateForeignKey(input); err != nil { + return nil, err + } + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + if data.IsDone() { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-state-mismatch", + Detail: "encounter is done", + Raw: errors.New("encounter is done"), + } + return pl.SetLogError(&event, input) + } + + if err := updateCheckInData(input, data, &event, tx); err != nil { + return err + } + + if input.Responsible_Doctor_Id != nil { + // insert responsible_doctor_hist + if _, err = urdh.Create(erdh.CreateDto{ + Encounter_Id: &input.Id, + Doctor_Id: input.Responsible_Doctor_Id, + StartedAt: input.Responsible_Doctor_StartedAt, + FinishedAt: input.Responsible_Doctor_FinishedAt, + }); err != nil { + return err + } + } + + if input.Adm_Employee_Id != nil { + // insert responsible_doctor_hist + if _, err = uaeh.Create(eaeh.CreateDto{ + Encounter_Id: &input.Id, + Employee_Id: input.Adm_Employee_Id, + StartedAt: input.Adm_Employee_StartedAt, + FinishedAt: input.Adm_Employee_FinishedAt, + }); err != nil { + return err + } + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "checkIn", + }, + Data: data.ToResponse(), + }, nil +} + +func validateForeignKey(input e.CheckinDto) error { + // validate employee_Id + if input.Adm_Employee_Id != nil { + if _, err := uem.ReadDetail(eem.ReadDetailDto{Id: uint16(*input.Adm_Employee_Id)}); err != nil { + return err + } + } + + // validate doctor_id + if input.Responsible_Doctor_Id != nil { + if _, err := ud.ReadDetail(ed.ReadDetailDto{Id: uint16(*input.Responsible_Doctor_Id)}); err != nil { + return err + } + } + + return nil +} diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 41fd6a51..878a472f 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -327,6 +327,11 @@ func setDataUpdateStatus(src e.UpdateStatusDto, dst *e.Encounter) { dst.Status_Code = src.StatusCode } +func setDataCheckIn(src e.CheckinDto, dst *e.Encounter) { + dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id + dst.Adm_Employee_Id = src.Adm_Employee_Id +} + func createInternalReferences(input e.DischargeDto, event *pl.Event, tx *gorm.DB) error { unitIDs := make(map[uint16]struct{}) doctorIDs := make(map[uint]struct{}) diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index ccc4b811..d1dd1afd 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -212,3 +212,28 @@ func UpdateStatusData(input e.UpdateStatusDto, data *e.Encounter, event *pl.Even pl.SetLogInfo(event, nil, "complete") return nil } + +func updateCheckInData(input e.CheckinDto, data *e.Encounter, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, data, "started", "DBUpdate") + setDataCheckIn(input, data) + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.Save(&data).Error; err != nil { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-update-fail", + Detail: "Database update failed", + Raw: err, + } + return pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} diff --git a/internal/use-case/main-use-case/responsible-doctor-hist/case.go b/internal/use-case/main-use-case/responsible-doctor-hist/case.go index 591c6dd7..3451a88f 100644 --- a/internal/use-case/main-use-case/responsible-doctor-hist/case.go +++ b/internal/use-case/main-use-case/responsible-doctor-hist/case.go @@ -1,14 +1,9 @@ package responsible_doctor_hist import ( - ed "simrs-vx/internal/domain/main-entities/doctor" - ee "simrs-vx/internal/domain/main-entities/encounter" e "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" "strconv" - ud "simrs-vx/internal/use-case/main-use-case/doctor" - ue "simrs-vx/internal/use-case/main-use-case/encounter" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -39,10 +34,6 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } - if err := validateForeignKey(input); err != nil { - return err - } - if resData, err := CreateData(input, &event, tx); err != nil { return err } else { @@ -200,10 +191,6 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - if err := validateForeignKey(input.CreateDto); err != nil { - return err - } - if err := UpdateData(input, data, &event, tx); err != nil { return err } @@ -287,17 +274,3 @@ func Delete(input e.DeleteDto) (*d.Data, error) { }, nil } - -func validateForeignKey(input e.CreateDto) error { - // validate encounter_id - if _, err := ue.ReadDetail(ee.ReadDetailDto{Id: uint16(*input.Encounter_Id)}); err != nil { - return err - } - - // validate doctor_id - if _, err := ud.ReadDetail(ed.ReadDetailDto{Id: uint16(*input.Doctor_Id)}); err != nil { - return err - } - - return nil -} From 0aa2ed730ca857c1226824f7ff530ccbbf95945d Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 27 Oct 2025 14:34:52 +0700 Subject: [PATCH 04/14] add checkin --- .../domain/main-entities/encounter/dto.go | 13 ++-- .../main-handler/encounter/handler.go | 8 ++ .../use-case/main-use-case/encounter/case.go | 44 ++++++----- .../main-use-case/encounter/helper.go | 9 ++- .../use-case/main-use-case/encounter/lib.go | 73 +++++++++++++++++++ 5 files changed, 119 insertions(+), 28 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index d07665f0..b552ba86 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -114,14 +114,11 @@ type DischargeDto struct { } type CheckinDto struct { - Id uint `json:"id"` - Responsible_Doctor_Id *uint `json:"responsible_doctor_id"` - Responsible_Doctor_StartedAt *time.Time `json:"responsible_doctor_startedAt"` - Responsible_Doctor_FinishedAt *time.Time `json:"responsible_doctor_finishedAt"` - - Adm_Employee_Id *uint `json:"adm_employee_id"` - Adm_Employee_StartedAt *time.Time `json:"adm_employee_startedAt"` - Adm_Employee_FinishedAt *time.Time `json:"adm_employee_finishedAt"` + Id uint `json:"id"` + Responsible_Doctor_Id *uint `json:"responsible_doctor_id"` + Adm_Employee_Id *uint `json:"adm_employee_id"` + StartedAt *time.Time `json:"startedAt"` + FinishedAt *time.Time `json:"finishedAt"` } type ResponseDto struct { diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index ed8b1fbc..a5e74855 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -2,6 +2,7 @@ package encounter import ( "net/http" + "time" rw "github.com/karincake/risoles" sf "github.com/karincake/semprit" @@ -124,6 +125,13 @@ func (obj myBase) CheckIn(w http.ResponseWriter, r *http.Request) { } dto.Id = uint(id) + + // validate startedAt + if dto.StartedAt == nil { + now := time.Now() + dto.StartedAt = &now + } + res, err := u.CheckIn(dto) rw.DataResponse(w, res, err) } diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 4e41954e..6786eb51 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -2,9 +2,12 @@ package encounter import ( "errors" + eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" + erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" + uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" + urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" "strconv" - eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" ea "simrs-vx/internal/domain/main-entities/ambulatory" ec "simrs-vx/internal/domain/main-entities/chemo" edc "simrs-vx/internal/domain/main-entities/death-cause" @@ -13,9 +16,8 @@ import ( eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" ei "simrs-vx/internal/domain/main-entities/inpatient" - erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" - - uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" + erc "simrs-vx/internal/domain/references/common" + ere "simrs-vx/internal/domain/references/encounter" 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" @@ -23,10 +25,6 @@ import ( ue "simrs-vx/internal/use-case/main-use-case/emergency" uem "simrs-vx/internal/use-case/main-use-case/employee" ui "simrs-vx/internal/use-case/main-use-case/inpatient" - urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" - - erc "simrs-vx/internal/domain/references/common" - ere "simrs-vx/internal/domain/references/encounter" pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" @@ -552,34 +550,44 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { return pl.SetLogError(&event, input) } - if err := updateCheckInData(input, data, &event, tx); err != nil { - return err - } + // Insert responsible_doctor_hist if responsible_doctor_id has changed && update latest history + if input.Responsible_Doctor_Id != nil && *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { + // update finishedAt in latest responsible_doctor_hist + if err = updateLatestResponsibleDoctorHist(input, &event, tx); err != nil { + return err + } - if input.Responsible_Doctor_Id != nil { - // insert responsible_doctor_hist + //insert responsible_doctor_hist if _, err = urdh.Create(erdh.CreateDto{ Encounter_Id: &input.Id, Doctor_Id: input.Responsible_Doctor_Id, - StartedAt: input.Responsible_Doctor_StartedAt, - FinishedAt: input.Responsible_Doctor_FinishedAt, + StartedAt: input.StartedAt, }); err != nil { return err } } - if input.Adm_Employee_Id != nil { + // Insert adm_employee_hist if adm_employee_id has changed && update latest history + if input.Adm_Employee_Id != nil && *input.Adm_Employee_Id != *data.Adm_Employee_Id { + // update finishedAt in latest adm_employee_hist + if err = updateLatestAdmEmployeeHist(input, &event, tx); err != nil { + return err + } + // insert responsible_doctor_hist if _, err = uaeh.Create(eaeh.CreateDto{ Encounter_Id: &input.Id, Employee_Id: input.Adm_Employee_Id, - StartedAt: input.Adm_Employee_StartedAt, - FinishedAt: input.Adm_Employee_FinishedAt, + StartedAt: input.StartedAt, }); err != nil { return err } } + if err := updateCheckInData(input, data, &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 878a472f..2e74eff6 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -328,8 +328,13 @@ func setDataUpdateStatus(src e.UpdateStatusDto, dst *e.Encounter) { } func setDataCheckIn(src e.CheckinDto, dst *e.Encounter) { - dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id - dst.Adm_Employee_Id = src.Adm_Employee_Id + if src.Responsible_Doctor_Id != nil { + dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id + } + + if src.Adm_Employee_Id != nil { + dst.Adm_Employee_Id = src.Adm_Employee_Id + } } func createInternalReferences(input e.DischargeDto, event *pl.Event, tx *gorm.DB) error { diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index f8bee755..6e960e49 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -3,6 +3,7 @@ package encounter import ( // std "errors" + "fmt" // external dg "github.com/karincake/apem/db-gorm-pg" @@ -15,7 +16,9 @@ import ( pu "simrs-vx/pkg/use-case-helper" // internal + eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" e "simrs-vx/internal/domain/main-entities/encounter" + erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" ) func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.Encounter, error) { @@ -250,3 +253,73 @@ func updateCheckInData(input e.CheckinDto, data *e.Encounter, event *pl.Event, d pl.SetLogInfo(event, nil, "complete") return nil } + +func updateLatestResponsibleDoctorHist(input e.CheckinDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, "started", "DBUpdate") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + subQuery := tx. + Select("\"Id\""). + Model(&erdh.ResponsibleDoctorHist{}). + Where("\"Encounter_Id\" = ?", input.Id). + Order("\"CreatedAt\" DESC"). + Limit(1) + + fmt.Println(subQuery) + + if err := tx. + Model(&erdh.ResponsibleDoctorHist{}). + Where("\"Id\" = (?)", subQuery). + Update("\"FinishedAt\"", input.StartedAt).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 updateLatestAdmEmployeeHist(input e.CheckinDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, "started", "DBUpdate") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + subQuery := tx. + Select("\"Id\""). + Model(&eaeh.AdmEmployeeHist{}). + Where("\"Encounter_Id\" = ?", input.Id). + Order("\"CreatedAt\" DESC"). + Limit(1) + + if err := tx. + Model(&eaeh.AdmEmployeeHist{}). + Where("\"Id\" = (?)", subQuery). + Update("\"FinishedAt\"", input.StartedAt).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 +} From 88e7f99e6d6e3e1a27e65665d32ee0de6aa67bee Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 27 Oct 2025 17:10:00 +0700 Subject: [PATCH 05/14] improve check-in --- .../domain/main-entities/encounter/dto.go | 2 + .../use-case/main-use-case/encounter/case.go | 113 +++++++++++++----- .../main-use-case/encounter/helper.go | 5 + .../use-case/main-use-case/encounter/lib.go | 30 +++-- 4 files changed, 113 insertions(+), 37 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index b552ba86..89f12caf 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -46,6 +46,8 @@ type CreateDto struct { Responsible_Doctor_Id *uint `json:"responsible_doctor_id"` RefSource_Name *string `json:"refSource_name" validate:"maxLength=100"` Appointment_Id *uint `json:"appointment_id"` + RefTypeCode ere.RefTypeCode `json:"refTypeCode"` + NewStatus bool `json:"newStatus"` pa.AuthInfo } diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index 6786eb51..c55b5b4b 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -2,12 +2,21 @@ package encounter import ( "errors" - eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" - erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" - uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" - urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" + authhelper "simrs-vx/internal/lib/auth" "strconv" + "time" + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + erc "simrs-vx/internal/domain/references/common" + ere "simrs-vx/internal/domain/references/encounter" + + eaeh "simrs-vx/internal/domain/main-entities/adm-employee-hist" ea "simrs-vx/internal/domain/main-entities/ambulatory" ec "simrs-vx/internal/domain/main-entities/chemo" edc "simrs-vx/internal/domain/main-entities/death-cause" @@ -16,8 +25,10 @@ import ( eem "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/encounter" ei "simrs-vx/internal/domain/main-entities/inpatient" - erc "simrs-vx/internal/domain/references/common" - ere "simrs-vx/internal/domain/references/encounter" + erdh "simrs-vx/internal/domain/main-entities/responsible-doctor-hist" + es "simrs-vx/internal/domain/main-entities/soapi" + + uaeh "simrs-vx/internal/use-case/main-use-case/adm-employee-hist" ua "simrs-vx/internal/use-case/main-use-case/ambulatory" uc "simrs-vx/internal/use-case/main-use-case/chemo" udc "simrs-vx/internal/use-case/main-use-case/death-cause" @@ -25,18 +36,14 @@ import ( ue "simrs-vx/internal/use-case/main-use-case/emergency" uem "simrs-vx/internal/use-case/main-use-case/employee" ui "simrs-vx/internal/use-case/main-use-case/inpatient" - - 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" + urdh "simrs-vx/internal/use-case/main-use-case/responsible-doctor-hist" + us "simrs-vx/internal/use-case/main-use-case/soapi" ) const source = "encounter" +var now = time.Now() + func Create(input e.CreateDto) (*d.Data, error) { data := e.Encounter{} @@ -48,7 +55,21 @@ func Create(input e.CreateDto) (*d.Data, error) { // Start log pl.SetLogInfo(&event, input, "started", "create") - err := dg.I.Transaction(func(tx *gorm.DB) error { + // check if patient is new in the hospital + dataPatient, err := ReadList(e.ReadListDto{ + FilterDto: e.FilterDto{Patient_Id: input.Patient_Id}, + AuthInfo: authhelper.AuthInfo{User_Id: input.User_Id}}) + if err != nil { + return nil, err + } + + if list, ok := dataPatient.Data.([]e.ResponseDto); ok { + if len(list) < 1 { + input.NewStatus = true + } + } + + err = dg.I.Transaction(func(tx *gorm.DB) error { mwRunner := newMiddlewareRunner(&event, tx) mwRunner.setMwType(pu.MWTPre) // Run pre-middleware @@ -126,6 +147,15 @@ func Create(input e.CreateDto) (*d.Data, error) { return errors.New("invalid encounter class code") } + // insert adm_employee_hist + if _, err := uaeh.Create(eaeh.CreateDto{ + Encounter_Id: &data.Main.Id, + Employee_Id: data.Adm_Employee_Id, + StartedAt: &now, + }); err != nil { + return err + } + mwRunner.setMwType(pu.MWTPost) // Run post-middleware if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { @@ -418,6 +448,16 @@ func CheckOut(input e.DischargeDto) (*d.Data, error) { } } + // update finishedAt in latest responsible_doctor_hist + if err = updateLatestResponsibleDoctorHist(e.CheckinDto{Id: input.Id, StartedAt: &now}, &event, tx); err != nil { + return err + } + + // update finishedAt in latest adm_employee_hist + if err = updateLatestAdmEmployeeHist(e.CheckinDto{Id: input.Id, StartedAt: &now}, &event, tx); err != nil { + return err + } + switch *input.Discharge_Method_Code { case ere.DMCDeath: // insert data death-cause @@ -534,6 +574,22 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { return nil, err } + dataSoapi, err := us.ReadList(es.ReadListDto{FilterDto: es.FilterDto{Encounter_Id: &input.Id}}) + if err != nil { + return nil, err + } + if list, ok := dataSoapi.Data.([]es.ResponseDto); ok { + if len(list) > 0 { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-state-mismatch", + Detail: "soapi already exist", + Raw: errors.New("soapi already exist"), + } + return nil, pl.SetLogError(&event, input) + } + } + err = dg.I.Transaction(func(tx *gorm.DB) error { pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") if data, err = ReadDetailData(rdDto, &event, tx); err != nil { @@ -551,20 +607,23 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { } // Insert responsible_doctor_hist if responsible_doctor_id has changed && update latest history - if input.Responsible_Doctor_Id != nil && *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { - // update finishedAt in latest responsible_doctor_hist - if err = updateLatestResponsibleDoctorHist(input, &event, tx); err != nil { - return err + if input.Responsible_Doctor_Id != nil { + if data.Responsible_Doctor_Id == nil || *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { + // update finishedAt in latest responsible_doctor_hist + if err = updateLatestResponsibleDoctorHist(input, &event, tx); err != nil { + return err + } + + //insert responsible_doctor_hist + if _, err = urdh.Create(erdh.CreateDto{ + Encounter_Id: &input.Id, + Doctor_Id: input.Responsible_Doctor_Id, + StartedAt: input.StartedAt, + }); err != nil { + return err + } } - //insert responsible_doctor_hist - if _, err = urdh.Create(erdh.CreateDto{ - Encounter_Id: &input.Id, - Doctor_Id: input.Responsible_Doctor_Id, - StartedAt: input.StartedAt, - }); err != nil { - return err - } } // Insert adm_employee_hist if adm_employee_id has changed && update latest history diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 2e74eff6..d2647a38 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -69,6 +69,8 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) { data.RefSource_Name = inputSrc.RefSource_Name data.Appointment_Id = inputSrc.Appointment_Id data.Status_Code = erc.DSCProcess + data.RefType_Code = &inputSrc.RefTypeCode + data.NewStatus = inputSrc.NewStatus } func setDataUpdate(src e.UpdateDto, dst *e.Encounter) { @@ -90,6 +92,7 @@ func setDataDischarge(src e.DischargeDto, dst *e.Encounter) { now := time.Now() dst.Discharge_Date = &now + dst.FinishedAt = &now } func checkSoapiByDocExists(encounter_id uint, event *pl.Event, tx *gorm.DB) error { @@ -335,6 +338,8 @@ func setDataCheckIn(src e.CheckinDto, dst *e.Encounter) { if src.Adm_Employee_Id != nil { dst.Adm_Employee_Id = src.Adm_Employee_Id } + + dst.StartedAt = src.StartedAt } func createInternalReferences(input e.DischargeDto, event *pl.Event, tx *gorm.DB) error { diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index 6e960e49..3327bcfa 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -3,8 +3,6 @@ package encounter import ( // std "errors" - "fmt" - // external dg "github.com/karincake/apem/db-gorm-pg" gh "github.com/karincake/getuk" @@ -271,21 +269,26 @@ func updateLatestResponsibleDoctorHist(input e.CheckinDto, event *pl.Event, dbx Order("\"CreatedAt\" DESC"). Limit(1) - fmt.Println(subQuery) - - if err := tx. + result := tx. Model(&erdh.ResponsibleDoctorHist{}). Where("\"Id\" = (?)", subQuery). - Update("\"FinishedAt\"", input.StartedAt).Error; err != nil { + Update("\"FinishedAt\"", input.StartedAt) + + if result.Error != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-update-fail", Detail: "Database update failed", - Raw: err, + Raw: result.Error, } return pl.SetLogError(event, input) } + if result.RowsAffected == 0 { + pl.SetLogInfo(event, input, "no previous data found to update") + return nil + } + pl.SetLogInfo(event, nil, "complete") return nil } @@ -307,19 +310,26 @@ func updateLatestAdmEmployeeHist(input e.CheckinDto, event *pl.Event, dbx ...*go Order("\"CreatedAt\" DESC"). Limit(1) - if err := tx. + result := tx. Model(&eaeh.AdmEmployeeHist{}). Where("\"Id\" = (?)", subQuery). - Update("\"FinishedAt\"", input.StartedAt).Error; err != nil { + Update("\"FinishedAt\"", input.StartedAt) + + if result.Error != nil { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-update-fail", Detail: "Database update failed", - Raw: err, + Raw: result.Error, } return pl.SetLogError(event, input) } + if result.RowsAffected == 0 { + pl.SetLogInfo(event, input, "no previous data found to update") + return nil + } + pl.SetLogInfo(event, nil, "complete") return nil } From 159db551ebd51de6b85bebaf0a3414e8b7515906 Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 27 Oct 2025 17:20:46 +0700 Subject: [PATCH 06/14] set responsible doctor to required --- .../encounter/request-validation.go | 4 +-- .../use-case/main-use-case/encounter/case.go | 27 +++++++++---------- .../main-use-case/encounter/helper.go | 5 +--- 3 files changed, 15 insertions(+), 21 deletions(-) diff --git a/internal/interface/main-handler/encounter/request-validation.go b/internal/interface/main-handler/encounter/request-validation.go index 9e92ea88..025298fc 100644 --- a/internal/interface/main-handler/encounter/request-validation.go +++ b/internal/interface/main-handler/encounter/request-validation.go @@ -53,10 +53,10 @@ func validateRequestCheckout(w http.ResponseWriter, i e.DischargeDto) (valid boo } func validateRequestCheckIn(w http.ResponseWriter, i e.CheckinDto) (valid bool) { - if i.Responsible_Doctor_Id == nil && i.Adm_Employee_Id == nil { + if i.Responsible_Doctor_Id == nil { rw.DataResponse(w, nil, d.FieldError{ Code: dataValidationFail, - Message: "responsible_doctor_id or adm_employee_id required", + Message: "responsible_doctor_id required", }) return } diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index c55b5b4b..d1124b3e 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -607,23 +607,20 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { } // Insert responsible_doctor_hist if responsible_doctor_id has changed && update latest history - if input.Responsible_Doctor_Id != nil { - if data.Responsible_Doctor_Id == nil || *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { - // update finishedAt in latest responsible_doctor_hist - if err = updateLatestResponsibleDoctorHist(input, &event, tx); err != nil { - return err - } - - //insert responsible_doctor_hist - if _, err = urdh.Create(erdh.CreateDto{ - Encounter_Id: &input.Id, - Doctor_Id: input.Responsible_Doctor_Id, - StartedAt: input.StartedAt, - }); err != nil { - return err - } + if data.Responsible_Doctor_Id == nil || *input.Responsible_Doctor_Id != *data.Responsible_Doctor_Id { + // update finishedAt in latest responsible_doctor_hist + if err = updateLatestResponsibleDoctorHist(input, &event, tx); err != nil { + return err } + //insert responsible_doctor_hist + if _, err = urdh.Create(erdh.CreateDto{ + Encounter_Id: &input.Id, + Doctor_Id: input.Responsible_Doctor_Id, + StartedAt: input.StartedAt, + }); err != nil { + return err + } } // Insert adm_employee_hist if adm_employee_id has changed && update latest history diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index d2647a38..c5eb155e 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -331,14 +331,11 @@ func setDataUpdateStatus(src e.UpdateStatusDto, dst *e.Encounter) { } func setDataCheckIn(src e.CheckinDto, dst *e.Encounter) { - if src.Responsible_Doctor_Id != nil { - dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id - } - if src.Adm_Employee_Id != nil { dst.Adm_Employee_Id = src.Adm_Employee_Id } + dst.Responsible_Doctor_Id = src.Responsible_Doctor_Id dst.StartedAt = src.StartedAt } From 15179c746c4caddec4c64a2da3b434a0132a3922 Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 27 Oct 2025 17:28:50 +0700 Subject: [PATCH 07/14] add comment --- internal/interface/main-handler/encounter/handler.go | 8 -------- internal/use-case/main-use-case/encounter/case.go | 8 ++++++++ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index a5e74855..ed8b1fbc 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -2,7 +2,6 @@ package encounter import ( "net/http" - "time" rw "github.com/karincake/risoles" sf "github.com/karincake/semprit" @@ -125,13 +124,6 @@ func (obj myBase) CheckIn(w http.ResponseWriter, r *http.Request) { } dto.Id = uint(id) - - // validate startedAt - if dto.StartedAt == nil { - now := time.Now() - dto.StartedAt = &now - } - res, err := u.CheckIn(dto) rw.DataResponse(w, res, err) } diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go index d1124b3e..11b17e2b 100644 --- a/internal/use-case/main-use-case/encounter/case.go +++ b/internal/use-case/main-use-case/encounter/case.go @@ -574,10 +574,17 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { return nil, err } + // set startedAt + if input.StartedAt == nil { + input.StartedAt = &now + } + + // validate if soapi exist dataSoapi, err := us.ReadList(es.ReadListDto{FilterDto: es.FilterDto{Encounter_Id: &input.Id}}) if err != nil { return nil, err } + if list, ok := dataSoapi.Data.([]es.ResponseDto); ok { if len(list) > 0 { event.Status = "failed" @@ -640,6 +647,7 @@ func CheckIn(input e.CheckinDto) (*d.Data, error) { } } + // update encounter data if err := updateCheckInData(input, data, &event, tx); err != nil { return err } From 43f9c0f4c85448f0535734351f9f6fc588fb3628 Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 30 Oct 2025 13:57:52 +0700 Subject: [PATCH 08/14] add json tag to unit response Dto --- internal/domain/main-entities/unit/dto.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/domain/main-entities/unit/dto.go b/internal/domain/main-entities/unit/dto.go index ce1dd505..eca0594f 100644 --- a/internal/domain/main-entities/unit/dto.go +++ b/internal/domain/main-entities/unit/dto.go @@ -50,11 +50,11 @@ type MetaDto struct { type ResponseDto struct { ecore.SmallMain - Installation_Id *uint16 `json:"installation_id"` - Installation *ei.Installation - Code string `json:"code"` - Name string `json:"name"` - UnitPositions []eipb.Basic `json:"unitPositions,omitempty"` + Installation_Id *uint16 `json:"installation_id"` + Installation *ei.Installation `json:"installation"` + Code string `json:"code"` + Name string `json:"name"` + UnitPositions []eipb.Basic `json:"unitPositions,omitempty"` } func (d Unit) ToResponse() ResponseDto { From 2ce8ecb1b9add66e8e96e1c8e4250050daa1fc97 Mon Sep 17 00:00:00 2001 From: vanilia Date: Thu, 30 Oct 2025 13:59:28 +0700 Subject: [PATCH 09/14] add json tag to unit response Dto --- internal/domain/main-entities/unit/dto.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/domain/main-entities/unit/dto.go b/internal/domain/main-entities/unit/dto.go index eca0594f..e47afed9 100644 --- a/internal/domain/main-entities/unit/dto.go +++ b/internal/domain/main-entities/unit/dto.go @@ -51,7 +51,7 @@ type MetaDto struct { type ResponseDto struct { ecore.SmallMain Installation_Id *uint16 `json:"installation_id"` - Installation *ei.Installation `json:"installation"` + Installation *ei.Installation `json:"installation,omitempty"` Code string `json:"code"` Name string `json:"name"` UnitPositions []eipb.Basic `json:"unitPositions,omitempty"` From 1b7c29dc116ea20b33c1df1966d5c6a26c72115d Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Thu, 30 Oct 2025 15:32:36 +0700 Subject: [PATCH 10/14] feat (patient): now also create vclaim member + hit bpjs service --- cmd/main-api/config.yml-example | 5 +- internal/domain/main-entities/patient/dto.go | 2 +- .../interface/main-handler/main-handler.go | 2 + internal/lib/auth/auth.go | 2 +- internal/lib/auth/tycovar.go | 2 +- .../bpjs-use-case/vclaim-member/case.go | 9 ++- .../bpjs-use-case/vclaim-member/lib.go | 32 +++++++++ .../use-case/main-use-case/patient/case.go | 8 +++ .../use-case/main-use-case/patient/helper.go | 11 +++ .../main-use-case/patient/middleware.go | 68 +++++++++++++++++-- .../use-case/main-use-case/patient/tycovar.go | 13 ++++ pkg/use-case-helper/use-case-helper.go | 2 +- 12 files changed, 144 insertions(+), 12 deletions(-) diff --git a/cmd/main-api/config.yml-example b/cmd/main-api/config.yml-example index f131a725..60b1644c 100644 --- a/cmd/main-api/config.yml-example +++ b/cmd/main-api/config.yml-example @@ -63,4 +63,7 @@ bpjsCfg: corsCfg: allowedOrigins: - http://example.com - allowedMethod: \ No newline at end of file + allowedMethod: + +bpjsCfg: + baseUrl: \ No newline at end of file diff --git a/internal/domain/main-entities/patient/dto.go b/internal/domain/main-entities/patient/dto.go index abe3b46b..1190ce22 100644 --- a/internal/domain/main-entities/patient/dto.go +++ b/internal/domain/main-entities/patient/dto.go @@ -14,7 +14,7 @@ import ( erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" - pa "simrs-vx/pkg/auth-helper" + pa "simrs-vx/internal/lib/auth" ) type CreateDto struct { diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index b2c78cb5..bedf3b71 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -50,6 +50,7 @@ import ( hk "github.com/karincake/hongkue" /******************** infra ********************/ + ibpjs "simrs-vx/internal/infra/bpjs" gs "simrs-vx/internal/infra/gorm-setting" minio "simrs-vx/internal/infra/minio" ssdb "simrs-vx/internal/infra/ss-db" @@ -118,6 +119,7 @@ func SetRoutes() http.Handler { a.RegisterExtCall(lh.Populate) a.RegisterExtCall(minio.Connect) a.RegisterExtCall(mh.I.SetClient) + a.RegisterExtCall(ibpjs.SetConfig) a.RegisterExtCall(validation.RegisterValidation) r := http.NewServeMux() diff --git a/internal/lib/auth/auth.go b/internal/lib/auth/auth.go index 934d8d61..3efa980f 100644 --- a/internal/lib/auth/auth.go +++ b/internal/lib/auth/auth.go @@ -1,4 +1,4 @@ -package authhelper +package auth import ( "errors" diff --git a/internal/lib/auth/tycovar.go b/internal/lib/auth/tycovar.go index 654115fc..881a9072 100644 --- a/internal/lib/auth/tycovar.go +++ b/internal/lib/auth/tycovar.go @@ -1,4 +1,4 @@ -package authhelper +package auth import ( ero "simrs-vx/internal/domain/references/organization" diff --git a/internal/use-case/bpjs-use-case/vclaim-member/case.go b/internal/use-case/bpjs-use-case/vclaim-member/case.go index 7c572f5e..14c6aaff 100644 --- a/internal/use-case/bpjs-use-case/vclaim-member/case.go +++ b/internal/use-case/bpjs-use-case/vclaim-member/case.go @@ -303,10 +303,17 @@ func Delete(input e.DeleteDto) (*d.Data, error) { // return err // } -// if err := CreateOrUpdateData(input, data, &event, tx); err != nil { +// if data.Person_Id == input.Person_Id { +// return nil +// } +// if err := UpdateData(e.UpdateDto{CreateDto: input}, data, &event, tx); err != nil { // return err // } +// // if err := CreateOrUpdateData(input, data, &event, tx); err != nil { +// // return err +// // } + // pl.SetLogInfo(&event, nil, "complete") // mwRunner.setMwType(pu.MWTPost) diff --git a/internal/use-case/bpjs-use-case/vclaim-member/lib.go b/internal/use-case/bpjs-use-case/vclaim-member/lib.go index 9044664d..3c477298 100644 --- a/internal/use-case/bpjs-use-case/vclaim-member/lib.go +++ b/internal/use-case/bpjs-use-case/vclaim-member/lib.go @@ -145,3 +145,35 @@ func DeleteData(data *e.VclaimMember, event *pl.Event, dbx ...*gorm.DB) error { pl.SetLogInfo(event, nil, "complete") return nil } + +func CreateOrUpdateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "createOrUpdateData") + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + data, err := ReadDetailData(e.ReadDetailDto{CardNumber: input.CardNumber}, event, tx) + if err != nil { + if !pu.IsDataNotFoundError(err) { + return err + } + } + + if data == nil { + if _, err := CreateData(input, event, tx); err != nil { + return err + } + } else { + if err := UpdateData(e.UpdateDto{CreateDto: input}, data, event, tx); err != nil { + return err + } + } + + pl.SetLogInfo(event, nil, "complete") + return nil + +} diff --git a/internal/use-case/main-use-case/patient/case.go b/internal/use-case/main-use-case/patient/case.go index b52215ac..cee92a77 100644 --- a/internal/use-case/main-use-case/patient/case.go +++ b/internal/use-case/main-use-case/patient/case.go @@ -4,8 +4,10 @@ import ( "errors" "strconv" + evm "simrs-vx/internal/domain/bpjs-entities/vclaim-member" e "simrs-vx/internal/domain/main-entities/patient" + uvm "simrs-vx/internal/use-case/bpjs-use-case/vclaim-member" upe "simrs-vx/internal/use-case/main-use-case/person" upa "simrs-vx/internal/use-case/main-use-case/person-address" upc "simrs-vx/internal/use-case/main-use-case/person-contact" @@ -49,6 +51,12 @@ func Create(input e.CreateDto) (*d.Data, error) { input.Person_Id = person_id } + if input.Person.VclaimMember_CardNumber != nil && input.Person.ResidentIdentityNumber != nil { + if err := uvm.CreateOrUpdateData(evm.CreateDto{CardNumber: input.Person.VclaimMember_CardNumber, Person_Id: input.Person_Id}, &event, tx); err != nil { + return err + } + } + for idx := range input.PersonAddresses { input.PersonAddresses[idx].Person_Id = *input.Person_Id } diff --git a/internal/use-case/main-use-case/patient/helper.go b/internal/use-case/main-use-case/patient/helper.go index 8abfeaf3..aac8d677 100644 --- a/internal/use-case/main-use-case/patient/helper.go +++ b/internal/use-case/main-use-case/patient/helper.go @@ -12,6 +12,7 @@ import ( "time" e "simrs-vx/internal/domain/main-entities/patient" + ibpjs "simrs-vx/internal/infra/bpjs" pl "simrs-vx/pkg/logger" pmh "simrs-vx/pkg/minio-helper" @@ -135,3 +136,13 @@ func removeUploadedFile(bucket, fileUrl string, event *pl.Event) error { pl.SetLogInfo(event, nil, "complete") return nil } + +func endpointMapper(noBpjs string) string { + today := getTodayDate() + return fmt.Sprintf("%speserta/nokartu?noKartu=%s&tglpelayanan=%s", ibpjs.O.BaseUrl, noBpjs, today) + +} + +func getTodayDate() string { + return time.Now().Format("2006-01-02") +} diff --git a/internal/use-case/main-use-case/patient/middleware.go b/internal/use-case/main-use-case/patient/middleware.go index 5fe75e0a..6b0175dc 100644 --- a/internal/use-case/main-use-case/patient/middleware.go +++ b/internal/use-case/main-use-case/patient/middleware.go @@ -1,9 +1,65 @@ package patient +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + e "simrs-vx/internal/domain/main-entities/patient" + + "gorm.io/gorm" +) + // example of middleware -// func init() { -// createPreMw = append(createPreMw, -// CreateMw{Name: "modif-input", Func: pm.ModifInput}, -// CreateMw{Name: "check-data", Func: pm.CheckData}, -// ) -// } +func init() { + createPreMw = append(createPreMw, + createMw{Name: "check-vclaim-member", Func: checkVclaimMember}, + ) +} + +func checkVclaimMember(input *e.CreateDto, data *e.Patient, tx *gorm.DB) error { + if input.Person.VclaimMember_CardNumber != nil && input.Person.ResidentIdentityNumber != nil { + return isVclaimMemberMatch(input) + } + return nil +} + +func isVclaimMemberMatch(input *e.CreateDto) error { + endpoint := endpointMapper(*input.Person.VclaimMember_CardNumber) + req, err := http.NewRequest("GET", endpoint, nil) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + var data BPJSResponse + + if err := json.Unmarshal(body, &data); err != nil { + return fmt.Errorf("failed to parse response JSON: %w", err) + } + + if data.MetaData.Code != "200" { + return fmt.Errorf("failed to get vclaim member: %s", data.MetaData.Message) + } + + if data.Response.Peserta.NoKartu == "" || data.Response.Peserta.NIK == "" { + return fmt.Errorf("failed to get vclaim member: noKartu or nik is empty") + } + + if data.Response.Peserta.NIK != *input.Person.ResidentIdentityNumber { + return fmt.Errorf("nik(residentIdentityNumber) is not match with bpjs number(vclaimMember_cardNumber)") + } + return nil +} diff --git a/internal/use-case/main-use-case/patient/tycovar.go b/internal/use-case/main-use-case/patient/tycovar.go index d7e9abfe..29e5c368 100644 --- a/internal/use-case/main-use-case/patient/tycovar.go +++ b/internal/use-case/main-use-case/patient/tycovar.go @@ -42,3 +42,16 @@ var updatePreMw []readDetailMw var updatePostMw []readDetailMw var deletePreMw []readDetailMw var deletePostMw []readDetailMw + +type BPJSResponse struct { + MetaData struct { + Code string `json:"code"` + Message string `json:"message"` + } `json:"metaData"` + Response *struct { + Peserta struct { + NoKartu string `json:"noKartu"` + NIK string `json:"nik"` + } `json:"peserta"` + } `json:"response"` +} diff --git a/pkg/use-case-helper/use-case-helper.go b/pkg/use-case-helper/use-case-helper.go index 8c41266f..0df8fe3e 100644 --- a/pkg/use-case-helper/use-case-helper.go +++ b/pkg/use-case-helper/use-case-helper.go @@ -97,7 +97,7 @@ func HandleMiddlewareError(event *pl.Event, mwType, mwName string, logData inter event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: GetMiddlewareErrorCode(MWType(mwType)), - Detail: fmt.Sprintf("%s middleware %s failed", mwType, mwName), + Detail: fmt.Sprintf("%s middleware %s failed: %s", mwType, mwName, err.Error()), Raw: err, } return pl.SetLogError(event, logData) From 60f8db982d38670a86a2902d283c88bd8d3c9dd7 Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 31 Oct 2025 11:27:44 +0700 Subject: [PATCH 11/14] add response unit in specialist --- internal/domain/main-entities/specialist/dto.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/internal/domain/main-entities/specialist/dto.go b/internal/domain/main-entities/specialist/dto.go index 2cd4acee..8724b9c5 100644 --- a/internal/domain/main-entities/specialist/dto.go +++ b/internal/domain/main-entities/specialist/dto.go @@ -4,6 +4,7 @@ import ( ecore "simrs-vx/internal/domain/base-entities/core" espb "simrs-vx/internal/domain/main-entities/specialist-position/base" essb "simrs-vx/internal/domain/main-entities/subspecialist/base" + eu "simrs-vx/internal/domain/main-entities/unit" ) type CreateDto struct { @@ -52,6 +53,7 @@ type ResponseDto struct { Code string `json:"code"` Name string `json:"name"` Unit_Id *uint16 `json:"unit_id"` + Unit *eu.Unit `json:"unit,omitempty"` SpecialistPositions []espb.Basic `json:"specialistPositions,omitempty"` Subspecialists []essb.Basic `json:"subspecialists,omitempty"` } @@ -60,6 +62,7 @@ func (d Specialist) ToResponse() ResponseDto { resp := ResponseDto{ Code: d.Code, Name: d.Name, + Unit: d.Unit, Unit_Id: d.Unit_Id, SpecialistPositions: d.SpecialistPositions, Subspecialists: d.Subspecialists, From fd2fcec2d63e636d360d780c72959bfd91cffcaa Mon Sep 17 00:00:00 2001 From: vanilia Date: Fri, 31 Oct 2025 15:14:16 +0700 Subject: [PATCH 12/14] add response in subspecialis --- internal/domain/main-entities/subspecialist/dto.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/domain/main-entities/subspecialist/dto.go b/internal/domain/main-entities/subspecialist/dto.go index ed0c97aa..9a93394d 100644 --- a/internal/domain/main-entities/subspecialist/dto.go +++ b/internal/domain/main-entities/subspecialist/dto.go @@ -2,6 +2,7 @@ package subspecialist import ( ecore "simrs-vx/internal/domain/base-entities/core" + es "simrs-vx/internal/domain/main-entities/specialist" espb "simrs-vx/internal/domain/main-entities/subspecialist-position/base" ) @@ -48,10 +49,11 @@ type MetaDto struct { type ResponseDto struct { ecore.SmallMain - Code string `json:"code"` - Name string `json:"name"` - Specialist_Id *uint16 `json:"specialist_id"` - Subspecialist []espb.Basic `json:"subspecialistPositions,omitempty"` + Code string `json:"code"` + Name string `json:"name"` + Specialist_Id *uint16 `json:"specialist_id"` + Specialist *es.Specialist `json:"specialist,omitempty"` + Subspecialist []espb.Basic `json:"subspecialistPositions,omitempty"` } func (d Subspecialist) ToResponse() ResponseDto { @@ -59,6 +61,7 @@ func (d Subspecialist) ToResponse() ResponseDto { Code: d.Code, Name: d.Name, Specialist_Id: d.Specialist_Id, + Specialist: d.Specialist, Subspecialist: d.SubspecialistPositions, } resp.SmallMain = d.SmallMain From ab9f1934fe31a4079010c37709fe1a593b0f83a2 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 31 Oct 2025 16:06:36 +0700 Subject: [PATCH 13/14] at employee_position_code in claims --- internal/use-case/main-use-case/authentication/case.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index 18ae9b8a..a32b4253 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -128,6 +128,7 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { // employee position if employee.Id > 0 && employee.Position_Code != nil { + atClaims["employee_position_code"] = *employee.Position_Code switch *employee.Position_Code { case erg.EPCDoc: doctor := ed.Doctor{} From 13e703b31bce692a5f440ec405f88d85733c6bdd Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Fri, 31 Oct 2025 16:27:05 +0700 Subject: [PATCH 14/14] feat (adime,soapi,sbar): no longer get employee id, use employee id from authinfo --- internal/use-case/main-use-case/adime/case.go | 13 ++++++------- .../use-case/main-use-case/authentication/case.go | 1 + internal/use-case/main-use-case/sbar/case.go | 13 ++++++------- internal/use-case/main-use-case/soapi/case.go | 13 ++++++------- 4 files changed, 19 insertions(+), 21 deletions(-) diff --git a/internal/use-case/main-use-case/adime/case.go b/internal/use-case/main-use-case/adime/case.go index 01d101ae..bc7d4e98 100644 --- a/internal/use-case/main-use-case/adime/case.go +++ b/internal/use-case/main-use-case/adime/case.go @@ -5,9 +5,6 @@ import ( "strconv" e "simrs-vx/internal/domain/main-entities/adime" - ee "simrs-vx/internal/domain/main-entities/employee" - - ue "simrs-vx/internal/use-case/main-use-case/employee" dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -49,11 +46,13 @@ func Create(input e.CreateDto) (*d.Data, error) { return pl.SetLogError(&event, input) } - employee, err := ue.ReadDetailData(ee.ReadDetailDto{User_Id: &input.AuthInfo.User_Id}, &event, tx) - if err != nil { - return err + if input.AuthInfo.Employee_Id != nil { + v := uint(*input.AuthInfo.Employee_Id) + input.Employee_Id = &v + } else { + input.Employee_Id = nil } - input.Employee_Id = &employee.Id + if resData, err := CreateData(input, &event, tx); err != nil { return err } else { diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index a32b4253..6c645208 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -315,6 +315,7 @@ func ExtractToken(r *http.Request, tokenType TokenType) (data *pa.AuthInfo, err data.Laborant_Id = checkIntPtrClaims(claims, "laborant_id") data.Pharmachist_Id = checkIntPtrClaims(claims, "pharmachist_id") data.Intern_Position_Code = checkStrPtrClaims(claims, "intern_position_code") + data.Employee_Id = checkIntPtrClaims(claims, "employee_id") return } return nil, d.FieldError{Code: "token", Message: "token-invalid"} diff --git a/internal/use-case/main-use-case/sbar/case.go b/internal/use-case/main-use-case/sbar/case.go index 6c92f2b0..45045e35 100644 --- a/internal/use-case/main-use-case/sbar/case.go +++ b/internal/use-case/main-use-case/sbar/case.go @@ -4,11 +4,8 @@ import ( "errors" "strconv" - ee "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/sbar" - ue "simrs-vx/internal/use-case/main-use-case/employee" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -49,11 +46,13 @@ func Create(input e.CreateDto) (*d.Data, error) { return pl.SetLogError(&event, input) } - employee, err := ue.ReadDetailData(ee.ReadDetailDto{User_Id: &input.AuthInfo.User_Id}, &event, tx) - if err != nil { - return err + if input.AuthInfo.Employee_Id != nil { + v := uint(*input.AuthInfo.Employee_Id) + input.Employee_Id = &v + } else { + input.Employee_Id = nil } - input.Employee_Id = &employee.Id + if resData, err := CreateData(input, &event, tx); err != nil { return err } else { diff --git a/internal/use-case/main-use-case/soapi/case.go b/internal/use-case/main-use-case/soapi/case.go index fd06e140..3dc8e195 100644 --- a/internal/use-case/main-use-case/soapi/case.go +++ b/internal/use-case/main-use-case/soapi/case.go @@ -4,11 +4,8 @@ import ( "errors" "strconv" - ee "simrs-vx/internal/domain/main-entities/employee" e "simrs-vx/internal/domain/main-entities/soapi" - ue "simrs-vx/internal/use-case/main-use-case/employee" - dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" @@ -49,11 +46,13 @@ func Create(input e.CreateDto) (*d.Data, error) { return pl.SetLogError(&event, input) } - employee, err := ue.ReadDetailData(ee.ReadDetailDto{User_Id: &input.AuthInfo.User_Id}, &event, tx) - if err != nil { - return err + if input.AuthInfo.Employee_Id != nil { + v := uint(*input.AuthInfo.Employee_Id) + input.Employee_Id = &v + } else { + input.Employee_Id = nil } - input.Employee_Id = &employee.Id + if resData, err := CreateData(input, &event, tx); err != nil { return err } else {