268 lines
8.9 KiB
Go
268 lines
8.9 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type RegistrationParams struct {
|
|
MedrecID string
|
|
NIK string
|
|
BirthDate time.Time
|
|
RegistrationDate time.Time
|
|
DepartmentID string
|
|
DoctorID string
|
|
PatientTypeID int64
|
|
RegistrationType int64
|
|
TariffPeriodID int64
|
|
CompanyID string
|
|
InsuranceID string
|
|
GroupTariffID int64
|
|
ProviderID string
|
|
PolicyNumber string
|
|
GuaranteeNumber string
|
|
EntryCode string
|
|
ClassID string
|
|
Notes string
|
|
Channel string
|
|
}
|
|
|
|
type RegistrationOutcome struct {
|
|
HTTPStatus int
|
|
Success bool
|
|
Status string
|
|
Message string
|
|
Data any
|
|
}
|
|
|
|
type RegistrationData struct {
|
|
Patient MedrecPatient `json:"patient"`
|
|
Registration RegistrationRecord `json:"registration"`
|
|
}
|
|
|
|
type RegistrationRecord struct {
|
|
RegID string `json:"RegID"`
|
|
MEDRECID string `json:"MEDRECID"`
|
|
Tanggal string `json:"Tanggal"`
|
|
DepartmentID string `json:"DepartemenID"`
|
|
DoctorID string `json:"DokterID"`
|
|
PatientTypeID int64 `json:"JenisPasienID"`
|
|
RegistrationID int64 `json:"JenisRegID"`
|
|
TariffPeriodID int64 `json:"PeriodeTarifID"`
|
|
CompanyID string `json:"PerusahaanID"`
|
|
InsuranceID string `json:"AsuransiID"`
|
|
ProviderID string `json:"ProviderID"`
|
|
GroupTariffID int64 `json:"GroupTarifID"`
|
|
PolicyNumber string `json:"NomerPolis"`
|
|
Pulang string `json:"Pulang"`
|
|
BatalDaftar string `json:"BatalDaftar"`
|
|
}
|
|
|
|
func (r MySQLLayananRepository) RegisterPatient(ctx context.Context, params RegistrationParams) (RegistrationOutcome, error) {
|
|
for attempt := 0; attempt < 3; attempt++ {
|
|
outcome, err := r.registerPatientAttempt(ctx, params)
|
|
if err == nil {
|
|
return outcome, nil
|
|
}
|
|
if !isConcurrencyError(err) || ctx.Err() != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
}
|
|
return RegistrationOutcome{}, fmt.Errorf("registrasi pasien gagal karena konflik transaksi")
|
|
}
|
|
|
|
func (r MySQLLayananRepository) registerPatientAttempt(ctx context.Context, params RegistrationParams) (outcome RegistrationOutcome, err error) {
|
|
tx, err := r.db.BeginTx(ctx, nil)
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
defer func() {
|
|
if recovered := recover(); recovered != nil {
|
|
_ = tx.Rollback()
|
|
panic(recovered)
|
|
}
|
|
if err != nil {
|
|
_ = tx.Rollback()
|
|
}
|
|
}()
|
|
|
|
outcome, err = registerPatientInTransaction(ctx, tx, params)
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
if err = tx.Commit(); err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
return outcome, nil
|
|
}
|
|
|
|
func registerPatientInTransaction(ctx context.Context, tx *sql.Tx, params RegistrationParams) (RegistrationOutcome, error) {
|
|
patient, found, err := findRegistrationPatient(ctx, tx, params)
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
if !found {
|
|
return registrationOutcome(404, false, "patient_not_found", "Pasien tidak ditemukan", nil), nil
|
|
}
|
|
|
|
existing, found, err := findExistingRegistration(ctx, tx, patient.MEDRECID, params)
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
if found {
|
|
data := RegistrationData{Patient: patient, Registration: existing}
|
|
return registrationOutcome(200, true, "already_registered_visit", "Pasien sudah memiliki registrasi aktif pada tujuan yang sama", data), nil
|
|
}
|
|
|
|
jakarta, err := time.LoadLocation("Asia/Jakarta")
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
now := time.Now().In(jakarta)
|
|
nowText := now.Format("2006-01-02 15:04:05")
|
|
regDate := params.RegistrationDate.Format("2006-01-02")
|
|
prefix := "P" + params.RegistrationDate.Format("0601")
|
|
regID, err := nextRegistrationID(ctx, tx, prefix)
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
shiftID, err := resolveDailyShiftID(ctx, tx, nil, params.RegistrationDate)
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
|
|
var previousVisits int64
|
|
if err := tx.QueryRowContext(ctx, `SELECT COUNT(*) FROM regpas WHERE MEDRECID = ? AND NA = 'N'`, patient.MEDRECID).Scan(&previousVisits); err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
|
|
_, err = tx.ExecContext(ctx, `
|
|
INSERT INTO regpas (
|
|
RegID, MEDRECID, Nama, ALAMATEXTPAS, TELPEXTPAS, TglLahir, JnsKelamin,
|
|
KunjunganKe, Tanggal, JenisPasienID, PeriodeTarifID, PerusahaanID,
|
|
GroupTarifID, AsuransiID, ProviderID, NomerPolis, NIK, NamaKaryawan,
|
|
NomerJaminan, KodeMasukID, DepartemenID, DepartemenID2, JenisRegID,
|
|
DokterID, DokterID2, KelasID, MataUang, Rate, Pulang, BolehPulang, Lunas,
|
|
BatalDaftar, Catatan, CatatanMasuk, ShiftHarianIDDaftar, LoginBuat, TanggalBuat, NA
|
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, '', ?, ?, ?, ?, ?, ?, '', ?, 'IDR', 1, 'N', 'N', 'N', 'N', ?, '', ?, ?, ?, 'N')`,
|
|
regID,
|
|
patient.MEDRECID,
|
|
patient.Nama,
|
|
"",
|
|
firstNonEmpty(pointerValue(patient.Ponsel), pointerValue(patient.Telepon)),
|
|
pointerValue(patient.TanggalLahir),
|
|
firstNonEmpty(pointerValue(patient.Kelamin), "L"),
|
|
previousVisits+1,
|
|
regDate,
|
|
params.PatientTypeID,
|
|
params.TariffPeriodID,
|
|
params.CompanyID,
|
|
params.GroupTariffID,
|
|
params.InsuranceID,
|
|
params.ProviderID,
|
|
params.PolicyNumber,
|
|
firstNonEmpty(pointerValue(patient.NomerSosial), params.NIK),
|
|
params.GuaranteeNumber,
|
|
params.EntryCode,
|
|
params.DepartmentID,
|
|
params.DepartmentID,
|
|
params.RegistrationType,
|
|
params.DoctorID,
|
|
params.ClassID,
|
|
params.Notes,
|
|
shiftID,
|
|
params.Channel,
|
|
nowText,
|
|
)
|
|
if err != nil {
|
|
return RegistrationOutcome{}, err
|
|
}
|
|
|
|
registration := RegistrationRecord{
|
|
RegID: regID, MEDRECID: patient.MEDRECID, Tanggal: regDate,
|
|
DepartmentID: params.DepartmentID, DoctorID: params.DoctorID,
|
|
PatientTypeID: params.PatientTypeID, RegistrationID: params.RegistrationType,
|
|
TariffPeriodID: params.TariffPeriodID, CompanyID: params.CompanyID,
|
|
InsuranceID: params.InsuranceID, ProviderID: params.ProviderID,
|
|
GroupTariffID: params.GroupTariffID, PolicyNumber: params.PolicyNumber,
|
|
Pulang: "N", BatalDaftar: "N",
|
|
}
|
|
return registrationOutcome(201, true, "registered", "Registrasi berhasil disimpan", RegistrationData{Patient: patient, Registration: registration}), nil
|
|
}
|
|
|
|
func findRegistrationPatient(ctx context.Context, tx *sql.Tx, params RegistrationParams) (MedrecPatient, bool, error) {
|
|
query := medrecPatientSelect + ` WHERE NA = 'N' AND MEDRECID = ? LIMIT 1 FOR UPDATE`
|
|
args := []any{params.MedrecID}
|
|
if strings.TrimSpace(params.MedrecID) == "" {
|
|
query = medrecPatientSelect + ` WHERE NA = 'N' AND JenisNomerSosial = 'KTP' AND NomerSosial = ? AND DATE(TanggalLahir) = ? LIMIT 1 FOR UPDATE`
|
|
args = []any{params.NIK, params.BirthDate.Format("2006-01-02")}
|
|
}
|
|
patient, err := scanMedrecPatient(tx.QueryRowContext(ctx, query, args...))
|
|
if err == sql.ErrNoRows {
|
|
return MedrecPatient{}, false, nil
|
|
}
|
|
return patient, err == nil, err
|
|
}
|
|
|
|
func findExistingRegistration(ctx context.Context, tx *sql.Tx, medrecID string, params RegistrationParams) (RegistrationRecord, bool, error) {
|
|
query := `
|
|
SELECT RegID, MEDRECID, DATE_FORMAT(Tanggal, '%Y-%m-%d'), DepartemenID, DokterID,
|
|
JenisPasienID, JenisRegID, PeriodeTarifID, PerusahaanID, AsuransiID,
|
|
ProviderID, GroupTarifID, NomerPolis, Pulang, BatalDaftar
|
|
FROM regpas
|
|
WHERE MEDRECID = ? AND DATE(Tanggal) = ? AND DepartemenID = ?
|
|
AND Pulang = 'N' AND (BatalDaftar IS NULL OR BatalDaftar IN ('N', '')) AND NA = 'N'`
|
|
args := []any{medrecID, params.RegistrationDate.Format("2006-01-02"), params.DepartmentID}
|
|
if params.DoctorID != "" {
|
|
query += ` AND DokterID = ?`
|
|
args = append(args, params.DoctorID)
|
|
}
|
|
query += ` ORDER BY RegID DESC LIMIT 1 FOR UPDATE`
|
|
|
|
var registration RegistrationRecord
|
|
err := tx.QueryRowContext(ctx, query, args...).Scan(
|
|
®istration.RegID, ®istration.MEDRECID, ®istration.Tanggal, ®istration.DepartmentID,
|
|
®istration.DoctorID, ®istration.PatientTypeID, ®istration.RegistrationID,
|
|
®istration.TariffPeriodID, ®istration.CompanyID, ®istration.InsuranceID,
|
|
®istration.ProviderID, ®istration.GroupTariffID, ®istration.PolicyNumber,
|
|
®istration.Pulang, ®istration.BatalDaftar,
|
|
)
|
|
if err == sql.ErrNoRows {
|
|
return RegistrationRecord{}, false, nil
|
|
}
|
|
return registration, err == nil, err
|
|
}
|
|
|
|
func nextRegistrationID(ctx context.Context, tx *sql.Tx, prefix string) (string, error) {
|
|
var lastID string
|
|
err := tx.QueryRowContext(ctx, `SELECT RegID FROM regpas WHERE RegID LIKE ? ORDER BY RegID DESC LIMIT 1 FOR UPDATE`, prefix+"%").Scan(&lastID)
|
|
if err == sql.ErrNoRows {
|
|
return prefix + "00001", nil
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
number, err := strconv.Atoi(strings.TrimPrefix(lastID, prefix))
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return fmt.Sprintf("%s%05d", prefix, number+1), nil
|
|
}
|
|
|
|
func registrationOutcome(statusCode int, success bool, status, message string, data any) RegistrationOutcome {
|
|
return RegistrationOutcome{HTTPStatus: statusCode, Success: success, Status: status, Message: message, Data: data}
|
|
}
|
|
|
|
func firstNonEmpty(values ...string) string {
|
|
for _, value := range values {
|
|
if strings.TrimSpace(value) != "" {
|
|
return value
|
|
}
|
|
}
|
|
return ""
|
|
}
|