808 lines
26 KiB
Go
808 lines
26 KiB
Go
package transaction
|
|
|
|
import (
|
|
"api-service/internal/config"
|
|
"api-service/internal/database"
|
|
"api-service/internal/models"
|
|
"api-service/internal/models/transaction"
|
|
"api-service/pkg/logger"
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/google/uuid"
|
|
"github.com/jmoiron/sqlx"
|
|
"log"
|
|
"net/http"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
var (
|
|
db database.Service
|
|
once sync.Once
|
|
validate *validator.Validate
|
|
)
|
|
|
|
func validated(fl validator.FieldLevel) bool {
|
|
return models.IsValidStatus(fl.Field().String())
|
|
}
|
|
|
|
func init() {
|
|
once.Do(func() {
|
|
db = database.New(config.LoadConfig())
|
|
validate = validator.New()
|
|
|
|
// Register custom validations if needed
|
|
validate.RegisterValidation("retribusi_status", validated)
|
|
|
|
if db == nil {
|
|
logger.Fatal("Failed to initialize database connection")
|
|
}
|
|
})
|
|
}
|
|
|
|
type TiketHandler struct {
|
|
db database.Service
|
|
}
|
|
|
|
func NewtiketHandler() *TiketHandler {
|
|
return &TiketHandler{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func (h *TiketHandler) respondError(c *gin.Context, message string, err error, statusCode int) {
|
|
errorMessage := message
|
|
if gin.Mode() == gin.ReleaseMode {
|
|
errorMessage = "Internal server error"
|
|
}
|
|
c.JSON(statusCode, models.ErrorResponse{Error: errorMessage, Code: statusCode, Message: err.Error(), Timestamp: time.Now()})
|
|
}
|
|
|
|
func (h *TiketHandler) Createtiket(c *gin.Context) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var req transaction.CreateTicketRequest
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := validate.Struct(&req); err != nil {
|
|
h.respondError(c, "Validation failed", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
log.Println(req)
|
|
|
|
kodeID, err := strconv.Atoi(req.IDLoket)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Kode harus berupa angka"})
|
|
return
|
|
}
|
|
|
|
IDKlinik, err := strconv.Atoi(req.IdKlinik)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Kode harus berupa angka"})
|
|
return
|
|
}
|
|
|
|
dbConn, err := h.db.GetSQLXDB("postgres_antrean")
|
|
if err != nil {
|
|
logger.Error("Database connection failed", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
|
|
return
|
|
}
|
|
newUUID := uuid.New().String()
|
|
currentDate := time.Now().Format("2006-01-02")
|
|
|
|
codeResult, err := h.GetKode(ctx, dbConn, kodeID)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
Shift, err := h.GetShift(ctx, dbConn, IDKlinik)
|
|
if err != nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": err.Error()})
|
|
return
|
|
}
|
|
|
|
quotaSeat, usedQuota, err := h.CheckAndLockQuota(ctx, dbConn, kodeID, currentDate)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "Loket tidak ditemukan"})
|
|
return
|
|
}
|
|
logger.Error("Failed to check quota", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to check quota"})
|
|
return
|
|
}
|
|
|
|
availableQuota := quotaSeat - usedQuota
|
|
if availableQuota <= 0 {
|
|
logger.Warn("Quota loket sudah penuh", map[string]interface{}{
|
|
"id_loket": kodeID,
|
|
"quota_seat": quotaSeat,
|
|
"used_quota": usedQuota,
|
|
"available_quota": availableQuota,
|
|
})
|
|
c.JSON(http.StatusConflict, gin.H{
|
|
"error": "Quota loket sudah penuh",
|
|
"details": fmt.Sprintf("Quota tersedia: %d", availableQuota),
|
|
})
|
|
return
|
|
}
|
|
|
|
newUsedQuota, err := h.IncrementUsedQuota(ctx, dbConn, kodeID, currentDate, newUUID)
|
|
if err != nil {
|
|
logger.Error("Failed to update quota", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update quota"})
|
|
return
|
|
}
|
|
|
|
logger.Info("Quota updated successfully", map[string]interface{}{
|
|
"id_loket": req.IDLoket,
|
|
"new_used_quota": newUsedQuota,
|
|
"remaining_quota": quotaSeat - newUsedQuota,
|
|
})
|
|
|
|
barcodeNumber, err := h.GenerateVisitCode(ctx, dbConn.DB)
|
|
if err != nil {
|
|
logger.Error("Failed to generate barcode", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate barcode: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
barcodeData, err := h.insertBarcode(ctx, dbConn.DB, barcodeNumber)
|
|
if err != nil {
|
|
logger.Error("Failed to insert barcode", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert barcode: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
ticketNumber, err := h.generateTicketNumber(ctx, dbConn.DB, codeResult, currentDate)
|
|
if err != nil {
|
|
logger.Error("Failed to generate ticket", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate ticket: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
idtiket, datetimeStart, err := h.insertTicket(ctx, dbConn.DB, barcodeData.IdBarcode, ticketNumber, codeResult, req.IdKlinik, req.IDLoket, req.IDpembayaran)
|
|
if err != nil {
|
|
logger.Error("Failed to insert ticket", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert ticket: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
errVisit := h.insertcalling(ctx, dbConn.DB, req.Idklinikstatus, req.Statuspasien, idtiket)
|
|
if errVisit != nil {
|
|
logger.Error("Failed to insert calling ", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
errVisitservice := h.insertvisit(ctx, dbConn.DB, req.Statuspasien, idtiket)
|
|
if errVisitservice != nil {
|
|
logger.Error("Failed to insert calling ", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
errVisit2 := h.insertcalling(ctx, dbConn.DB, req.Idklinikstatus, req.Statuspasien2, idtiket)
|
|
if errVisit2 != nil {
|
|
logger.Error("Failed to insert calling ", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
errVisitservice2 := h.insertvisit(ctx, dbConn.DB, req.Statuspasien2, idtiket)
|
|
if errVisitservice2 != nil {
|
|
logger.Error("Failed to insert calling ", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling: " + err.Error()})
|
|
return
|
|
}
|
|
|
|
logger.Info("Barcode and ticket generated successfully", map[string]interface{}{
|
|
"barcode": barcodeNumber,
|
|
"ticket": req.IDLoket + ticketNumber,
|
|
"visit_id": barcodeData.IdBarcode,
|
|
})
|
|
|
|
resbarcodeData := &transaction.BarcodeData{
|
|
IdBarcode: barcodeData.IdBarcode,
|
|
Ticket: codeResult + ticketNumber,
|
|
Barcode: barcodeNumber,
|
|
NamaKlinik: req.NamaKlinik,
|
|
CreatedAt: barcodeData.CreatedAt,
|
|
Active: true,
|
|
Shift: "shift" + Shift,
|
|
Dokter: req.NamaDokter,
|
|
DatetimeStart: datetimeStart,
|
|
RemainingQuota: strconv.Itoa(quotaSeat - newUsedQuota),
|
|
}
|
|
|
|
c.JSON(http.StatusCreated, transaction.BarcodeResponse{
|
|
Message: "Barcode berhasil di-generate",
|
|
Data: resbarcodeData,
|
|
})
|
|
}
|
|
|
|
func (h *TiketHandler) GetKode(ctx context.Context, dbConn *sqlx.DB, kodeID int) (string, error) {
|
|
var code string
|
|
query := `SELECT mrc.code FROM master.ms_registration_counter mrc WHERE mrc.id = $1`
|
|
|
|
err := dbConn.QueryRowContext(ctx, query, kodeID).Scan(&code)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return "", fmt.Errorf("kode dengan id %d tidak ditemukan", kodeID)
|
|
}
|
|
return "", fmt.Errorf("failed to get kode: %w", err)
|
|
}
|
|
|
|
return code, nil
|
|
}
|
|
|
|
func (h *TiketHandler) GetShift(ctx context.Context, dbConn *sqlx.DB, IDKlinik int) (string, error) {
|
|
var shift string
|
|
query := ` select mhss.shift_number from master.ms_healthcare_service mhs
|
|
inner join master.ms_healthcare_service_shift mhss on mhss.fk_ms_healthcare_service_id = mhs.id
|
|
where mhs.id = $1`
|
|
|
|
err := dbConn.QueryRowContext(ctx, query, IDKlinik).Scan(&shift)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return "", fmt.Errorf("kode dengan id %d tidak ditemukan", IDKlinik)
|
|
}
|
|
return "", fmt.Errorf("failed to get kode: %w", err)
|
|
}
|
|
|
|
return shift, nil
|
|
}
|
|
|
|
func (h *TiketHandler) GenerateVisitCode(ctx context.Context, dbConn *sql.DB) (string, error) {
|
|
today := time.Now().Format("060102") // Format: YYMMDD
|
|
|
|
query := `
|
|
SELECT tpv.visit_code::TEXT
|
|
FROM transaction.tr_patient_visit tpv
|
|
WHERE tpv.visit_code::TEXT LIKE $1 || '%'
|
|
AND tpv.registration_datetime >= CURRENT_DATE
|
|
AND tpv.registration_datetime < CURRENT_DATE + INTERVAL '1 day'
|
|
ORDER BY tpv.visit_code DESC
|
|
LIMIT 1
|
|
`
|
|
|
|
var lastVisitCode string
|
|
err := dbConn.QueryRowContext(ctx, query, today).Scan(&lastVisitCode)
|
|
|
|
var newNumber int
|
|
if err == sql.ErrNoRows {
|
|
newNumber = 1
|
|
} else if err != nil {
|
|
return "", fmt.Errorf("failed to get last visit code: %w", err)
|
|
} else {
|
|
|
|
if len(lastVisitCode) >= 11 {
|
|
lastNumberStr := lastVisitCode[6:] // Ambil 5 digit terakhir
|
|
lastNumber, _ := strconv.Atoi(lastNumberStr)
|
|
newNumber = lastNumber + 1
|
|
} else {
|
|
newNumber = 1
|
|
}
|
|
}
|
|
|
|
// Format: YYMMDD + 5 digit dengan leading zero
|
|
newVisitCode := fmt.Sprintf("%s%05d", today, newNumber)
|
|
|
|
return newVisitCode, nil
|
|
}
|
|
|
|
func (h *TiketHandler) insertBarcode(ctx context.Context, dbConn *sql.DB, barcode string) (*transaction.BarcodeData, error) {
|
|
barcodeInt, err := strconv.ParseInt(barcode, 10, 64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to convert barcode to integer: %w", err)
|
|
}
|
|
|
|
query := `
|
|
INSERT INTO transaction.tr_patient_visit
|
|
(visit_code, registration_datetime,active)
|
|
VALUES
|
|
($1, NOW(),true)
|
|
RETURNING id,visit_code, registration_datetime, active
|
|
`
|
|
|
|
var barcodeData transaction.BarcodeData
|
|
var visitCodeInt int64
|
|
|
|
err = dbConn.QueryRowContext(ctx, query, barcodeInt).Scan(
|
|
&barcodeData.IdBarcode,
|
|
&visitCodeInt,
|
|
&barcodeData.CreatedAt,
|
|
&barcodeData.Active,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to insert barcode: %w", err)
|
|
}
|
|
|
|
barcodeData.Barcode = strconv.FormatInt(visitCodeInt, 10)
|
|
|
|
return &barcodeData, nil
|
|
}
|
|
|
|
// func (h *TiketHandler) generateTicketNumber(ctx context.Context, dbConn *sql.DB, healthcareServiceCode string, currentDate string) (string, error) {
|
|
// query := `
|
|
// SELECT ticket
|
|
// FROM transaction.tr_patient_visit_healthcare_service tpvhs
|
|
// WHERE tpvhs.healtcare_service_code = $1
|
|
// AND DATE(tpvhs.datetime_start) = $2
|
|
// ORDER BY tpvhs.datetime_start DESC, tpvhs.id DESC
|
|
// LIMIT 1
|
|
// `
|
|
//
|
|
// var lastTicket string
|
|
// err := dbConn.QueryRowContext(ctx, query, healthcareServiceCode, currentDate).Scan(&lastTicket)
|
|
//
|
|
// var nextNumber int
|
|
//
|
|
// if err == sql.ErrNoRows {
|
|
// nextNumber = 1
|
|
// logger.Info("No ticket found for today, starting from 001", map[string]interface{}{
|
|
// "code": healthcareServiceCode,
|
|
// })
|
|
// } else if err != nil {
|
|
// return "", fmt.Errorf("failed to query last ticket: %w", err)
|
|
// } else {
|
|
// lastNumber, err := strconv.Atoi(lastTicket)
|
|
// if err != nil {
|
|
// nextNumber = 1
|
|
// logger.Warn("Failed to parse ticket number, resetting to 001", map[string]interface{}{
|
|
// "last_ticket": lastTicket,
|
|
// "error": err.Error(),
|
|
// })
|
|
// } else {
|
|
// nextNumber = lastNumber + 1
|
|
// }
|
|
//
|
|
// logger.Info("Found last ticket", map[string]interface{}{
|
|
// "last_ticket": lastTicket,
|
|
// "next_number": nextNumber,
|
|
// })
|
|
// }
|
|
//
|
|
// if nextNumber > 999 {
|
|
// return "", fmt.Errorf("maximum ticket limit reached for today (999)")
|
|
// }
|
|
//
|
|
// ticketNumber := fmt.Sprintf("%03d", nextNumber)
|
|
//
|
|
// return ticketNumber, nil
|
|
// }
|
|
func (h *TiketHandler) generateTicketNumber(ctx context.Context, dbConn *sql.DB, healthcareServiceCode string, currentDate string) (string, error) {
|
|
query := `
|
|
SELECT COALESCE(MAX(CAST(ticket AS INTEGER)), 0) as max_ticket
|
|
FROM transaction.tr_patient_visit_healthcare_service tpvhs
|
|
WHERE tpvhs.healtcare_service_code = $1
|
|
AND DATE(tpvhs.datetime_start) = $2
|
|
`
|
|
|
|
var maxTicket int
|
|
err := dbConn.QueryRowContext(ctx, query, healthcareServiceCode, currentDate).Scan(&maxTicket)
|
|
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to query max ticket: %w", err)
|
|
}
|
|
|
|
nextNumber := maxTicket + 1
|
|
|
|
logger.Info("Generating next ticket number", map[string]interface{}{
|
|
"code": healthcareServiceCode,
|
|
"date": currentDate,
|
|
"max_ticket": maxTicket,
|
|
"next_number": nextNumber,
|
|
})
|
|
|
|
if nextNumber > 999 {
|
|
return "", fmt.Errorf("maximum ticket limit reached for today (999)")
|
|
}
|
|
|
|
ticketNumber := fmt.Sprintf("%03d", nextNumber)
|
|
|
|
return ticketNumber, nil
|
|
}
|
|
|
|
func (h *TiketHandler) inserttiket(ctx context.Context, dbcoon *sql.DB, idticket int64, idklinik string) error {
|
|
query := `
|
|
INSERT INTO "transaction".tr_patient_visit_healthcare_service_healthcare_service
|
|
(fk_tr_patient_visit_healthcare_service_id, fk_ms_healthcare_service_id)
|
|
VALUES
|
|
($1, $2)
|
|
`
|
|
|
|
_, err := dbcoon.ExecContext(ctx, query, idticket, idklinik)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert ticket relation: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *TiketHandler) insertcalling(ctx context.Context, dbcoon *sql.DB, IdHealthcarestatus string, IdVisitstatus string, idtiket int64) error {
|
|
query := `
|
|
INSERT INTO "transaction".tr_patient_visit_healthcare_service_calling
|
|
(fk_ref_healthcare_status_id, fk_ref_visit_status_id,fk_ref_healthcare_type_id,fk_tr_patient_visit_healthcare_service_id,active,datetime)
|
|
VALUES
|
|
($1,$2,$3,$4,TRUE,NOW())
|
|
`
|
|
|
|
_, err := dbcoon.ExecContext(ctx, query, IdHealthcarestatus, IdVisitstatus, IdHealthcarestatus, idtiket)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert ticket relation: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *TiketHandler) insertvisit(ctx context.Context, dbcoon *sql.DB, IdVisitstatus string, idtiket int64) error {
|
|
query := `
|
|
INSERT INTO "transaction".tr_patient_visit_healthcare_service_visit_status
|
|
(fk_ref_visit_status_id,fk_tr_patient_visit_healthcare_service_id,datetime)
|
|
VALUES
|
|
($1,$2,NOW())
|
|
`
|
|
|
|
_, err := dbcoon.ExecContext(ctx, query, IdVisitstatus, idtiket)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert ticket relation: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *TiketHandler) insertTicket(ctx context.Context, dbcoon *sql.DB, visitID string, ticket string, healthcareServiceCode string, idKlinik string, idloket string, idpembayaran string) (int64, time.Time, error) {
|
|
query := `
|
|
INSERT INTO transaction.tr_patient_visit_healthcare_service
|
|
(ticket, healtcare_service_code, fk_tr_patient_visit_id,fk_ms_healthcare_service_id,fk_ms_registration_counter_id,fk_ref_payment_type_id,datetime_start,active)
|
|
VALUES
|
|
($1,$2,$3,$4,$5,$6,NOW(),true)
|
|
RETURNING id, datetime_start
|
|
`
|
|
var id int64
|
|
var datetimeStart time.Time
|
|
|
|
err := dbcoon.QueryRowContext(ctx, query, ticket, healthcareServiceCode, visitID, idKlinik, idloket, idpembayaran).Scan(&id, &datetimeStart)
|
|
if err != nil {
|
|
return 0, time.Time{}, fmt.Errorf("failed to insert ticket: %w", err)
|
|
}
|
|
|
|
return id, datetimeStart, nil
|
|
}
|
|
|
|
func (h *TiketHandler) Updatecekin(c *gin.Context) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var req transaction.Checkin
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := validate.Struct(&req); err != nil {
|
|
h.respondError(c, "Validation failed", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
log.Println(req)
|
|
|
|
dbConn, err := h.db.GetSQLXDB("postgres_antrean")
|
|
if err != nil {
|
|
logger.Error("Database connection failed", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
|
|
return
|
|
}
|
|
|
|
ticketID, err := h.GetTicketIDByVisitCode(ctx, dbConn, req.Visitcode)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"error": "Ticket tidak ditemukan",
|
|
"visit_code": req.Visitcode,
|
|
})
|
|
return
|
|
}
|
|
|
|
logger.Error("Failed to get ticket", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve ticket"})
|
|
return
|
|
}
|
|
|
|
log.Println(ticketID)
|
|
|
|
errVisit := h.insertcalling2(ctx, dbConn.DB, req.Idklinikstatus, req.Statuspasien, ticketID)
|
|
if errVisit != nil {
|
|
logger.Error("Failed to insert calling", map[string]interface{}{"error": errVisit.Error()}) // PERBAIKAN: errVisit bukan err
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling: " + errVisit.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
errVisitservice := h.insertvisit2(ctx, dbConn.DB, req.Statuspasien, ticketID)
|
|
if errVisitservice != nil {
|
|
logger.Error("Failed to insert visit service", map[string]interface{}{"error": errVisitservice.Error()}) // PERBAIKAN
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert visit service: " + errVisitservice.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
errVisit2 := h.insertcalling2(ctx, dbConn.DB, req.Idklinikstatus2, req.Statuspasien2, ticketID)
|
|
if errVisit2 != nil {
|
|
logger.Error("Failed to insert calling 2", map[string]interface{}{"error": errVisit2.Error()}) // PERBAIKAN
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling 2: " + errVisit2.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
errVisitservice2 := h.insertvisit2(ctx, dbConn.DB, req.Statuspasien2, ticketID)
|
|
if errVisitservice2 != nil {
|
|
logger.Error("Failed to insert visit service 2", map[string]interface{}{"error": errVisitservice2.Error()}) // PERBAIKAN
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert visit service 2: " + errVisitservice2.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
err = h.UpdateCheckin(ctx, dbConn, &req)
|
|
if err != nil {
|
|
logger.Error("Failed to update check-in", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"visitCode": req.Visitcode,
|
|
})
|
|
c.JSON(http.StatusInternalServerError, gin.H{
|
|
"error": "Failed to update check-in",
|
|
"message": err.Error(),
|
|
})
|
|
return
|
|
}
|
|
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "Check-in berhasil",
|
|
"data": req.Visitcode,
|
|
})
|
|
}
|
|
|
|
func (h *TiketHandler) UpdateCheckin(ctx context.Context, dbConn *sqlx.DB, req *transaction.Checkin) error {
|
|
query := `
|
|
UPDATE "transaction".tr_patient_visit_healthcare_service tpvhs
|
|
SET check_in = true, check_in_datetime = NOW()
|
|
WHERE fk_tr_patient_visit_id = (
|
|
SELECT id
|
|
FROM "transaction".tr_patient_visit tpv
|
|
WHERE tpv.visit_code = $1
|
|
)
|
|
`
|
|
|
|
result, err := dbConn.ExecContext(ctx, query, req.Visitcode)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to execute update query: %w", err)
|
|
}
|
|
|
|
rowsAffected, err := result.RowsAffected()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get rows affected: %w", err)
|
|
}
|
|
|
|
if rowsAffected == 0 {
|
|
return fmt.Errorf("visit code tidak ditemukan: %s", req.Visitcode)
|
|
}
|
|
|
|
logger.Info("Check-in updated successfully", map[string]interface{}{
|
|
"visitCode": req.Visitcode,
|
|
"rowsAffected": rowsAffected,
|
|
})
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *TiketHandler) GetTicketIDByVisitCode(ctx context.Context, dbConn *sqlx.DB, visitCode string) (*transaction.TicketID, error) {
|
|
query := `
|
|
SELECT tpvhs.id
|
|
FROM "transaction".tr_patient_visit tpv
|
|
INNER JOIN "transaction".tr_patient_visit_healthcare_service tpvhs
|
|
ON tpvhs.fk_tr_patient_visit_id = tpv.id
|
|
WHERE tpv.visit_code = $1
|
|
and tpvhs.active = true
|
|
`
|
|
|
|
var ticketID transaction.TicketID
|
|
|
|
err := dbConn.QueryRowContext(ctx, query, visitCode).Scan(&ticketID.ID)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return nil, sql.ErrNoRows
|
|
}
|
|
return nil, fmt.Errorf("failed to get ticket ID: %w", err)
|
|
}
|
|
|
|
logger.Info("Ticket ID retrieved successfully", map[string]interface{}{
|
|
"visitCode": visitCode,
|
|
"ticketID": ticketID.ID,
|
|
})
|
|
|
|
return &ticketID, nil
|
|
}
|
|
|
|
func (h *TiketHandler) insertcalling2(ctx context.Context, dbcoon *sql.DB, IdHealthcarestatus string, IdVisitstatus string, idtiket *transaction.TicketID) error {
|
|
query := `
|
|
INSERT INTO "transaction".tr_patient_visit_healthcare_service_calling
|
|
(fk_ref_healthcare_status_id, fk_ref_visit_status_id, fk_ref_healthcare_type_id, fk_tr_patient_visit_healthcare_service_id, active, datetime)
|
|
VALUES
|
|
($1, $2, $3, $4, TRUE, NOW())
|
|
`
|
|
|
|
_, err := dbcoon.ExecContext(ctx, query, IdHealthcarestatus, IdVisitstatus, IdHealthcarestatus, idtiket.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert calling: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
func (h *TiketHandler) insertvisit2(ctx context.Context, dbcoon *sql.DB, IdVisitstatus string, idtiket *transaction.TicketID) error {
|
|
query := `
|
|
INSERT INTO "transaction".tr_patient_visit_healthcare_service_visit_status
|
|
(fk_ref_visit_status_id, fk_tr_patient_visit_healthcare_service_id, datetime)
|
|
VALUES
|
|
($1, $2, NOW())
|
|
`
|
|
|
|
_, err := dbcoon.ExecContext(ctx, query, IdVisitstatus, idtiket.ID)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to insert visit status: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (h *TiketHandler) CreateStatus(c *gin.Context) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var req transaction.UpdateTiket
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := validate.Struct(&req); err != nil {
|
|
h.respondError(c, "Validation failed", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
log.Println(req)
|
|
|
|
dbConn, err := h.db.GetSQLXDB("postgres_antrean")
|
|
if err != nil {
|
|
logger.Error("Database connection failed", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
|
|
return
|
|
}
|
|
ticketID, err := h.GetTicketIDByVisitCode(ctx, dbConn, req.Visitcode)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"error": "Ticket tidak ditemukan",
|
|
"visit_code": req.Visitcode,
|
|
})
|
|
return
|
|
}
|
|
|
|
logger.Error("Failed to get ticket", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve ticket"})
|
|
return
|
|
}
|
|
|
|
errVisit := h.insertcalling2(ctx, dbConn.DB, req.Idklinikstatus, req.Statuspasien, ticketID)
|
|
if errVisit != nil {
|
|
logger.Error("Failed to insert calling", map[string]interface{}{"error": errVisit.Error()}) // PERBAIKAN: errVisit bukan err
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling: " + errVisit.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
errVisitservice := h.insertvisit2(ctx, dbConn.DB, req.Statuspasien, ticketID)
|
|
if errVisitservice != nil {
|
|
logger.Error("Failed to insert visit service", map[string]interface{}{"error": errVisitservice.Error()}) // PERBAIKAN
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert visit service: " + errVisitservice.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
errVisit2 := h.insertcalling2(ctx, dbConn.DB, req.Idklinikstatus2, req.Statuspasien2, ticketID)
|
|
if errVisit2 != nil {
|
|
logger.Error("Failed to insert calling 2", map[string]interface{}{"error": errVisit2.Error()}) // PERBAIKAN
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling 2: " + errVisit2.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
errVisitservice2 := h.insertvisit2(ctx, dbConn.DB, req.Statuspasien2, ticketID)
|
|
if errVisitservice2 != nil {
|
|
logger.Error("Failed to insert visit service 2", map[string]interface{}{"error": errVisitservice2.Error()}) // PERBAIKAN
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert visit service 2: " + errVisitservice2.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "Update berhasil",
|
|
"data": req.Visitcode,
|
|
})
|
|
}
|
|
func (h *TiketHandler) SelesaiStatus(c *gin.Context) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
|
defer cancel()
|
|
|
|
var req transaction.TiketSelesai
|
|
if err := c.ShouldBindJSON(&req); err != nil {
|
|
h.respondError(c, "Invalid request body", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
if err := validate.Struct(&req); err != nil {
|
|
h.respondError(c, "Validation failed", err, http.StatusBadRequest)
|
|
return
|
|
}
|
|
log.Println(req)
|
|
loketID, err := strconv.Atoi(req.IDLoket)
|
|
if err != nil {
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "ID Loket harus berupa angka"})
|
|
return
|
|
}
|
|
|
|
dbConn, err := h.db.GetSQLXDB("postgres_antrean")
|
|
if err != nil {
|
|
logger.Error("Database connection failed", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
|
|
return
|
|
}
|
|
ticketID, err := h.GetTicketIDByVisitCode(ctx, dbConn, req.Visitcode)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
c.JSON(http.StatusNotFound, gin.H{
|
|
"error": "Ticket tidak ditemukan",
|
|
"visit_code": req.Visitcode,
|
|
})
|
|
return
|
|
}
|
|
|
|
logger.Error("Failed to get ticket", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve ticket"})
|
|
return
|
|
}
|
|
|
|
errVisit := h.insertcalling2(ctx, dbConn.DB, req.Idklinikstatus, req.Statuspasien, ticketID)
|
|
if errVisit != nil {
|
|
logger.Error("Failed to insert calling", map[string]interface{}{"error": errVisit.Error()}) // PERBAIKAN: errVisit bukan err
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert calling: " + errVisit.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
errVisitservice := h.insertvisit2(ctx, dbConn.DB, req.Statuspasien, ticketID)
|
|
if errVisitservice != nil {
|
|
logger.Error("Failed to insert visit service", map[string]interface{}{"error": errVisitservice.Error()}) // PERBAIKAN
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to insert visit service: " + errVisitservice.Error()}) // PERBAIKAN
|
|
return
|
|
}
|
|
|
|
currentDate := time.Now().Format("2006-01-02")
|
|
newUsedQuota, err := h.MinQuota(ctx, dbConn, loketID, currentDate)
|
|
if err != nil {
|
|
logger.Error("Failed to return quota", map[string]interface{}{
|
|
"error": err.Error(),
|
|
"loket_id": loketID,
|
|
"ticket_id": ticketID,
|
|
"visit_code": req.Visitcode,
|
|
})
|
|
} else {
|
|
logger.Info("Quota returned successfully", map[string]interface{}{
|
|
"loket_id": loketID,
|
|
"new_used_quota": newUsedQuota,
|
|
"ticket_id": ticketID,
|
|
"visit_code": req.Visitcode,
|
|
})
|
|
}
|
|
c.JSON(http.StatusOK, gin.H{
|
|
"message": "Update status selesai berhasil",
|
|
"data": req.Visitcode,
|
|
"Quota": newUsedQuota,
|
|
})
|
|
}
|