Use CPONE user name for HIS LoginBuat

This commit is contained in:
2026-09-25 22:09:05 +07:00
parent 20ec75fbcf
commit 0d2044926e
22 changed files with 127 additions and 47 deletions

View File

@@ -203,7 +203,8 @@ Contoh payload pembuatan pasien HIS:
"nik_karyawan": "TSB2026002", "nik_karyawan": "TSB2026002",
"dependant_id": "D001", "dependant_id": "D001",
"nama_karyawan": "BUDI SANTOS", "nama_karyawan": "BUDI SANTOS",
"channel": "CPONE" "channel": "CPONE",
"login_buat": "Nama Pengguna"
} }
``` ```
@@ -237,12 +238,16 @@ Content-Type: application/json
"group_tarif_id": "0", "group_tarif_id": "0",
"nomer_polis": "", "nomer_polis": "",
"kelas_id": "Default", "kelas_id": "Default",
"channel": "CPONE" "channel": "CPONE",
"login_buat": "Nama Pengguna"
} }
``` ```
Endpoint ini idempoten untuk pasien, tanggal, departemen, dan dokter yang sama; Endpoint ini idempoten untuk pasien, tanggal, departemen, dan dokter yang sama;
registrasi aktif yang sudah ada dikembalikan dengan status `already_registered_visit`. registrasi aktif yang sudah ada dikembalikan dengan status `already_registered_visit`.
Semua endpoint POST yang menulis data HIS mewajibkan `login_buat` berisi nama
pengguna CPONE yang sedang login (maksimal 30 karakter). Field `channel` tetap
menandai sumber integrasi dan tidak lagi dipakai untuk kolom `LoginBuat` HIS.
Contoh payload registrasi laboratorium: Contoh payload registrasi laboratorium:
@@ -260,7 +265,8 @@ Contoh payload registrasi laboratorium:
"shift_harian_id": null, "shift_harian_id": null,
"diagnosa_kerja": "Pemeriksaan laboratorium", "diagnosa_kerja": "Pemeriksaan laboratorium",
"catatan": null, "catatan": null,
"channel": "CPONE" "channel": "CPONE",
"login_buat": "Nama Pengguna"
} }
``` ```
@@ -281,7 +287,8 @@ Contoh payload penambahan layanan:
"catatan": null "catatan": null
} }
], ],
"channel": "CPONE" "channel": "CPONE",
"login_buat": "Nama Pengguna"
} }
``` ```
@@ -297,7 +304,8 @@ Contoh payload penambahan Paket Dispenser HIS:
"cito": false, "cito": false,
"markup_cito": 0, "markup_cito": 0,
"catatan": "", "catatan": "",
"channel": "CPONE" "channel": "CPONE",
"login_buat": "Nama Pengguna"
} }
``` ```

View File

@@ -77,8 +77,12 @@ func validateAddLabPackage(payload map[string]any) (repository.AddLabPackagePara
if !present { if !present {
channel = "CPONE" channel = "CPONE"
} }
loginBuat, message := requiredLoginBuat(payload)
if message != "" {
return repository.AddLabPackageParams{}, message
}
return repository.AddLabPackageParams{ return repository.AddLabPackageParams{
PackageID: packageID, Cito: cito, MarkupCito: markup, PackageID: packageID, Cito: cito, MarkupCito: markup,
Notes: strings.TrimSpace(notes), Channel: strings.ToUpper(strings.TrimSpace(channel)), Notes: strings.TrimSpace(notes), Channel: strings.ToUpper(strings.TrimSpace(channel)), LoginBuat: loginBuat,
}, "" }, ""
} }

View File

@@ -15,7 +15,7 @@ func TestAddLabPackageValidPayload(t *testing.T) {
Message: "Paket dispenser berhasil ditambahkan ke registrasi laboratorium", Message: "Paket dispenser berhasil ditambahkan ke registrasi laboratorium",
}} }}
handler := Handler{labRepo: repo} handler := Handler{labRepo: repo}
request := httptest.NewRequest(http.MethodPost, "/packages", strings.NewReader(`{"paket_id":"disp-260800011","cito":true,"markup_cito":"10","catatan":" test ","channel":"cpone"}`)) request := httptest.NewRequest(http.MethodPost, "/packages", strings.NewReader(`{"paket_id":"disp-260800011","cito":true,"markup_cito":"10","catatan":" test ","channel":"cpone","login_buat":"Joko Hudoro"}`))
request.SetPathValue("regId", "lab-2608210003") request.SetPathValue("regId", "lab-2608210003")
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
handler.AddLabPackage(recorder, request) handler.AddLabPackage(recorder, request)

View File

