404 lines
12 KiB
Go
404 lines
12 KiB
Go
package master
|
|
|
|
import (
|
|
"api-service/internal/config"
|
|
"api-service/internal/database"
|
|
"api-service/internal/models"
|
|
"api-service/internal/models/master"
|
|
"api-service/pkg/logger"
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/go-playground/validator/v10"
|
|
"github.com/jmoiron/sqlx"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
func init() {
|
|
once.Do(func() {
|
|
db = database.New(config.LoadConfig())
|
|
validate = validator.New()
|
|
|
|
// Register custom validations if needed
|
|
validate.RegisterValidation("retribusi_status", validateloketstatus)
|
|
|
|
if db == nil {
|
|
logger.Fatal("Failed to initialize database connection")
|
|
}
|
|
})
|
|
}
|
|
|
|
func validateloketstatus(fl validator.FieldLevel) bool {
|
|
return models.IsValidStatus(fl.Field().String())
|
|
}
|
|
|
|
type LoketHandler struct {
|
|
db database.Service
|
|
}
|
|
|
|
func NewLoketGHandler() *LoketHandler {
|
|
return &LoketHandler{
|
|
db: db,
|
|
}
|
|
}
|
|
|
|
func (h *LoketHandler) GetLoket(c *gin.Context) {
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second)
|
|
defer cancel()
|
|
|
|
IdLoket := c.Param("loket")
|
|
|
|
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
|
|
}
|
|
|
|
remainingQuota, err := h.GetRemainingQuota(ctx, dbConn, IdLoket)
|
|
if err != nil {
|
|
logger.Warn("Failed to get remaining quota, defaulting to 0", map[string]interface{}{
|
|
"error": err.Error(),
|
|
})
|
|
remainingQuota = 0
|
|
}
|
|
|
|
ticketQueue, err := h.GetTicketQueueData(ctx, dbConn, IdLoket)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
c.JSON(http.StatusOK, master.TicketQueueResponse{
|
|
Message: "Tidak ada antrian ticket",
|
|
Data: []*master.LoketTiketResponse{},
|
|
Meta: map[string]interface{}{
|
|
"total": 0,
|
|
"counter_id": IdLoket,
|
|
},
|
|
})
|
|
return
|
|
}
|
|
logger.Error("Failed to get ticket queue", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve ticket queue"})
|
|
return
|
|
}
|
|
|
|
mappedData := h.MapToLoketTiketResponse(ticketQueue)
|
|
|
|
c.JSON(http.StatusOK, master.TicketQueueResponse{
|
|
Message: "Data antrian ticket berhasil diambil",
|
|
KuotaLoket: remainingQuota,
|
|
Data: mappedData,
|
|
Meta: map[string]interface{}{
|
|
"total": len(ticketQueue),
|
|
"counter_id": IdLoket,
|
|
},
|
|
})
|
|
}
|
|
|
|
func (h *LoketHandler) GetTicketQueueData(ctx context.Context, dbConn *sqlx.DB, counterID string) ([]*master.TicketQueue, error) {
|
|
query := `
|
|
select tpvhs.id ,tpvhs.healtcare_service_code ,tpvhs.ticket ,tpvhs.datetime_start,
|
|
tpv.visit_code , mhs."name" , mhs.code ,mhss.shift_number, rpt."name" as "payment",
|
|
rvs."name" as "posisi", rvs.description as "deskripsi", tpvhsvs.datetime as "waktuposisi"
|
|
from "transaction".tr_patient_visit_healthcare_service tpvhs
|
|
left join master.ms_healthcare_service mhs on tpvhs.fk_ms_healthcare_service_id = mhs.id
|
|
left join master.ms_registration_counter mrc on tpvhs.fk_ms_registration_counter_id = mrc.id
|
|
left join "transaction".tr_patient_visit tpv on tpvhs.fk_tr_patient_visit_id = tpv.id
|
|
left join reference.ref_healthcare_type rht on tpvhs.fk_ref_healthcare_type_id = rht.id
|
|
left join reference.ref_service_type rst on tpvhs.fk_ref_service_type_id = rst.id
|
|
left join master.ms_sub_healthcare_service mshs on tpvhs.fk_ms_sub_healthcare_service_id = mshs.id
|
|
left join reference.ref_visit_type rvt on tpvhs.fk_ref_visit_type_id = rvt.id
|
|
left join reference.ref_payment_type rpt on tpvhs.fk_ref_payment_type_id = rpt.id
|
|
left join master.ms_healthcare_service_shift mhss on mhss.fk_ms_healthcare_service_id = mhs.id
|
|
left join "transaction".tr_patient_visit_healthcare_service_visit_status tpvhsvs
|
|
on tpvhsvs.fk_tr_patient_visit_healthcare_service_id =tpvhs.id
|
|
left join reference.ref_visit_status rvs on tpvhsvs.fk_ref_visit_status_id = rvs.id
|
|
where mrc.id = $1
|
|
and tpvhs.active = true
|
|
and tpv.active = true
|
|
ORDER BY tpvhs.datetime_start ASC
|
|
`
|
|
|
|
rows, err := dbConn.QueryContext(ctx, query, counterID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to execute query: %w", err)
|
|
}
|
|
defer rows.Close()
|
|
|
|
var ticketQueue []*master.TicketQueue
|
|
|
|
for rows.Next() {
|
|
var ticket master.TicketQueue
|
|
|
|
err := rows.Scan(
|
|
&ticket.ID,
|
|
&ticket.HealthcareServiceCode,
|
|
&ticket.Ticket,
|
|
&ticket.DatetimeStart,
|
|
&ticket.VisitCode,
|
|
&ticket.HealthcareServiceName,
|
|
&ticket.HealthcareServiceCodeMS,
|
|
&ticket.ShiftNumber,
|
|
&ticket.PaymentTypeName,
|
|
&ticket.Posisi,
|
|
&ticket.Deskripsi,
|
|
&ticket.WaktuPosisi,
|
|
)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to scan row: %w", err)
|
|
}
|
|
|
|
ticketQueue = append(ticketQueue, &ticket)
|
|
}
|
|
|
|
if err = rows.Err(); err != nil {
|
|
return nil, fmt.Errorf("rows iteration error: %w", err)
|
|
}
|
|
|
|
// Check if no data found
|
|
if len(ticketQueue) == 0 {
|
|
return nil, sql.ErrNoRows
|
|
}
|
|
|
|
logger.Info("Ticket queue retrieved successfully", map[string]interface{}{
|
|
"counterID": counterID,
|
|
"total": len(ticketQueue),
|
|
})
|
|
|
|
return ticketQueue, nil
|
|
}
|
|
|
|
func (h *LoketHandler) MapToLoketTiketResponse(tickets []*master.TicketQueue) []*master.LoketTiketResponse {
|
|
ticketMap := make(map[int64]*master.LoketTiketResponse)
|
|
|
|
for _, ticket := range tickets {
|
|
// Jika ticket ID belum ada di map, buat entry baru
|
|
if _, exists := ticketMap[ticket.ID]; !exists {
|
|
ticketNumber := ticket.Ticket
|
|
if ticket.HealthcareServiceCode.Valid && ticket.HealthcareServiceCode.String != "" {
|
|
ticketNumber = ticket.HealthcareServiceCode.String + ticket.Ticket
|
|
}
|
|
|
|
klinikName := ""
|
|
if ticket.HealthcareServiceName.Valid {
|
|
klinikName = ticket.HealthcareServiceName.String
|
|
}
|
|
|
|
shift := 0
|
|
if ticket.ShiftNumber.Valid {
|
|
shift = int(ticket.ShiftNumber.Int64)
|
|
}
|
|
|
|
payment := ""
|
|
if ticket.PaymentTypeName.Valid {
|
|
payment = ticket.PaymentTypeName.String
|
|
}
|
|
|
|
ticketMap[ticket.ID] = &master.LoketTiketResponse{
|
|
IdLoketTiket: fmt.Sprintf("%d", ticket.ID),
|
|
TiketLoket: ticketNumber,
|
|
KlinikTiketLoket: klinikName,
|
|
BarcodeTiketLoket: ticket.VisitCode,
|
|
TanggalTiketLoket: ticket.DatetimeStart.Format("2006-01-02"),
|
|
WaktuTiketLoket: ticket.DatetimeStart.Format("15:04:05"),
|
|
ShiftTiketLoket: shift,
|
|
Pembayaran: payment,
|
|
Posisi: []master.Posisi{},
|
|
}
|
|
}
|
|
|
|
// Tambahkan posisi ke array (jika ada data posisi)
|
|
if (ticket.Posisi.Valid && ticket.Posisi.String != "") ||
|
|
(ticket.Deskripsi.Valid && ticket.Deskripsi.String != "") ||
|
|
(ticket.WaktuPosisi.Valid) {
|
|
|
|
posisiData := master.Posisi{}
|
|
|
|
if ticket.Posisi.Valid {
|
|
posisiData.Posisi = ticket.Posisi.String
|
|
}
|
|
|
|
if ticket.Deskripsi.Valid {
|
|
posisiData.Deskripsi = ticket.Deskripsi.String
|
|
}
|
|
|
|
if ticket.WaktuPosisi.Valid {
|
|
posisiData.TanggalPosisi = ticket.WaktuPosisi.Time.Format("2006-01-02")
|
|
posisiData.WaktuPosisi = ticket.WaktuPosisi.Time.Format("15:04:05")
|
|
}
|
|
|
|
ticketMap[ticket.ID].Posisi = append(ticketMap[ticket.ID].Posisi, posisiData)
|
|
}
|
|
}
|
|
|
|
// Convert map ke slice
|
|
result := make([]*master.LoketTiketResponse, 0, len(ticketMap))
|
|
for _, ticket := range ticketMap {
|
|
result = append(result, ticket)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func (h *LoketHandler) GetRemainingQuota(ctx context.Context, dbConn *sqlx.DB, counterID string) (int, error) {
|
|
currentDate := time.Now().Format("2006-01-02")
|
|
|
|
query := `
|
|
SELECT
|
|
mrc.quota,
|
|
COALESCE(tmrcsq.seat_quota, 0) as used_quota
|
|
FROM master.ms_registration_counter mrc
|
|
LEFT JOIN temporary.tm_registration_counter_seat_quota tmrcsq
|
|
ON tmrcsq.fk_ms_registration_counter_id = mrc.id
|
|
AND tmrcsq.date = $1
|
|
WHERE mrc.id = $2
|
|
`
|
|
|
|
var maxQuota, usedQuota int
|
|
err := dbConn.QueryRowContext(ctx, query, currentDate, counterID).Scan(&maxQuota, &usedQuota)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
return 0, fmt.Errorf("loket not found")
|
|
}
|
|
return 0, fmt.Errorf("failed to get quota: %w", err)
|
|
}
|
|
|
|
remainingQuota := maxQuota - usedQuota
|
|
|
|
logger.Info("Remaining quota retrieved", map[string]interface{}{
|
|
"counter_id": counterID,
|
|
"max_quota": maxQuota,
|
|
"used_quota": usedQuota,
|
|
"remaining_quota": remainingQuota,
|
|
})
|
|
|
|
return remainingQuota, nil
|
|
}
|
|
|
|
func (h *LoketHandler) GetKlinikRuang(c *gin.Context) {
|
|
ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second)
|
|
defer cancel()
|
|
|
|
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
|
|
}
|
|
|
|
// Get data klinik dan ruangan
|
|
listKlinikRuangan, err := h.GetRuang(ctx, dbConn)
|
|
if err != nil {
|
|
if err == sql.ErrNoRows {
|
|
c.JSON(http.StatusOK, master.KlinikRuanganResponse{
|
|
Message: "Data klinik dan ruangan tidak ditemukan",
|
|
Data: []*master.KlinikRuanganGrouped{},
|
|
Meta: map[string]interface{}{
|
|
"total": 0,
|
|
},
|
|
})
|
|
return
|
|
}
|
|
|
|
logger.Error("Failed to get klinik ruangan", map[string]interface{}{"error": err.Error()})
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve data"})
|
|
return
|
|
}
|
|
|
|
// Group by klinik
|
|
groupedData := h.groupByKlinikRuangan(listKlinikRuangan)
|
|
|
|
c.JSON(http.StatusOK, master.KlinikRuanganResponse{
|
|
Message: "Data klinik dan ruangan berhasil diambil",
|
|
Data: groupedData,
|
|
Meta: map[string]interface{}{
|
|
"total": len(groupedData),
|
|
},
|
|
})
|
|
}
|
|
|
|
func (h *LoketHandler) GetRuang(ctx context.Context, dbConn *sqlx.DB) ([]*master.KlinikRuangan, error) {
|
|
query := `
|
|
SELECT
|
|
mhs.id as "idklinik",
|
|
mhs.name as "namaklinik",
|
|
mshs.id as "idruangan",
|
|
mshs.name as "namaruangan"
|
|
FROM master.ms_healthcare_service mhs
|
|
LEFT JOIN master.ms_sub_healthcare_service mshs
|
|
ON mshs.fk_ms_healthcare_service_id = mhs.id
|
|
ORDER BY mhs.id, mshs.id
|
|
`
|
|
|
|
var listKlinikRuangan []*master.KlinikRuangan
|
|
|
|
err := dbConn.SelectContext(ctx, &listKlinikRuangan, query)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get klinik ruangan: %w", err)
|
|
}
|
|
|
|
if len(listKlinikRuangan) == 0 {
|
|
return nil, sql.ErrNoRows
|
|
}
|
|
|
|
logger.Info("Klinik ruangan retrieved successfully", map[string]interface{}{
|
|
"total": len(listKlinikRuangan),
|
|
})
|
|
|
|
return listKlinikRuangan, nil
|
|
}
|
|
|
|
func (h *LoketHandler) groupByKlinikRuangan(data []*master.KlinikRuangan) []*master.KlinikRuanganGrouped {
|
|
klinikMap := make(map[string]*master.KlinikRuanganGrouped)
|
|
ruanganMap := make(map[string]map[string]bool)
|
|
|
|
for _, item := range data {
|
|
if !item.IDKlinik.Valid || item.IDKlinik.String == "" {
|
|
continue
|
|
}
|
|
|
|
idKlinik := item.IDKlinik.String
|
|
|
|
if _, exists := klinikMap[idKlinik]; !exists {
|
|
namaKlinik := ""
|
|
if item.NamaKlinik.Valid {
|
|
namaKlinik = item.NamaKlinik.String
|
|
}
|
|
|
|
klinikMap[idKlinik] = &master.KlinikRuanganGrouped{
|
|
IDKlinik: idKlinik,
|
|
NamaKlinik: namaKlinik,
|
|
Ruangan: []master.RuanganDetail{},
|
|
}
|
|
ruanganMap[idKlinik] = make(map[string]bool)
|
|
}
|
|
|
|
if item.IDRuangan.Valid && item.IDRuangan.String != "" {
|
|
idRuangan := item.IDRuangan.String
|
|
|
|
if !ruanganMap[idKlinik][idRuangan] {
|
|
namaRuangan := ""
|
|
if item.NamaRuangan.Valid {
|
|
namaRuangan = item.NamaRuangan.String
|
|
}
|
|
|
|
ruanganDetail := master.RuanganDetail{
|
|
IDRuangan: idRuangan,
|
|
NamaRuangan: namaRuangan,
|
|
}
|
|
|
|
klinikMap[idKlinik].Ruangan = append(klinikMap[idKlinik].Ruangan, ruanganDetail)
|
|
ruanganMap[idKlinik][idRuangan] = true
|
|
}
|
|
}
|
|
}
|
|
|
|
result := make([]*master.KlinikRuanganGrouped, 0, len(klinikMap))
|
|
for _, klinik := range klinikMap {
|
|
result = append(result, klinik)
|
|
}
|
|
|
|
return result
|
|
}
|