package http import ( "errors" "net/http" "regexp" "strings" "time" "primaya-api/cpone-middleware/internal/repository" ) var ( spaces = regexp.MustCompile(`\s+`) nonAlphaNumber = regexp.MustCompile(`[^A-Za-z0-9]`) nonDigit = regexp.MustCompile(`\D`) ktpNumber = regexp.MustCompile(`^\d{16}$`) emailAddress = regexp.MustCompile(`^[^\s@]+@[^\s@]+\.[^\s@]+$`) ) type MedrecResponse struct { Success bool `json:"success"` Status string `json:"status"` Message string `json:"message"` Data any `json:"data"` } func (h Handler) StoreMedrec(w http.ResponseWriter, r *http.Request) { payload, _ := decodeJSONObject(r) params, message := validateMedrec(payload) if message != "" { writeMedrecResponse(w, http.StatusUnprocessableEntity, false, "validation_error", message, nil) return } outcome, err := h.patientRepo.StoreMedrec(r.Context(), params) if err != nil { var validationErr repository.ValidationError if errors.As(err, &validationErr) { writeMedrecResponse(w, http.StatusUnprocessableEntity, false, "validation_error", validationErr.Message, nil) return } writeMedrecResponse(w, http.StatusInternalServerError, false, "error", err.Error(), nil) return } writeMedrecResponse(w, outcome.HTTPStatus, outcome.Success, outcome.Status, outcome.Message, outcome.Data) } func validateMedrec(payload map[string]any) (repository.MedrecParams, string) { rs, message := requiredString(payload, "rs", 20, "Kode RS wajib diisi") if message != "" { return repository.MedrecParams{}, message } name, message := requiredString(payload, "nama", 255, "Nama pasien wajib diisi") if message != "" { return repository.MedrecParams{}, message } gender, message := requiredString(payload, "jenis_kelamin", 0, "Jenis kelamin wajib diisi") if message != "" { return repository.MedrecParams{}, message } if gender != "L" && gender != "P" && gender != "l" && gender != "p" { return repository.MedrecParams{}, "Jenis kelamin harus L atau P" } dateOfBirth, message := requiredString(payload, "tgl_lahir", 0, "Tanggal lahir wajib diisi") if message != "" { return repository.MedrecParams{}, message } jakarta, err := time.LoadLocation("Asia/Jakarta") if err != nil { return repository.MedrecParams{}, err.Error() } birthDate, err := time.ParseInLocation("2006-01-02", dateOfBirth, jakarta) if err != nil || birthDate.Format("2006-01-02") != dateOfBirth { return repository.MedrecParams{}, "Tanggal lahir harus format Y-m-d" } now := time.Now().In(jakarta) today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, jakarta) if birthDate.After(today) { return repository.MedrecParams{}, "Tanggal lahir tidak boleh lebih dari hari ini" } identityType, present, message := optionalString(payload, "jenis_nomer_sosial", 20) if message != "" { return repository.MedrecParams{}, message } if !present || identityType == "" { identityType = "KTP" } identityNumber, identityPresent, message := optionalString(payload, "nomer_sosial", 50) if message != "" { return repository.MedrecParams{}, message } nik, nikPresent, message := optionalString(payload, "nik", 50) if message != "" { return repository.MedrecParams{}, message } if !identityPresent || identityNumber == "" { if nikPresent { identityNumber = nik } } phone, phonePresent, message := optionalString(payload, "telepon", 30) if message != "" { return repository.MedrecParams{}, message } mobile, mobilePresent, message := optionalString(payload, "ponsel", 30) if message != "" { return repository.MedrecParams{}, message } if !phonePresent && !mobilePresent { return repository.MedrecParams{}, "Telepon atau ponsel wajib diisi" } email, _, message := optionalString(payload, "email", 255) if message != "" { return repository.MedrecParams{}, message } if email != "" && !emailAddress.MatchString(strings.TrimSpace(email)) { return repository.MedrecParams{}, "The email must be a valid email address." } placeOfBirth, message := optionalMedrecString(payload, "tempat_lahir", 100) if message != "" { return repository.MedrecParams{}, message } address, message := optionalMedrecString(payload, "alamat", 500) if message != "" { return repository.MedrecParams{}, message } province, message := optionalMedrecString(payload, "propinsi", 50) if message != "" { return repository.MedrecParams{}, message } city, message := optionalMedrecString(payload, "kabupaten", 50) if message != "" { return repository.MedrecParams{}, message } district, message := optionalMedrecString(payload, "kecamatan", 50) if message != "" { return repository.MedrecParams{}, message } village, message := optionalMedrecString(payload, "kelurahan", 50) if message != "" { return repository.MedrecParams{}, message } postalCode, message := optionalMedrecString(payload, "kode_pos", 20) if message != "" { return repository.MedrecParams{}, message } companyID, message := optionalMedrecString(payload, "perusahaan_id", 50) if message != "" { return repository.MedrecParams{}, message } employeeID, message := optionalMedrecString(payload, "nik_karyawan", 50) if message != "" { return repository.MedrecParams{}, message } dependantID, message := optionalMedrecString(payload, "dependant_id", 4) if message != "" { return repository.MedrecParams{}, message } employeeName, message := optionalMedrecString(payload, "nama_karyawan", 100) if message != "" { return repository.MedrecParams{}, message } channel, channelPresent, message := optionalString(payload, "channel", 50) if message != "" { return repository.MedrecParams{}, message } if !channelPresent || channel == "" { channel = "CPONE" } identityType = normalizeText(identityType) identityNumber = normalizeIdentity(identityNumber) if identityNumber == "" { return repository.MedrecParams{}, "NIK/nomer_sosial wajib diisi" } if identityType == "KTP" && !ktpNumber.MatchString(identityNumber) { return repository.MedrecParams{}, "NIK/KTP harus 16 digit angka" } phone = normalizePhone(phone) mobile = normalizePhone(mobile) if phone == "" && mobile == "" { return repository.MedrecParams{}, "Telepon atau ponsel wajib diisi" } return repository.MedrecParams{ HospitalCode: normalizeText(rs), Name: normalizeName(name), Gender: normalizeText(gender), DateOfBirth: birthDate, IdentityType: identityType, IdentityNumber: identityNumber, Phone: phone, Mobile: mobile, Email: strings.ToLower(strings.TrimSpace(email)), PlaceOfBirth: normalizeName(placeOfBirth), Address: strings.TrimSpace(address), Province: strings.TrimSpace(province), City: strings.TrimSpace(city), District: strings.TrimSpace(district), Village: strings.TrimSpace(village), PostalCode: strings.TrimSpace(postalCode), CompanyID: strings.TrimSpace(companyID), EmployeeID: strings.TrimSpace(employeeID), DependantID: strings.TrimSpace(dependantID), EmployeeName: strings.TrimSpace(employeeName), Channel: normalizeText(channel), }, "" } func optionalMedrecString(payload map[string]any, key string, maxLength int) (string, string) { value, _, message := optionalString(payload, key, maxLength) return value, message } func normalizeName(value string) string { return strings.ToUpper(strings.TrimSpace(spaces.ReplaceAllString(value, " "))) } func normalizeText(value string) string { return strings.ToUpper(strings.TrimSpace(value)) } func normalizeIdentity(value string) string { return strings.ToUpper(nonAlphaNumber.ReplaceAllString(value, "")) } func normalizePhone(value string) string { phone := nonDigit.ReplaceAllString(value, "") if strings.HasPrefix(phone, "62") { return "0" + strings.TrimPrefix(phone, "62") } return phone } func writeMedrecResponse(w http.ResponseWriter, statusCode int, success bool, status, message string, data any) { WriteJSON(w, statusCode, MedrecResponse{Success: success, Status: status, Message: message, Data: data}) }