@@ -233,6 +233,10 @@ func validateLabRegistration(payload map[string]any) (validatedLabRegistration,
channel = "CPONE" channel = "CPONE"
} }
channel = strings.ToUpper(strings.TrimSpace(channel)) channel = strings.ToUpper(strings.TrimSpace(channel))
loginBuat, message := requiredLoginBuat(payload)
if message != "" {
return validatedLabRegistration{}, message
}
return validatedLabRegistration{Params: repository.LabRegistrationParams{ return validatedLabRegistration{Params: repository.LabRegistrationParams{
MedrecID: medrecID, MedrecID: medrecID,
@@ -255,6 +259,7 @@ func validateLabRegistration(payload map[string]any) (validatedLabRegistration,
WorkingDiagnosis: workingDiagnosis, WorkingDiagnosis: workingDiagnosis,
Notes: notes, Notes: notes,
Channel: channel, Channel: channel,
LoginBuat: loginBuat,
}}, "" }}, ""
} }

View File

@@ -77,7 +77,8 @@ func TestRegisterLabValidPayloadAndResponseParity(t *testing.T) {
"kelas_id":"", "kelas_id":"",
"diagnosa_kerja":"Pemeriksaan laboratorium", "diagnosa_kerja":"Pemeriksaan laboratorium",
"catatan":" catatan ", "catatan":" catatan ",
"channel":" cpone-api " "channel":" cpone-api ",
"login_buat":"Joko Hudoro"
}` }`
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body))
@@ -95,7 +96,7 @@ func TestRegisterLabValidPayloadAndResponseParity(t *testing.T) {
if repo.params.MedrecID != "00001234" || repo.params.DepartmentID != "LAB" || repo.params.DoctorID != "DR01" { if repo.params.MedrecID != "00001234" || repo.params.DepartmentID != "LAB" || repo.params.DoctorID != "DR01" {
t.Fatalf("unexpected repository params: %+v", repo.params) t.Fatalf("unexpected repository params: %+v", repo.params)
} }
if repo.params.TariffPeriodID != 10 || repo.params.ClassID != "Default" || repo.params.Channel != "CPONE-API" { if repo.params.TariffPeriodID != 10 || repo.params.ClassID != "Default" || repo.params.Channel != "CPONE-API" || repo.params.LoginBuat != "Joko Hudoro" {
t.Fatalf("normalization mismatch: %+v", repo.params) t.Fatalf("normalization mismatch: %+v", repo.params)
} }
if repo.params.Referrer != " Klinik A " || repo.params.Notes != " catatan " { if repo.params.Referrer != " Klinik A " || repo.params.Notes != " catatan " {
@@ -118,7 +119,8 @@ func TestRegisterLabUsesConfiguredDepartmentID(t *testing.T) {
"dokter_pengirim_id":"DR02", "dokter_pengirim_id":"DR02",
"jenis_pasien_id":"0", "jenis_pasien_id":"0",
"periode_tarif_id":10, "periode_tarif_id":10,
"diagnosa_kerja":"Pemeriksaan laboratorium" "diagnosa_kerja":"Pemeriksaan laboratorium",
"login_buat":"Joko Hudoro"
}` }`
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body))
@@ -158,7 +160,8 @@ func TestRegisterLabCompanyPayload(t *testing.T) {
"nama_karyawan":"INTAN SEPTIANI", "nama_karyawan":"INTAN SEPTIANI",
"kelas_id":"Default", "kelas_id":"Default",
"shift_harian_id":48049, "shift_harian_id":48049,
"diagnosa_kerja":"Test diagnosa" "diagnosa_kerja":"Test diagnosa",
"login_buat":"Joko Hudoro"
}` }`
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration", strings.NewReader(body))
@@ -186,6 +189,7 @@ func TestRegisterLabValidationParity(t *testing.T) {
"jenis_pasien_id": "0", "jenis_pasien_id": "0",
"periode_tarif_id": 10, "periode_tarif_id": 10,
"diagnosa_kerja": "Pemeriksaan laboratorium", "diagnosa_kerja": "Pemeriksaan laboratorium",
"login_buat": "Joko Hudoro",
} }
tests := []struct { tests := []struct {
@@ -290,7 +294,7 @@ func TestRegisterLabRepositoryErrorsMatchLaravelEnvelope(t *testing.T) {
}, },
} }
const validBody = `{"medrec_id":"00001234","departemen_id":"LAB","dokter_id":"DR01","dokter_pengirim_id":"DR02","jenis_pasien_id":"0","periode_tarif_id":10,"diagnosa_kerja":"Pemeriksaan laboratorium"}` const validBody = `{"medrec_id":"00001234","departemen_id":"LAB","dokter_id":"DR01","dokter_pengirim_id":"DR02","jenis_pasien_id":"0","periode_tarif_id":10,"diagnosa_kerja":"Pemeriksaan laboratorium","login_buat":"Joko Hudoro"}`
for _, test := range tests { for _, test := range tests {
t.Run(test.name, func(t *testing.T) { t.Run(test.name, func(t *testing.T) {
handler := Handler{labRepo: &fakeLabRegistrationRepository{err: test.err}} handler := Handler{labRepo: &fakeLabRegistrationRepository{err: test.err}}

View File

@@ -128,8 +128,12 @@ func validateAddLabServices(payload map[string]any) (repository.AddLabServicesPa
channel = "CPONE" channel = "CPONE"
} }
channel = strings.ToUpper(strings.TrimSpace(channel)) channel = strings.ToUpper(strings.TrimSpace(channel))
loginBuat, message := requiredLoginBuat(payload)
if message != "" {
return repository.AddLabServicesParams{}, message
}
return repository.AddLabServicesParams{Layanan: services, Channel: channel}, "" return repository.AddLabServicesParams{Layanan: services, Channel: channel, LoginBuat: loginBuat}, ""
} }
func requiredNestedString(item map[string]any, key string, index int, maxLength int, requiredMessage string) (string, string) { func requiredNestedString(item map[string]any, key string, index int, maxLength int, requiredMessage string) (string, string) {

View File

@@ -47,7 +47,8 @@ func TestAddLabServicesValidPayloadAndResponseParity(t *testing.T) {
{"layanan_id":"6413","jumlah":"2","cito":true,"markup_cito":"10.5","catatan":" urgent "}, {"layanan_id":"6413","jumlah":"2","cito":true,"markup_cito":"10.5","catatan":" urgent "},
{"layanan_id":"6414","cito":"0"} {"layanan_id":"6414","cito":"0"}
], ],
"channel":" cpone-api " "channel":" cpone-api ",
"login_buat":"Joko Hudoro"
}` }`
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration/CPLAB2608180001/services", strings.NewReader(body)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/lab-registration/CPLAB2608180001/services", strings.NewReader(body))
request.SetPathValue("regId", "CPLAB2608180001") request.SetPathValue("regId", "CPLAB2608180001")

View File

@@ -0,0 +1,16 @@
package http
import "strings"
// HIS package tables limit LoginBuat to 30 characters.
func requiredLoginBuat(payload map[string]any) (string, string) {
name, message := requiredString(payload, "login_buat", 30, "login_buat wajib diisi dengan nama pengguna CPONE")
if message != "" {
return "", message
}
name = strings.TrimSpace(name)
if name == "" {
return "", "login_buat wajib diisi dengan nama pengguna CPONE"
}
return name, ""
}

View File

@@ -171,6 +171,10 @@ func validateMedrec(payload map[string]any) (repository.MedrecParams, string) {
if !channelPresent || channel == "" { if !channelPresent || channel == "" {
channel = "CPONE" channel = "CPONE"
} }
loginBuat, message := requiredLoginBuat(payload)
if message != "" {
return repository.MedrecParams{}, message
}
identityType = normalizeText(identityType) identityType = normalizeText(identityType)
identityNumber = normalizeIdentity(identityNumber) identityNumber = normalizeIdentity(identityNumber)
@@ -208,6 +212,7 @@ func validateMedrec(payload map[string]any) (repository.MedrecParams, string) {
DependantID: strings.TrimSpace(dependantID), DependantID: strings.TrimSpace(dependantID),
EmployeeName: strings.TrimSpace(employeeName), EmployeeName: strings.TrimSpace(employeeName),
Channel: normalizeText(channel), Channel: normalizeText(channel),
LoginBuat: loginBuat,
}, "" }, ""
} }

