From f51b9baeed7f1670c3d78df3244e322290190a53 Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 3 Sep 2025 16:39:26 +0700 Subject: [PATCH 01/15] feat (encounter): basic crud --- .../main-handler/encounter/handler.go | 71 +++++ .../interface/main-handler/main-handler.go | 3 +- .../use-case/main-use-case/encounter/case.go | 275 ++++++++++++++++++ .../main-use-case/encounter/helper.go | 29 ++ .../use-case/main-use-case/encounter/lib.go | 149 ++++++++++ .../encounter/middleware-runner.go | 103 +++++++ .../main-use-case/encounter/middleware.go | 9 + .../main-use-case/encounter/tycovar.go | 44 +++ 8 files changed, 682 insertions(+), 1 deletion(-) create mode 100644 internal/interface/main-handler/encounter/handler.go create mode 100644 internal/use-case/main-use-case/encounter/case.go create mode 100644 internal/use-case/main-use-case/encounter/helper.go create mode 100644 internal/use-case/main-use-case/encounter/lib.go create mode 100644 internal/use-case/main-use-case/encounter/middleware-runner.go create mode 100644 internal/use-case/main-use-case/encounter/middleware.go create mode 100644 internal/use-case/main-use-case/encounter/tycovar.go diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go new file mode 100644 index 00000000..01350b87 --- /dev/null +++ b/internal/interface/main-handler/encounter/handler.go @@ -0,0 +1,71 @@ +package encounter + +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/encounter" + u "simrs-vx/internal/use-case/main-use-case/encounter" +) + +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 3b6c0c47..87c17f91 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -6,6 +6,7 @@ import ( /******************** main / transaction ********************/ auth "simrs-vx/internal/interface/main-handler/authentication" counter "simrs-vx/internal/interface/main-handler/counter" + encounter "simrs-vx/internal/interface/main-handler/encounter" medicicinemix "simrs-vx/internal/interface/main-handler/medicine-mix" medicicinemixitem "simrs-vx/internal/interface/main-handler/medicine-mix-item" practiceschedule "simrs-vx/internal/interface/main-handler/practice-schedule" @@ -88,12 +89,12 @@ func SetRoutes() http.Handler { r.HandleFunc("/", home.Home) r.HandleFunc("POST /v1/authentication/login", auth.Login) - // r.HandleFunc("POST /v1/authentication/logout", auth.Logout) hk.Route("POST /v1/authentication/logout", r, auth.GuardMW, auth.Logout) hc.RegCrud(r, "/v1/practice-schedule", practiceschedule.O) hc.RegCrud(r, "/v1/counter", counter.O) hc.RegCrud(r, "/v1/medicine-mix", medicicinemix.O) hc.RegCrud(r, "/v1/medicine-mix-item", medicicinemixitem.O) + hc.RegCrud(r, "/v1/encounter", encounter.O) /******************** actor ********************/ hk.GroupRoutes("/v1/user", r, hk.MapHandlerFunc{ diff --git a/internal/use-case/main-use-case/encounter/case.go b/internal/use-case/main-use-case/encounter/case.go new file mode 100644 index 00000000..32c630f7 --- /dev/null +++ b/internal/use-case/main-use-case/encounter/case.go @@ -0,0 +1,275 @@ +package encounter + +import ( + e "simrs-vx/internal/domain/main-entities/encounter" + "strconv" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + "gorm.io/gorm" +) + +const source = "encounter" + +func Create(input e.CreateDto) (*d.Data, error) { + data := e.Encounter{} + + 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.Encounter + var dataList []e.Encounter + 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), + }, + Data: e.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input e.ReadDetailDto) (*d.Data, error) { + var data *e.Encounter + 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.Encounter + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input e.DeleteDto) (*d.Data, error) { + rdDto := e.ReadDetailDto{Id: input.Id} + var data *e.Encounter + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go new file mode 100644 index 00000000..ede229f4 --- /dev/null +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -0,0 +1,29 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package encounter + +import ( + e "simrs-vx/internal/domain/main-entities/encounter" +) + +func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) { + var inputSrc *e.CreateDto + if inputT, ok := any(input).(*e.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*e.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Patient_Id = inputSrc.Patient_Id + data.RegisteredAt = inputSrc.RegisteredAt + data.Class_Code = inputSrc.Class_Code + data.Unit_Id = inputSrc.Unit_Id + data.VisitDate = inputSrc.VisitDate + data.Assignment_Doctor_Id = inputSrc.Assignment_Doctor_Id + data.Responsible_Doctor_Id = inputSrc.Responsible_Doctor_Id + data.DischardeMethod_Code = inputSrc.DischardeMethod_Code + data.RefSource_Name = inputSrc.RefSource_Name +} diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go new file mode 100644 index 00000000..dec500de --- /dev/null +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -0,0 +1,149 @@ +package encounter + +import ( + e "simrs-vx/internal/domain/main-entities/encounter" + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input e.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*e.Encounter, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := e.Encounter{} + 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 { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-create-fail", + Detail: "Database insert failed", + Raw: err, + } + return nil, pl.SetLogError(event, input) + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func ReadListData(input e.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]e.Encounter, *e.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []e.Encounter{} + 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.Encounter{}). + Scopes(gh.Filter(input)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Order("\"CreatedAt\" DESC") + + if err := tx.Debug().Find(&data).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return nil, &meta, nil + } + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-get-fail", + Detail: "Database get failed", + Raw: err, + } + return nil, nil, pl.SetLogError(event, input) + + } + 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.Encounter, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := e.Encounter{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.First(&data, input.Id).Error; err != nil { + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input e.UpdateDto, data *e.Encounter, 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.Encounter, 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/encounter/middleware-runner.go b/internal/use-case/main-use-case/encounter/middleware-runner.go new file mode 100644 index 00000000..1448a7af --- /dev/null +++ b/internal/use-case/main-use-case/encounter/middleware-runner.go @@ -0,0 +1,103 @@ +package encounter + +import ( + e "simrs-vx/internal/domain/main-entities/encounter" + 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.Encounter) 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.Encounter) 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.Encounter) 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.Encounter) 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.Encounter) 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/encounter/middleware.go b/internal/use-case/main-use-case/encounter/middleware.go new file mode 100644 index 00000000..8a047d33 --- /dev/null +++ b/internal/use-case/main-use-case/encounter/middleware.go @@ -0,0 +1,9 @@ +package encounter + +// 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/encounter/tycovar.go b/internal/use-case/main-use-case/encounter/tycovar.go new file mode 100644 index 00000000..17890a1b --- /dev/null +++ b/internal/use-case/main-use-case/encounter/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 encounter + +import ( + "gorm.io/gorm" + + e "simrs-vx/internal/domain/main-entities/encounter" +) + +type createMw struct { + Name string + Func func(input *e.CreateDto, data *e.Encounter, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *e.ReadListDto, data *e.Encounter, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *e.ReadDetailDto, data *e.Encounter, 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 c82ee6a96c26682df623255c36fd6489be6cddbc Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 17 Oct 2025 15:29:56 +0700 Subject: [PATCH 02/15] migration from server --- cmd/main-migration/migrations/atlas.sum | 102 ++++++++++++------------ 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index 77732bde..42636d28 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,51 +1,51 @@ -h1:/NZRnBd4SNPxw+Sv7Fhn/VBxN0wRNU4OldkTUn6GtbI= -20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= -20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= -20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= -20250908062323.sql h1:oXl6Z143tOpIl4EfP4B8JNU8LrMvVmHEtCgAfiB4gs8= -20250908073811.sql h1:m2aNXfnGxnLq1+rVWrh4f60q7fhyhV3gEwNu/OIqQlE= -20250908073839.sql h1:cPk54xjLdMs26uY8ZHjNWLuyfAMzV7Zb0/9oJQrsw04= -20250910055902.sql h1:5xwjAV6QbtZT9empTJKfhyAjdknbHzb15B0Ku5dzqtQ= -20250915123412.sql h1:D83xaU2YlDEd21HLup/YQpQ2easMToYCyy/oK6AFgQs= -20250916043819.sql h1:ekoTJsBqQZ8G8n0qJ03d13+eoNoc7sAUEQGA5D/CCxk= -20250917040616.sql h1:zoCnmcXuM7AVv85SmN7RmFglCgJnoDmpRWExH0LAc9Q= -20250917040751.sql h1:J1xyRrh32y1+lezwAyNwPcUQ6ABBSgbvzNLva4SVdQU= -20250917045138.sql h1:jKe1Z0uOLG4SGBYM+S/3P+/zMPztmgoderD5swnMuCg= -20250917093645.sql h1:cNI3Pbz1R3LxvIXLuexafJFCXUXrmuFCgXXJ2sG+FW0= -20250918073552.sql h1:RJ1SvMzP6aeWnoPVD3eVAmIQOkcp6Php8z3QRri6v4g= -20250918073742.sql h1:+cEsnJTJFybe2fR69ZoOiX2R6c6iITl4m6WTZ1hjyzY= -20250918074745.sql h1:2hNVQCXF/dVYXAh+T/7oBFgERGWxzVb2FXJjwkFWGCI= -20250923025134.sql h1:Ykz/qpHiGDXPsCsWTjydQFVSibZP2D+h2fIeb2h2JGA= -20250924051317.sql h1:yQuW6SwJxIOM5fcxeAaie5lSm1oLysU/C2hH2xNCVoQ= -20250929034321.sql h1:101FJ8VH12mrZWlt/X1gvKUGOhoiF8tFbjiapAjnHzg= -20250929034428.sql h1:i+pROD9p+g5dOmmZma6WF/0Hw5g3Ha28NN85iTo1K34= -20250930025550.sql h1:+F+CsCUXD/ql0tHGEow70GhPBX1ZybVn+bh/T4YMh7Y= -20250930140351.sql h1:9AAEG1AnOAH+o0+oHL5G7I8vqlWOhwRlCGyyCpT/y1Q= -20251002085604.sql h1:3xZ68eYp4urXRnvotNH1XvG2mYOSDV/j3zHEZ/txg5E= -20251003032030.sql h1:HB+mQ2lXMNomHDpaRhB/9IwYI9/YiDO5eOJ+nAQH/jw= -20251005060450.sql h1:LbtCE2b+8osM3CvnmQJH1uCPtn+d7WchsslBOz8bL3Q= -20251006041122.sql h1:MlS7f21z06sutnf9dIekt5fuHJr4lgcQ4uCuCXAGsfc= -20251006045658.sql h1:3FmGCPCzjgMPdWDRodZTsx3KVaodd9zB9ilib69aewk= -20251006045928.sql h1:Z5g31PmnzNwk/OKdODcxZGm8fjJQdMFK32Xfnt3bRHg= -20251007022859.sql h1:FO03zEfaNEk/aXwY81d5Lp3MoBB9kPQuXlXJ4BPiSR8= -20251008031337.sql h1:l+sxUAGvcTfj3I6kAFHo+T6AYodC9k9GkR+jaKO2xXc= -20251008031554.sql h1:AqrVfIhSzY3PCy8ZlP5W91wn2iznfIuj5qQfubp6/94= -20251008052346.sql h1:nxnXmooIJ6r1mmzwnw+6efxLfc/k9h2aE6RMptPRons= -20251008073620.sql h1:6YsJp1W4SmQJ1lxpqF27BBlDC1zqhw7Yhc7pLzQTY6M= -20251009042854.sql h1:nkBV+R6j0fg7/JY6wH3eb5Vv0asJLnXmb6lINfT/GLQ= -20251009052657.sql h1:EPvdsib5rzCGPryd10HShGKvFPwM/R5S2lIVwtYxpms= -20251010031743.sql h1:T8IZmx8/btRFKLzTe78MzcBsPJNodnLvB0tby9QkirQ= -20251010070721.sql h1:5NQUk/yOV6sABLCB7swx++YIOyJe6MnU+yt1nRzde5w= -20251010072711.sql h1:ZJNqR2piyu8xJhBvVABSlnGEoKSKae3wuEs+wshPe4k= -20251013044536.sql h1:0Xjw8fNILiT8nnfrJDZgQnPf3dntmIoilbapnih8AE4= -20251013051438.sql h1:lfSuw5mgJnePBJamvhZ81osFIouXeiIEiSZ/evdwo48= -20251013081808.sql h1:ijgjNX08G6GBjA/ks8EKtb7P7Y7Cg7zbhqEOruGnv6M= -20251014060047.sql h1:0jqj49WTtneEIMQDBoo4c095ZGi8sCrA8NnHBrPU6D8= -20251014063537.sql h1:VZLXol0PTsTW21Epg6vBPsztWkDtcxup9F/z88EGgIg= -20251014063720.sql h1:2HVUyCV0ud3BJJDH2GEKZN/+IWLFPCsN1KqhP6csO14= -20251015045455.sql h1:MeLWmMhAOAz8b15Dd7IAQnt6JxjSml02XCXK22C0Lpg= -20251016010845.sql h1:4BncQdDOasRZJkzVJrSJJA7091A9VPNVx/faUCUPhBM= -20251016011023.sql h1:9JB9eFZKURK5RoCVDKR6glSvdJ8NTXrN7K/4q51zkz4= -20251016062912.sql h1:ACNn0fe+EMqUt3hoY+Dr3uqAV/QICBa1+mIW7fUc9Fk= -20251017060617.sql h1:4T3t9ifWrEQTPMSM0XJ98pF7Qdt+UfgtMui17bhrnWI= -20251017082207.sql h1:M3k5WPGt8gxv/4kB71di+78L/FyXrxhgGtXxGedwEps= +h1:8zie3PyS55y+G80bxoc2K/aE8n0tDc8iH3Tc+FUB4o8= +20250904105930.sql h1:Vv4vCurl7m7/ZB6TjRpkubHpQ4RYwSUn0QHdzfoGpzY= +20250904141448.sql h1:FYCHH9Os4KkrZMDu/jR8FMP+wLMRW+Mb0PkLU/9BRDg= +20250908062237.sql h1:oanBpKZd+akPu2I/xYhUSbd0G5tAFbXzKLER/Zs8ENI= +20250908062323.sql h1:miNG9COddXkD1jGTgaROMAZ618eT6oiLGiJhXWnQwhE= +20250908073811.sql h1:gOi5cnGG1htlpfizybYmUIT0vYjZTBfXiI0nPSYK2u8= +20250908073839.sql h1:cWNDA4YikjoOteAJuNLFILjQUJPFB6o8Wxreiek4QyI= +20250910055902.sql h1:nxxOGnU0BbH/v3IPgeIOXOwH8d3tKomw7h6FTeMnnBs= +20250915123412.sql h1:mz7SiWfrdf0qE1VTSAAnA/147d6gyp6ry5vZ2bR9SH0= +20250916043819.sql h1:RHXVtmMkB6wfv06HfPyHMBmUfIpFt1xveafNz0kwKnE= +20250917040616.sql h1:MYVDht+akBlzQGKNu2hTTTLPEcH1bxT/Q8MK6WEtuhs= +20250917040751.sql h1:J79YyS2JzWgh5oKXMTgh67uo3gLxKaAsxRiZmSIfjBs= +20250917045138.sql h1:/SM1N4O8X3yxpoJgMEARmS1uOkuLKsTOy4PLsRCOKaQ= +20250917093645.sql h1:PNBTGZ7s10e5b5+Tie8YfVQBN0zKtJ5T34oK1iOUEb4= +20250918073552.sql h1:jG7+g3i8ODYaJdcdZz12v3nbsZ5mB9wG6kWnGyTQIRI= +20250918073742.sql h1:j+rgw7puxE7s+phqPVZHmPk0af3rcaA56Itp86y1suY= +20250918074745.sql h1:rPmP4DXs6OnY4Vp+xO/z9jFpJt/RrJ52SJJjIIxeDvc= +20250923025134.sql h1:2r6pcwnBSU5Y9Czk1OHBoh4yZXiMtEca9X8843fTEX0= +20250924051317.sql h1:iUAk2gsGoEGIPQ0lEEUp8maMSId8emNbP+kP712ABIA= +20250929034321.sql h1:UlpALNVmdi95zOIT0yc6ZyTj9bBjQEIpZhvgrc52M+k= +20250929034428.sql h1:feF+H4nDyHh5bdx48Oiz0A1qecZfi6v3qTTdjzJ45Dg= +20250930025550.sql h1:6XT1kXI3Z3ZIxxmvT7poufZWWCW0QiejZPaFV5wBnjI= +20250930140351.sql h1:HxnmAbh9gCy8jwl/9ycGktiByaUripsjFFvohofY2CY= +20251002085604.sql h1:SjLPi+ZN6qDccK3DaEQCgNsZpPwr5kynWXwbwEsziCI= +20251003032030.sql h1:oHfxNSuqTxU8Zaf9H+h8TuUb1Da03wcyc6hZjDrUQ2s= +20251005060450.sql h1:GIuCcrd4MwjmXpvbzDzPYL18BV3QaZZ+Y2FmEzjvi0E= +20251006041122.sql h1:uNDQbSw0M08lYoMvUNlQtS3iDzpPM1ixT13ugSAoWjE= +20251006045658.sql h1:z+t7yCK54Q4SSiF9kUyUhkYB2F+kzSW9TB7ogxd9wzw= +20251006045928.sql h1:1lATLFLp4BWwGZqAjZdP0Dc6ypNXiYcwjoNkqGa8NFE= +20251007022859.sql h1:HXXwWrkyvzJzJGAt9mGskCRBBV/c1JfPmfjDocmJhQ4= +20251008031337.sql h1:Ln5pCF3Hxa5foHZLcds+z/us2eH6VAhhEj3w0TAGlVs= +20251008031554.sql h1:aB4MUS2lmqG0//4HKUWorcPSpWya0VC4QItvGyskEVI= +20251008052346.sql h1:MI3AZgU5XcwZT2OvvlWAxdRtL0eJ3jjRwt56IY1+pRU= +20251008073620.sql h1:sztWXuSNYwpEraaSapSsYwno75LO5H/N7ob7OJQ8X/A= +20251009042854.sql h1:TnPXj+dCJls3IU//cuqJZymyBzZMKs7ayazfgtAFRxM= +20251009052657.sql h1:leXbs0CP8r5dRilmYyLRk1MICqak3ea1/LWMtFrijqQ= +20251010031743.sql h1:SgHNY/lQ88G2F4nZyMfiOkDntb+gtOR+nEQLqXBTwv4= +20251010070721.sql h1:AnJnhXsMzDvK4AFgYw6B16Kpo/hljrZtcpc9m2VOSHQ= +20251010072711.sql h1:aXPTtNwLcTuw8C/yAxwxvqs0ayEjNzI1uuE0vE3ERa8= +20251013044536.sql h1:7Pq6JcvTpPBYDCW2dz3HdgUwY65HlhEVWy9TiG8iONE= +20251013051438.sql h1:X6t8bkqpUYYokBunSufMQUe5vCg0VyO6dxbm7ngosUc= +20251013081808.sql h1:495pLguXL2Ozh+ycn4UYZgZbn6WbjXNbyZUc3JU8qhI= +20251014060047.sql h1:nCgImMRGHhziiW57O1ofWaXCAPGaCOHN7PldQ3OSmM4= +20251014063537.sql h1:2cLmID79jP6cuQ1YJaWTtghFiM1GtHMC0ZQl30Hpy1M= +20251014063720.sql h1:bzLKKVAjSHgDFoiI/glj7t1ETlSAKx+AlsIAaP0ru2g= +20251015045455.sql h1:S547+UugQhlTRcn1Lm1IfqT5RNPttIWIiD+RTx69YaE= +20251016010845.sql h1:c9DUvxl17pUkf0azdYGM/YDzYxIJkLcfZOcMI4rL+R0= +20251016011023.sql h1:u3ivg83bXgYHBbojbWpemLxPE9Dmmj53B6LXo664jxw= +20251016062912.sql h1:W9n1hWchfYkqNX9LO9uxFxEXAb/iY+Pexjnhmp6PbgI= +20251017060617.sql h1:VU6yZ2+LfHpDZ3+TIH40t3F5YXPCpTppuF9+uSqa4b8= +20251017082207.sql h1:QshZslfedckz7iDpSGmPyY9sP6dy6ckHbs8L1TuXIA4= From 3d165447f52d8fccbec9f5b15f5528a0e6737524 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Sun, 19 Oct 2025 07:09:48 +0700 Subject: [PATCH 03/15] migration: + added intern + updated employee + updated user --- .../migrations/20251018032635.sql | 4 + .../migrations/20251018040322.sql | 13 +++ cmd/main-migration/migrations/atlas.sum | 104 +++++++++--------- .../domain/main-entities/employee/entity.go | 20 ++-- internal/domain/main-entities/intern/dto.go | 81 ++++++++++++++ .../domain/main-entities/intern/entity.go | 17 +++ internal/domain/main-entities/user/dto.go | 43 ++++---- internal/domain/main-entities/user/entity.go | 19 ++-- internal/interface/migration/main-entities.go | 2 + 9 files changed, 213 insertions(+), 90 deletions(-) create mode 100644 cmd/main-migration/migrations/20251018032635.sql create mode 100644 cmd/main-migration/migrations/20251018040322.sql create mode 100644 internal/domain/main-entities/intern/dto.go create mode 100644 internal/domain/main-entities/intern/entity.go diff --git a/cmd/main-migration/migrations/20251018032635.sql b/cmd/main-migration/migrations/20251018032635.sql new file mode 100644 index 00000000..d8c5a2c9 --- /dev/null +++ b/cmd/main-migration/migrations/20251018032635.sql @@ -0,0 +1,4 @@ +-- Modify "Employee" table +ALTER TABLE "public"."Employee" ADD COLUMN "Position_Code" character varying(20) NULL; +-- Rename a column from "Position_Code" to "ContractPosition_Code" +ALTER TABLE "public"."User" RENAME COLUMN "Position_Code" TO "ContractPosition_Code"; diff --git a/cmd/main-migration/migrations/20251018040322.sql b/cmd/main-migration/migrations/20251018040322.sql new file mode 100644 index 00000000..d35e9199 --- /dev/null +++ b/cmd/main-migration/migrations/20251018040322.sql @@ -0,0 +1,13 @@ +-- Create "Intern" table +CREATE TABLE "public"."Intern" ( + "Id" bigserial NOT NULL, + "CreatedAt" timestamptz NULL, + "UpdatedAt" timestamptz NULL, + "DeletedAt" timestamptz NULL, + "Person_Id" bigint NULL, + "Position_Code" character varying(20) NULL, + "User_Id" bigint NULL, + PRIMARY KEY ("Id"), + CONSTRAINT "fk_Intern_Person" FOREIGN KEY ("Person_Id") REFERENCES "public"."Person" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION, + CONSTRAINT "fk_Intern_User" FOREIGN KEY ("User_Id") REFERENCES "public"."User" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +); diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index 42636d28..bf732e89 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,51 +1,53 @@ -h1:8zie3PyS55y+G80bxoc2K/aE8n0tDc8iH3Tc+FUB4o8= -20250904105930.sql h1:Vv4vCurl7m7/ZB6TjRpkubHpQ4RYwSUn0QHdzfoGpzY= -20250904141448.sql h1:FYCHH9Os4KkrZMDu/jR8FMP+wLMRW+Mb0PkLU/9BRDg= -20250908062237.sql h1:oanBpKZd+akPu2I/xYhUSbd0G5tAFbXzKLER/Zs8ENI= -20250908062323.sql h1:miNG9COddXkD1jGTgaROMAZ618eT6oiLGiJhXWnQwhE= -20250908073811.sql h1:gOi5cnGG1htlpfizybYmUIT0vYjZTBfXiI0nPSYK2u8= -20250908073839.sql h1:cWNDA4YikjoOteAJuNLFILjQUJPFB6o8Wxreiek4QyI= -20250910055902.sql h1:nxxOGnU0BbH/v3IPgeIOXOwH8d3tKomw7h6FTeMnnBs= -20250915123412.sql h1:mz7SiWfrdf0qE1VTSAAnA/147d6gyp6ry5vZ2bR9SH0= -20250916043819.sql h1:RHXVtmMkB6wfv06HfPyHMBmUfIpFt1xveafNz0kwKnE= -20250917040616.sql h1:MYVDht+akBlzQGKNu2hTTTLPEcH1bxT/Q8MK6WEtuhs= -20250917040751.sql h1:J79YyS2JzWgh5oKXMTgh67uo3gLxKaAsxRiZmSIfjBs= -20250917045138.sql h1:/SM1N4O8X3yxpoJgMEARmS1uOkuLKsTOy4PLsRCOKaQ= -20250917093645.sql h1:PNBTGZ7s10e5b5+Tie8YfVQBN0zKtJ5T34oK1iOUEb4= -20250918073552.sql h1:jG7+g3i8ODYaJdcdZz12v3nbsZ5mB9wG6kWnGyTQIRI= -20250918073742.sql h1:j+rgw7puxE7s+phqPVZHmPk0af3rcaA56Itp86y1suY= -20250918074745.sql h1:rPmP4DXs6OnY4Vp+xO/z9jFpJt/RrJ52SJJjIIxeDvc= -20250923025134.sql h1:2r6pcwnBSU5Y9Czk1OHBoh4yZXiMtEca9X8843fTEX0= -20250924051317.sql h1:iUAk2gsGoEGIPQ0lEEUp8maMSId8emNbP+kP712ABIA= -20250929034321.sql h1:UlpALNVmdi95zOIT0yc6ZyTj9bBjQEIpZhvgrc52M+k= -20250929034428.sql h1:feF+H4nDyHh5bdx48Oiz0A1qecZfi6v3qTTdjzJ45Dg= -20250930025550.sql h1:6XT1kXI3Z3ZIxxmvT7poufZWWCW0QiejZPaFV5wBnjI= -20250930140351.sql h1:HxnmAbh9gCy8jwl/9ycGktiByaUripsjFFvohofY2CY= -20251002085604.sql h1:SjLPi+ZN6qDccK3DaEQCgNsZpPwr5kynWXwbwEsziCI= -20251003032030.sql h1:oHfxNSuqTxU8Zaf9H+h8TuUb1Da03wcyc6hZjDrUQ2s= -20251005060450.sql h1:GIuCcrd4MwjmXpvbzDzPYL18BV3QaZZ+Y2FmEzjvi0E= -20251006041122.sql h1:uNDQbSw0M08lYoMvUNlQtS3iDzpPM1ixT13ugSAoWjE= -20251006045658.sql h1:z+t7yCK54Q4SSiF9kUyUhkYB2F+kzSW9TB7ogxd9wzw= -20251006045928.sql h1:1lATLFLp4BWwGZqAjZdP0Dc6ypNXiYcwjoNkqGa8NFE= -20251007022859.sql h1:HXXwWrkyvzJzJGAt9mGskCRBBV/c1JfPmfjDocmJhQ4= -20251008031337.sql h1:Ln5pCF3Hxa5foHZLcds+z/us2eH6VAhhEj3w0TAGlVs= -20251008031554.sql h1:aB4MUS2lmqG0//4HKUWorcPSpWya0VC4QItvGyskEVI= -20251008052346.sql h1:MI3AZgU5XcwZT2OvvlWAxdRtL0eJ3jjRwt56IY1+pRU= -20251008073620.sql h1:sztWXuSNYwpEraaSapSsYwno75LO5H/N7ob7OJQ8X/A= -20251009042854.sql h1:TnPXj+dCJls3IU//cuqJZymyBzZMKs7ayazfgtAFRxM= -20251009052657.sql h1:leXbs0CP8r5dRilmYyLRk1MICqak3ea1/LWMtFrijqQ= -20251010031743.sql h1:SgHNY/lQ88G2F4nZyMfiOkDntb+gtOR+nEQLqXBTwv4= -20251010070721.sql h1:AnJnhXsMzDvK4AFgYw6B16Kpo/hljrZtcpc9m2VOSHQ= -20251010072711.sql h1:aXPTtNwLcTuw8C/yAxwxvqs0ayEjNzI1uuE0vE3ERa8= -20251013044536.sql h1:7Pq6JcvTpPBYDCW2dz3HdgUwY65HlhEVWy9TiG8iONE= -20251013051438.sql h1:X6t8bkqpUYYokBunSufMQUe5vCg0VyO6dxbm7ngosUc= -20251013081808.sql h1:495pLguXL2Ozh+ycn4UYZgZbn6WbjXNbyZUc3JU8qhI= -20251014060047.sql h1:nCgImMRGHhziiW57O1ofWaXCAPGaCOHN7PldQ3OSmM4= -20251014063537.sql h1:2cLmID79jP6cuQ1YJaWTtghFiM1GtHMC0ZQl30Hpy1M= -20251014063720.sql h1:bzLKKVAjSHgDFoiI/glj7t1ETlSAKx+AlsIAaP0ru2g= -20251015045455.sql h1:S547+UugQhlTRcn1Lm1IfqT5RNPttIWIiD+RTx69YaE= -20251016010845.sql h1:c9DUvxl17pUkf0azdYGM/YDzYxIJkLcfZOcMI4rL+R0= -20251016011023.sql h1:u3ivg83bXgYHBbojbWpemLxPE9Dmmj53B6LXo664jxw= -20251016062912.sql h1:W9n1hWchfYkqNX9LO9uxFxEXAb/iY+Pexjnhmp6PbgI= -20251017060617.sql h1:VU6yZ2+LfHpDZ3+TIH40t3F5YXPCpTppuF9+uSqa4b8= -20251017082207.sql h1:QshZslfedckz7iDpSGmPyY9sP6dy6ckHbs8L1TuXIA4= +h1:S3ggQSrIa2Lwhkx+yWmD9N2hZxRuVwCMZcEpsk83PJY= +20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= +20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= +20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= +20250908062323.sql h1:oXl6Z143tOpIl4EfP4B8JNU8LrMvVmHEtCgAfiB4gs8= +20250908073811.sql h1:m2aNXfnGxnLq1+rVWrh4f60q7fhyhV3gEwNu/OIqQlE= +20250908073839.sql h1:cPk54xjLdMs26uY8ZHjNWLuyfAMzV7Zb0/9oJQrsw04= +20250910055902.sql h1:5xwjAV6QbtZT9empTJKfhyAjdknbHzb15B0Ku5dzqtQ= +20250915123412.sql h1:D83xaU2YlDEd21HLup/YQpQ2easMToYCyy/oK6AFgQs= +20250916043819.sql h1:ekoTJsBqQZ8G8n0qJ03d13+eoNoc7sAUEQGA5D/CCxk= +20250917040616.sql h1:zoCnmcXuM7AVv85SmN7RmFglCgJnoDmpRWExH0LAc9Q= +20250917040751.sql h1:J1xyRrh32y1+lezwAyNwPcUQ6ABBSgbvzNLva4SVdQU= +20250917045138.sql h1:jKe1Z0uOLG4SGBYM+S/3P+/zMPztmgoderD5swnMuCg= +20250917093645.sql h1:cNI3Pbz1R3LxvIXLuexafJFCXUXrmuFCgXXJ2sG+FW0= +20250918073552.sql h1:RJ1SvMzP6aeWnoPVD3eVAmIQOkcp6Php8z3QRri6v4g= +20250918073742.sql h1:+cEsnJTJFybe2fR69ZoOiX2R6c6iITl4m6WTZ1hjyzY= +20250918074745.sql h1:2hNVQCXF/dVYXAh+T/7oBFgERGWxzVb2FXJjwkFWGCI= +20250923025134.sql h1:Ykz/qpHiGDXPsCsWTjydQFVSibZP2D+h2fIeb2h2JGA= +20250924051317.sql h1:yQuW6SwJxIOM5fcxeAaie5lSm1oLysU/C2hH2xNCVoQ= +20250929034321.sql h1:101FJ8VH12mrZWlt/X1gvKUGOhoiF8tFbjiapAjnHzg= +20250929034428.sql h1:i+pROD9p+g5dOmmZma6WF/0Hw5g3Ha28NN85iTo1K34= +20250930025550.sql h1:+F+CsCUXD/ql0tHGEow70GhPBX1ZybVn+bh/T4YMh7Y= +20250930140351.sql h1:9AAEG1AnOAH+o0+oHL5G7I8vqlWOhwRlCGyyCpT/y1Q= +20251002085604.sql h1:3xZ68eYp4urXRnvotNH1XvG2mYOSDV/j3zHEZ/txg5E= +20251003032030.sql h1:HB+mQ2lXMNomHDpaRhB/9IwYI9/YiDO5eOJ+nAQH/jw= +20251005060450.sql h1:LbtCE2b+8osM3CvnmQJH1uCPtn+d7WchsslBOz8bL3Q= +20251006041122.sql h1:MlS7f21z06sutnf9dIekt5fuHJr4lgcQ4uCuCXAGsfc= +20251006045658.sql h1:3FmGCPCzjgMPdWDRodZTsx3KVaodd9zB9ilib69aewk= +20251006045928.sql h1:Z5g31PmnzNwk/OKdODcxZGm8fjJQdMFK32Xfnt3bRHg= +20251007022859.sql h1:FO03zEfaNEk/aXwY81d5Lp3MoBB9kPQuXlXJ4BPiSR8= +20251008031337.sql h1:l+sxUAGvcTfj3I6kAFHo+T6AYodC9k9GkR+jaKO2xXc= +20251008031554.sql h1:AqrVfIhSzY3PCy8ZlP5W91wn2iznfIuj5qQfubp6/94= +20251008052346.sql h1:nxnXmooIJ6r1mmzwnw+6efxLfc/k9h2aE6RMptPRons= +20251008073620.sql h1:6YsJp1W4SmQJ1lxpqF27BBlDC1zqhw7Yhc7pLzQTY6M= +20251009042854.sql h1:nkBV+R6j0fg7/JY6wH3eb5Vv0asJLnXmb6lINfT/GLQ= +20251009052657.sql h1:EPvdsib5rzCGPryd10HShGKvFPwM/R5S2lIVwtYxpms= +20251010031743.sql h1:T8IZmx8/btRFKLzTe78MzcBsPJNodnLvB0tby9QkirQ= +20251010070721.sql h1:5NQUk/yOV6sABLCB7swx++YIOyJe6MnU+yt1nRzde5w= +20251010072711.sql h1:ZJNqR2piyu8xJhBvVABSlnGEoKSKae3wuEs+wshPe4k= +20251013044536.sql h1:0Xjw8fNILiT8nnfrJDZgQnPf3dntmIoilbapnih8AE4= +20251013051438.sql h1:lfSuw5mgJnePBJamvhZ81osFIouXeiIEiSZ/evdwo48= +20251013081808.sql h1:ijgjNX08G6GBjA/ks8EKtb7P7Y7Cg7zbhqEOruGnv6M= +20251014060047.sql h1:0jqj49WTtneEIMQDBoo4c095ZGi8sCrA8NnHBrPU6D8= +20251014063537.sql h1:VZLXol0PTsTW21Epg6vBPsztWkDtcxup9F/z88EGgIg= +20251014063720.sql h1:2HVUyCV0ud3BJJDH2GEKZN/+IWLFPCsN1KqhP6csO14= +20251015045455.sql h1:MeLWmMhAOAz8b15Dd7IAQnt6JxjSml02XCXK22C0Lpg= +20251016010845.sql h1:4BncQdDOasRZJkzVJrSJJA7091A9VPNVx/faUCUPhBM= +20251016011023.sql h1:9JB9eFZKURK5RoCVDKR6glSvdJ8NTXrN7K/4q51zkz4= +20251016062912.sql h1:ACNn0fe+EMqUt3hoY+Dr3uqAV/QICBa1+mIW7fUc9Fk= +20251017060617.sql h1:4T3t9ifWrEQTPMSM0XJ98pF7Qdt+UfgtMui17bhrnWI= +20251017082207.sql h1:8vLG1l/saRRMHXkyA4nelJyjaSddhZd6r7R+Uo4JS/c= +20251018032635.sql h1:UltiY1Jm1KjAyBx/oRbhKzwEl3Aqh3o+eaOIfWdroJ8= +20251018040322.sql h1:24VgOPgO2pxfwTcAv7xCv8BHiRPqmIbHUCXNLyvrGfc= diff --git a/internal/domain/main-entities/employee/entity.go b/internal/domain/main-entities/employee/entity.go index 8113fc96..2e8cd953 100644 --- a/internal/domain/main-entities/employee/entity.go +++ b/internal/domain/main-entities/employee/entity.go @@ -6,16 +6,18 @@ import ( ep "simrs-vx/internal/domain/main-entities/person" eu "simrs-vx/internal/domain/main-entities/user" erc "simrs-vx/internal/domain/references/common" + erg "simrs-vx/internal/domain/references/organization" ) type Employee struct { - ecore.Main // adjust this according to the needs - User_Id *uint `json:"user_id"` - User *eu.User `json:"user,omitempty" gorm:"foreignKey:User_Id;references:Id"` - Person_Id *uint `json:"person_id"` - Person *ep.Person `json:"person,omitempty" gorm:"foreignKey:Person_Id;references:Id"` - Division_Code *string `json:"division_code"` - Division *ed.Division `json:"division,omitempty" gorm:"foreignKey:Division_Code;references:Code"` - Number *string `json:"number" gorm:"size:20"` - Status_Code erc.ActiveStatusCode `json:"status_code" gorm:"not null;size:10"` + ecore.Main // adjust this according to the needs + User_Id *uint `json:"user_id"` + User *eu.User `json:"user,omitempty" gorm:"foreignKey:User_Id;references:Id"` + Person_Id *uint `json:"person_id"` + Person *ep.Person `json:"person,omitempty" gorm:"foreignKey:Person_Id;references:Id"` + Position_Code *erg.EmployeePosisitionCode `json:"position_code" gorm:"size:20"` + Division_Code *string `json:"division_code"` + Division *ed.Division `json:"division,omitempty" gorm:"foreignKey:Division_Code;references:Code"` + Number *string `json:"number" gorm:"size:20"` + Status_Code erc.ActiveStatusCode `json:"status_code" gorm:"not null;size:10"` } diff --git a/internal/domain/main-entities/intern/dto.go b/internal/domain/main-entities/intern/dto.go new file mode 100644 index 00000000..b8c60aee --- /dev/null +++ b/internal/domain/main-entities/intern/dto.go @@ -0,0 +1,81 @@ +package intern + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ep "simrs-vx/internal/domain/main-entities/person" + es "simrs-vx/internal/domain/main-entities/specialist" + ess "simrs-vx/internal/domain/main-entities/subspecialist" + eu "simrs-vx/internal/domain/main-entities/user" +) + +type CreateDto struct { + Person_Id *uint `json:"person_id"` + Specialist_Id *uint16 `json:"specialist_id"` + Subspecialist_Id *uint16 `json:"subspecialist_id"` + User_Id *uint `json:"user_id"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Preloads []string `json:"-"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Person_Id *uint `json:"person_id"` + Specialist_Id *uint16 `json:"specialist_id"` + Subspecialist_Id *uint16 `json:"subspecialist_id"` + User_Id *uint `json:"user_id"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + User_Id *uint `json:"user_id"` +} + +type UpdateDto struct { + Id uint `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Person_Id *uint `json:"person_id"` + Person *ep.Person `json:"person,omitempty"` + Specialist_Id *uint16 `json:"specialist_id"` + Specialist *es.Specialist `json:"specialist,omitempty"` + Subspecialist_Id *uint16 `json:"subspecialist_id"` + Subspecialist *ess.Subspecialist `json:"subspecialist,omitempty"` + User_Id *uint `json:"user_id"` + User *eu.User `json:"user,omitempty"` +} + +func (d Intern) ToResponse() ResponseDto { + resp := ResponseDto{ + Person_Id: d.Person_Id, + Person: d.Person, + User_Id: d.User_Id, + User: d.User, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []Intern) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/intern/entity.go b/internal/domain/main-entities/intern/entity.go new file mode 100644 index 00000000..5ac2df49 --- /dev/null +++ b/internal/domain/main-entities/intern/entity.go @@ -0,0 +1,17 @@ +package intern + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ep "simrs-vx/internal/domain/main-entities/person" + eu "simrs-vx/internal/domain/main-entities/user" + erg "simrs-vx/internal/domain/references/organization" +) + +type Intern struct { + ecore.Main // adjust this according to the needs + Person_Id *uint `json:"person_id"` + Person *ep.Person `json:"person,omitempty" gorm:"foreignKey:Person_Id"` + Position_Code *erg.InternPosisitionCode `json:"position_code" gorm:"size:20"` + User_Id *uint `json:"user_id"` + User *eu.User `json:"user,omitempty" gorm:"foreignKey:User_Id"` +} diff --git a/internal/domain/main-entities/user/dto.go b/internal/domain/main-entities/user/dto.go index 62a0c587..21f16c3f 100644 --- a/internal/domain/main-entities/user/dto.go +++ b/internal/domain/main-entities/user/dto.go @@ -13,21 +13,21 @@ import ( ) type CreateDto struct { - Name string `json:"name" validate:"maxLength=25"` - Password string `json:"password" validate:"maxLength=255"` - Status_Code erc.UserStatusCode `json:"status_code" validate:"maxLength=10"` - Position_Code ero.UserPosisitionCode `json:"position_code" validate:"maxLength=20"` - Person_Id *uint `json:"-"` - Person *ep.UpdateDto `json:"person"` - PersonAddresses []epa.UpdateDto `json:"personAddresses"` - PersonContacts []epc.UpdateDto `json:"personContacts"` - Employee *EmployeUpdateDto `json:"employee"` - IHS_Number *string `json:"ihs_number" validate:"maxLength=20"` - SIP_Number *string `json:"sip_number" validate:"maxLength=20"` - Unit_Id *uint16 `json:"unit_id"` - Infra_Id *uint16 `json:"infra_id"` - Specialist_Id *uint16 `json:"specialist_id"` - Subspecialist_Id *uint16 `json:"subspecialist_id"` + Name string `json:"name" validate:"maxLength=25"` + Password string `json:"password" validate:"maxLength=255"` + Status_Code erc.UserStatusCode `json:"status_code" validate:"maxLength=10"` + ContractPosition_Code ero.ContractPositionCode `json:"contractPosition_code" validate:"maxLength=20"` + Person_Id *uint `json:"-"` + Person *ep.UpdateDto `json:"person"` + PersonAddresses []epa.UpdateDto `json:"personAddresses"` + PersonContacts []epc.UpdateDto `json:"personContacts"` + Employee *EmployeUpdateDto `json:"employee"` + IHS_Number *string `json:"ihs_number" validate:"maxLength=20"` + SIP_Number *string `json:"sip_number" validate:"maxLength=20"` + Unit_Id *uint16 `json:"unit_id"` + Infra_Id *uint16 `json:"infra_id"` + Specialist_Id *uint16 `json:"specialist_id"` + Subspecialist_Id *uint16 `json:"subspecialist_id"` } type ReadListDto struct { @@ -84,12 +84,13 @@ func (d *User) ToResponse() ResponseDto { } type EmployeUpdateDto struct { - Id uint `json:"id"` - User_Id *uint `json:"-"` - Person_Id *uint `json:"-"` - Division_Code *string `json:"division_code"` - Number *string `json:"number" validate:"maxLength=20"` - Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` + Id uint `json:"id"` + User_Id *uint `json:"-"` + Person_Id *uint `json:"-"` + Division_Code *string `json:"division_code"` + Number *string `json:"number" validate:"maxLength=20"` + Position_Code ero.EmployeePosisitionCode `json:"status_code" validate:"maxLength=10"` + Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` } func ToResponseList(data []User) []ResponseDto { diff --git a/internal/domain/main-entities/user/entity.go b/internal/domain/main-entities/user/entity.go index 4f7d8792..7a644262 100644 --- a/internal/domain/main-entities/user/entity.go +++ b/internal/domain/main-entities/user/entity.go @@ -9,13 +9,14 @@ import ( ) type User struct { - ecore.Main // adjust this according to the needs - Name string `json:"name" gorm:"unique;not null;size:25"` - Password string `json:"password" gorm:"not null;size:255"` - Status_Code erc.UserStatusCode `json:"status_code" gorm:"not null;size:10"` - FailedLoginCount uint8 `json:"failedLoginCount" gorm:"type:smallint"` - Position_Code ero.UserPosisitionCode `json:"position_code" gorm:"not null;size:20"` - LoginAttemptCount int `json:"-"` - LastSuccessLogin *time.Time `json:"lastSuccessLogin,omitempty"` - LastAllowdLogin *time.Time `json:"lastAllowdLogin,omitempty"` + ecore.Main // adjust this according to the needs + Name string `json:"name" gorm:"unique;not null;size:25"` + Password string `json:"password" gorm:"not null;size:255"` + Status_Code erc.UserStatusCode `json:"status_code" gorm:"not null;size:10"` + FailedLoginCount uint8 `json:"failedLoginCount" gorm:"type:smallint"` + // Position_Code ero.EmployeePosisitionCode `json:"position_code" gorm:"not null;size:20"` + ContractPosition_Code ero.ContractPositionCode `json:"contractPosition_Code" gorm:"not null;size:20"` + LoginAttemptCount int `json:"-"` + LastSuccessLogin *time.Time `json:"lastSuccessLogin,omitempty"` + LastAllowdLogin *time.Time `json:"lastAllowdLogin,omitempty"` } diff --git a/internal/interface/migration/main-entities.go b/internal/interface/migration/main-entities.go index ae155e9c..131b2972 100644 --- a/internal/interface/migration/main-entities.go +++ b/internal/interface/migration/main-entities.go @@ -25,6 +25,7 @@ import ( inpatient "simrs-vx/internal/domain/main-entities/inpatient" installation "simrs-vx/internal/domain/main-entities/installation" insurancecompany "simrs-vx/internal/domain/main-entities/insurance-company" + intern "simrs-vx/internal/domain/main-entities/intern" internalreference "simrs-vx/internal/domain/main-entities/internal-reference" item "simrs-vx/internal/domain/main-entities/item" itemprice "simrs-vx/internal/domain/main-entities/item-price" @@ -105,6 +106,7 @@ func getMainEntities() []any { &diagnosesrc.DiagnoseSrc{}, &proceduresrc.ProcedureSrc{}, &employee.Employee{}, + &intern.Intern{}, &doctor.Doctor{}, &nurse.Nurse{}, &nutritionist.Nutritionist{}, From d6c56b96b45b3561edf261db78dc752d4d6b1e5a Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Sun, 19 Oct 2025 09:07:25 +0700 Subject: [PATCH 04/15] Merge dev into feat/user --- internal/domain/main-entities/user/dto.go | 8 +- internal/domain/main-entities/user/entity.go | 15 ++- .../references/organization/organization.go | 41 ++++--- .../main-use-case/authentication/case.go | 104 +++++++++++------- .../main-use-case/encounter/helper.go | 15 ++- internal/use-case/main-use-case/user/case.go | 64 +++-------- .../use-case/main-use-case/user/helper.go | 26 ++--- pkg/auth-helper/tycovar.go | 61 +++++++--- 8 files changed, 180 insertions(+), 154 deletions(-) diff --git a/internal/domain/main-entities/user/dto.go b/internal/domain/main-entities/user/dto.go index 093a597e..55a53686 100644 --- a/internal/domain/main-entities/user/dto.go +++ b/internal/domain/main-entities/user/dto.go @@ -1,22 +1,21 @@ package user import ( + ecore "simrs-vx/internal/domain/base-entities/core" "time" - ecore "simrs-vx/internal/domain/base-entities/core" ep "simrs-vx/internal/domain/main-entities/person" epa "simrs-vx/internal/domain/main-entities/person-address" epc "simrs-vx/internal/domain/main-entities/person-contact" erc "simrs-vx/internal/domain/references/common" - ero "simrs-vx/internal/domain/references/organization" + erg "simrs-vx/internal/domain/references/organization" ) type CreateDto struct { Name string `json:"name" validate:"maxLength=25"` Password string `json:"password" validate:"maxLength=255"` Status_Code erc.UserStatusCode `json:"status_code" validate:"maxLength=10"` - ContractPosition_Code ero.ContractPositionCode `json:"contractPosition_code" validate:"maxLength=20"` Person_Id *uint `json:"-"` Person *ep.UpdateDto `json:"person"` PersonAddresses []epa.UpdateDto `json:"personAddresses"` @@ -28,6 +27,7 @@ type CreateDto struct { Infra_Id *uint16 `json:"infra_id"` Specialist_Id *uint16 `json:"specialist_id"` Subspecialist_Id *uint16 `json:"subspecialist_id"` + ContractPosition_Code erg.ContractPositionCode `json:"contractPosition_code" gorm:"not null;size:20"` } type ReadListDto struct { @@ -89,7 +89,7 @@ type EmployeUpdateDto struct { Person_Id *uint `json:"-"` Division_Code *string `json:"division_code"` Number *string `json:"number" validate:"maxLength=20"` - Position_Code ero.EmployeePosisitionCode `json:"status_code" validate:"maxLength=10"` + Position_Code erg.EmployeePosisitionCode `json:"status_code" validate:"maxLength=10"` Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` } diff --git a/internal/domain/main-entities/user/entity.go b/internal/domain/main-entities/user/entity.go index 7a644262..f51dbf14 100644 --- a/internal/domain/main-entities/user/entity.go +++ b/internal/domain/main-entities/user/entity.go @@ -5,17 +5,16 @@ import ( ecore "simrs-vx/internal/domain/base-entities/core" erc "simrs-vx/internal/domain/references/common" - ero "simrs-vx/internal/domain/references/organization" + erg "simrs-vx/internal/domain/references/organization" ) type User struct { - ecore.Main // adjust this according to the needs - Name string `json:"name" gorm:"unique;not null;size:25"` - Password string `json:"password" gorm:"not null;size:255"` - Status_Code erc.UserStatusCode `json:"status_code" gorm:"not null;size:10"` - FailedLoginCount uint8 `json:"failedLoginCount" gorm:"type:smallint"` - // Position_Code ero.EmployeePosisitionCode `json:"position_code" gorm:"not null;size:20"` - ContractPosition_Code ero.ContractPositionCode `json:"contractPosition_Code" gorm:"not null;size:20"` + ecore.Main // adjust this according to the needs + Name string `json:"name" gorm:"unique;not null;size:25"` + Password string `json:"password" gorm:"not null;size:255"` + Status_Code erc.UserStatusCode `json:"status_code" gorm:"not null;size:10"` + ContractPosition_Code erg.ContractPositionCode `json:"contractPosition_code" gorm:"not null;size:20"` + FailedLoginCount uint8 `json:"failedLoginCount" gorm:"type:smallint"` LoginAttemptCount int `json:"-"` LastSuccessLogin *time.Time `json:"lastSuccessLogin,omitempty"` LastAllowdLogin *time.Time `json:"lastAllowdLogin,omitempty"` diff --git a/internal/domain/references/organization/organization.go b/internal/domain/references/organization/organization.go index 04c4fa2a..60270a93 100644 --- a/internal/domain/references/organization/organization.go +++ b/internal/domain/references/organization/organization.go @@ -1,26 +1,33 @@ package organization type ( - UserPosisitionCode string - ItemGroupCode string - InfraGroupCode string - UnitTypeCode string - DoctorFeeTypeCode string + ContractPositionCode string + EmployeePosisitionCode string + InternPosisitionCode string + ItemGroupCode string + InfraGroupCode string + UnitTypeCode string + DoctorFeeTypeCode string ) const ( - UPCReg UserPosisitionCode = "registration" // Admisi/Pendaftaran - UPCNur UserPosisitionCode = "nurse" // Perawat - UPCDoc UserPosisitionCode = "doctor" // Dokter - UPCNut UserPosisitionCode = "nutritionist" // Ahli gizi - UPCMwi UserPosisitionCode = "mid-wife" // Bidan - UPCLab UserPosisitionCode = "laborant" // Laboran - UPCPha UserPosisitionCode = "pharmacy" // Farmasi - UPCPay UserPosisitionCode = "payment" // Pembayaran - UPCHur UserPosisitionCode = "human-resource" // Sumber Daya Manusia - UPCGea UserPosisitionCode = "general-affair" // Bagian Umum - UPCInt UserPosisitionCode = "specialist-intern" // PPDS - UPCMan UserPosisitionCode = "management" // Manajemen + CSCEmp ContractPositionCode = "employee" // Pegawai + CSCSpi ContractPositionCode = "intern" // PPDS + + EPCReg EmployeePosisitionCode = "registration" // Admisi/Pendaftaran + EPCNur EmployeePosisitionCode = "nurse" // Perawat + EPCDoc EmployeePosisitionCode = "doctor" // Dokter + EPCNut EmployeePosisitionCode = "nutritionist" // Ahli gizi + EPCMwi EmployeePosisitionCode = "mid-wife" // Bidan + EPCLab EmployeePosisitionCode = "laborant" // Laboran + EPCPha EmployeePosisitionCode = "pharmacy" // Farmasi + EPCPay EmployeePosisitionCode = "payment" // Pembayaran + EPCHur EmployeePosisitionCode = "human-resource" // Sumber Daya Manusia + EPCGea EmployeePosisitionCode = "general-affair" // Bagian Umum + EPCMan EmployeePosisitionCode = "management" // Manajemen + + IPCSpecialist = "specialist-intern" + IPCNurse = "nurse-intern" ITGCInfra ItemGroupCode = "infra" ITGCMedicine ItemGroupCode = "medicine" diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index b6e9f2f4..e03b0293 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -11,12 +11,14 @@ import ( "github.com/google/uuid" eu "simrs-vx/internal/domain/main-entities/user" - erc "simrs-vx/internal/domain/references/common" pa "simrs-vx/pkg/auth-helper" el "simrs-vx/pkg/logger" p "simrs-vx/pkg/password" + ee "simrs-vx/internal/domain/main-entities/employee" + erc "simrs-vx/internal/domain/references/common" + a "github.com/karincake/apem" dg "github.com/karincake/apem/db-gorm-pg" ms "github.com/karincake/apem/ms-redis" @@ -90,11 +92,26 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { atExpires := time.Now().Add(duration).Unix() atSecretKey := authCfg.AtSecretKey + // extra + role := []string{} + if user.ContractPosition_Code == "employee" { + employee := ee.Employee{} + dg.I.Where("user_id = ?", user.Id).First(&employee) + role = append(role, "emp-"+string(*employee.Position_Code)) + } else if user.ContractPosition_Code == "intern" { + // specialistIntern := esi.SpecialistIntern{} + // dg.I.Where("user_id = ?", user.Id).First(&specialistIntern) + role = append(role, "spi") + } + // Creating Access Token atClaims := jwt.MapClaims{} atClaims["user_id"] = user.Id atClaims["user_name"] = user.Name - atClaims["user_position_code"] = user.Position_Code + // atClaims["user_email"] = user.Email + // atClaims["user_position_code"] = user.Position_Code + atClaims["user_employementStatus_code"] = user.ContractPosition_Code + // atClaims["user_ref_id"] = user.Ref_Id atClaims["exp"] = atExpires atClaims["uuid"] = aUuid atClaims["user_division_positions"] = userDivisionPositions @@ -104,6 +121,15 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { return nil, d.FieldErrors{"user": d.FieldError{Code: "token-sign-err", Message: el.GenMessage("token-sign-err")}} } + outputData := d.II{ + "user_id": strconv.Itoa(int(user.Id)), + "user_name": user.Name, + // "user_email": user.Email, + "user_contractPosition_code": user.ContractPosition_Code, + // "user_ref_id": user.Ref_Id, + "accessToken": ats, + } + // Save to redis now := time.Now() atx := time.Unix(atExpires, 0) //converting Unix to UTC(to Time object) @@ -125,13 +151,7 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { "structure": "single-data", "status": "verified", }, - Data: d.II{ - "user_id": strconv.Itoa(int(user.Id)), - "user_name": user.Name, - "user_position_code": user.Position_Code, - "accessToken": ats, - "user_division_positions": userDivisionPositions, - }, + Data: outputData, }, nil } @@ -186,41 +206,41 @@ func ExtractToken(r *http.Request, tokenType TokenType) (data *pa.AuthInfo, err return nil, d.FieldError{Code: "token-unidentified", Message: el.GenMessage("token-unidentified")} } user_name := fmt.Sprintf("%v", claims["user_name"]) + // user_email := "" + // if v, exist := claims["user_email"]; exist && v != nil { + // user_email = v.(string) + // } + // ref_id := 0 + // if v, exist := claims["user_ref_id"]; exist && v != nil { + // tmp := v.(float64) + // ref_id = int(tmp) + // } + // position_code := "" + // if v, exist := claims["user_position_code"]; exist && v != nil { + // position_code = v.(string) + // } + // data = &AuthInfo{ + // Uuid: accessUuid, + // User_Id: int(user_id), + // User_Name: user_name, + // User_Email: user_email, + // User_Ref_Id: ref_id, + // User_Position_Code: position_code, + contractPosition_code := "" + if v, exist := claims["contractPosition_code"]; exist && v != nil { + contractPosition_code = v.(string) + } + employee_position_code := "" + if v, exist := claims["employee_position_code"]; exist && v != nil { + employee_position_code = v.(string) + } - var userDivisionPositions []pa.DivisionPosition - raw := claims["user_division_positions"] - if raw == nil { - fmt.Println("No user_division_positions found in claims") - } else { - list, ok := raw.([]interface{}) - if !ok { - fmt.Printf("user_division_positions is not []interface{}, but %T\n", raw) - } else { - fmt.Printf("Found %d division positions\n", len(list)) - for i, item := range list { - if m, ok := item.(map[string]interface{}); ok { - fmt.Printf("Item %d: %v\n", i, m) - dp := pa.DivisionPosition{ - Division_Code: fmt.Sprintf("%v", m["division_code"]), - DivisionPosition_Code: fmt.Sprintf("%v", m["divisionPosition_code"]), - } - userDivisionPositions = append(userDivisionPositions, dp) - } else { - fmt.Printf("Item %d not map[string]interface{} but %T\n", i, item) - } - } - } - } - position_code := "" - if v, exist := claims["user_position_code"]; exist && v != nil { - position_code = v.(string) - } data = &pa.AuthInfo{ - Uuid: accessUuid, - User_Id: uint(user_id), - User_Name: user_name, - User_Position_Code: position_code, - User_DivisionPositions: userDivisionPositions, + Uuid: accessUuid, + User_Id: uint(user_id), + User_Name: user_name, + User_ContractPosition_code: contractPosition_code, + Employee_Position_Code: &employee_position_code, } return } diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 20abadd1..0ee5d2cd 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -9,8 +9,10 @@ import ( "fmt" "strings" - e "simrs-vx/internal/domain/main-entities/encounter" - es "simrs-vx/internal/domain/main-entities/soapi" + "gorm.io/gorm" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" edo "simrs-vx/internal/domain/main-entities/device-order" emo "simrs-vx/internal/domain/main-entities/material-order" @@ -23,6 +25,7 @@ import ( epi "simrs-vx/internal/domain/main-entities/prescription-item" // udo "simrs-vx/internal/use-case/main-use-case/device-order" + es "simrs-vx/internal/domain/main-entities/soapi" um "simrs-vx/internal/use-case/main-use-case/medication" umei "simrs-vx/internal/use-case/main-use-case/medication-item" umi "simrs-vx/internal/use-case/main-use-case/medicine-mix" @@ -30,13 +33,9 @@ import ( up "simrs-vx/internal/use-case/main-use-case/prescription" upi "simrs-vx/internal/use-case/main-use-case/prescription-item" - pl "simrs-vx/pkg/logger" - pu "simrs-vx/pkg/use-case-helper" - + e "simrs-vx/internal/domain/main-entities/encounter" erc "simrs-vx/internal/domain/references/common" ero "simrs-vx/internal/domain/references/organization" - - "gorm.io/gorm" ) func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.Encounter) { @@ -114,7 +113,7 @@ func checkSoapiByDocExists(encounter_id uint, event *pl.Event, tx *gorm.DB) erro } for _, s := range soapies { - if s.Employee != nil && s.Employee.User != nil && s.Employee.User.Position_Code == ero.UPCDoc { + if s.Employee != nil && s.Employee.User != nil && *s.Employee.Position_Code == ero.EPCDoc { return nil } } diff --git a/internal/use-case/main-use-case/user/case.go b/internal/use-case/main-use-case/user/case.go index ba5859ee..ca5d0485 100644 --- a/internal/use-case/main-use-case/user/case.go +++ b/internal/use-case/main-use-case/user/case.go @@ -9,7 +9,6 @@ import ( ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/employee" el "simrs-vx/internal/domain/main-entities/laborant" - em "simrs-vx/internal/domain/main-entities/midwife" en "simrs-vx/internal/domain/main-entities/nurse" et "simrs-vx/internal/domain/main-entities/nutritionist" ep "simrs-vx/internal/domain/main-entities/pharmacist" @@ -19,7 +18,6 @@ import ( ud "simrs-vx/internal/use-case/main-use-case/doctor" ue "simrs-vx/internal/use-case/main-use-case/employee" ul "simrs-vx/internal/use-case/main-use-case/laborant" - um "simrs-vx/internal/use-case/main-use-case/midwife" un "simrs-vx/internal/use-case/main-use-case/nurse" ut "simrs-vx/internal/use-case/main-use-case/nutritionist" upe "simrs-vx/internal/use-case/main-use-case/person" @@ -85,7 +83,7 @@ func Create(input e.CreateDto) (*d.Data, error) { data = *resData } - if input.Position_Code == ero.UPCInt { + if input.ContractPosition_Code == ero.CSCSpi { createInt := esi.CreateDto{ Person_Id: input.Person_Id, Specialist_Id: input.Specialist_Id, @@ -105,8 +103,8 @@ func Create(input e.CreateDto) (*d.Data, error) { return err } - switch input.Position_Code { - case ero.UPCDoc: + switch input.Employee.Position_Code { + case ero.EPCDoc: createDoc := ed.CreateDto{ Employee_Id: &employeeData.Id, IHS_Number: input.IHS_Number, @@ -118,7 +116,7 @@ func Create(input e.CreateDto) (*d.Data, error) { if _, err := ud.CreateData(createDoc, &event, tx); err != nil { return err } - case ero.UPCNur: + case ero.EPCNur: createNurse := en.CreateDto{ Employee_Id: &employeeData.Id, IHS_Number: input.IHS_Number, @@ -128,7 +126,7 @@ func Create(input e.CreateDto) (*d.Data, error) { if _, err := un.CreateData(createNurse, &event, tx); err != nil { return err } - case ero.UPCNut: + case ero.EPCNut: createNutritionist := et.CreateDto{ Employee_Id: &employeeData.Id, IHS_Number: input.IHS_Number, @@ -136,7 +134,7 @@ func Create(input e.CreateDto) (*d.Data, error) { if _, err := ut.CreateData(createNutritionist, &event, tx); err != nil { return err } - case ero.UPCPha: + case ero.EPCPha: createPharmacist := ep.CreateDto{ Employee_Id: &employeeData.Id, IHS_Number: input.IHS_Number, @@ -144,7 +142,7 @@ func Create(input e.CreateDto) (*d.Data, error) { if _, err := up.CreateData(createPharmacist, &event, tx); err != nil { return err } - case ero.UPCLab: + case ero.EPCLab: createLaborant := el.CreateDto{ Employee_Id: &employeeData.Id, IHS_Number: input.IHS_Number, @@ -152,16 +150,6 @@ func Create(input e.CreateDto) (*d.Data, error) { if _, err := ul.CreateData(createLaborant, &event, tx); err != nil { return err } - case ero.UPCMwi: - createMidwife := em.CreateDto{ - Employee_Id: &employeeData.Id, - IHS_Number: input.IHS_Number, - } - if _, err := um.CreateData(em.CreateDto(createMidwife), &event, tx); err != nil { - return err - } - case ero.UPCReg, ero.UPCHur, ero.UPCGea, ero.UPCMan: - // do nothing default: return errors.New("invalid employee position") } @@ -317,7 +305,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - person_id, err := getPersonIdByUserId(data.Id, input.Position_Code, &event, tx) + person_id, err := getPersonIdByUserId(data.Id, input.ContractPosition_Code, &event, tx) if err != nil { return err } else { @@ -348,7 +336,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - if input.Position_Code == ero.UPCInt { + if input.ContractPosition_Code == ero.CSCSpi { readInt := esi.ReadDetailDto{User_Id: &data.Id} readIntData, err := usi.ReadDetailData(readInt, &event, tx) if err != nil { @@ -384,8 +372,8 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - switch input.Position_Code { - case ero.UPCDoc: + switch input.Employee.Position_Code { + case ero.EPCDoc: readDoc := ed.ReadDetailDto{Employee_Id: &employeeData.Id} readDocData, err := ud.ReadDetailData(readDoc, &event, tx) if err != nil { @@ -407,7 +395,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { if _, err := ud.CreateData(createDoc, &event, tx); err != nil { return err } - case ero.UPCNur: + case ero.EPCNur: readNur := en.ReadDetailDto{Employee_Id: &employeeData.Id} readNurData, err := un.ReadDetailData(readNur, &event, tx) if err != nil { @@ -427,7 +415,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { if _, err := un.CreateData(createNur, &event, tx); err != nil { return err } - case ero.UPCNut: + case ero.EPCNut: readNut := et.ReadDetailDto{Employee_Id: &employeeData.Id} readNutData, err := ut.ReadDetailData(readNut, &event, tx) if err != nil { @@ -447,7 +435,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { if _, err := ut.CreateData(createNut, &event, tx); err != nil { return err } - case ero.UPCPha: + case ero.EPCPha: readPha := ep.ReadDetailDto{Employee_Id: &employeeData.Id} readPhaData, err := up.ReadDetailData(readPha, &event, tx) if err != nil { @@ -467,7 +455,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { if _, err := up.CreateData(createPha, &event, tx); err != nil { return err } - case ero.UPCLab: + case ero.EPCLab: readLab := el.ReadDetailDto{Employee_Id: &employeeData.Id} readLabData, err := ul.ReadDetailData(readLab, &event, tx) if err != nil { @@ -487,28 +475,6 @@ func Update(input e.UpdateDto) (*d.Data, error) { if _, err := ul.CreateData(createLab, &event, tx); err != nil { return err } - case ero.UPCMwi: - readMidwife := em.ReadDetailDto{Employee_Id: &employeeData.Id} - readMidwifeData, err := um.ReadDetailData(readMidwife, &event, tx) - if err != nil { - return err - } - createMidwife := em.CreateDto{ - Employee_Id: &employeeData.Id, - IHS_Number: input.IHS_Number, - } - if readMidwifeData != nil { - if err := um.UpdateData(em.UpdateDto{CreateDto: createMidwife}, readMidwifeData, &event, tx); err != nil { - return err - } - return nil - } - - if _, err := um.CreateData(createMidwife, &event, tx); err != nil { - return err - } - case ero.UPCReg, ero.UPCHur, ero.UPCGea, ero.UPCMan: - // do nothing default: return errors.New("invalid employee position") } diff --git a/internal/use-case/main-use-case/user/helper.go b/internal/use-case/main-use-case/user/helper.go index 111d333f..4ce0259d 100644 --- a/internal/use-case/main-use-case/user/helper.go +++ b/internal/use-case/main-use-case/user/helper.go @@ -6,19 +6,19 @@ package user import ( "errors" - ee "simrs-vx/internal/domain/main-entities/employee" - esi "simrs-vx/internal/domain/main-entities/specialist-intern" - e "simrs-vx/internal/domain/main-entities/user" - ue "simrs-vx/internal/use-case/main-use-case/employee" - usi "simrs-vx/internal/use-case/main-use-case/specialist-intern" - - ero "simrs-vx/internal/domain/references/organization" + "gorm.io/gorm" pl "simrs-vx/pkg/logger" p "simrs-vx/pkg/password" - "gorm.io/gorm" + ue "simrs-vx/internal/use-case/main-use-case/employee" + usi "simrs-vx/internal/use-case/main-use-case/specialist-intern" + + ee "simrs-vx/internal/domain/main-entities/employee" + esi "simrs-vx/internal/domain/main-entities/specialist-intern" + e "simrs-vx/internal/domain/main-entities/user" + erg "simrs-vx/internal/domain/references/organization" ) func setCreate(src e.CreateDto, dst *e.User) error { @@ -30,15 +30,14 @@ func setCreate(src e.CreateDto, dst *e.User) error { dst.Name = src.Name dst.Password = pass dst.Status_Code = src.Status_Code - dst.Position_Code = src.Position_Code + dst.ContractPosition_Code = src.ContractPosition_Code return nil } func setUpdate(src e.UpdateDto, dst *e.User) { dst.Status_Code = src.Status_Code - dst.Position_Code = src.Position_Code - + dst.ContractPosition_Code = src.ContractPosition_Code } func setDataEmployeeUpdate(src e.EmployeUpdateDto) ee.UpdateDto { @@ -54,9 +53,10 @@ func setDataEmployeeUpdate(src e.EmployeUpdateDto) ee.UpdateDto { } } -func getPersonIdByUserId(userId uint, positionCode ero.UserPosisitionCode, event *pl.Event, tx *gorm.DB) (*uint, error) { +func getPersonIdByUserId(userId uint, positionCode erg.ContractPositionCode, event *pl.Event, tx *gorm.DB) (*uint, error) { pl.SetLogInfo(event, nil, "started", "DBGetPersonIdByUserId") - if positionCode == ero.UPCInt { + if positionCode == erg.CSCEmp { + } else if positionCode == erg.CSCSpi { specInt, err := usi.ReadDetailData(esi.ReadDetailDto{User_Id: &userId}, event, tx) if err != nil { return nil, err diff --git a/pkg/auth-helper/tycovar.go b/pkg/auth-helper/tycovar.go index 288c9d21..06618350 100644 --- a/pkg/auth-helper/tycovar.go +++ b/pkg/auth-helper/tycovar.go @@ -7,11 +7,15 @@ import ( type AuthKey struct{} type AuthInfo struct { - Uuid string - User_Id uint - User_Name string - User_DivisionPositions []DivisionPosition - User_Position_Code string + Uuid string + User_Id uint + User_Name string + User_ContractPosition_code string + Employee_Position_Code *string + Intern_Position_Code *string + User_DivisionPositions []DivisionPosition + // User_DivisionPositions []DivisionPosition + // User_Position_Code string } type DivisionPosition struct { @@ -20,33 +24,64 @@ type DivisionPosition struct { } func (a AuthInfo) IsDoctor() bool { - return a.User_Position_Code == string(ero.UPCDoc) + if a.Employee_Position_Code == nil { + return false + } + return *a.Employee_Position_Code == string(ero.EPCDoc) } func (a AuthInfo) IsNurse() bool { - return a.User_Position_Code == string(ero.UPCNur) + if a.Employee_Position_Code == nil { + return false + } + return *a.Employee_Position_Code == string(ero.EPCNur) } func (a AuthInfo) IsNutritionist() bool { - return a.User_Position_Code == string(ero.UPCNut) + if a.Employee_Position_Code == nil { + return false + } + return *a.Employee_Position_Code == string(ero.EPCNut) } func (a AuthInfo) IsLaborant() bool { - return a.User_Position_Code == string(ero.UPCLab) + if a.Employee_Position_Code == nil { + return false + } + return *a.Employee_Position_Code == string(ero.EPCLab) } func (a AuthInfo) IsPharmacist() bool { - return a.User_Position_Code == string(ero.UPCPha) + if a.Employee_Position_Code == nil { + return false + } + return *a.Employee_Position_Code == string(ero.EPCPha) } func (a AuthInfo) IsPayment() bool { - return a.User_Position_Code == string(ero.UPCPay) + if a.Employee_Position_Code == nil { + return false + } + return *a.Employee_Position_Code == string(ero.EPCPay) } func (a AuthInfo) IsManagement() bool { - return a.User_Position_Code == string(ero.UPCMan) + if a.Employee_Position_Code == nil { + return false + } + return *a.Employee_Position_Code == string(ero.EPCMan) } func (a AuthInfo) IsSpecialistIntern() bool { - return a.User_Position_Code == string(ero.UPCInt) + if a.Intern_Position_Code == nil { + return false + } + return *a.Intern_Position_Code == string(ero.IPCSpecialist) +} + +func (a AuthInfo) IsNurseIntern() bool { + if a.Intern_Position_Code == nil { + return false + } + return *a.Intern_Position_Code == string(ero.IPCNurse) } From 19e5ddf65d575f64965764170645302738fb0db6 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Sun, 19 Oct 2025 15:10:14 +0700 Subject: [PATCH 05/15] feat/user: reworking auth --- .../references/organization/organization.go | 2 +- .../main-use-case/authentication/case.go | 76 ++++++++++++------- .../main-use-case/authentication/helper.go | 2 +- internal/use-case/main-use-case/user/case.go | 4 +- .../use-case/main-use-case/user/helper.go | 2 +- 5 files changed, 53 insertions(+), 33 deletions(-) diff --git a/internal/domain/references/organization/organization.go b/internal/domain/references/organization/organization.go index 60270a93..93eabe63 100644 --- a/internal/domain/references/organization/organization.go +++ b/internal/domain/references/organization/organization.go @@ -12,7 +12,7 @@ type ( const ( CSCEmp ContractPositionCode = "employee" // Pegawai - CSCSpi ContractPositionCode = "intern" // PPDS + CSCInt ContractPositionCode = "intern" // PPDS EPCReg EmployeePosisitionCode = "registration" // Admisi/Pendaftaran EPCNur EmployeePosisitionCode = "nurse" // Perawat diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index e03b0293..f9aec70c 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -10,14 +10,17 @@ import ( "github.com/golang-jwt/jwt" "github.com/google/uuid" + "simrs-vx/internal/domain/main-entities/intern" eu "simrs-vx/internal/domain/main-entities/user" pa "simrs-vx/pkg/auth-helper" el "simrs-vx/pkg/logger" p "simrs-vx/pkg/password" + ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/employee" erc "simrs-vx/internal/domain/references/common" + erg "simrs-vx/internal/domain/references/organization" a "github.com/karincake/apem" dg "github.com/karincake/apem/db-gorm-pg" @@ -92,43 +95,60 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { atExpires := time.Now().Add(duration).Unix() atSecretKey := authCfg.AtSecretKey - // extra - role := []string{} - if user.ContractPosition_Code == "employee" { - employee := ee.Employee{} - dg.I.Where("user_id = ?", user.Id).First(&employee) - role = append(role, "emp-"+string(*employee.Position_Code)) - } else if user.ContractPosition_Code == "intern" { - // specialistIntern := esi.SpecialistIntern{} - // dg.I.Where("user_id = ?", user.Id).First(&specialistIntern) - role = append(role, "spi") - } - - // Creating Access Token + // Create Claim atClaims := jwt.MapClaims{} atClaims["user_id"] = user.Id atClaims["user_name"] = user.Name - // atClaims["user_email"] = user.Email - // atClaims["user_position_code"] = user.Position_Code - atClaims["user_employementStatus_code"] = user.ContractPosition_Code - // atClaims["user_ref_id"] = user.Ref_Id - atClaims["exp"] = atExpires + atClaims["user_contractPosition_code"] = user.ContractPosition_Code + atClaims["division_positions"] = userDivisionPositions atClaims["uuid"] = aUuid - atClaims["user_division_positions"] = userDivisionPositions + atClaims["exp"] = atExpires + + // Create output + outputData := d.II{ + "user_id": strconv.Itoa(int(user.Id)), + "user_name": user.Name, + "user_contractPosition_code": user.ContractPosition_Code, + } + + // extra + role := []string{} + switch user.ContractPosition_Code { + case erg.CSCEmp: + // employee + employee := ee.Employee{} + dg.I.Where("\"User_Id\" = ?", user.Id).First(&employee) + role = append(role, "emp-"+string(*employee.Position_Code)) + atClaims["employee_division_code"] = employee.Division_Code + outputData["employee_division_code"] = employee.Division_Code + // doctor + if employee.Id > 0 && employee.Position_Code != nil && *employee.Position_Code == erg.EPCDoc { + doctor := ed.Doctor{} + dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&doctor) + if doctor.Specialist_Id != nil { + atClaims["specialist_id"] = doctor.Specialist_Id + outputData["specialist_id"] = doctor.Specialist_Id + } + if doctor.Subspecialist_Id != nil { + atClaims["subspecialist_id"] = doctor.Subspecialist_Id + outputData["subspecialist_id"] = doctor.Subspecialist_Id + } + } + case erg.CSCInt: + intern := intern.Intern{} + dg.I.Where("\"User_Id\" = ?", user.Id).First(&intern) + role = append(role, "int-"+string(*intern.Position_Code)) + } + atClaims["roles"] = role + outputData["roles"] = role + + // Generate jwt at := jwt.NewWithClaims(jwt.SigningMethodHS256, atClaims) ats, err := at.SignedString([]byte(atSecretKey)) if err != nil { return nil, d.FieldErrors{"user": d.FieldError{Code: "token-sign-err", Message: el.GenMessage("token-sign-err")}} } - - outputData := d.II{ - "user_id": strconv.Itoa(int(user.Id)), - "user_name": user.Name, - // "user_email": user.Email, - "user_contractPosition_code": user.ContractPosition_Code, - // "user_ref_id": user.Ref_Id, - "accessToken": ats, - } + outputData["accessToken"] = ats // Save to redis now := time.Now() diff --git a/internal/use-case/main-use-case/authentication/helper.go b/internal/use-case/main-use-case/authentication/helper.go index 50b0c8fd..fef5b07e 100644 --- a/internal/use-case/main-use-case/authentication/helper.go +++ b/internal/use-case/main-use-case/authentication/helper.go @@ -54,7 +54,7 @@ func getDivisionPosition(user_id uint) ([]pa.DivisionPosition, error) { result = append(result, pa.DivisionPosition{ Division_Code: func() string { if dp.Division != nil { - return dp.Division.Code + return "div-" + dp.Division.Code } return "" }(), diff --git a/internal/use-case/main-use-case/user/case.go b/internal/use-case/main-use-case/user/case.go index ca5d0485..7d2053de 100644 --- a/internal/use-case/main-use-case/user/case.go +++ b/internal/use-case/main-use-case/user/case.go @@ -83,7 +83,7 @@ func Create(input e.CreateDto) (*d.Data, error) { data = *resData } - if input.ContractPosition_Code == ero.CSCSpi { + if input.ContractPosition_Code == ero.CSCInt { createInt := esi.CreateDto{ Person_Id: input.Person_Id, Specialist_Id: input.Specialist_Id, @@ -336,7 +336,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { return err } - if input.ContractPosition_Code == ero.CSCSpi { + if input.ContractPosition_Code == ero.CSCInt { readInt := esi.ReadDetailDto{User_Id: &data.Id} readIntData, err := usi.ReadDetailData(readInt, &event, tx) if err != nil { diff --git a/internal/use-case/main-use-case/user/helper.go b/internal/use-case/main-use-case/user/helper.go index 4ce0259d..1663bb24 100644 --- a/internal/use-case/main-use-case/user/helper.go +++ b/internal/use-case/main-use-case/user/helper.go @@ -56,7 +56,7 @@ func setDataEmployeeUpdate(src e.EmployeUpdateDto) ee.UpdateDto { func getPersonIdByUserId(userId uint, positionCode erg.ContractPositionCode, event *pl.Event, tx *gorm.DB) (*uint, error) { pl.SetLogInfo(event, nil, "started", "DBGetPersonIdByUserId") if positionCode == erg.CSCEmp { - } else if positionCode == erg.CSCSpi { + } else if positionCode == erg.CSCInt { specInt, err := usi.ReadDetailData(esi.ReadDetailDto{User_Id: &userId}, event, tx) if err != nil { return nil, err From 0f407e5df353a56949aebc1e68093d1fbfbc9925 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Sun, 19 Oct 2025 18:10:20 +0700 Subject: [PATCH 06/15] feat/user: auth reworking --- .../main-use-case/authentication/case.go | 66 ++++++++++++++----- .../main-use-case/authentication/helper.go | 36 ++++------ 2 files changed, 61 insertions(+), 41 deletions(-) diff --git a/internal/use-case/main-use-case/authentication/case.go b/internal/use-case/main-use-case/authentication/case.go index f9aec70c..ffefa7ef 100644 --- a/internal/use-case/main-use-case/authentication/case.go +++ b/internal/use-case/main-use-case/authentication/case.go @@ -19,6 +19,7 @@ import ( ed "simrs-vx/internal/domain/main-entities/doctor" ee "simrs-vx/internal/domain/main-entities/employee" + en "simrs-vx/internal/domain/main-entities/nurse" erc "simrs-vx/internal/domain/references/common" erg "simrs-vx/internal/domain/references/organization" @@ -77,11 +78,6 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-login-unverified", Message: el.GenMessage("auth-login-unverified")}} } - userDivisionPositions, err := getDivisionPosition(user.Id) - if err != nil { - return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-getData-failed", Message: el.GenMessage("auth-getData-failed")}} - } - // Access token prep id, err := uuid.NewRandom() if err != nil { @@ -100,7 +96,6 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { atClaims["user_id"] = user.Id atClaims["user_name"] = user.Name atClaims["user_contractPosition_code"] = user.ContractPosition_Code - atClaims["division_positions"] = userDivisionPositions atClaims["uuid"] = aUuid atClaims["exp"] = atExpires @@ -118,21 +113,56 @@ func GenToken(input eu.LoginDto) (*d.Data, error) { // employee employee := ee.Employee{} dg.I.Where("\"User_Id\" = ?", user.Id).First(&employee) + if employee.Id == 0 { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-noEmployee", Message: el.GenMessage("auth-noEmployee")}} + } + atClaims["employee_id"] = employee.Id + outputData["employee_id"] = employee.Id role = append(role, "emp-"+string(*employee.Position_Code)) - atClaims["employee_division_code"] = employee.Division_Code - outputData["employee_division_code"] = employee.Division_Code - // doctor - if employee.Id > 0 && employee.Position_Code != nil && *employee.Position_Code == erg.EPCDoc { - doctor := ed.Doctor{} - dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&doctor) - if doctor.Specialist_Id != nil { - atClaims["specialist_id"] = doctor.Specialist_Id - outputData["specialist_id"] = doctor.Specialist_Id + + if employee.Division_Code != nil { + atClaims["employee_division_code"] = employee.Division_Code + outputData["employee_division_code"] = employee.Division_Code + } + + // employee position + if employee.Id > 0 && employee.Position_Code != nil { + switch *employee.Position_Code { + case erg.EPCDoc: + doctor := ed.Doctor{} + dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&doctor) + if doctor.Id == 0 { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-noDoctor", Message: el.GenMessage("auth-noDoctor")}} + } + atClaims["doctor_id"] = doctor.Id + outputData["doctor_id"] = doctor.Id + + // specialist + if doctor.Specialist_Id != nil { + atClaims["specialist_id"] = doctor.Specialist_Id + outputData["specialist_id"] = doctor.Specialist_Id + } + if doctor.Subspecialist_Id != nil { + atClaims["subspecialist_id"] = doctor.Subspecialist_Id + outputData["subspecialist_id"] = doctor.Subspecialist_Id + } + case erg.EPCNur: + nurse := en.Nurse{} + dg.I.Where("\"Employee_Id\" = ?", employee.Id).First(&nurse) + if nurse.Id == 0 { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-noNurse", Message: el.GenMessage("auth-noNurse")}} + } + atClaims["nurse_id"] = nurse.Id + outputData["nurse_id"] = nurse.Id } - if doctor.Subspecialist_Id != nil { - atClaims["subspecialist_id"] = doctor.Subspecialist_Id - outputData["subspecialist_id"] = doctor.Subspecialist_Id + // division position + divsionPositions, err := getDivisionPosition(employee.Id) + if err != nil { + return nil, d.FieldErrors{"authentication": d.FieldError{Code: "auth-getData-failed", Message: el.GenMessage("auth-getData-failed")}} } + role = append(role, divsionPositions...) + // atClaims["division_positions"] = divsionPositions + // outputData["division_positions"] = divsionPositions } case erg.CSCInt: intern := intern.Intern{} diff --git a/internal/use-case/main-use-case/authentication/helper.go b/internal/use-case/main-use-case/authentication/helper.go index fef5b07e..c4e67200 100644 --- a/internal/use-case/main-use-case/authentication/helper.go +++ b/internal/use-case/main-use-case/authentication/helper.go @@ -1,11 +1,7 @@ package authentication import ( - "errors" edp "simrs-vx/internal/domain/main-entities/division-position" - ee "simrs-vx/internal/domain/main-entities/employee" - - pa "simrs-vx/pkg/auth-helper" dg "github.com/karincake/apem/db-gorm-pg" "gorm.io/gorm" @@ -27,21 +23,21 @@ func getDocName(id uint) string { return "authentication" } -func getDivisionPosition(user_id uint) ([]pa.DivisionPosition, error) { - var result []pa.DivisionPosition +func getDivisionPosition(employee_id uint) ([]string, error) { + var result []string - var employee ee.Employee - if err := dg.I.Where("\"User_Id\" = ?", user_id).First(&employee).Error; err != nil { - if err == gorm.ErrRecordNotFound { - return result, nil - } - return result, errors.New("no employee found") - } + // var employee ee.Employee + // if err := dg.I.Where("\"Employee_Id\" = ?", employee_id).First(&employee).Error; err != nil { + // if err == gorm.ErrRecordNotFound { + // return result, nil + // } + // return result, errors.New("no employee found") + // } var divisionPositions []edp.DivisionPosition err := dg.I. Preload("Division"). - Where("\"Employee_Id\" = ?", employee.Id). + Where("\"Employee_Id\" = ?", employee_id). Find(&divisionPositions).Error if err != nil { if err == gorm.ErrRecordNotFound { @@ -51,15 +47,9 @@ func getDivisionPosition(user_id uint) ([]pa.DivisionPosition, error) { } for _, dp := range divisionPositions { - result = append(result, pa.DivisionPosition{ - Division_Code: func() string { - if dp.Division != nil { - return "div-" + dp.Division.Code - } - return "" - }(), - DivisionPosition_Code: dp.Code, - }) + if dp.Division != nil { + result = append(result, "div-"+dp.Division.Code+"-"+dp.Code) + } } return result, nil From 57d9f99468a05d44700d0369283dcfee1dca15c0 Mon Sep 17 00:00:00 2001 From: Munawwirul Jamal Date: Sun, 19 Oct 2025 21:58:07 +0700 Subject: [PATCH 07/15] feat/encounter: improved the processing --- internal/domain/main-entities/encounter/dto.go | 7 ++++--- .../references/organization/organization.go | 17 +++++++++++------ .../interface/main-handler/encounter/handler.go | 1 + .../use-case/main-use-case/encounter/lib.go | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/internal/domain/main-entities/encounter/dto.go b/internal/domain/main-entities/encounter/dto.go index 73a9ec5b..67810d27 100644 --- a/internal/domain/main-entities/encounter/dto.go +++ b/internal/domain/main-entities/encounter/dto.go @@ -70,7 +70,8 @@ type FilterDto struct { } type ReadDetailDto struct { - Id uint16 `json:"id"` + Id uint16 `json:"id"` + Includes string `json:"includes"` } type UpdateDto struct { @@ -119,8 +120,8 @@ type ResponseDto struct { Member_Number *string `json:"member_number"` Ref_Number *string `json:"ref_number"` Trx_Number *string `json:"trx_number"` - Appointment_Doctor_Id *uint `json:"assignment_doctor_id"` - Appointment_Doctor *ed.Doctor `json:"assignment_doctor,omitempty"` + Appointment_Doctor_Id *uint `json:"appointment_doctor_id"` + Appointment_Doctor *ed.Doctor `json:"appointment_doctor,omitempty"` Responsible_Doctor_Id *uint `json:"responsible_doctor_id"` Responsible_Doctor *ed.Doctor `json:"responsible_doctor,omitempty"` Adm_Employee_Id *uint `json:"adm_employee_id"` diff --git a/internal/domain/references/organization/organization.go b/internal/domain/references/organization/organization.go index b2de9772..93eabe63 100644 --- a/internal/domain/references/organization/organization.go +++ b/internal/domain/references/organization/organization.go @@ -33,15 +33,20 @@ const ( ITGCMedicine ItemGroupCode = "medicine" ITGCDevice ItemGroupCode = "device" ITGCMaterial ItemGroupCode = "material" + ITGCMCU ItemGroupCode = "mcu" + ITGCMCUSub ItemGroupCode = "mcuSub" ITGCEmpFee ItemGroupCode = "employee-fee" ITGCDocFee ItemGroupCode = "doctor-fee" + ITGCMedAct ItemGroupCode = "medical-action" - IFGCBuilding InfraGroupCode = "building" - IFGCFloor InfraGroupCode = "floor" - IFGCRoom InfraGroupCode = "room" - IFGCChamber InfraGroupCode = "chamber" - IFGCBed InfraGroupCode = "bed" - IFGCWarehouse InfraGroupCode = "warehouse" + IFGCBuilding InfraGroupCode = "building" // Bangunan + IFGCFloor InfraGroupCode = "floor" // Lantai + IFGCRoom InfraGroupCode = "room" // Ruang + IFGCChamber InfraGroupCode = "chamber" // Kamar + IFGCBed InfraGroupCode = "bed" // Ranjang + IFGCWarehouse InfraGroupCode = "warehouse" // Gudang/Depo + IFGCCounter InfraGroupCode = "counter" // Counter + IFGCPubScreen InfraGroupCode = "public-screen" // Public Screen UTCReg UnitTypeCode = "reg" // Registrasi UTCExa UnitTypeCode = "exa" // Pemeriksaan diff --git a/internal/interface/main-handler/encounter/handler.go b/internal/interface/main-handler/encounter/handler.go index 09191c96..96063298 100644 --- a/internal/interface/main-handler/encounter/handler.go +++ b/internal/interface/main-handler/encounter/handler.go @@ -49,6 +49,7 @@ func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { return } dto := e.ReadDetailDto{} + sf.UrlQueryParam(&dto, *r.URL) dto.Id = uint16(id) res, err := u.ReadDetail(dto) rw.DataResponse(w, res, err) diff --git a/internal/use-case/main-use-case/encounter/lib.go b/internal/use-case/main-use-case/encounter/lib.go index 2f4d69fa..ccc4b811 100644 --- a/internal/use-case/main-use-case/encounter/lib.go +++ b/internal/use-case/main-use-case/encounter/lib.go @@ -81,7 +81,7 @@ func ReadDetailData(input e.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*e tx = dg.I } - if err := tx.First(&data, input.Id).Error; err != nil { + if err := tx.Scopes(gh.Preload(input.Includes)).First(&data, input.Id).Error; err != nil { if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { return nil, processedErr } From a11087a4de3ca61e726261170f025a3fa6e18ee5 Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 20 Oct 2025 08:53:36 +0700 Subject: [PATCH 08/15] add crud vehicle --- go.mod | 19 +- go.sum | 22 ++ internal/domain/main-entities/vehicle/dto.go | 82 +++++ .../interface/main-handler/main-handler.go | 2 + .../interface/main-handler/vehicle/handler.go | 73 ++++ .../use-case/main-use-case/vehicle/case.go | 334 ++++++++++++++++++ .../use-case/main-use-case/vehicle/helper.go | 74 ++++ .../use-case/main-use-case/vehicle/lib.go | 164 +++++++++ .../vehicle/middleware-runner.go | 103 ++++++ .../main-use-case/vehicle/middleware.go | 9 + .../use-case/main-use-case/vehicle/tycovar.go | 44 +++ 11 files changed, 919 insertions(+), 7 deletions(-) create mode 100644 internal/domain/main-entities/vehicle/dto.go create mode 100644 internal/interface/main-handler/vehicle/handler.go create mode 100644 internal/use-case/main-use-case/vehicle/case.go create mode 100644 internal/use-case/main-use-case/vehicle/helper.go create mode 100644 internal/use-case/main-use-case/vehicle/lib.go create mode 100644 internal/use-case/main-use-case/vehicle/middleware-runner.go create mode 100644 internal/use-case/main-use-case/vehicle/middleware.go create mode 100644 internal/use-case/main-use-case/vehicle/tycovar.go diff --git a/go.mod b/go.mod index 132d6e88..a7704150 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module simrs-vx -go 1.24 +go 1.24.0 toolchain go1.24.6 @@ -19,14 +19,18 @@ require ( github.com/karincake/serabi v0.0.14 github.com/minio/minio-go/v7 v7.0.95 github.com/rs/zerolog v1.33.0 - golang.org/x/crypto v0.41.0 + golang.org/x/crypto v0.42.0 gorm.io/gorm v1.25.12 ) require ( ariga.io/atlas v0.36.2-0.20250806044935-5bb51a0a956e // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/go-ini/ini v1.67.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.28.0 // indirect github.com/go-redis/redis v6.15.9+incompatible // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/goccy/go-json v0.10.5 // indirect @@ -35,12 +39,13 @@ require ( github.com/google/go-cmp v0.7.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/now v1.1.5 // indirect github.com/karincake/pentol v0.0.3 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.11 // indirect + github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-sqlite3 v1.14.28 // indirect @@ -52,10 +57,10 @@ require ( github.com/rs/xid v1.6.0 // indirect github.com/tinylib/msgp v1.3.0 // indirect golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect - golang.org/x/net v0.42.0 // indirect - golang.org/x/sync v0.16.0 // indirect - golang.org/x/sys v0.35.0 // indirect - golang.org/x/text v0.28.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/sync v0.17.0 // indirect + golang.org/x/sys v0.36.0 // indirect + golang.org/x/text v0.29.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/mysql v1.5.7 // indirect gorm.io/driver/postgres v1.5.11 // indirect diff --git a/go.sum b/go.sum index 555dac70..e932c508 100644 --- a/go.sum +++ b/go.sum @@ -29,8 +29,16 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= +github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= +github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= @@ -65,6 +73,8 @@ github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= @@ -104,6 +114,8 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -168,6 +180,8 @@ golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= +golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= @@ -187,11 +201,15 @@ golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -212,6 +230,8 @@ golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= +golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -234,6 +254,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= +golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= diff --git a/internal/domain/main-entities/vehicle/dto.go b/internal/domain/main-entities/vehicle/dto.go new file mode 100644 index 00000000..5ee0b8d9 --- /dev/null +++ b/internal/domain/main-entities/vehicle/dto.go @@ -0,0 +1,82 @@ +package vehicle + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ercl "simrs-vx/internal/domain/references/clinical" + ercm "simrs-vx/internal/domain/references/common" +) + +type CreateDto struct { + Type_Code ercl.VehicleTypeCode `json:"type_code" validate:"required"` + PoliceNumber *string `json:"policeNumber" validate:"required"` + FrameNumber *string `json:"frameNumber"` + RegNumber *string `json:"regNumber"` + AvailableStatus bool `json:"availableStatus"` +} + +type InsertVehicleHist struct { + Vehicle + Crud_Code ercm.CrudCode `json:"crud_code"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Type_Code ercl.VehicleTypeCode `json:"type-code"` + PoliceNumber *string `json:"policeNumber"` + FrameNumber *string `json:"frameNumber"` + RegNumber *string `json:"regNumber"` + AvailableStatus bool `json:"availableStatus"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` +} + +type UpdateDto struct { + Id uint `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 + Type_Code *ercl.VehicleTypeCode `json:"type_code"` + PoliceNumber *string `json:"policeNumber"` + FrameNumber *string `json:"frameNumber"` + RegNumber *string `json:"regNumber"` + AvailableStatus bool `json:"availableStatus"` +} + +func (d Vehicle) ToResponse() ResponseDto { + resp := ResponseDto{ + Type_Code: d.Type_Code, + PoliceNumber: d.PoliceNumber, + FrameNumber: d.FrameNumber, + RegNumber: d.RegNumber, + AvailableStatus: d.AvailableStatus, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []Vehicle) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 3b31e2e5..4e0acd0b 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -2,6 +2,7 @@ package handler import ( "net/http" + "simrs-vx/internal/interface/main-handler/vehicle" /******************** main / transaction ********************/ adime "simrs-vx/internal/interface/main-handler/adime" @@ -295,6 +296,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/specialist", specialist.O) hc.RegCrud(r, "/v1/subspecialist", subspecialist.O) hc.RegCrud(r, "/v1/mcu-sub-src", mcusubsrc.O) + hc.RegCrud(r, "/v1/vehicle", vehicle.O) hc.RegCrud(r, "/v1/village", village.O) hc.RegCrud(r, "/v1/district", district.O) diff --git a/internal/interface/main-handler/vehicle/handler.go b/internal/interface/main-handler/vehicle/handler.go new file mode 100644 index 00000000..970579e2 --- /dev/null +++ b/internal/interface/main-handler/vehicle/handler.go @@ -0,0 +1,73 @@ +package vehicle + +import ( + "net/http" + + e "simrs-vx/internal/domain/main-entities/vehicle" + u "simrs-vx/internal/use-case/main-use-case/vehicle" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := e.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + res, err := u.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.ReadDetailDto{} + 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 = uint(id) + res, err := u.Update(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.DeleteDto{} + + dto.Id = uint16(id) + res, err := u.Delete(dto) + rw.DataResponse(w, res, err) +} diff --git a/internal/use-case/main-use-case/vehicle/case.go b/internal/use-case/main-use-case/vehicle/case.go new file mode 100644 index 00000000..d07cc7a4 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle/case.go @@ -0,0 +1,334 @@ +package vehicle + +import ( + rcm "simrs-vx/internal/domain/references/common" + "strconv" + + "gorm.io/gorm" + + v "simrs-vx/internal/domain/main-entities/vehicle" + 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" +) + +const source = "vehicle" + +func Create(input v.CreateDto) (*d.Data, error) { + data := v.Vehicle{} + + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + // Validate request + if err := validateRequest(input, &event); err != nil { + return err + } + + // Create Vehicle + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + // Set data for vehicle hist + vehicleHistData := v.InsertVehicleHist{ + Crud_Code: rcm.CCCreate, + Vehicle: data, + } + + // Insert Vehicle Hist + if err := InsertVehicleHist(vehicleHistData, &event, tx); err != nil { + return err + } + + 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 v.ReadListDto) (*d.Data, error) { + var data *v.Vehicle + var dataList []v.Vehicle + var metaList *v.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: v.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input v.ReadDetailDto) (*d.Data, error) { + var data *v.Vehicle + 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 + } + + // Set data for vehicle hist + vehicleHistData := v.InsertVehicleHist{ + Crud_Code: rcm.CCRead, + Vehicle: *data, + } + + // Insert Vehicle Hist + if err := InsertVehicleHist(vehicleHistData, &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 v.UpdateDto) (*d.Data, error) { + rdDto := v.ReadDetailDto{Id: uint16(input.Id)} + var data *v.Vehicle + 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 + } + + // Validate request + if err := validateRequest(input.CreateDto, &event); err != nil { + return err + } + + // Update Vehicle + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + // Set data for vehicle hist + vehicleHistData := v.InsertVehicleHist{ + Crud_Code: rcm.CCUpdate, + Vehicle: *data, + } + + // Insert Vehicle Hist + if err := InsertVehicleHist(vehicleHistData, &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 v.DeleteDto) (*d.Data, error) { + rdDto := v.ReadDetailDto{Id: input.Id} + var data *v.Vehicle + 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 + } + + // Set data for vehicle hist + vehicleHistData := v.InsertVehicleHist{ + Crud_Code: rcm.CCDelete, + Vehicle: *data, + } + + // Insert Vehicle Hist + if err := InsertVehicleHist(vehicleHistData, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil + +} diff --git a/internal/use-case/main-use-case/vehicle/helper.go b/internal/use-case/main-use-case/vehicle/helper.go new file mode 100644 index 00000000..adc7f91b --- /dev/null +++ b/internal/use-case/main-use-case/vehicle/helper.go @@ -0,0 +1,74 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package vehicle + +import ( + "encoding/json" + "errors" + v "simrs-vx/internal/domain/main-entities/vehicle" + vh "simrs-vx/internal/domain/main-entities/vehicle-hist" + rcl "simrs-vx/internal/domain/references/clinical" + pl "simrs-vx/pkg/logger" + + "time" +) + +func setData[T *v.CreateDto | *v.UpdateDto](input T, data *v.Vehicle) { + var inputSrc *v.CreateDto + if inputT, ok := any(input).(*v.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*v.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Type_Code = &inputSrc.Type_Code + data.PoliceNumber = inputSrc.PoliceNumber + data.FrameNumber = inputSrc.FrameNumber + data.RegNumber = inputSrc.RegNumber + data.AvailableStatus = inputSrc.AvailableStatus +} + +func setDataVehicleHist(input v.InsertVehicleHist, data *vh.VehicleHist) { + now := time.Now() + data.Vehicle_Id = &input.Id + data.Date = &now + data.Data = toStringPtr(input.Vehicle) + data.Crud_Code = &input.Crud_Code +} + +func toStringPtr(v any) *string { + b, _ := json.Marshal(v) + s := string(b) + return &s +} + +func validateRequest(input v.CreateDto, event *pl.Event) error { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-validation-fail", + } + + if input.Type_Code == "" { + event.ErrInfo.Detail = "Field 'type_code' is required" + event.ErrInfo.Raw = errors.New("type_code is required") + return pl.SetLogError(event, input) + } + + if input.PoliceNumber == nil { + event.ErrInfo.Detail = "Field 'policeNumber' is required" + event.ErrInfo.Raw = errors.New("policeNumber is required") + return pl.SetLogError(event, input) + } + + switch input.Type_Code { + case rcl.VTCAmbulance, rcl.VTCTransport, rcl.VTCHearse: + return nil + default: + event.ErrInfo.Detail = "type_code value is not recognized" + event.ErrInfo.Raw = errors.New("type_code value is not recognized") + return pl.SetLogError(event, input) + } +} diff --git a/internal/use-case/main-use-case/vehicle/lib.go b/internal/use-case/main-use-case/vehicle/lib.go new file mode 100644 index 00000000..aef2d336 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle/lib.go @@ -0,0 +1,164 @@ +package vehicle + +import ( + "errors" + v "simrs-vx/internal/domain/main-entities/vehicle" + vh "simrs-vx/internal/domain/main-entities/vehicle-hist" + pu "simrs-vx/pkg/use-case-helper" + + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input v.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*v.Vehicle, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := v.Vehicle{} + 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 InsertVehicleHist(input v.InsertVehicleHist, event *pl.Event, dbx ...*gorm.DB) error { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := vh.VehicleHist{} + setDataVehicleHist(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 plh.HandleCreateError(input, event, err) + } + + pl.SetLogInfo(event, nil, "complete") + return nil +} + +func ReadListData(input v.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]v.Vehicle, *v.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []v.Vehicle{} + pagination := gh.Pagination{} + count := int64(0) + meta := v.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&v.Vehicle{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Order("\"CreatedAt\" DESC") + + if err := tx.Find(&data).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input v.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*v.Vehicle, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := v.Vehicle{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx.First(&data, input.Id).Error; err != nil { + + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input v.UpdateDto, data *v.Vehicle, 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 *v.Vehicle, 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/vehicle/middleware-runner.go b/internal/use-case/main-use-case/vehicle/middleware-runner.go new file mode 100644 index 00000000..cd019abf --- /dev/null +++ b/internal/use-case/main-use-case/vehicle/middleware-runner.go @@ -0,0 +1,103 @@ +package vehicle + +import ( + v "simrs-vx/internal/domain/main-entities/vehicle" + 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 *v.CreateDto, data *v.Vehicle) 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 *v.ReadListDto, data *v.Vehicle) 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 *v.ReadDetailDto, data *v.Vehicle) 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 *v.ReadDetailDto, data *v.Vehicle) 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 *v.ReadDetailDto, data *v.Vehicle) 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/vehicle/middleware.go b/internal/use-case/main-use-case/vehicle/middleware.go new file mode 100644 index 00000000..4f5099d1 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle/middleware.go @@ -0,0 +1,9 @@ +package vehicle + +// 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/vehicle/tycovar.go b/internal/use-case/main-use-case/vehicle/tycovar.go new file mode 100644 index 00000000..17fd71c0 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle/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 vehicle + +import ( + "gorm.io/gorm" + + v "simrs-vx/internal/domain/main-entities/vehicle" +) + +type createMw struct { + Name string + Func func(input *v.CreateDto, data *v.Vehicle, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *v.ReadListDto, data *v.Vehicle, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *v.ReadDetailDto, data *v.Vehicle, 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 d05ba1d2cac4d9abbe1a3383277c12bcdf1b821a Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 20 Oct 2025 09:09:39 +0700 Subject: [PATCH 09/15] update handler --- go.mod | 5 ----- go.sum | 22 ------------------- .../interface/main-handler/main-handler.go | 2 +- 3 files changed, 1 insertion(+), 28 deletions(-) diff --git a/go.mod b/go.mod index a7704150..9d557637 100644 --- a/go.mod +++ b/go.mod @@ -26,11 +26,7 @@ require ( require ( ariga.io/atlas v0.36.2-0.20250806044935-5bb51a0a956e // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/gabriel-vasile/mimetype v1.4.10 // indirect github.com/go-ini/ini v1.67.0 // indirect - github.com/go-playground/locales v0.14.1 // indirect - github.com/go-playground/universal-translator v0.18.1 // indirect - github.com/go-playground/validator/v10 v10.28.0 // indirect github.com/go-redis/redis v6.15.9+incompatible // indirect github.com/go-sql-driver/mysql v1.7.0 // indirect github.com/goccy/go-json v0.10.5 // indirect @@ -45,7 +41,6 @@ require ( github.com/karincake/pentol v0.0.3 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/cpuid/v2 v2.2.11 // indirect - github.com/leodido/go-urn v1.4.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-sqlite3 v1.14.28 // indirect diff --git a/go.sum b/go.sum index e932c508..9681a749 100644 --- a/go.sum +++ b/go.sum @@ -29,16 +29,8 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIpU+bX4+ZK0= -github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= -github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= -github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= -github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= -github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= -github.com/go-playground/validator/v10 v10.28.0 h1:Q7ibns33JjyW48gHkuFT91qX48KG0ktULL6FgHdG688= -github.com/go-playground/validator/v10 v10.28.0/go.mod h1:GoI6I1SjPBh9p7ykNE/yj3fFYbyDOpwMn5KXd+m2hUU= github.com/go-redis/redis v6.15.9+incompatible h1:K0pv1D7EQUjfyoMql+r/jZqCLizCGKFlFgcHWWmHQjg= github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA= github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= @@ -71,8 +63,6 @@ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw= github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= @@ -114,8 +104,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= -github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= @@ -178,8 +166,6 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= -golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= -golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY= @@ -199,15 +185,11 @@ golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= -golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= -golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= -golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -228,8 +210,6 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= -golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= @@ -252,8 +232,6 @@ golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= -golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index 2b7ff784..d0762780 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -2,7 +2,6 @@ package handler import ( "net/http" - "simrs-vx/internal/interface/main-handler/vehicle" /******************** main / transaction ********************/ adime "simrs-vx/internal/interface/main-handler/adime" @@ -88,6 +87,7 @@ import ( subspecialist "simrs-vx/internal/interface/main-handler/subspecialist" unit "simrs-vx/internal/interface/main-handler/unit" uom "simrs-vx/internal/interface/main-handler/uom" + vehicle "simrs-vx/internal/interface/main-handler/vehicle" district "simrs-vx/internal/interface/main-handler/district" postalregion "simrs-vx/internal/interface/main-handler/postal-region" From 71cb7d621df9549e5b4b7aed2ba7cd0145a2c5a4 Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 20 Oct 2025 10:28:23 +0700 Subject: [PATCH 10/15] adjust vehicle hist --- .../domain/main-entities/vehicle-hist/dto.go | 64 ++++++++++ internal/domain/main-entities/vehicle/dto.go | 6 - .../interface/main-handler/main-handler.go | 2 + .../main-handler/vehicle-hist/handler.go | 42 +++++++ .../main-use-case/vehicle-hist/case.go | 116 ++++++++++++++++++ .../main-use-case/vehicle-hist/helper.go | 25 ++++ .../main-use-case/vehicle-hist/lib.go | 96 +++++++++++++++ .../vehicle-hist/middleware-runner.go | 103 ++++++++++++++++ .../main-use-case/vehicle-hist/middleware.go | 9 ++ .../main-use-case/vehicle-hist/tycovar.go | 44 +++++++ .../use-case/main-use-case/vehicle/case.go | 19 +-- .../use-case/main-use-case/vehicle/helper.go | 30 ----- .../use-case/main-use-case/vehicle/lib.go | 22 ---- 13 files changed, 512 insertions(+), 66 deletions(-) create mode 100644 internal/domain/main-entities/vehicle-hist/dto.go create mode 100644 internal/interface/main-handler/vehicle-hist/handler.go create mode 100644 internal/use-case/main-use-case/vehicle-hist/case.go create mode 100644 internal/use-case/main-use-case/vehicle-hist/helper.go create mode 100644 internal/use-case/main-use-case/vehicle-hist/lib.go create mode 100644 internal/use-case/main-use-case/vehicle-hist/middleware-runner.go create mode 100644 internal/use-case/main-use-case/vehicle-hist/middleware.go create mode 100644 internal/use-case/main-use-case/vehicle-hist/tycovar.go diff --git a/internal/domain/main-entities/vehicle-hist/dto.go b/internal/domain/main-entities/vehicle-hist/dto.go new file mode 100644 index 00000000..252e119f --- /dev/null +++ b/internal/domain/main-entities/vehicle-hist/dto.go @@ -0,0 +1,64 @@ +package vehicle_hist + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + ev "simrs-vx/internal/domain/main-entities/vehicle" + ercm "simrs-vx/internal/domain/references/common" + "time" +) + +type CreateDto struct { + Vehicle ev.Vehicle + Crud_Code ercm.CrudCode `json:"crud_code"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Vehicle_Id *uint `json:"vehicle-id"` + Crud_Code ercm.CrudCode `json:"crud_code"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Includes string `json:"includes"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Vehicle_Id *uint `json:"vehicle_id"` + Vehicle *ev.Vehicle `json:"vehicle,omitempty" gorm:"foreignKey:Vehicle_Id;references:Id"` + Date *time.Time `json:"date"` + Data *string `json:"data"` + Crud_Code *ercm.CrudCode `json:"crud_code"` +} + +func (d VehicleHist) ToResponse() ResponseDto { + resp := ResponseDto{ + Vehicle_Id: d.Vehicle_Id, + Vehicle: d.Vehicle, + Date: d.Date, + Data: d.Data, + Crud_Code: d.Crud_Code, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []VehicleHist) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/domain/main-entities/vehicle/dto.go b/internal/domain/main-entities/vehicle/dto.go index 5ee0b8d9..d232126b 100644 --- a/internal/domain/main-entities/vehicle/dto.go +++ b/internal/domain/main-entities/vehicle/dto.go @@ -3,7 +3,6 @@ package vehicle import ( ecore "simrs-vx/internal/domain/base-entities/core" ercl "simrs-vx/internal/domain/references/clinical" - ercm "simrs-vx/internal/domain/references/common" ) type CreateDto struct { @@ -14,11 +13,6 @@ type CreateDto struct { AvailableStatus bool `json:"availableStatus"` } -type InsertVehicleHist struct { - Vehicle - Crud_Code ercm.CrudCode `json:"crud_code"` -} - type ReadListDto struct { FilterDto Includes string `json:"includes"` diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index d0762780..5123b86b 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -88,6 +88,7 @@ import ( unit "simrs-vx/internal/interface/main-handler/unit" uom "simrs-vx/internal/interface/main-handler/uom" vehicle "simrs-vx/internal/interface/main-handler/vehicle" + vehiclehist "simrs-vx/internal/interface/main-handler/vehicle-hist" district "simrs-vx/internal/interface/main-handler/district" postalregion "simrs-vx/internal/interface/main-handler/postal-region" @@ -299,6 +300,7 @@ func SetRoutes() http.Handler { hc.RegCrud(r, "/v1/subspecialist", subspecialist.O) hc.RegCrud(r, "/v1/mcu-sub-src", mcusubsrc.O) hc.RegCrud(r, "/v1/vehicle", vehicle.O) + hc.RegCrud(r, "/v1/vehicle-hist", vehiclehist.O) hc.RegCrud(r, "/v1/village", village.O) hc.RegCrud(r, "/v1/district", district.O) diff --git a/internal/interface/main-handler/vehicle-hist/handler.go b/internal/interface/main-handler/vehicle-hist/handler.go new file mode 100644 index 00000000..312f5791 --- /dev/null +++ b/internal/interface/main-handler/vehicle-hist/handler.go @@ -0,0 +1,42 @@ +package vehiclehist + +import ( + "net/http" + + e "simrs-vx/internal/domain/main-entities/vehicle-hist" + u "simrs-vx/internal/use-case/main-use-case/vehicle-hist" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) {} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := e.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := u.ReadList(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { + id := rw.ValidateInt(w, "id", r.PathValue("id")) + if id <= 0 { + return + } + + dto := e.ReadDetailDto{} + sf.UrlQueryParam(&dto, *r.URL) + + dto.Id = uint16(id) + res, err := u.ReadDetail(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) Update(w http.ResponseWriter, r *http.Request) {} + +func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) {} diff --git a/internal/use-case/main-use-case/vehicle-hist/case.go b/internal/use-case/main-use-case/vehicle-hist/case.go new file mode 100644 index 00000000..f382e6d4 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle-hist/case.go @@ -0,0 +1,116 @@ +package vehiclehist + +import ( + "strconv" + + "gorm.io/gorm" + + vh "simrs-vx/internal/domain/main-entities/vehicle-hist" + 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" +) + +const source = "vehicle-hist" + +func ReadList(input vh.ReadListDto) (*d.Data, error) { + var data *vh.VehicleHist + var dataList []vh.VehicleHist + var metaList *vh.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: vh.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input vh.ReadDetailDto) (*d.Data, error) { + var data *vh.VehicleHist + 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 +} diff --git a/internal/use-case/main-use-case/vehicle-hist/helper.go b/internal/use-case/main-use-case/vehicle-hist/helper.go new file mode 100644 index 00000000..634bf465 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle-hist/helper.go @@ -0,0 +1,25 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package vehiclehist + +import ( + "encoding/json" + vh "simrs-vx/internal/domain/main-entities/vehicle-hist" + "time" +) + +func setData(input vh.CreateDto, data *vh.VehicleHist) { + now := time.Now() + data.Vehicle_Id = &input.Vehicle.Id + data.Date = &now + data.Data = toStringPtr(input.Vehicle) + data.Crud_Code = &input.Crud_Code +} + +func toStringPtr(v any) *string { + b, _ := json.Marshal(v) + s := string(b) + return &s +} diff --git a/internal/use-case/main-use-case/vehicle-hist/lib.go b/internal/use-case/main-use-case/vehicle-hist/lib.go new file mode 100644 index 00000000..6d0ffaae --- /dev/null +++ b/internal/use-case/main-use-case/vehicle-hist/lib.go @@ -0,0 +1,96 @@ +package vehiclehist + +import ( + "errors" + vh "simrs-vx/internal/domain/main-entities/vehicle-hist" + pu "simrs-vx/pkg/use-case-helper" + + plh "simrs-vx/pkg/lib-helper" + pl "simrs-vx/pkg/logger" + + dg "github.com/karincake/apem/db-gorm-pg" + gh "github.com/karincake/getuk" + "gorm.io/gorm" +) + +func CreateData(input vh.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*vh.VehicleHist, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := vh.VehicleHist{} + 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 vh.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]vh.VehicleHist, *vh.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []vh.VehicleHist{} + pagination := gh.Pagination{} + count := int64(0) + meta := vh.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&vh.VehicleHist{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Order("\"CreatedAt\" DESC") + + if err := tx.Find(&data).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input vh.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*vh.VehicleHist, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := vh.VehicleHist{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx. + Scopes(gh.Preload(input.Includes)). + First(&data, input.Id).Error; err != nil { + + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} diff --git a/internal/use-case/main-use-case/vehicle-hist/middleware-runner.go b/internal/use-case/main-use-case/vehicle-hist/middleware-runner.go new file mode 100644 index 00000000..8f214371 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle-hist/middleware-runner.go @@ -0,0 +1,103 @@ +package vehiclehist + +import ( + vh "simrs-vx/internal/domain/main-entities/vehicle-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 *vh.CreateDto, data *vh.VehicleHist) 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 *vh.ReadListDto, data *vh.VehicleHist) 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 *vh.ReadDetailDto, data *vh.VehicleHist) 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 *vh.ReadDetailDto, data *vh.VehicleHist) 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 *vh.ReadDetailDto, data *vh.VehicleHist) 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/vehicle-hist/middleware.go b/internal/use-case/main-use-case/vehicle-hist/middleware.go new file mode 100644 index 00000000..8bcad403 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle-hist/middleware.go @@ -0,0 +1,9 @@ +package vehiclehist + +// 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/vehicle-hist/tycovar.go b/internal/use-case/main-use-case/vehicle-hist/tycovar.go new file mode 100644 index 00000000..f156a032 --- /dev/null +++ b/internal/use-case/main-use-case/vehicle-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 vehiclehist + +import ( + "gorm.io/gorm" + + vh "simrs-vx/internal/domain/main-entities/vehicle-hist" +) + +type createMw struct { + Name string + Func func(input *vh.CreateDto, data *vh.VehicleHist, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *vh.ReadListDto, data *vh.VehicleHist, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *vh.ReadDetailDto, data *vh.VehicleHist, 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/vehicle/case.go b/internal/use-case/main-use-case/vehicle/case.go index d07cc7a4..1c2e8d14 100644 --- a/internal/use-case/main-use-case/vehicle/case.go +++ b/internal/use-case/main-use-case/vehicle/case.go @@ -10,6 +10,9 @@ import ( pl "simrs-vx/pkg/logger" pu "simrs-vx/pkg/use-case-helper" + evh "simrs-vx/internal/domain/main-entities/vehicle-hist" + uvh "simrs-vx/internal/use-case/main-use-case/vehicle-hist" + dg "github.com/karincake/apem/db-gorm-pg" d "github.com/karincake/dodol" ) @@ -48,13 +51,13 @@ func Create(input v.CreateDto) (*d.Data, error) { } // Set data for vehicle hist - vehicleHistData := v.InsertVehicleHist{ + vehicleHistData := evh.CreateDto{ Crud_Code: rcm.CCCreate, Vehicle: data, } // Insert Vehicle Hist - if err := InsertVehicleHist(vehicleHistData, &event, tx); err != nil { + if _, err := uvh.CreateData(vehicleHistData, &event, tx); err != nil { return err } @@ -161,13 +164,13 @@ func ReadDetail(input v.ReadDetailDto) (*d.Data, error) { } // Set data for vehicle hist - vehicleHistData := v.InsertVehicleHist{ + vehicleHistData := evh.CreateDto{ Crud_Code: rcm.CCRead, Vehicle: *data, } // Insert Vehicle Hist - if err := InsertVehicleHist(vehicleHistData, &event, tx); err != nil { + if _, err := uvh.CreateData(vehicleHistData, &event, tx); err != nil { return err } @@ -231,13 +234,13 @@ func Update(input v.UpdateDto) (*d.Data, error) { } // Set data for vehicle hist - vehicleHistData := v.InsertVehicleHist{ + vehicleHistData := evh.CreateDto{ Crud_Code: rcm.CCUpdate, Vehicle: *data, } // Insert Vehicle Hist - if err := InsertVehicleHist(vehicleHistData, &event, tx); err != nil { + if _, err := uvh.CreateData(vehicleHistData, &event, tx); err != nil { return err } @@ -299,13 +302,13 @@ func Delete(input v.DeleteDto) (*d.Data, error) { } // Set data for vehicle hist - vehicleHistData := v.InsertVehicleHist{ + vehicleHistData := evh.CreateDto{ Crud_Code: rcm.CCDelete, Vehicle: *data, } // Insert Vehicle Hist - if err := InsertVehicleHist(vehicleHistData, &event, tx); err != nil { + if _, err := uvh.CreateData(vehicleHistData, &event, tx); err != nil { return err } diff --git a/internal/use-case/main-use-case/vehicle/helper.go b/internal/use-case/main-use-case/vehicle/helper.go index adc7f91b..1bcb384d 100644 --- a/internal/use-case/main-use-case/vehicle/helper.go +++ b/internal/use-case/main-use-case/vehicle/helper.go @@ -5,14 +5,10 @@ Any functions that are used internally by the use-case package vehicle import ( - "encoding/json" "errors" v "simrs-vx/internal/domain/main-entities/vehicle" - vh "simrs-vx/internal/domain/main-entities/vehicle-hist" rcl "simrs-vx/internal/domain/references/clinical" pl "simrs-vx/pkg/logger" - - "time" ) func setData[T *v.CreateDto | *v.UpdateDto](input T, data *v.Vehicle) { @@ -31,38 +27,12 @@ func setData[T *v.CreateDto | *v.UpdateDto](input T, data *v.Vehicle) { data.AvailableStatus = inputSrc.AvailableStatus } -func setDataVehicleHist(input v.InsertVehicleHist, data *vh.VehicleHist) { - now := time.Now() - data.Vehicle_Id = &input.Id - data.Date = &now - data.Data = toStringPtr(input.Vehicle) - data.Crud_Code = &input.Crud_Code -} - -func toStringPtr(v any) *string { - b, _ := json.Marshal(v) - s := string(b) - return &s -} - func validateRequest(input v.CreateDto, event *pl.Event) error { event.Status = "failed" event.ErrInfo = pl.ErrorInfo{ Code: "data-validation-fail", } - if input.Type_Code == "" { - event.ErrInfo.Detail = "Field 'type_code' is required" - event.ErrInfo.Raw = errors.New("type_code is required") - return pl.SetLogError(event, input) - } - - if input.PoliceNumber == nil { - event.ErrInfo.Detail = "Field 'policeNumber' is required" - event.ErrInfo.Raw = errors.New("policeNumber is required") - return pl.SetLogError(event, input) - } - switch input.Type_Code { case rcl.VTCAmbulance, rcl.VTCTransport, rcl.VTCHearse: return nil diff --git a/internal/use-case/main-use-case/vehicle/lib.go b/internal/use-case/main-use-case/vehicle/lib.go index aef2d336..d5c13a8a 100644 --- a/internal/use-case/main-use-case/vehicle/lib.go +++ b/internal/use-case/main-use-case/vehicle/lib.go @@ -3,7 +3,6 @@ package vehicle import ( "errors" v "simrs-vx/internal/domain/main-entities/vehicle" - vh "simrs-vx/internal/domain/main-entities/vehicle-hist" pu "simrs-vx/pkg/use-case-helper" plh "simrs-vx/pkg/lib-helper" @@ -35,27 +34,6 @@ func CreateData(input v.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*v.Vehicle return &data, nil } -func InsertVehicleHist(input v.InsertVehicleHist, event *pl.Event, dbx ...*gorm.DB) error { - pl.SetLogInfo(event, nil, "started", "DBCreate") - - data := vh.VehicleHist{} - setDataVehicleHist(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 plh.HandleCreateError(input, event, err) - } - - pl.SetLogInfo(event, nil, "complete") - return nil -} - func ReadListData(input v.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]v.Vehicle, *v.MetaDto, error) { pl.SetLogInfo(event, input, "started", "DBReadList") data := []v.Vehicle{} From f4dbe1322564dfb9415daa91f174ae125428a6a6 Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 20 Oct 2025 13:21:59 +0700 Subject: [PATCH 11/15] crud ambulance-transport-req --- .../ambulance-transport-req/dto.go | 137 ++++++ .../ambulance-transport-req/handler.go | 72 ++++ .../interface/main-handler/main-handler.go | 2 + .../ambulance-transport-req/case.go | 398 ++++++++++++++++++ .../ambulance-transport-req/helper.go | 81 ++++ .../ambulance-transport-req/lib.go | 144 +++++++ .../middleware-runner.go | 103 +++++ .../ambulance-transport-req/middleware.go | 9 + .../ambulance-transport-req/tycovar.go | 44 ++ 9 files changed, 990 insertions(+) create mode 100644 internal/domain/main-entities/ambulance-transport-req/dto.go create mode 100644 internal/interface/main-handler/ambulance-transport-req/handler.go create mode 100644 internal/use-case/main-use-case/ambulance-transport-req/case.go create mode 100644 internal/use-case/main-use-case/ambulance-transport-req/helper.go create mode 100644 internal/use-case/main-use-case/ambulance-transport-req/lib.go create mode 100644 internal/use-case/main-use-case/ambulance-transport-req/middleware-runner.go create mode 100644 internal/use-case/main-use-case/ambulance-transport-req/middleware.go create mode 100644 internal/use-case/main-use-case/ambulance-transport-req/tycovar.go diff --git a/internal/domain/main-entities/ambulance-transport-req/dto.go b/internal/domain/main-entities/ambulance-transport-req/dto.go new file mode 100644 index 00000000..f832b955 --- /dev/null +++ b/internal/domain/main-entities/ambulance-transport-req/dto.go @@ -0,0 +1,137 @@ +package ambulance_transport_req + +import ( + ecore "simrs-vx/internal/domain/base-entities/core" + eds "simrs-vx/internal/domain/main-entities/district" + ept "simrs-vx/internal/domain/main-entities/patient" + epr "simrs-vx/internal/domain/main-entities/province" + erg "simrs-vx/internal/domain/main-entities/regency" + evl "simrs-vx/internal/domain/main-entities/village" + eren "simrs-vx/internal/domain/references/encounter" + erp "simrs-vx/internal/domain/references/person" + "time" +) + +type CreateDto struct { + Patient_Id *uint `json:"patient_id"` + Diagnoses *string `json:"diagnoses" validate:"maxLength=1024"` + RequestDate *time.Time `json:"requestDate"` + UsageDate *time.Time `json:"usageDate"` + + Address *string `json:"address" validate:"maxLength=100"` + RtRw *string `json:"rtRw" validate:"maxLength=10"` + + Province_Code *string `json:"province_code" validate:"maxLength=2"` + Regency_Code *string `json:"regency_code" validate:"maxLength=4"` + District_Code *string `json:"district_code" validate:"maxLength=6"` + Village_Code *string `json:"village_code" validate:"maxLength=10"` + + Facility_Code *eren.AmbulanceFacilityCode `json:"facility_code" validate:"maxLength=10"` + Needs_Code *eren.AmbulanceNeedsCode `json:"needs_code" validate:"maxLength=10"` + Contact_Name *string `json:"contact_name" validate:"maxLength=100"` + Contact_Relationship_Code *erp.RelationshipCode `json:"contact_relationship_code" validate:"maxLength=10"` + Contact_PhoneNumber *string `json:"contact_phoneNumber" validate:"maxLength=20"` +} + +type ReadListDto struct { + FilterDto + Includes string `json:"includes"` + Pagination ecore.Pagination +} + +type FilterDto struct { + Patient_Id *uint `json:"patient-id"` + Facility_Code *eren.AmbulanceFacilityCode `json:"facility-code"` + Needs_Code *eren.AmbulanceNeedsCode `json:"needs-code"` + + Province_Code *string `json:"province-code"` + Regency_Code *string `json:"regency-code"` + District_Code *string `json:"district-code"` + Village_Code *string `json:"village-code"` +} + +type ReadDetailDto struct { + Id uint16 `json:"id"` + Includes string `json:"includes"` +} + +type UpdateDto struct { + Id uint16 `json:"id"` + CreateDto +} + +type DeleteDto struct { + Id uint16 `json:"id"` +} + +type MetaDto struct { + PageNumber int `json:"page_number"` + PageSize int `json:"page_size"` + Count int `json:"count"` +} + +type ResponseDto struct { + ecore.Main + Patient_Id *uint `json:"patient_id"` + Patient *ept.Patient `json:"patient,omitempty"` + + Diagnoses *string `json:"diagnoses"` + RequestDate *time.Time `json:"requestDate"` + UsageDate *time.Time `json:"usageDate"` + + Address *string `json:"address"` + RtRw *string `json:"rtRw"` + + Province_Code *string `json:"province_code"` + Province *epr.Province `json:"province,omitempty"` + + Regency_Code *string `json:"regency_code"` + Regency *erg.Regency `json:"regency,omitempty"` + + District_Code *string `json:"district_code"` + District *eds.District `json:"district,omitempty"` + + Village_Code *string `json:"village_code"` + Village *evl.Village `json:"village,omitempty"` + + Facility_Code *eren.AmbulanceFacilityCode `json:"facility_code"` + Needs_Code *eren.AmbulanceNeedsCode `json:"needs_code"` + Contact_Name *string `json:"contact_name"` + Contact_Relationship_Code *erp.RelationshipCode `json:"contact_relationship_code"` + Contact_PhoneNumber *string `json:"contact_phoneNumber"` +} + +func (d AmbulanceTransportReq) ToResponse() ResponseDto { + resp := ResponseDto{ + Patient_Id: d.Patient_Id, + Patient: d.Patient, + Diagnoses: d.Diagnoses, + RequestDate: d.RequestData, + UsageDate: d.UsageDate, + Address: d.Address, + RtRw: d.RtRw, + Province_Code: d.Province_Code, + Province: d.Province, + Regency_Code: d.Regency_Code, + Regency: d.Regency, + District_Code: d.District_Code, + District: d.District, + Village_Code: d.Village_Code, + Village: d.Village, + Facility_Code: d.Facility_Code, + Needs_Code: d.Needs_Code, + Contact_Name: d.Contact_Name, + Contact_Relationship_Code: d.Contact_Relationship_Code, + Contact_PhoneNumber: d.Contact_PhoneNumber, + } + resp.Main = d.Main + return resp +} + +func ToResponseList(data []AmbulanceTransportReq) []ResponseDto { + resp := make([]ResponseDto, len(data)) + for i, u := range data { + resp[i] = u.ToResponse() + } + return resp +} diff --git a/internal/interface/main-handler/ambulance-transport-req/handler.go b/internal/interface/main-handler/ambulance-transport-req/handler.go new file mode 100644 index 00000000..25f22f9f --- /dev/null +++ b/internal/interface/main-handler/ambulance-transport-req/handler.go @@ -0,0 +1,72 @@ +package ambulance_transport_req + +import ( + "net/http" + atr "simrs-vx/internal/domain/main-entities/ambulance-transport-req" + uatr "simrs-vx/internal/use-case/main-use-case/ambulance-transport-req" + + rw "github.com/karincake/risoles" + sf "github.com/karincake/semprit" +) + +type myBase struct{} + +var O myBase + +func (obj myBase) Create(w http.ResponseWriter, r *http.Request) { + dto := atr.CreateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + + res, err := uatr.Create(dto) + rw.DataResponse(w, res, err) +} + +func (obj myBase) GetList(w http.ResponseWriter, r *http.Request) { + dto := atr.ReadListDto{} + sf.UrlQueryParam(&dto, *r.URL) + res, err := uatr.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 := atr.ReadDetailDto{} + sf.UrlQueryParam(&dto, *r.URL) + dto.Id = uint16(id) + + res, err := uatr.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 := atr.UpdateDto{} + if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { + return + } + dto.Id = uint16(id) + res, err := uatr.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 := atr.DeleteDto{} + dto.Id = uint16(id) + res, err := uatr.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 d141b469..0638e656 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -5,6 +5,7 @@ import ( /******************** main / transaction ********************/ adime "simrs-vx/internal/interface/main-handler/adime" + ambulancetransportrequest "simrs-vx/internal/interface/main-handler/ambulance-transport-req" auth "simrs-vx/internal/interface/main-handler/authentication" chemo "simrs-vx/internal/interface/main-handler/chemo" consultation "simrs-vx/internal/interface/main-handler/consultation" @@ -238,6 +239,7 @@ func SetRoutes() http.Handler { }) hc.RegCrud(r, "/v1/internal-reference", internalreference.O) + hc.RegCrud(r, "/v1/ambulance-transport-req", ambulancetransportrequest.O) /******************** actor ********************/ hc.RegCrud(r, "/v1/person", person.O) diff --git a/internal/use-case/main-use-case/ambulance-transport-req/case.go b/internal/use-case/main-use-case/ambulance-transport-req/case.go new file mode 100644 index 00000000..712d6241 --- /dev/null +++ b/internal/use-case/main-use-case/ambulance-transport-req/case.go @@ -0,0 +1,398 @@ +package ambulancetransport + +import ( + "errors" + "strconv" + + // main entities + eatr "simrs-vx/internal/domain/main-entities/ambulance-transport-req" + edis "simrs-vx/internal/domain/main-entities/district" + ep "simrs-vx/internal/domain/main-entities/patient" + epro "simrs-vx/internal/domain/main-entities/province" + ereg "simrs-vx/internal/domain/main-entities/regency" + evil "simrs-vx/internal/domain/main-entities/village" + + // main use case + udis "simrs-vx/internal/use-case/main-use-case/district" + up "simrs-vx/internal/use-case/main-use-case/patient" + upro "simrs-vx/internal/use-case/main-use-case/province" + ureg "simrs-vx/internal/use-case/main-use-case/regency" + uvil "simrs-vx/internal/use-case/main-use-case/village" + + pl "simrs-vx/pkg/logger" + pu "simrs-vx/pkg/use-case-helper" + + dg "github.com/karincake/apem/db-gorm-pg" + d "github.com/karincake/dodol" + "gorm.io/gorm" +) + +const source = "ambulance-transport-req" + +func Create(input eatr.CreateDto) (*d.Data, error) { + data := eatr.AmbulanceTransportReq{} + event := pl.Event{ + Feature: "Create", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "create") + + // validate request code value + if err := validateRequestCode(&input, &event); err != nil { + return nil, err + } + + // validateForeignKeys ensures that all foreign key references in the given input + // exist in their respective tables. It returns an error if any referenced record + // is missing or invalid. + if err := validateForeignKeys(input, &event); err != nil { + return nil, err + } + + err := dg.I.Transaction(func(tx *gorm.DB) error { + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunCreateMiddleware(createPreMw, &input, &data); err != nil { + return err + } + + if resData, err := CreateData(input, &event, tx); err != nil { + return err + } else { + data = *resData + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunCreateMiddleware(createPostMw, &input, &data); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.II{ + "source": source, + "structure": "single-data", + "status": "created", + }, + Data: data.ToResponse(), + }, nil +} + +func validateForeignKeys(input eatr.CreateDto, event *pl.Event) (err error) { + // Validate Patient Id + if input.Patient_Id != nil { + _, err = up.ReadDetail(ep.ReadDetailDto{Id: uint16(*input.Patient_Id)}) + if err != nil { + return err + } + } + + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-not-found", + } + + // Validate Province Code + if input.Province_Code != nil { + dataProvince, err := upro.ReadList(epro.ReadListDto{FilterDto: epro.FilterDto{Code: *input.Province_Code}}) + if err != nil { + return err + } + + if list, ok := dataProvince.Data.([]epro.ResponseDto); ok { + if len(list) < 1 { + event.ErrInfo.Detail = "province_code not found" + event.ErrInfo.Raw = errors.New("province_code not found") + return pl.SetLogError(event, input) + } + } + } + + // Validate Regency Code + if input.Regency_Code != nil { + dataRegency, err := ureg.ReadList(ereg.ReadListDto{FilterDto: ereg.FilterDto{Code: *input.Regency_Code}}) + if err != nil { + return err + } + + if list, ok := dataRegency.Data.([]ereg.ResponseDto); ok { + if len(list) < 1 { + event.ErrInfo.Detail = "regency_code not found" + event.ErrInfo.Raw = errors.New("regency_code not found") + return pl.SetLogError(event, input) + } + } + } + + // Validate District Code + if input.District_Code != nil { + dataDistrict, err := udis.ReadList(edis.ReadListDto{FilterDto: edis.FilterDto{Code: *input.District_Code}}) + if err != nil { + return err + } + + if list, ok := dataDistrict.Data.([]edis.ResponseDto); ok { + if len(list) < 1 { + event.ErrInfo.Detail = "district_code not found" + event.ErrInfo.Raw = errors.New("district_code not found") + return pl.SetLogError(event, input) + } + } + } + + // Validate Village Code + if input.Village_Code != nil { + dataVillage, err := uvil.ReadList(evil.ReadListDto{FilterDto: evil.FilterDto{Code: *input.Village_Code}}) + if err != nil { + return err + } + + if list, ok := dataVillage.Data.([]evil.ResponseDto); ok { + if len(list) < 1 { + event.ErrInfo.Detail = "village_code not found" + event.ErrInfo.Raw = errors.New("village_code not found") + return pl.SetLogError(event, input) + } + } + } + + return +} + +func ReadList(input eatr.ReadListDto) (*d.Data, error) { + var data *eatr.AmbulanceTransportReq + var dataList []eatr.AmbulanceTransportReq + var metaList *eatr.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: eatr.ToResponseList(dataList), + }, nil +} + +func ReadDetail(input eatr.ReadDetailDto) (*d.Data, error) { + var data *eatr.AmbulanceTransportReq + 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 eatr.UpdateDto) (*d.Data, error) { + rdDto := eatr.ReadDetailDto{Id: input.Id} + var data *eatr.AmbulanceTransportReq + var err error + + event := pl.Event{ + Feature: "Update", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "update") + + // validate request code value + if err := validateRequestCode(&input.CreateDto, &event); err != nil { + return nil, err + } + + // validateForeignKeys ensures that all foreign key references in the given input + // exist in their respective tables. It returns an error if any referenced record + // is missing or invalid. + if err := validateForeignKeys(input.CreateDto, &event); err != nil { + return nil, err + } + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := UpdateData(input, data, &event, tx); err != nil { + return err + } + + // Get Updated Data + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + pl.SetLogInfo(&event, nil, "complete") + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunUpdateMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "updated", + }, + Data: data.ToResponse(), + }, nil + +} + +func Delete(input eatr.DeleteDto) (*d.Data, error) { + rdDto := eatr.ReadDetailDto{Id: input.Id} + var data *eatr.AmbulanceTransportReq + var err error + + event := pl.Event{ + Feature: "Delete", + Source: source, + } + + // Start log + pl.SetLogInfo(&event, input, "started", "delete") + + err = dg.I.Transaction(func(tx *gorm.DB) error { + pl.SetLogInfo(&event, rdDto, "started", "DBReadDetail") + if data, err = ReadDetailData(rdDto, &event, tx); err != nil { + return err + } + + mwRunner := newMiddlewareRunner(&event, tx) + mwRunner.setMwType(pu.MWTPre) + // Run pre-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPreMw, &rdDto, data); err != nil { + return err + } + + if err := DeleteData(data, &event, tx); err != nil { + return err + } + + mwRunner.setMwType(pu.MWTPost) + // Run post-middleware + if err := mwRunner.RunDeleteMiddleware(readDetailPostMw, &rdDto, data); err != nil { + return err + } + + return nil + }) + + if err != nil { + return nil, err + } + + return &d.Data{ + Meta: d.IS{ + "source": source, + "structure": "single-data", + "status": "deleted", + }, + Data: data.ToResponse(), + }, nil +} diff --git a/internal/use-case/main-use-case/ambulance-transport-req/helper.go b/internal/use-case/main-use-case/ambulance-transport-req/helper.go new file mode 100644 index 00000000..950730e5 --- /dev/null +++ b/internal/use-case/main-use-case/ambulance-transport-req/helper.go @@ -0,0 +1,81 @@ +/* +DESCRIPTION: +Any functions that are used internally by the use-case +*/ +package ambulancetransport + +import ( + "errors" + atr "simrs-vx/internal/domain/main-entities/ambulance-transport-req" + eren "simrs-vx/internal/domain/references/encounter" + erp "simrs-vx/internal/domain/references/person" + pl "simrs-vx/pkg/logger" +) + +func setData[T *atr.CreateDto | *atr.UpdateDto](input T, data *atr.AmbulanceTransportReq) { + var inputSrc *atr.CreateDto + if inputT, ok := any(input).(*atr.CreateDto); ok { + inputSrc = inputT + } else { + inputTemp := any(input).(*atr.UpdateDto) + inputSrc = &inputTemp.CreateDto + } + + data.Patient_Id = inputSrc.Patient_Id + data.Diagnoses = inputSrc.Diagnoses + data.RequestData = inputSrc.RequestDate + data.UsageDate = inputSrc.UsageDate + + data.Address = inputSrc.Address + data.RtRw = inputSrc.RtRw + data.Province_Code = inputSrc.Province_Code + data.Regency_Code = inputSrc.Regency_Code + data.District_Code = inputSrc.District_Code + data.Village_Code = inputSrc.Village_Code + + data.Facility_Code = inputSrc.Facility_Code + data.Needs_Code = inputSrc.Needs_Code + data.Contact_Name = inputSrc.Contact_Name + data.Contact_Relationship_Code = inputSrc.Contact_Relationship_Code + data.Contact_PhoneNumber = inputSrc.Contact_PhoneNumber +} + +func validateRequestCode(i *atr.CreateDto, event *pl.Event) (err error) { + event.Status = "failed" + event.ErrInfo = pl.ErrorInfo{ + Code: "data-validation-fail", + } + + // validate facility code value + if i.Facility_Code != nil { + if *i.Facility_Code != eren.AFCStd && *i.Facility_Code != eren.AFCIcu { + event.ErrInfo.Detail = "facilty_code value is not recognized" + event.ErrInfo.Raw = errors.New("facilty_code value is not recognized") + return pl.SetLogError(event, i) + } + } + + // validate needs code value + if i.Needs_Code != nil { + if *i.Needs_Code != eren.ANCAssist && *i.Needs_Code != eren.ANCNonassist { + event.ErrInfo.Detail = "needs_code value is not recognized" + event.ErrInfo.Raw = errors.New("needs_code value is not recognized") + return pl.SetLogError(event, i) + } + } + + // validate contact relationship code value + if i.Contact_Relationship_Code != nil { + switch *i.Contact_Relationship_Code { + case erp.RCMother, erp.RCFather, erp.RCUncle, erp.RCAunt, + erp.RCSibling, erp.RCGdMother, erp.RCGdFather, erp.RCChild, + erp.RCNephew, erp.RCGdChild, erp.RCOther: + return + default: + event.ErrInfo.Detail = "contact_relationship_code value is not recognized" + event.ErrInfo.Raw = errors.New("contact_relationship_code value is not recognized") + return pl.SetLogError(event, i) + } + } + return +} diff --git a/internal/use-case/main-use-case/ambulance-transport-req/lib.go b/internal/use-case/main-use-case/ambulance-transport-req/lib.go new file mode 100644 index 00000000..e8555a6f --- /dev/null +++ b/internal/use-case/main-use-case/ambulance-transport-req/lib.go @@ -0,0 +1,144 @@ +package ambulancetransport + +import ( + "errors" + atr "simrs-vx/internal/domain/main-entities/ambulance-transport-req" + 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 atr.CreateDto, event *pl.Event, dbx ...*gorm.DB) (*atr.AmbulanceTransportReq, error) { + pl.SetLogInfo(event, nil, "started", "DBCreate") + + data := atr.AmbulanceTransportReq{} + 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 atr.ReadListDto, event *pl.Event, dbx ...*gorm.DB) ([]atr.AmbulanceTransportReq, *atr.MetaDto, error) { + pl.SetLogInfo(event, input, "started", "DBReadList") + data := []atr.AmbulanceTransportReq{} + pagination := gh.Pagination{} + count := int64(0) + meta := atr.MetaDto{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + tx = tx. + Model(&atr.AmbulanceTransportReq{}). + Scopes(gh.Preload(input.Includes)). + Scopes(gh.Filter(input.FilterDto)). + Count(&count). + Scopes(gh.Paginate(input, &pagination)). + Order("\"CreatedAt\" DESC") + + if err := tx.Find(&data).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, &meta, nil + } + return nil, nil, plh.HandleListError(input, event, err) + } + + meta.Count = int(count) + meta.PageNumber = pagination.PageNumber + meta.PageSize = pagination.PageSize + + pl.SetLogInfo(event, nil, "complete") + return data, &meta, nil +} + +func ReadDetailData(input atr.ReadDetailDto, event *pl.Event, dbx ...*gorm.DB) (*atr.AmbulanceTransportReq, error) { + pl.SetLogInfo(event, input, "started", "DBReadDetail") + data := atr.AmbulanceTransportReq{} + + var tx *gorm.DB + if len(dbx) > 0 { + tx = dbx[0] + } else { + tx = dg.I + } + + if err := tx. + Scopes(gh.Preload(input.Includes)). + First(&data, input.Id). + Error; err != nil { + + if processedErr := pu.HandleReadError(err, event, source, input.Id, data); processedErr != nil { + return nil, processedErr + } + } + + pl.SetLogInfo(event, nil, "complete") + return &data, nil +} + +func UpdateData(input atr.UpdateDto, data *atr.AmbulanceTransportReq, 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 *atr.AmbulanceTransportReq, 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/ambulance-transport-req/middleware-runner.go b/internal/use-case/main-use-case/ambulance-transport-req/middleware-runner.go new file mode 100644 index 00000000..b98c497d --- /dev/null +++ b/internal/use-case/main-use-case/ambulance-transport-req/middleware-runner.go @@ -0,0 +1,103 @@ +package ambulancetransport + +import ( + atr "simrs-vx/internal/domain/main-entities/ambulance-transport-req" + 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 *atr.CreateDto, data *atr.AmbulanceTransportReq) 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 *atr.ReadListDto, data *atr.AmbulanceTransportReq) 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 *atr.ReadDetailDto, data *atr.AmbulanceTransportReq) 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 *atr.ReadDetailDto, data *atr.AmbulanceTransportReq) 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 *atr.ReadDetailDto, data *atr.AmbulanceTransportReq) 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/ambulance-transport-req/middleware.go b/internal/use-case/main-use-case/ambulance-transport-req/middleware.go new file mode 100644 index 00000000..847aa3dd --- /dev/null +++ b/internal/use-case/main-use-case/ambulance-transport-req/middleware.go @@ -0,0 +1,9 @@ +package ambulancetransport + +// 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/ambulance-transport-req/tycovar.go b/internal/use-case/main-use-case/ambulance-transport-req/tycovar.go new file mode 100644 index 00000000..5d3ac027 --- /dev/null +++ b/internal/use-case/main-use-case/ambulance-transport-req/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 ambulancetransport + +import ( + "gorm.io/gorm" + + atr "simrs-vx/internal/domain/main-entities/ambulance-transport-req" +) + +type createMw struct { + Name string + Func func(input *atr.CreateDto, data *atr.AmbulanceTransportReq, tx *gorm.DB) error +} + +type readListMw struct { + Name string + Func func(input *atr.ReadListDto, data *atr.AmbulanceTransportReq, tx *gorm.DB) error +} + +type readDetailMw struct { + Name string + Func func(input *atr.ReadDetailDto, data *atr.AmbulanceTransportReq, 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 e1c441bdb0153c8c30d6adb81335ca937636a0f2 Mon Sep 17 00:00:00 2001 From: vanilia Date: Mon, 20 Oct 2025 14:21:03 +0700 Subject: [PATCH 12/15] update entitiy ambulance transport request --- internal/domain/main-entities/ambulance-transport-req/dto.go | 2 +- .../use-case/main-use-case/ambulance-transport-req/helper.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/domain/main-entities/ambulance-transport-req/dto.go b/internal/domain/main-entities/ambulance-transport-req/dto.go index f832b955..25bbd01b 100644 --- a/internal/domain/main-entities/ambulance-transport-req/dto.go +++ b/internal/domain/main-entities/ambulance-transport-req/dto.go @@ -106,7 +106,7 @@ func (d AmbulanceTransportReq) ToResponse() ResponseDto { Patient_Id: d.Patient_Id, Patient: d.Patient, Diagnoses: d.Diagnoses, - RequestDate: d.RequestData, + RequestDate: d.RequestDate, UsageDate: d.UsageDate, Address: d.Address, RtRw: d.RtRw, diff --git a/internal/use-case/main-use-case/ambulance-transport-req/helper.go b/internal/use-case/main-use-case/ambulance-transport-req/helper.go index 950730e5..3c739eb0 100644 --- a/internal/use-case/main-use-case/ambulance-transport-req/helper.go +++ b/internal/use-case/main-use-case/ambulance-transport-req/helper.go @@ -23,7 +23,7 @@ func setData[T *atr.CreateDto | *atr.UpdateDto](input T, data *atr.AmbulanceTran data.Patient_Id = inputSrc.Patient_Id data.Diagnoses = inputSrc.Diagnoses - data.RequestData = inputSrc.RequestDate + data.RequestDate = inputSrc.RequestDate data.UsageDate = inputSrc.UsageDate data.Address = inputSrc.Address From 6274c92eff902b7387731d8a9de8d84546da0e8f Mon Sep 17 00:00:00 2001 From: dpurbosakti Date: Wed, 22 Oct 2025 11:02:54 +0700 Subject: [PATCH 13/15] fixing error sql script --- .../migrations/20251019093915.sql | 30 +++++++++---------- cmd/main-migration/migrations/atlas.sum | 12 ++++---- internal/domain/main-entities/user/dto.go | 2 +- .../main-use-case/encounter/helper.go | 2 +- 4 files changed, 24 insertions(+), 22 deletions(-) diff --git a/cmd/main-migration/migrations/20251019093915.sql b/cmd/main-migration/migrations/20251019093915.sql index 31ac3655..add924ea 100644 --- a/cmd/main-migration/migrations/20251019093915.sql +++ b/cmd/main-migration/migrations/20251019093915.sql @@ -1,15 +1,15 @@ --- Rename a column from "Position_Code" to "ContractPosition_Code" -ALTER TABLE "public"."User" RENAME COLUMN "Position_Code" TO "ContractPosition_Code"; --- Create "Intern" table -CREATE TABLE "public"."Intern" ( - "Id" bigserial NOT NULL, - "CreatedAt" timestamptz NULL, - "UpdatedAt" timestamptz NULL, - "DeletedAt" timestamptz NULL, - "Person_Id" bigint NULL, - "Position_Code" character varying(20) NULL, - "User_Id" bigint NULL, - PRIMARY KEY ("Id"), - CONSTRAINT "fk_Intern_Person" FOREIGN KEY ("Person_Id") REFERENCES "public"."Person" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION, - CONSTRAINT "fk_Intern_User" FOREIGN KEY ("User_Id") REFERENCES "public"."User" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION -); +-- -- Rename a column from "Position_Code" to "ContractPosition_Code" +-- ALTER TABLE "public"."User" RENAME COLUMN "Position_Code" TO "ContractPosition_Code"; +-- -- Create "Intern" table +-- CREATE TABLE "public"."Intern" ( +-- "Id" bigserial NOT NULL, +-- "CreatedAt" timestamptz NULL, +-- "UpdatedAt" timestamptz NULL, +-- "DeletedAt" timestamptz NULL, +-- "Person_Id" bigint NULL, +-- "Position_Code" character varying(20) NULL, +-- "User_Id" bigint NULL, +-- PRIMARY KEY ("Id"), +-- CONSTRAINT "fk_Intern_Person" FOREIGN KEY ("Person_Id") REFERENCES "public"."Person" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION, +-- CONSTRAINT "fk_Intern_User" FOREIGN KEY ("User_Id") REFERENCES "public"."User" ("Id") ON UPDATE NO ACTION ON DELETE NO ACTION +-- ); diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index e2b20c67..d28421e6 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,4 +1,4 @@ -h1:5zzKM5mZWg7boY0OYotYXr6yCVyHsJYrwx+WHnYZ25A= +h1:nkA4yHX6MSnA6zb6xACKXMBMwaAu4BRoT1Cs4DW3z80= 20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= 20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= 20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= @@ -49,7 +49,9 @@ h1:5zzKM5mZWg7boY0OYotYXr6yCVyHsJYrwx+WHnYZ25A= 20251016062912.sql h1:ACNn0fe+EMqUt3hoY+Dr3uqAV/QICBa1+mIW7fUc9Fk= 20251017060617.sql h1:4T3t9ifWrEQTPMSM0XJ98pF7Qdt+UfgtMui17bhrnWI= 20251017082207.sql h1:8vLG1l/saRRMHXkyA4nelJyjaSddhZd6r7R+Uo4JS/c= -20251019093915.sql h1:vWqTEc9bZpnAj53DLNKaTYbEoiSqdQJ9Mx4Qv9zmXU8= -20251020062553.sql h1:TMlFzG+6P/rqIDzF6y8OLrWy4vusEDonVHhfRwRhrqw= -20251021041042.sql h1:jeEBAgnfPZpkfe5wDE7oG37JpF74IXAeYCbAx7zzRro= -20251021075552.sql h1:vokB4CkMSWJ3eexhYRfuX0PQZ4AYU5Yc3bFM+2R0Wuk= +20251018032635.sql h1:2xey5gnO3y2XSOrU8MLlIfoylPKbRGDRtHDD07B3MbQ= +20251018040322.sql h1:k/pdNiSoT8zFPqNQ/avOD0vYkNh3BTD64IlHrfVXr7I= +20251019093915.sql h1:hFcQE0y+p5dZiVwePGsRGto9m/q6kJNiUZbVDd5Rnjk= +20251020062553.sql h1:Iw7hulcm5iRQlfW+ygA4iTPxLqkxx6h9vXMXEwUAHKs= +20251021041042.sql h1:wMgSivBV2A0NDcsLmKGIp0kMcVh2IODSG9b4dgzCaOM= +20251021075552.sql h1:8gfSMAglflNO6L0sSzxFNEubYN8/O4thT7OQT+WH+3M= diff --git a/internal/domain/main-entities/user/dto.go b/internal/domain/main-entities/user/dto.go index 55a53686..767cef3e 100644 --- a/internal/domain/main-entities/user/dto.go +++ b/internal/domain/main-entities/user/dto.go @@ -89,7 +89,7 @@ type EmployeUpdateDto struct { Person_Id *uint `json:"-"` Division_Code *string `json:"division_code"` Number *string `json:"number" validate:"maxLength=20"` - Position_Code erg.EmployeePosisitionCode `json:"status_code" validate:"maxLength=10"` + Position_Code erg.EmployeePosisitionCode `json:"position_code" validate:"maxLength=20"` Status_Code erc.ActiveStatusCode `json:"status_code" validate:"maxLength=10"` } diff --git a/internal/use-case/main-use-case/encounter/helper.go b/internal/use-case/main-use-case/encounter/helper.go index 7cb72938..34e076f1 100644 --- a/internal/use-case/main-use-case/encounter/helper.go +++ b/internal/use-case/main-use-case/encounter/helper.go @@ -77,7 +77,7 @@ func setDataUpdate(src e.UpdateDto, dst *e.Encounter) { } func setDataDischarge(src e.DischargeDto, dst *e.Encounter) { - dst.DischargeMethod_Code = src.DischargeMethod_Code + dst.Discharge_Method_Code = src.DischargeMethod_Code dst.EarlyEducation = src.EarlyEducation dst.MedicalDischargeEducation = src.MedicalDischargeEducation dst.AdmDischargeEducation = src.AdmDischargeEducation From 4fb856ed2511de9c4942f76b99c9b2c223a97d49 Mon Sep 17 00:00:00 2001 From: vanilia Date: Wed, 22 Oct 2025 11:39:03 +0700 Subject: [PATCH 14/15] wip --- .../references/organization/organization.go | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/internal/domain/references/organization/organization.go b/internal/domain/references/organization/organization.go index 93eabe63..0309cbee 100644 --- a/internal/domain/references/organization/organization.go +++ b/internal/domain/references/organization/organization.go @@ -1,30 +1,30 @@ package organization type ( - ContractPositionCode string - EmployeePosisitionCode string - InternPosisitionCode string - ItemGroupCode string - InfraGroupCode string - UnitTypeCode string - DoctorFeeTypeCode string + ContractPositionCode string + EmployeePositionCode string + InternPosisitionCode string + ItemGroupCode string + InfraGroupCode string + UnitTypeCode string + DoctorFeeTypeCode string ) const ( CSCEmp ContractPositionCode = "employee" // Pegawai CSCInt ContractPositionCode = "intern" // PPDS - EPCReg EmployeePosisitionCode = "registration" // Admisi/Pendaftaran - EPCNur EmployeePosisitionCode = "nurse" // Perawat - EPCDoc EmployeePosisitionCode = "doctor" // Dokter - EPCNut EmployeePosisitionCode = "nutritionist" // Ahli gizi - EPCMwi EmployeePosisitionCode = "mid-wife" // Bidan - EPCLab EmployeePosisitionCode = "laborant" // Laboran - EPCPha EmployeePosisitionCode = "pharmacy" // Farmasi - EPCPay EmployeePosisitionCode = "payment" // Pembayaran - EPCHur EmployeePosisitionCode = "human-resource" // Sumber Daya Manusia - EPCGea EmployeePosisitionCode = "general-affair" // Bagian Umum - EPCMan EmployeePosisitionCode = "management" // Manajemen + EPCReg EmployeePositionCode = "registration" // Admisi/Pendaftaran + EPCNur EmployeePositionCode = "nurse" // Perawat + EPCDoc EmployeePositionCode = "doctor" // Dokter + EPCNut EmployeePositionCode = "nutritionist" // Ahli gizi + EPCMwi EmployeePositionCode = "mid-wife" // Bidan + EPCLab EmployeePositionCode = "laborant" // Laboran + EPCPha EmployeePositionCode = "pharmacy" // Farmasi + EPCPay EmployeePositionCode = "payment" // Pembayaran + EPCHur EmployeePositionCode = "human-resource" // Sumber Daya Manusia + EPCGea EmployeePositionCode = "general-affair" // Bagian Umum + EPCMan EmployeePositionCode = "management" // Manajemen IPCSpecialist = "specialist-intern" IPCNurse = "nurse-intern" From 704622beda63fea4943e4c3a59e8378029124d29 Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Wed, 22 Oct 2025 11:53:22 +0700 Subject: [PATCH 15/15] migration from server --- cmd/main-migration/migrations/atlas.sum | 114 ++++++++++++------------ 1 file changed, 57 insertions(+), 57 deletions(-) diff --git a/cmd/main-migration/migrations/atlas.sum b/cmd/main-migration/migrations/atlas.sum index d28421e6..49982d7c 100644 --- a/cmd/main-migration/migrations/atlas.sum +++ b/cmd/main-migration/migrations/atlas.sum @@ -1,57 +1,57 @@ -h1:nkA4yHX6MSnA6zb6xACKXMBMwaAu4BRoT1Cs4DW3z80= -20250904105930.sql h1:MEM6blCgke9DzWQSTnLzasbPIrcHssNNrJqZpSkEo6k= -20250904141448.sql h1:J8cmYNk4ZrG9fhfbi2Z1IWz7YkfvhFqTzrLFo58BPY0= -20250908062237.sql h1:Pu23yEW/aKkwozHoOuROvHS/GK4ngARJGdO7FB7HZuI= -20250908062323.sql h1:oXl6Z143tOpIl4EfP4B8JNU8LrMvVmHEtCgAfiB4gs8= -20250908073811.sql h1:m2aNXfnGxnLq1+rVWrh4f60q7fhyhV3gEwNu/OIqQlE= -20250908073839.sql h1:cPk54xjLdMs26uY8ZHjNWLuyfAMzV7Zb0/9oJQrsw04= -20250910055902.sql h1:5xwjAV6QbtZT9empTJKfhyAjdknbHzb15B0Ku5dzqtQ= -20250915123412.sql h1:D83xaU2YlDEd21HLup/YQpQ2easMToYCyy/oK6AFgQs= -20250916043819.sql h1:ekoTJsBqQZ8G8n0qJ03d13+eoNoc7sAUEQGA5D/CCxk= -20250917040616.sql h1:zoCnmcXuM7AVv85SmN7RmFglCgJnoDmpRWExH0LAc9Q= -20250917040751.sql h1:J1xyRrh32y1+lezwAyNwPcUQ6ABBSgbvzNLva4SVdQU= -20250917045138.sql h1:jKe1Z0uOLG4SGBYM+S/3P+/zMPztmgoderD5swnMuCg= -20250917093645.sql h1:cNI3Pbz1R3LxvIXLuexafJFCXUXrmuFCgXXJ2sG+FW0= -20250918073552.sql h1:RJ1SvMzP6aeWnoPVD3eVAmIQOkcp6Php8z3QRri6v4g= -20250918073742.sql h1:+cEsnJTJFybe2fR69ZoOiX2R6c6iITl4m6WTZ1hjyzY= -20250918074745.sql h1:2hNVQCXF/dVYXAh+T/7oBFgERGWxzVb2FXJjwkFWGCI= -20250923025134.sql h1:Ykz/qpHiGDXPsCsWTjydQFVSibZP2D+h2fIeb2h2JGA= -20250924051317.sql h1:yQuW6SwJxIOM5fcxeAaie5lSm1oLysU/C2hH2xNCVoQ= -20250929034321.sql h1:101FJ8VH12mrZWlt/X1gvKUGOhoiF8tFbjiapAjnHzg= -20250929034428.sql h1:i+pROD9p+g5dOmmZma6WF/0Hw5g3Ha28NN85iTo1K34= -20250930025550.sql h1:+F+CsCUXD/ql0tHGEow70GhPBX1ZybVn+bh/T4YMh7Y= -20250930140351.sql h1:9AAEG1AnOAH+o0+oHL5G7I8vqlWOhwRlCGyyCpT/y1Q= -20251002085604.sql h1:3xZ68eYp4urXRnvotNH1XvG2mYOSDV/j3zHEZ/txg5E= -20251003032030.sql h1:HB+mQ2lXMNomHDpaRhB/9IwYI9/YiDO5eOJ+nAQH/jw= -20251005060450.sql h1:LbtCE2b+8osM3CvnmQJH1uCPtn+d7WchsslBOz8bL3Q= -20251006041122.sql h1:MlS7f21z06sutnf9dIekt5fuHJr4lgcQ4uCuCXAGsfc= -20251006045658.sql h1:3FmGCPCzjgMPdWDRodZTsx3KVaodd9zB9ilib69aewk= -20251006045928.sql h1:Z5g31PmnzNwk/OKdODcxZGm8fjJQdMFK32Xfnt3bRHg= -20251007022859.sql h1:FO03zEfaNEk/aXwY81d5Lp3MoBB9kPQuXlXJ4BPiSR8= -20251008031337.sql h1:l+sxUAGvcTfj3I6kAFHo+T6AYodC9k9GkR+jaKO2xXc= -20251008031554.sql h1:AqrVfIhSzY3PCy8ZlP5W91wn2iznfIuj5qQfubp6/94= -20251008052346.sql h1:nxnXmooIJ6r1mmzwnw+6efxLfc/k9h2aE6RMptPRons= -20251008073620.sql h1:6YsJp1W4SmQJ1lxpqF27BBlDC1zqhw7Yhc7pLzQTY6M= -20251009042854.sql h1:nkBV+R6j0fg7/JY6wH3eb5Vv0asJLnXmb6lINfT/GLQ= -20251009052657.sql h1:EPvdsib5rzCGPryd10HShGKvFPwM/R5S2lIVwtYxpms= -20251010031743.sql h1:T8IZmx8/btRFKLzTe78MzcBsPJNodnLvB0tby9QkirQ= -20251010070721.sql h1:5NQUk/yOV6sABLCB7swx++YIOyJe6MnU+yt1nRzde5w= -20251010072711.sql h1:ZJNqR2piyu8xJhBvVABSlnGEoKSKae3wuEs+wshPe4k= -20251013044536.sql h1:0Xjw8fNILiT8nnfrJDZgQnPf3dntmIoilbapnih8AE4= -20251013051438.sql h1:lfSuw5mgJnePBJamvhZ81osFIouXeiIEiSZ/evdwo48= -20251013081808.sql h1:ijgjNX08G6GBjA/ks8EKtb7P7Y7Cg7zbhqEOruGnv6M= -20251014060047.sql h1:0jqj49WTtneEIMQDBoo4c095ZGi8sCrA8NnHBrPU6D8= -20251014063537.sql h1:VZLXol0PTsTW21Epg6vBPsztWkDtcxup9F/z88EGgIg= -20251014063720.sql h1:2HVUyCV0ud3BJJDH2GEKZN/+IWLFPCsN1KqhP6csO14= -20251015045455.sql h1:MeLWmMhAOAz8b15Dd7IAQnt6JxjSml02XCXK22C0Lpg= -20251016010845.sql h1:4BncQdDOasRZJkzVJrSJJA7091A9VPNVx/faUCUPhBM= -20251016011023.sql h1:9JB9eFZKURK5RoCVDKR6glSvdJ8NTXrN7K/4q51zkz4= -20251016062912.sql h1:ACNn0fe+EMqUt3hoY+Dr3uqAV/QICBa1+mIW7fUc9Fk= -20251017060617.sql h1:4T3t9ifWrEQTPMSM0XJ98pF7Qdt+UfgtMui17bhrnWI= -20251017082207.sql h1:8vLG1l/saRRMHXkyA4nelJyjaSddhZd6r7R+Uo4JS/c= -20251018032635.sql h1:2xey5gnO3y2XSOrU8MLlIfoylPKbRGDRtHDD07B3MbQ= -20251018040322.sql h1:k/pdNiSoT8zFPqNQ/avOD0vYkNh3BTD64IlHrfVXr7I= -20251019093915.sql h1:hFcQE0y+p5dZiVwePGsRGto9m/q6kJNiUZbVDd5Rnjk= -20251020062553.sql h1:Iw7hulcm5iRQlfW+ygA4iTPxLqkxx6h9vXMXEwUAHKs= -20251021041042.sql h1:wMgSivBV2A0NDcsLmKGIp0kMcVh2IODSG9b4dgzCaOM= -20251021075552.sql h1:8gfSMAglflNO6L0sSzxFNEubYN8/O4thT7OQT+WH+3M= +h1:9voEyn7242M8hCdJhgPotJkwJ0DX7UiXrqu3+trN7jw= +20250904105930.sql h1:Vv4vCurl7m7/ZB6TjRpkubHpQ4RYwSUn0QHdzfoGpzY= +20250904141448.sql h1:FYCHH9Os4KkrZMDu/jR8FMP+wLMRW+Mb0PkLU/9BRDg= +20250908062237.sql h1:oanBpKZd+akPu2I/xYhUSbd0G5tAFbXzKLER/Zs8ENI= +20250908062323.sql h1:miNG9COddXkD1jGTgaROMAZ618eT6oiLGiJhXWnQwhE= +20250908073811.sql h1:gOi5cnGG1htlpfizybYmUIT0vYjZTBfXiI0nPSYK2u8= +20250908073839.sql h1:cWNDA4YikjoOteAJuNLFILjQUJPFB6o8Wxreiek4QyI= +20250910055902.sql h1:nxxOGnU0BbH/v3IPgeIOXOwH8d3tKomw7h6FTeMnnBs= +20250915123412.sql h1:mz7SiWfrdf0qE1VTSAAnA/147d6gyp6ry5vZ2bR9SH0= +20250916043819.sql h1:RHXVtmMkB6wfv06HfPyHMBmUfIpFt1xveafNz0kwKnE= +20250917040616.sql h1:MYVDht+akBlzQGKNu2hTTTLPEcH1bxT/Q8MK6WEtuhs= +20250917040751.sql h1:J79YyS2JzWgh5oKXMTgh67uo3gLxKaAsxRiZmSIfjBs= +20250917045138.sql h1:/SM1N4O8X3yxpoJgMEARmS1uOkuLKsTOy4PLsRCOKaQ= +20250917093645.sql h1:PNBTGZ7s10e5b5+Tie8YfVQBN0zKtJ5T34oK1iOUEb4= +20250918073552.sql h1:jG7+g3i8ODYaJdcdZz12v3nbsZ5mB9wG6kWnGyTQIRI= +20250918073742.sql h1:j+rgw7puxE7s+phqPVZHmPk0af3rcaA56Itp86y1suY= +20250918074745.sql h1:rPmP4DXs6OnY4Vp+xO/z9jFpJt/RrJ52SJJjIIxeDvc= +20250923025134.sql h1:2r6pcwnBSU5Y9Czk1OHBoh4yZXiMtEca9X8843fTEX0= +20250924051317.sql h1:iUAk2gsGoEGIPQ0lEEUp8maMSId8emNbP+kP712ABIA= +20250929034321.sql h1:UlpALNVmdi95zOIT0yc6ZyTj9bBjQEIpZhvgrc52M+k= +20250929034428.sql h1:feF+H4nDyHh5bdx48Oiz0A1qecZfi6v3qTTdjzJ45Dg= +20250930025550.sql h1:6XT1kXI3Z3ZIxxmvT7poufZWWCW0QiejZPaFV5wBnjI= +20250930140351.sql h1:HxnmAbh9gCy8jwl/9ycGktiByaUripsjFFvohofY2CY= +20251002085604.sql h1:SjLPi+ZN6qDccK3DaEQCgNsZpPwr5kynWXwbwEsziCI= +20251003032030.sql h1:oHfxNSuqTxU8Zaf9H+h8TuUb1Da03wcyc6hZjDrUQ2s= +20251005060450.sql h1:GIuCcrd4MwjmXpvbzDzPYL18BV3QaZZ+Y2FmEzjvi0E= +20251006041122.sql h1:uNDQbSw0M08lYoMvUNlQtS3iDzpPM1ixT13ugSAoWjE= +20251006045658.sql h1:z+t7yCK54Q4SSiF9kUyUhkYB2F+kzSW9TB7ogxd9wzw= +20251006045928.sql h1:1lATLFLp4BWwGZqAjZdP0Dc6ypNXiYcwjoNkqGa8NFE= +20251007022859.sql h1:HXXwWrkyvzJzJGAt9mGskCRBBV/c1JfPmfjDocmJhQ4= +20251008031337.sql h1:Ln5pCF3Hxa5foHZLcds+z/us2eH6VAhhEj3w0TAGlVs= +20251008031554.sql h1:aB4MUS2lmqG0//4HKUWorcPSpWya0VC4QItvGyskEVI= +20251008052346.sql h1:MI3AZgU5XcwZT2OvvlWAxdRtL0eJ3jjRwt56IY1+pRU= +20251008073620.sql h1:sztWXuSNYwpEraaSapSsYwno75LO5H/N7ob7OJQ8X/A= +20251009042854.sql h1:TnPXj+dCJls3IU//cuqJZymyBzZMKs7ayazfgtAFRxM= +20251009052657.sql h1:leXbs0CP8r5dRilmYyLRk1MICqak3ea1/LWMtFrijqQ= +20251010031743.sql h1:SgHNY/lQ88G2F4nZyMfiOkDntb+gtOR+nEQLqXBTwv4= +20251010070721.sql h1:AnJnhXsMzDvK4AFgYw6B16Kpo/hljrZtcpc9m2VOSHQ= +20251010072711.sql h1:aXPTtNwLcTuw8C/yAxwxvqs0ayEjNzI1uuE0vE3ERa8= +20251013044536.sql h1:7Pq6JcvTpPBYDCW2dz3HdgUwY65HlhEVWy9TiG8iONE= +20251013051438.sql h1:X6t8bkqpUYYokBunSufMQUe5vCg0VyO6dxbm7ngosUc= +20251013081808.sql h1:495pLguXL2Ozh+ycn4UYZgZbn6WbjXNbyZUc3JU8qhI= +20251014060047.sql h1:nCgImMRGHhziiW57O1ofWaXCAPGaCOHN7PldQ3OSmM4= +20251014063537.sql h1:2cLmID79jP6cuQ1YJaWTtghFiM1GtHMC0ZQl30Hpy1M= +20251014063720.sql h1:bzLKKVAjSHgDFoiI/glj7t1ETlSAKx+AlsIAaP0ru2g= +20251015045455.sql h1:S547+UugQhlTRcn1Lm1IfqT5RNPttIWIiD+RTx69YaE= +20251016010845.sql h1:c9DUvxl17pUkf0azdYGM/YDzYxIJkLcfZOcMI4rL+R0= +20251016011023.sql h1:u3ivg83bXgYHBbojbWpemLxPE9Dmmj53B6LXo664jxw= +20251016062912.sql h1:W9n1hWchfYkqNX9LO9uxFxEXAb/iY+Pexjnhmp6PbgI= +20251017060617.sql h1:VU6yZ2+LfHpDZ3+TIH40t3F5YXPCpTppuF9+uSqa4b8= +20251017082207.sql h1:QshZslfedckz7iDpSGmPyY9sP6dy6ckHbs8L1TuXIA4= +20251018032635.sql h1:M1U/9W/F9wlW5YDmVAmHFfUJU7FWFaX0DblpfZcYWrE= +20251018040322.sql h1:Zk/vw0e6AzWFO2ElLOzB+OrSz6k+h1Ynxp0TImAzxwY= +20251019093915.sql h1:3Q0kPiZwJnHn5rAvdh0w1LBdiA7W2xBmZWncoPXb044= +20251020062553.sql h1:mJwC/J8GzPAIXckNMvy1f/Nguk2VVf8roD/Raclhbao= +20251021041042.sql h1:d1BAOGAQhqr+oOwcpAVozUsTh457VSDEk2qQFavGG58= +20251021075552.sql h1:TNChGQ1Zlr/1iQ6qvK4iDTAJpe6L/z/M6e/O0SkQVaM=