diff --git a/Dockerfile-main-api b/Dockerfile-main-api new file mode 100644 index 00000000..0c75acfa --- /dev/null +++ b/Dockerfile-main-api @@ -0,0 +1,13 @@ +FROM golang:1.24.10 AS builder +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o ./cmd/main-api/main-api ./cmd/main-api/main.go + +FROM alpine:latest +WORKDIR /app +COPY --from=builder /src/cmd/main-api/main-api . +COPY --from=builder /src/cmd/main-api/config.yml . +EXPOSE 8000 +CMD ["./main-api"] diff --git a/Dockerfile-simgos-sync-api b/Dockerfile-simgos-sync-api new file mode 100644 index 00000000..35660a2f --- /dev/null +++ b/Dockerfile-simgos-sync-api @@ -0,0 +1,13 @@ +FROM golang:1.24.10 AS builder +WORKDIR /src +COPY go.mod go.sum ./ +RUN go mod download +COPY . . +RUN CGO_ENABLED=0 GOOS=linux go build -o ./cmd/simgos-sync-api/simgos-sync-api ./cmd/simgos-sync-api/main.go + +FROM alpine:latest +WORKDIR /app +COPY --from=builder /src/cmd/simgos-sync-api/simgos-sync-api . +COPY --from=builder /src/cmd/simgos-sync-api/config.yml . +EXPOSE 8000 +CMD ["./simgos-sync-api"] diff --git a/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go b/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go index 19530c2c..1974e66c 100644 --- a/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go +++ b/internal/domain/bpjs-entities/vclaim-sep-control-letter/dto.go @@ -1,7 +1,13 @@ package vclaimsepcontrolletter import ( + "encoding/json" + "fmt" + "time" + ecore "simrs-vx/internal/domain/base-entities/core" + + pu "simrs-vx/pkg/use-case-helper" ) type CreateDto struct { @@ -9,6 +15,7 @@ type CreateDto struct { Number *string `json:"number" gorm:"unique;size:20"` Value *string `json:"value"` FileUrl *string `json:"fileUrl" gorm:"unique;size:1024"` + RequestPayload string `json:"requestPayload" validate:"maxLength=1024"` } type ReadListDto struct { @@ -69,3 +76,49 @@ func ToResponseList(data []VclaimSepControlLetter) []ResponseDto { } return resp } + +func (c CreateDto) RequestPayloadIntoJson() ([]byte, error) { + payload := map[string]interface{}{} + err := json.Unmarshal([]byte(c.RequestPayload), &payload) + if err != nil { + return nil, err + } + return json.Marshal(payload) +} + +type ResponseForPDF struct { + Number string `json:"noSuratKontrol"` + PlannedControlDate string `json:"tglRencanaKontrol"` + IssuedDate string `json:"tglTerbit"` + Doctor_Name string `json:"namaDokter"` + DstUnit_Name string `json:"namaPoliTujuan"` + ResponsibleDoctor_Name string `json:"namaDokterPembuat"` + VclaimSep VclaimSep `json:"sep"` +} + +type VclaimSep struct { + VclaimMember VclaimMember `json:"peserta"` + Diagnose string `json:"diagnosa"` + Number string `json:"noSep"` +} + +type VclaimMember struct { + CardNumber string `json:"noKartu"` + Name string `json:"nama"` + BirthDate string `json:"tglLahir"` + Gender string `json:"kelamin"` +} + +func (v ResponseForPDF) GenerateNameWithGender() string { + gender := "Perempuan" + if v.VclaimSep.VclaimMember.Gender == "L" { + gender = "Laki-Laki" + } + + return fmt.Sprintf("%s (%s)", v.VclaimSep.VclaimMember.Name, gender) +} + +func (v ResponseForPDF) GenerateBirthDate() string { + t, _ := time.Parse("2006-01-02", v.VclaimSep.VclaimMember.BirthDate) + return pu.FormatIndonesianDate(t) +} diff --git a/internal/interface/main-handler/main-handler.go b/internal/interface/main-handler/main-handler.go index ebeed55a..f6f6bfce 100644 --- a/internal/interface/main-handler/main-handler.go +++ b/internal/interface/main-handler/main-handler.go @@ -138,6 +138,7 @@ import ( reference "simrs-vx/internal/interface/main-handler/reference" referral "simrs-vx/internal/interface/main-handler/referral" vclaimsep "simrs-vx/internal/interface/main-handler/vclaim-sep" + vclaimsepcontrolletter "simrs-vx/internal/interface/main-handler/vclaim-sep-control-letter" vclaimsephist "simrs-vx/internal/interface/main-handler/vclaim-sep-hist" vclaimsepprint "simrs-vx/internal/interface/main-handler/vclaim-sep-print" ) @@ -414,6 +415,10 @@ func SetRoutes() http.Handler { "DELETE /{number}": vclaimsep.O.Delete, }) + hk.GroupRoutes("/v1/vclaim-sep-control-letter", r, hk.MapHandlerFunc{ + "POST /": vclaimsepcontrolletter.O.Create, + }) + hk.GroupRoutes("/v1/vclaim-sep-hist", r, hk.MapHandlerFunc{ "GET /": vclaimsephist.O.GetList, }) diff --git a/internal/interface/main-handler/vclaim-sep-control-letter/handler.go b/internal/interface/main-handler/vclaim-sep-control-letter/handler.go new file mode 100644 index 00000000..d26cc633 --- /dev/null +++ b/internal/interface/main-handler/vclaim-sep-control-letter/handler.go @@ -0,0 +1,67 @@ +package vclaimsepcontrolletter + +import ( + "net/http" + + rw "github.com/karincake/risoles" + + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" + u "simrs-vx/internal/use-case/bpjs-use-case/vclaim-sep-control-letter" +) + +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) { +// number := rw.ValidateString(w, "number", r.PathValue("number")) +// if number <= "" { +// return +// } +// dto := e.ReadDetailDto{} +// dto.Number = &number +// res, err := u.ReadDetail(dto) +// rw.DataResponse(w, res, err) +// } + +// func (obj myBase) Update(w http.ResponseWriter, r *http.Request) { +// number := rw.ValidateString(w, "number", r.PathValue("number")) +// if number != "" { +// return +// } + +// dto := e.UpdateDto{} +// if res := rw.ValidateStructByIOR(w, r.Body, &dto); !res { +// return +// } +// dto.Number = &number +// res, err := u.Update(dto) +// rw.DataResponse(w, res, err) +// } + +// func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { +// id := rw.ValidateInt(w, "id", r.PathValue("id")) +// if id <= 0 { +// return +// } +// dto := e.DeleteDto{} +// dto.Id = uint(id) +// res, err := u.Delete(dto) +// rw.DataResponse(w, res, err) +// } diff --git a/internal/interface/main-handler/vclaim-sep/handler.go b/internal/interface/main-handler/vclaim-sep/handler.go index 1529e2d8..2a3fe788 100644 --- a/internal/interface/main-handler/vclaim-sep/handler.go +++ b/internal/interface/main-handler/vclaim-sep/handler.go @@ -56,12 +56,12 @@ func (obj myBase) GetDetail(w http.ResponseWriter, r *http.Request) { // } func (obj myBase) Delete(w http.ResponseWriter, r *http.Request) { - id := rw.ValidateInt(w, "id", r.PathValue("id")) - if id <= 0 { + number := rw.ValidateString(w, "number", r.PathValue("number")) + if number == "" { return } dto := e.DeleteDto{} - dto.Id = uint(id) + dto.Number = &number res, err := u.Delete(dto) rw.DataResponse(w, res, err) } diff --git a/internal/use-case/bpjs-plugin/vclaim-sep-control-letter/plugin.go b/internal/use-case/bpjs-plugin/vclaim-sep-control-letter/plugin.go new file mode 100644 index 00000000..3ad81f74 --- /dev/null +++ b/internal/use-case/bpjs-plugin/vclaim-sep-control-letter/plugin.go @@ -0,0 +1,125 @@ +package vclaimsepcontrolletter + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "net/http" + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" + + ibpjs "simrs-vx/internal/infra/bpjs" + + "gorm.io/gorm" +) + +func CreateSepControlLetter(input *e.CreateDto, data *e.VclaimSepControlLetter, tx *gorm.DB) error { + payload, err := input.RequestPayloadIntoJson() + if err != nil { + return err + } + req, err := http.NewRequest("POST", ibpjs.O.BaseUrl+"RencanaKontrol", bytes.NewBuffer(payload)) + if err != nil { + return err + } + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return err + } + + var vresp Response + if err := json.Unmarshal(body, &vresp); err != nil { + return fmt.Errorf("failed to parse response JSON: %w", err) + } + + if vresp.MetaData.Code != "200" { + return fmt.Errorf("failed to create sep control letter: %s", vresp.MetaData.Message) + } + + pdfNeeds, err := vresp.ToPDFNeeds() + if err != nil { + return err + } + tmp := string(pdfNeeds) + input.Value = &tmp + input.Number = &vresp.Response.NoSuratKontrol + input.VclaimSep_Number = &vresp.Response.Sep.NoSep + return nil +} + +// func ReadDetailSep(input *e.ReadDetailDto, data *e.VclaimSep, tx *gorm.DB) error { +// endpoint := fmt.Sprintf("sep/%s", input.Number) +// req, err := http.NewRequest("GET", ibpjs.O.BaseUrl+endpoint, nil) +// if err != nil { +// return err +// } +// req.Header.Set("Content-Type", "application/json") + +// resp, err := http.DefaultClient.Do(req) +// if err != nil { +// return err +// } +// defer resp.Body.Close() + +// body, err := io.ReadAll(resp.Body) +// if err != nil { +// return err +// } + +// var detail e.SepResponse +// if err := json.Unmarshal(body, &detail); err != nil { +// return fmt.Errorf("failed to parse response JSON: %w", err) +// } + +// data.Detail = detail.Response + +// return nil +// } + +// func DeleteSep(input *e.DeleteDto, data *e.VclaimSep, tx *gorm.DB) error { +// payload := e.SepDeleteRequest{} +// payload.Request.TSep.NoSep = *input.Number +// payload.Request.TSep.User = "Coba Ws" + +// jsonPayload, err := json.Marshal(payload) +// if err != nil { +// return err +// } + +// req, err := http.NewRequest("DELETE", ibpjs.O.BaseUrl+"sep", bytes.NewBuffer(jsonPayload)) +// if err != nil { +// return err +// } + +// req.Header.Set("Content-Type", "application/json") + +// resp, err := http.DefaultClient.Do(req) +// if err != nil { +// return err +// } +// defer resp.Body.Close() + +// body, err := io.ReadAll(resp.Body) +// if err != nil { +// return err +// } + +// var detail e.SepResponse +// if err := json.Unmarshal(body, &detail); err != nil { +// return fmt.Errorf("failed to parse response JSON: %w", err) +// } + +// if detail.MetaData.Message == SepNotFound { +// return fmt.Errorf("sep with number %s not found", *data.Number) +// } + +// return nil +// } diff --git a/internal/use-case/bpjs-plugin/vclaim-sep-control-letter/tycovar.go b/internal/use-case/bpjs-plugin/vclaim-sep-control-letter/tycovar.go new file mode 100644 index 00000000..c732afa5 --- /dev/null +++ b/internal/use-case/bpjs-plugin/vclaim-sep-control-letter/tycovar.go @@ -0,0 +1,87 @@ +package vclaimsepcontrolletter + +import ( + "encoding/json" + + e "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" +) + +type SuratKontrol struct { + NoSuratKontrol string `json:"noSuratKontrol"` + TglRencanaKontrol string `json:"tglRencanaKontrol"` + TglTerbit string `json:"tglTerbit"` + JnsKontrol string `json:"jnsKontrol"` + PoliTujuan string `json:"poliTujuan"` + NamaPoliTujuan string `json:"namaPoliTujuan"` + KodeDokter string `json:"kodeDokter"` + NamaDokter string `json:"namaDokter"` + FlagKontrol string `json:"flagKontrol"` + KodeDokterPembuat *string `json:"kodeDokterPembuat"` + NamaDokterPembuat *string `json:"namaDokterPembuat"` + NamaJnsKontrol string `json:"namaJnsKontrol"` + Sep Sep `json:"sep"` +} + +type Sep struct { + NoSep string `json:"noSep"` + TglSep string `json:"tglSep"` + JnsPelayanan string `json:"jnsPelayanan"` + Poli string `json:"poli"` + Diagnosa string `json:"diagnosa"` + Peserta Peserta `json:"peserta"` + ProvUmum ProvUmum `json:"provUmum"` + ProvPerujuk ProvPerujuk `json:"provPerujuk"` +} + +type Peserta struct { + NoKartu string `json:"noKartu"` + Nama string `json:"nama"` + TglLahir string `json:"tglLahir"` + Kelamin string `json:"kelamin"` + HakKelas string `json:"hakKelas"` +} + +type ProvUmum struct { + KdProvider string `json:"kdProvider"` + NmProvider string `json:"nmProvider"` +} + +type ProvPerujuk struct { + KdProviderPerujuk string `json:"kdProviderPerujuk"` + NmProviderPerujuk string `json:"nmProviderPerujuk"` + AsalRujukan string `json:"asalRujukan"` + NoRujukan string `json:"noRujukan"` + TglRujukan string `json:"tglRujukan"` +} + +type Response struct { + MetaData MetaData `json:"metaData"` + Response *SuratKontrol `json:"response"` // nullable +} + +type MetaData struct { + Code string `json:"code"` + Message string `json:"message"` +} + +func (r Response) ToPDFNeeds() ([]byte, error) { + response := e.ResponseForPDF{ + Number: r.Response.Sep.NoSep, + PlannedControlDate: r.Response.TglRencanaKontrol, + IssuedDate: r.Response.TglTerbit, + Doctor_Name: r.Response.NamaDokter, + DstUnit_Name: r.Response.NamaPoliTujuan, + ResponsibleDoctor_Name: *r.Response.NamaDokterPembuat, + VclaimSep: e.VclaimSep{ + VclaimMember: e.VclaimMember{ + CardNumber: r.Response.Sep.Peserta.NoKartu, + Name: r.Response.Sep.Peserta.Nama, + BirthDate: r.Response.Sep.Peserta.TglLahir, + Gender: r.Response.Sep.Peserta.Kelamin, + }, + Diagnose: r.Response.Sep.Diagnosa, + Number: r.Response.Sep.NoSep, + }, + } + return json.Marshal(response) +} diff --git a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go index 891e7c4d..c878d00b 100644 --- a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/helper.go @@ -20,5 +20,5 @@ func setData[T *e.CreateDto | *e.UpdateDto](input T, data *e.VclaimSepControlLet data.VclaimSep_Number = inputSrc.VclaimSep_Number data.Number = inputSrc.Number data.Value = inputSrc.Value - data.FileUrl = inputSrc.FileUrl + // data.FileUrl = inputSrc.FileUrl } diff --git a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware.go b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware.go index 57262cc2..0cca1159 100644 --- a/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware.go +++ b/internal/use-case/bpjs-use-case/vclaim-sep-control-letter/middleware.go @@ -1,9 +1,12 @@ package vclaimsepcontrolletter +import ( + pvscl "simrs-vx/internal/use-case/bpjs-plugin/vclaim-sep-control-letter" +) + // example of middleware -// func init() { -// createPreMw = append(createPreMw, -// CreateMw{Name: "modif-input", Func: pm.ModifInput}, -// CreateMw{Name: "check-data", Func: pm.CheckData}, -// ) -// } +func init() { + createPreMw = append(createPreMw, + createMw{Name: "create-sep-control-letter", Func: pvscl.CreateSepControlLetter}, + ) +} diff --git a/internal/use-case/bpjs-use-case/vclaim-sep/case.go b/internal/use-case/bpjs-use-case/vclaim-sep/case.go index 1d0e4aab..0f0d5203 100644 --- a/internal/use-case/bpjs-use-case/vclaim-sep/case.go +++ b/internal/use-case/bpjs-use-case/vclaim-sep/case.go @@ -246,7 +246,7 @@ func Update(input e.UpdateDto) (*d.Data, error) { } func Delete(input e.DeleteDto) (*d.Data, error) { - rdDto := e.ReadDetailDto{Id: input.Id} + rdDto := e.ReadDetailDto{Number: input.Number} var data *e.VclaimSep var err error diff --git a/internal/use-case/main-use-case/generate-file/helper.go b/internal/use-case/main-use-case/generate-file/helper.go index a0e7c1e2..adcdc16d 100644 --- a/internal/use-case/main-use-case/generate-file/helper.go +++ b/internal/use-case/main-use-case/generate-file/helper.go @@ -183,9 +183,9 @@ func generateCL(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, e } // map template data - clData := VclaimControlLetter{} - if input.Data != nil { - err := json.Unmarshal([]byte(*input.Data), &clData) + clData := evscl.ResponseForPDF{} + if cl.Value != nil { + err := json.Unmarshal([]byte(*cl.Value), &clData) if err != nil { event.ErrInfo = pl.ErrorInfo{ Code: "data-unmarshal-fail", @@ -195,22 +195,11 @@ func generateCL(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, e return nil, err } } else { - return nil, errors.New("there is no data to be used") + return nil, errors.New("there is no value to be used") } - if cl == nil { - createCL := evscl.CreateDto{ - VclaimSep_Number: &clData.VclaimSep.Number, - Number: &clData.Number, - Value: input.Data, - } - if cl, err = uvscl.CreateData(createCL, &event, tx); err != nil { - return nil, err - } - - } // get encounter id by vclaim sep number - vs, err := uvs.ReadDetailData(evs.ReadDetailDto{Number: &clData.VclaimSep.Number}, &event) + vs, err := uvs.ReadDetailData(evs.ReadDetailDto{Number: cl.VclaimSep_Number}, &event) if err != nil { return nil, err } @@ -220,7 +209,7 @@ func generateCL(input GenerateDto, event pl.Event, tx *gorm.DB) (*ResponseDto, e input.Encounter_Id = vs.Encounter_Id input.UseA5Lanscape = true - templateData := clData.generateTemplateData() + templateData := generateTemplateData(clData) // generate file urlPub, err := generateFile(input, templateData) if err != nil { diff --git a/internal/use-case/main-use-case/generate-file/tycovar.go b/internal/use-case/main-use-case/generate-file/tycovar.go index a8fd5ef5..fd62787a 100644 --- a/internal/use-case/main-use-case/generate-file/tycovar.go +++ b/internal/use-case/main-use-case/generate-file/tycovar.go @@ -1,15 +1,11 @@ package generatefile import ( - "fmt" - "time" - + evscl "simrs-vx/internal/domain/bpjs-entities/vclaim-sep-control-letter" erc "simrs-vx/internal/domain/references/common" ere "simrs-vx/internal/domain/references/encounter" er "simrs-vx/internal/domain/main-entities/resume" - - pu "simrs-vx/pkg/use-case-helper" ) type GeneralConsentPDF struct { @@ -105,28 +101,6 @@ type GenerateDto struct { Data *string `json:"data"` } -type VclaimControlLetter struct { - Number string `json:"noSuratKontrol"` - PlannedControlDate string `json:"tglRencanaKontrol"` - Doctor_Name string `json:"namaDokter"` - DstUnit_Name string `json:"namaPoliTujuan"` - ResponsibleDoctor_Name string `json:"namaDokterPembuat"` - VclaimSep VclaimSep `json:"sep"` -} - -type VclaimSep struct { - VclaimMember VclaimMember `json:"peserta"` - Diagnose string `json:"diagnosa"` - Number string `json:"noSep"` -} - -type VclaimMember struct { - CardNumber string `json:"noKartu"` - Name string `json:"nama"` - BirthDate string `json:"tglLahir"` - Gender string `json:"kelamin"` -} - type GeneratePDFdto struct { TemplatePath string TemplateData any @@ -149,37 +123,17 @@ const ( TDNSB TemplateDocsName = "screening-form-b.html" ) -func (v VclaimControlLetter) generateTemplateData() ControlLetterPDF { - +func generateTemplateData(v evscl.ResponseForPDF) ControlLetterPDF { return ControlLetterPDF{ Number: v.Number, Doctor_Name: v.Doctor_Name, DstUnit_Name: v.DstUnit_Name, CardNumber: v.VclaimSep.VclaimMember.CardNumber, - Name: v.generateNameWithGender(), - BirthDate: v.generateBirthDate(), + Name: v.GenerateNameWithGender(), + BirthDate: v.GenerateBirthDate(), Diagnose: v.VclaimSep.Diagnose, PlanDate: v.PlannedControlDate, ResponsibleDoctor_Name: v.ResponsibleDoctor_Name, - PrintDate: generatePrintDate(), + PrintDate: v.IssuedDate, } } - -func (v VclaimControlLetter) generateNameWithGender() string { - gender := "Perempuan" - if v.VclaimSep.VclaimMember.Gender == "L" { - gender = "Laki-Laki" - } - - return fmt.Sprintf("%s (%s)", v.VclaimSep.VclaimMember.Name, gender) -} - -func (v VclaimControlLetter) generateBirthDate() string { - t, _ := time.Parse("2006-01-02", v.VclaimSep.VclaimMember.BirthDate) - return pu.FormatIndonesianDate(t) -} - -func generatePrintDate() string { - now := time.Now() - return now.Format("2006/01/02") -}