View File

@@ -69,7 +69,8 @@ func TestStoreMedrecValidPayloadAndCreatedResponseParity(t *testing.T) {
"nik_karyawan":" TSB2026002 ", "nik_karyawan":" TSB2026002 ",
"dependant_id":"D001", "dependant_id":"D001",
"nama_karyawan":" Budi Santos ", "nama_karyawan":" Budi Santos ",
"channel":" cpone " "channel":" cpone ",
"login_buat":" Joko Hudoro "
}` }`
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/medrec", strings.NewReader(body)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/medrec", strings.NewReader(body))
@@ -93,7 +94,7 @@ func TestStoreMedrecValidPayloadAndCreatedResponseParity(t *testing.T) {
if repo.params.Phone != "081234567890" || repo.params.Mobile != "081234567890" { if repo.params.Phone != "081234567890" || repo.params.Mobile != "081234567890" {
t.Fatalf("phone normalization mismatch: %+v", repo.params) t.Fatalf("phone normalization mismatch: %+v", repo.params)
} }
if repo.params.Email != "budi@example.com" || repo.params.PlaceOfBirth != "JAKARTA" || repo.params.Channel != "CPONE" { if repo.params.Email != "budi@example.com" || repo.params.PlaceOfBirth != "JAKARTA" || repo.params.Channel != "CPONE" || repo.params.LoginBuat != "Joko Hudoro" {
t.Fatalf("text normalization mismatch: %+v", repo.params) t.Fatalf("text normalization mismatch: %+v", repo.params)
} }
if repo.params.CompanyID != "PE260700001" || repo.params.EmployeeID != "TSB2026002" || repo.params.DependantID != "D001" || repo.params.EmployeeName != "Budi Santos" { if repo.params.CompanyID != "PE260700001" || repo.params.EmployeeID != "TSB2026002" || repo.params.DependantID != "D001" || repo.params.EmployeeName != "Budi Santos" {
@@ -112,6 +113,7 @@ func TestStoreMedrecValidationParity(t *testing.T) {
"jenis_nomer_sosial": "KTP", "jenis_nomer_sosial": "KTP",
"nomer_sosial": "3174011501900001", "nomer_sosial": "3174011501900001",
"telepon": "081234567890", "telepon": "081234567890",
"login_buat": "Joko Hudoro",
} }
tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02") tomorrow := time.Now().AddDate(0, 0, 1).Format("2006-01-02")
@@ -168,7 +170,7 @@ func TestStoreMedrecNikFallbackAndOutcomeForwarding(t *testing.T) {
Data: data, Data: data,
}} }}
handler := Handler{patientRepo: repo} handler := Handler{patientRepo: repo}
body := `{"nama":"BUDI","jenis_kelamin":"P","tgl_lahir":"1990-01-15","nik":"3174011501900001","ponsel":"08123"}` body := `{"nama":"BUDI","jenis_kelamin":"P","tgl_lahir":"1990-01-15","nik":"3174011501900001","ponsel":"08123","login_buat":"Joko Hudoro"}`
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/medrec", strings.NewReader(body)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/medrec", strings.NewReader(body))
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
handler.StoreMedrec(recorder, request) handler.StoreMedrec(recorder, request)
@@ -180,7 +182,7 @@ func TestStoreMedrecNikFallbackAndOutcomeForwarding(t *testing.T) {
func TestStoreMedrecRepositoryErrorParity(t *testing.T) { func TestStoreMedrecRepositoryErrorParity(t *testing.T) {
handler := Handler{patientRepo: &fakePatientRepository{err: errors.New("database unavailable")}} handler := Handler{patientRepo: &fakePatientRepository{err: errors.New("database unavailable")}}
body := `{"nama":"BUDI","jenis_kelamin":"L","tgl_lahir":"1990-01-15","nomer_sosial":"3174011501900001","telepon":"08123"}` body := `{"nama":"BUDI","jenis_kelamin":"L","tgl_lahir":"1990-01-15","nomer_sosial":"3174011501900001","telepon":"08123","login_buat":"Joko Hudoro"}`
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/medrec", strings.NewReader(body)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/medrec", strings.NewReader(body))
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
handler.StoreMedrec(recorder, request) handler.StoreMedrec(recorder, request)

View File

@@ -123,6 +123,10 @@ func validateCreatePaket(payload map[string]any) (repository.CreatePaketParams,
if !present || strings.TrimSpace(channel) == "" { if !present || strings.TrimSpace(channel) == "" {
channel = "CPONE" channel = "CPONE"
} }
loginBuat, message := requiredLoginBuat(payload)
if message != "" {
return repository.CreatePaketParams{}, message
}
days := int64(1) days := int64(1)
if value, present := nullableValue(payload, "jumlah_hari"); present { if value, present := nullableValue(payload, "jumlah_hari"); present {
@@ -251,7 +255,7 @@ func validateCreatePaket(payload map[string]any) (repository.CreatePaketParams,
RawatInap: rawatInap, RawatJalan: rawatJalan, MultiVisit: multiVisit, RawatInap: rawatInap, RawatJalan: rawatJalan, MultiVisit: multiVisit,
TanggalMulai: startDate, TanggalSelesai: endDate, KelasID: strings.TrimSpace(classID), TanggalMulai: startDate, TanggalSelesai: endDate, KelasID: strings.TrimSpace(classID),
HeaderPaketID: strings.TrimSpace(headerID), Keterangan: strings.TrimSpace(notes), JumlahHari: days, HeaderPaketID: strings.TrimSpace(headerID), Keterangan: strings.TrimSpace(notes), JumlahHari: days,
Channel: strings.ToUpper(strings.TrimSpace(channel)), Layanan: result, Channel: strings.ToUpper(strings.TrimSpace(channel)), LoginBuat: loginBuat, Layanan: result,
}, "" }, ""
} }

View File

@@ -18,6 +18,7 @@ const validCreatePaketJSON = `{
"tanggal_mulai":"2026-09-01", "tanggal_mulai":"2026-09-01",
"tanggal_selesai":"2026-09-30", "tanggal_selesai":"2026-09-30",
"kelas_id":"Default", "kelas_id":"Default",
"login_buat":"Joko Hudoro",
"layanan":[{ "layanan":[{
"layanan_id":"1200", "layanan_id":"1200",
"departemen_id":"MED-PNJ-LAB", "departemen_id":"MED-PNJ-LAB",

View File

@@ -149,6 +149,10 @@ func validateRegistration(payload map[string]any, configuredDepartment string) (
if !present || strings.TrimSpace(channel) == "" { if !present || strings.TrimSpace(channel) == "" {
channel = "CPONE" channel = "CPONE"
} }
loginBuat, message := requiredLoginBuat(payload)
if message != "" {
return repository.RegistrationParams{}, message
}
referrer, _, message := optionalString(payload, "perujuk", 150) referrer, _, message := optionalString(payload, "perujuk", 150)
if message != "" { if message != "" {
return repository.RegistrationParams{}, message return repository.RegistrationParams{}, message
@@ -183,7 +187,7 @@ func validateRegistration(payload map[string]any, configuredDepartment string) (
TariffPeriodID: tariffPeriod, CompanyID: strings.TrimSpace(companyID), InsuranceID: strings.TrimSpace(insuranceID), TariffPeriodID: tariffPeriod, CompanyID: strings.TrimSpace(companyID), InsuranceID: strings.TrimSpace(insuranceID),
GroupTariffID: groupTariff, ProviderID: strings.TrimSpace(providerID), PolicyNumber: strings.TrimSpace(policyNumber), GroupTariffID: groupTariff, ProviderID: strings.TrimSpace(providerID), PolicyNumber: strings.TrimSpace(policyNumber),
GuaranteeNumber: strings.TrimSpace(guaranteeNumber), EntryCode: strings.TrimSpace(entryCode), ClassID: strings.TrimSpace(classID), GuaranteeNumber: strings.TrimSpace(guaranteeNumber), EntryCode: strings.TrimSpace(entryCode), ClassID: strings.TrimSpace(classID),
Notes: strings.TrimSpace(notes), Referrer: strings.TrimSpace(referrer), EmployeeID: strings.TrimSpace(employeeID), EmployeeName: strings.TrimSpace(employeeName), Channel: normalizeText(channel), PackageID: packageID, Notes: strings.TrimSpace(notes), Referrer: strings.TrimSpace(referrer), EmployeeID: strings.TrimSpace(employeeID), EmployeeName: strings.TrimSpace(employeeName), Channel: normalizeText(channel), LoginBuat: loginBuat, PackageID: packageID,
}, "" }, ""
} }

View File

@@ -42,7 +42,8 @@ func TestRegisterPatientUsesConfiguredDepartmentAndNormalizesPayload(t *testing.
"group_tarif_id":18, "group_tarif_id":18,
"kode_paket":"PN2600036", "kode_paket":"PN2600036",
"kelas_id":"Default", "kelas_id":"Default",
"channel":"cpone" "channel":"cpone",
"login_buat":"Joko Hudoro"
}`)) }`))
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
handler.RegisterPatient(recorder, request) handler.RegisterPatient(recorder, request)
@@ -53,7 +54,7 @@ func TestRegisterPatientUsesConfiguredDepartmentAndNormalizesPayload(t *testing.
if repo.params.DepartmentID != "08_01" || repo.params.NIK != "3174011501900001" || repo.params.RegistrationType != 2 { if repo.params.DepartmentID != "08_01" || repo.params.NIK != "3174011501900001" || repo.params.RegistrationType != 2 {
t.Fatalf("unexpected normalized params: %+v", repo.params) t.Fatalf("unexpected normalized params: %+v", repo.params)
} }
if repo.params.PatientTypeID != 1 || repo.params.TariffPeriodID != 10 || repo.params.GroupTariffID != 18 || repo.params.Channel != "CPONE" { if repo.params.PatientTypeID != 1 || repo.params.TariffPeriodID != 10 || repo.params.GroupTariffID != 18 || repo.params.Channel != "CPONE" || repo.params.LoginBuat != "Joko Hudoro" {
t.Fatalf("unexpected numeric/text params: %+v", repo.params) t.Fatalf("unexpected numeric/text params: %+v", repo.params)
} }
if repo.params.PackageID != "PN2600036" { if repo.params.PackageID != "PN2600036" {
@@ -72,9 +73,20 @@ func TestRegisterPatientRequiresIdentityWhenMedrecIsMissing(t *testing.T) {
} }
} }
func TestRegisterPatientRequiresLoggedInName(t *testing.T) {
handler := Handler{registrationRepo: &fakeRegistrationRepository{}, departmentID: "08_01"}
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/registration", strings.NewReader(`{"medrec_id":"00001234","channel":"CPONE"}`))
recorder := httptest.NewRecorder()
handler.RegisterPatient(recorder, request)
if recorder.Code != http.StatusUnprocessableEntity || !strings.Contains(recorder.Body.String(), "login_buat wajib diisi") {
t.Fatalf("unexpected validation response: status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestRegisterPatientForwardsRepositoryError(t *testing.T) { func TestRegisterPatientForwardsRepositoryError(t *testing.T) {
handler := Handler{registrationRepo: &fakeRegistrationRepository{err: errors.New("database unavailable")}, departmentID: "08_01"} handler := Handler{registrationRepo: &fakeRegistrationRepository{err: errors.New("database unavailable")}, departmentID: "08_01"}
request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/registration", strings.NewReader(`{"medrec_id":"00001234"}`)) request := httptest.NewRequest(http.MethodPost, "/api/cpone/patients/registration", strings.NewReader(`{"medrec_id":"00001234","login_buat":"Joko Hudoro"}`))
recorder := httptest.NewRecorder() recorder := httptest.NewRecorder()
handler.RegisterPatient(recorder, request) handler.RegisterPatient(recorder, request)

View File

@@ -16,6 +16,7 @@ type AddLabPackageParams struct {
MarkupCito float64 MarkupCito float64
Notes string Notes string
Channel string Channel string
LoginBuat string
} }
type labPackageHeader struct { type labPackageHeader struct {
@@ -161,7 +162,7 @@ func addLabPackageInTransaction(ctx context.Context, tx *sql.Tx, regID string, p
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
now := time.Now().In(jkt).Format("2006-01-02 15:04:05") now := time.Now().In(jkt).Format("2006-01-02 15:04:05")
dispenserID, visitNumber, err := ensurePackageDispenser(ctx, tx, registration.Registration, packageHeader, params.Channel, now) dispenserID, visitNumber, err := ensurePackageDispenser(ctx, tx, registration.Registration, packageHeader, params.LoginBuat, now)
if err != nil { if err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
@@ -194,7 +195,7 @@ func addLabPackageInTransaction(ctx context.Context, tx *sql.Tx, regID string, p
if err != nil { if err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
dispenserServiceID, err := insertPackageDispenserService(ctx, tx, dispenserID, registration.Registration, packageHeader.ID, packageService, operationID, params.Channel, now) dispenserServiceID, err := insertPackageDispenserService(ctx, tx, dispenserID, registration.Registration, packageHeader.ID, packageService, operationID, params.LoginBuat, now)
if err != nil { if err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
@@ -218,19 +219,19 @@ func addLabPackageInTransaction(ctx context.Context, tx *sql.Tx, regID string, p
ShareExternal: parseNullableDecimal(packageService.ShareExternal), ShareLain: parseNullableDecimal(packageService.ShareOther), ShareExternal: parseNullableDecimal(packageService.ShareExternal), ShareLain: parseNullableDecimal(packageService.ShareOther),
Catatan: params.Notes, Catatan: params.Notes,
} }
trxServiceID, err := insertPackageTransactionService(ctx, tx, resolved, discountType, discount, patient, patient, guarantor, guarantor, packageHeader.ID, registration.Registration.RegID, transaction.Transaction.TrxDepartemenID, registration.Registration.MEDRECID, packageService.DepartmentID, doctorID, classID, params.Channel, now) trxServiceID, err := insertPackageTransactionService(ctx, tx, resolved, discountType, discount, patient, patient, guarantor, guarantor, packageHeader.ID, registration.Registration.RegID, transaction.Transaction.TrxDepartemenID, registration.Registration.MEDRECID, packageService.DepartmentID, doctorID, classID, params.LoginBuat, now)
if err != nil { if err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
if _, err := tx.ExecContext(ctx, `INSERT INTO trxlayanan_detail (TrxLayananID, PaketDispenserID, PaketDispenserLayananID, LoginBuat, TanggalBuat, NA) VALUES (?, ?, ?, ?, ?, 'N')`, trxServiceID, dispenserID, dispenserServiceID, params.Channel, now); err != nil { if _, err := tx.ExecContext(ctx, `INSERT INTO trxlayanan_detail (TrxLayananID, PaketDispenserID, PaketDispenserLayananID, LoginBuat, TanggalBuat, NA) VALUES (?, ?, ?, ?, ?, 'N')`, trxServiceID, dispenserID, dispenserServiceID, params.LoginBuat, now); err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
addedIDs = append(addedIDs, master.LayananID) addedIDs = append(addedIDs, master.LayananID)
} }
if err := insertPackageAdministration(ctx, tx, dispenserID, registration.Registration.RegID, packageHeader.ID, params.Channel, now); err != nil { if err := insertPackageAdministration(ctx, tx, dispenserID, registration.Registration.RegID, packageHeader.ID, params.LoginBuat, now); err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
if err := syncLabSpecimens(ctx, tx, transaction.Transaction.TrxDepartemenID, transaction.Transaction.TrxID, params.Channel, now); err != nil { if err := syncLabSpecimens(ctx, tx, transaction.Transaction.TrxDepartemenID, transaction.Transaction.TrxID, params.LoginBuat, now); err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
if err := updateLabTransactionTotal(ctx, tx, transaction.Transaction.TrxDepartemenID, registration.Registration.RegID, registration.Registration.DepartemenID); err != nil { if err := updateLabTransactionTotal(ctx, tx, transaction.Transaction.TrxDepartemenID, registration.Registration.RegID, registration.Registration.DepartemenID); err != nil {

View File

@@ -35,6 +35,7 @@ type LabRegistrationParams struct {
WorkingDiagnosis string WorkingDiagnosis string
Notes string Notes string
Channel string Channel string
LoginBuat string
} }
type LabRegistrationOutcome struct { type LabRegistrationOutcome struct {
@@ -290,7 +291,7 @@ func registerLabInTransaction(ctx context.Context, tx *sql.Tx, params LabRegistr
params.ClassID, params.ClassID,
strings.TrimSpace(params.Notes), strings.TrimSpace(params.Notes),
shiftID, shiftID,
params.Channel, params.LoginBuat,
nowText, nowText,
) )
if err != nil { if err != nil {
@@ -309,7 +310,7 @@ func registerLabInTransaction(ctx context.Context, tx *sql.Tx, params LabRegistr
senderDoctorID, senderDoctorID,
nowText, nowText,
params.ClassID, params.ClassID,
params.Channel, params.LoginBuat,
nowText, nowText,
) )
if err != nil { if err != nil {
@@ -323,7 +324,7 @@ func registerLabInTransaction(ctx context.Context, tx *sql.Tx, params LabRegistr
if err := upsertLabNominals(ctx, tx, regID, trxDepartmentID, params.DepartmentID); err != nil { if err := upsertLabNominals(ctx, tx, regID, trxDepartmentID, params.DepartmentID); err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
if err := saveLabDiagnosis(ctx, tx, regID, patient.MEDRECID, strings.TrimSpace(params.WorkingDiagnosis), params.Channel, nowText); err != nil { if err := saveLabDiagnosis(ctx, tx, regID, patient.MEDRECID, strings.TrimSpace(params.WorkingDiagnosis), params.LoginBuat, nowText); err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }

View File

@@ -14,6 +14,7 @@ import (
type AddLabServicesParams struct { type AddLabServicesParams struct {
Layanan []RequestedLabService Layanan []RequestedLabService
Channel string Channel string
LoginBuat string
} }
type RequestedLabService struct { type RequestedLabService struct {
@@ -225,13 +226,13 @@ func addLabServicesInTransaction(ctx context.Context, tx *sql.Tx, regID string,
registration.Registration.DepartemenID, registration.Registration.DepartemenID,
doctorValue, doctorValue,
classID, classID,
params.Channel, params.LoginBuat,
now, now,
); err != nil { ); err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
} }
if err := syncLabSpecimens(ctx, tx, transaction.Transaction.TrxDepartemenID, transaction.Transaction.TrxID, params.Channel, now); err != nil { if err := syncLabSpecimens(ctx, tx, transaction.Transaction.TrxDepartemenID, transaction.Transaction.TrxID, params.LoginBuat, now); err != nil {
return LabRegistrationOutcome{}, err return LabRegistrationOutcome{}, err
} }
if err := updateLabTransactionTotal( if err := updateLabTransactionTotal(

View File

@@ -30,6 +30,7 @@ type MedrecParams struct {
DependantID string DependantID string
EmployeeName string EmployeeName string
Channel string Channel string
LoginBuat string
} }
type MedrecOutcome struct { type MedrecOutcome struct {
@@ -263,7 +264,7 @@ func insertMedrec(ctx context.Context, tx *sql.Tx, medrecID string, params Medre
params.IdentityType, params.IdentityNumber, params.EmployeeID, params.DependantID, params.IdentityType, params.IdentityNumber, params.EmployeeID, params.DependantID,
params.EmployeeName, phone, mobile, params.Email, params.Address, params.Province, params.EmployeeName, phone, mobile, params.Email, params.Address, params.Province,
params.City, params.District, params.Village, params.City, params.District, params.Village,
params.PostalCode, params.CompanyID, params.Channel, now, now) params.PostalCode, params.CompanyID, params.LoginBuat, now, now)
if err != nil { if err != nil {
return MedrecPatient{}, err return MedrecPatient{}, err
} }

View File

@@ -37,6 +37,7 @@ type CreatePaketParams struct {
Keterangan string Keterangan string
JumlahHari int64 JumlahHari int64
Channel string Channel string
LoginBuat string
Layanan []CreatePaketLayananParams Layanan []CreatePaketLayananParams
} }

View File

@@ -93,7 +93,7 @@ func (r MySQLLayananRepository) CreatePaket(ctx context.Context, params CreatePa
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 2, ?, ?, 'N')`, ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 2, ?, ?, 'N')`,
packageID, params.Nama, params.JenisPaket, boolInt(params.RawatJalan), boolInt(params.RawatInap), packageID, params.Nama, params.JenisPaket, boolInt(params.RawatJalan), boolInt(params.RawatInap),
boolInt(params.MultiVisit), nullableDate(params.TanggalMulai), nullableDate(params.TanggalSelesai), boolInt(params.MultiVisit), nullableDate(params.TanggalMulai), nullableDate(params.TanggalSelesai),
params.HeaderPaketID, params.KelasID, params.JumlahHari, "0.00", params.Keterangan, params.Channel, now) params.HeaderPaketID, params.KelasID, params.JumlahHari, "0.00", params.Keterangan, params.LoginBuat, now)
if err != nil { if err != nil {
return CreatePaketResult{}, err return CreatePaketResult{}, err
} }
@@ -124,7 +124,7 @@ func (r MySQLLayananRepository) CreatePaket(ctx context.Context, params CreatePa
packageID, departmentID, service.DepartemenID, service.LayananID, service.Jumlah, service.JumlahHari, packageID, departmentID, service.DepartemenID, service.LayananID, service.Jumlah, service.JumlahHari,
decimalText(service.HargaCents), service.TipeDiskon, decimalText(service.DiskonCents), service.TipeShare, decimalText(service.HargaCents), service.TipeDiskon, decimalText(service.DiskonCents), service.TipeShare,
decimalText(service.ShareRSCents), decimalText(service.ShareDokterCents), decimalText(service.ShareExternalCents), decimalText(service.ShareRSCents), decimalText(service.ShareDokterCents), decimalText(service.ShareExternalCents),
decimalText(service.ShareLainCents), decimalText(subtotal), params.Channel, now) decimalText(service.ShareLainCents), decimalText(subtotal), params.LoginBuat, now)
if err != nil { if err != nil {
return CreatePaketResult{}, err return CreatePaketResult{}, err
} }

View File

@@ -33,6 +33,7 @@ type RegistrationParams struct {
EmployeeID string EmployeeID string
EmployeeName string EmployeeName string
Channel string Channel string
LoginBuat string
PackageID string PackageID string
} }
@@ -129,7 +130,7 @@ func registerPatientInTransaction(ctx context.Context, tx *sql.Tx, params Regist
if found { if found {
data := RegistrationData{Patient: patient, Registration: existing} data := RegistrationData{Patient: patient, Registration: existing}
if strings.TrimSpace(params.PackageID) != "" { if strings.TrimSpace(params.PackageID) != "" {
data.Package, err = addRegistrationPackage(ctx, tx, existing, patient.MEDRECID, params.PackageID, params.Channel, params.RegistrationDate) data.Package, err = addRegistrationPackage(ctx, tx, existing, patient.MEDRECID, params.PackageID, params.LoginBuat, params.RegistrationDate)
if err != nil { if err != nil {
return RegistrationOutcome{}, err return RegistrationOutcome{}, err
} }
@@ -196,7 +197,7 @@ func registerPatientInTransaction(ctx context.Context, tx *sql.Tx, params Regist
params.ClassID, params.ClassID,
params.Notes, params.Notes,
shiftID, shiftID,
params.Channel, params.LoginBuat,
nowText, nowText,
) )
if err != nil { if err != nil {
@@ -214,7 +215,7 @@ func registerPatientInTransaction(ctx context.Context, tx *sql.Tx, params Regist
} }
data := RegistrationData{Patient: patient, Registration: registration} data := RegistrationData{Patient: patient, Registration: registration}
if strings.TrimSpace(params.PackageID) != "" { if strings.TrimSpace(params.PackageID) != "" {
data.Package, err = addRegistrationPackage(ctx, tx, registration, patient.MEDRECID, params.PackageID, params.Channel, params.RegistrationDate) data.Package, err = addRegistrationPackage(ctx, tx, registration, patient.MEDRECID, params.PackageID, params.LoginBuat, params.RegistrationDate)
if err != nil { if err != nil {
return RegistrationOutcome{}, err return RegistrationOutcome{}, err
} }

View File

@@ -63,10 +63,10 @@ if [[ "$requested_scope" == "user" ]]; then
elif [[ "$requested_scope" == "system" ]]; then elif [[ "$requested_scope" == "system" ]]; then
systemctl_cmd=(systemctl) systemctl_cmd=(systemctl)
resolved_scope=system resolved_scope=system
elif systemctl --user show "$service" >/dev/null 2>&1; then elif [[ "$(systemctl --user show "$service" -p LoadState --value 2>/dev/null || true)" == loaded ]]; then
systemctl_cmd=(systemctl --user) systemctl_cmd=(systemctl --user)
resolved_scope=user resolved_scope=user
elif systemctl show "$service" >/dev/null 2>&1; then elif [[ "$(systemctl show "$service" -p LoadState --value 2>/dev/null || true)" == loaded ]]; then
systemctl_cmd=(systemctl) systemctl_cmd=(systemctl)
resolved_scope=system resolved_scope=system
else else
@@ -172,11 +172,15 @@ fi
for attempt in {1..20}; do for attempt in {1..20}; do
if "${systemctl_cmd[@]}" is-active --quiet "$service"; then if "${systemctl_cmd[@]}" is-active --quiet "$service"; then
new_pid="$("${systemctl_cmd[@]}" show "$service" -p MainPID --value 2>/dev/null || true)" new_pid="$("${systemctl_cmd[@]}" show "$service" -p MainPID --value 2>/dev/null || true)"
if [[ "$restart_mode" == "systemctl" || "$new_pid" != "$main_pid" ]]; then if [[ "$new_pid" =~ ^[1-9][0-9]*$ && ( "$restart_mode" == "systemctl" || "$new_pid" != "$main_pid" ) ]]; then
echo "Service aktif: $service (MainPID=${new_pid:-unknown})" sleep 2
confirmed_pid="$("${systemctl_cmd[@]}" show "$service" -p MainPID --value 2>/dev/null || true)"
if [[ "$confirmed_pid" == "$new_pid" ]] && "${systemctl_cmd[@]}" is-active --quiet "$service"; then
echo "Service aktif: $service (MainPID=$confirmed_pid)"
exit 0 exit 0
fi fi
fi fi
fi
sleep 1 sleep 1
done done