Files
api_antrean/internal/handlers/master/poliklinik.go
T
2026-02-13 15:57:28 +07:00

950 lines
27 KiB
Go

package master
import (
"api-service/internal/config"
"api-service/internal/database"
"api-service/internal/models"
modelsMaster "api-service/internal/models/master"
queryUtils "api-service/internal/utils/query"
"api-service/internal/utils/validation"
"api-service/pkg/logger"
"context"
"database/sql"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/jmoiron/sqlx"
"log"
"net/http"
"strings"
"sync"
"time"
)
var (
db database.Service
once sync.Once
validate *validator.Validate
)
func init() {
once.Do(func() {
db = database.New(config.LoadConfig())
validate = validator.New()
validate.RegisterValidation("satudata_status", validated)
if db == nil {
logger.Fatal("Failed to initialize database connection")
}
})
}
func validated(fl validator.FieldLevel) bool {
return models.IsValidStatus(fl.Field().String())
}
func NewPoliHandler() *PoliHandler {
// Initialize QueryBuilder with allowed columns list for security.
queryBuilder := queryUtils.NewQueryBuilder(queryUtils.DBTypePostgreSQL).
SetAllowedColumns([]string{
"mrc.id",
"mrc.name",
"mrc.code",
"mrc.quota",
"mrc.active",
"mrc.fk_ref_healthcare_type_id",
"mrc.fk_ref_service_type_id",
"rht.id",
"rht.name",
"rst.id",
"rst.name",
"mrcvt.id",
"mrcvt.fk_ms_registration_counter_id",
"mrcvt.fk_ref_visit_type_id",
"rvt.id",
"rvt.name",
"mrcpt.id",
"mrcpt.fk_ms_registration_counter_id",
"mrcpt.fk_ref_payment_type_id",
"rpt.id",
"rpt.name",
"mrchs.id",
"mrchs.fk_ms_registration_counter_id",
"mrchs.fk_ms_healthcare_service_id",
"mhss2.fk_ms_healthcare_service_id",
"master.ms_healthcare_service_shift",
"fk_ms_registration_counter_id",
"master.ms_healthcare_service_schedule",
"mhss3.fk_ms_healthcare_service_id",
"mhs.id",
"mhs.name",
"mhss2",
"mhss2.quota",
"mhss2.shift",
"namaloket",
"kodeloket",
"kuotaloket",
"loketaktif",
"jenisloket",
"tipeloket",
"tipevisit",
"pembayaran",
"namaklinik",
"idpayment",
"id",
"name",
"code",
"quota",
"active",
"shift",
"kuota",
})
return &PoliHandler{
db: db,
queryBuilder: queryBuilder,
validator: validation.NewDynamicValidator(queryBuilder),
}
}
type PoliHandler struct {
db database.Service
queryBuilder *queryUtils.QueryBuilder
validator *validation.DynamicValidator
}
func (h *PoliHandler) GetAnjungan(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second)
defer cancel()
req := c.Param("jenis")
var tipeKlinik string
switch strings.ToLower(req) {
case "reguler", "REGULER":
tipeKlinik = "KLINIK REGULER"
case "vip":
tipeKlinik = "KLINIK VIP"
case "eksekutif", "EKSEKUTIF":
tipeKlinik = "KLINIK EKSEKUTIF"
default:
}
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
}
currentDate := time.Now().Format("2006-01-02")
query := queryUtils.DynamicQuery{
From: "master.ms_kiosk",
Aliases: "mk",
Fields: []queryUtils.SelectField{
{Expression: "mk.name", Alias: "tipeanjungan"},
{Expression: "mk.active", Alias: "active"},
{Expression: "mhs.id", Alias: "idklinik"},
{Expression: "mhs.name", Alias: "namaklinik"},
{Expression: "mhs.code", Alias: "code"},
{Expression: "mhss.ds_sd_spesialis", Alias: "spesialis"},
{Expression: "mhss.fk_sd_spesialis_id", Alias: "idspesialis"},
{Expression: "mks.day_of_week", Alias: "hari"},
{Expression: "mhss3.available_start_time", Alias: "mulai"},
{Expression: "mhss3.available_end_time", Alias: "selesai"},
{Expression: "mks.available_start_time", Alias: "mulaianjungan"},
{Expression: "mks.available_end_time", Alias: "selesaianjungan"},
{Expression: "mhss2.shift_number", Alias: "shift"},
{Expression: "mhss2.quota", Alias: "kuota"},
{Expression: "mrc.id", Alias: "idloket"},
{Expression: "mrc.name", Alias: "namaloket"},
{Expression: "mrc.code", Alias: "kodeloket"},
{Expression: "mrc.quota", Alias: "quota_loket"},
{Expression: "(mrc.quota - COALESCE(tmrcsq.seat_quota, 0))", Alias: "available_quota"},
{Expression: "rpt.id", Alias: "idpayment"},
{Expression: "rpt.name", Alias: "paymenttype"},
},
Joins: []queryUtils.Join{
{
Type: "LEFT",
Table: "master.ms_kiosk_healthcare_service",
Alias: "mkhs",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mk.id", Operator: queryUtils.OpEqual, Value: "mkhs.fk_ms_kiosk_id"},
},
},
},
{
Type: "LEFT",
Table: "master.ms_healthcare_service",
Alias: "mhs",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mkhs.fk_ms_healthcare_service_id", Operator: queryUtils.OpEqual, Value: "mhs.id"},
},
},
},
{
Type: "LEFT",
Table: "master.ms_healthcare_service_spesialis",
Alias: "mhss",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mhs.id", Operator: queryUtils.OpEqual, Value: "mhss.fk_ms_healthcare_service_id"},
},
},
},
{
Type: "LEFT",
Table: "master.ms_kiosk_schedule",
Alias: "mks",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mks.fk_ms_kiosk_id", Operator: queryUtils.OpEqual, Value: "mk.id"},
},
},
},
{
Type: "LEFT",
Table: "master.ms_healthcare_service_shift",
Alias: "mhss2",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mhss2.fk_ms_healthcare_service_id", Operator: queryUtils.OpEqual, Value: "mkhs.id"},
},
},
},
{
Type: "LEFT",
Table: "master.ms_registration_counter_healthcare_service",
Alias: "mrchs",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrchs.fk_ms_healthcare_service_id", Operator: queryUtils.OpEqual, Value: "mhs.id"},
},
},
},
{
Type: "LEFT",
Table: "master.ms_registration_counter",
Alias: "mrc",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "fk_ms_registration_counter_id", Operator: queryUtils.OpEqual, Value: "mrc.id"},
},
},
},
{
Type: "LEFT",
Table: "temporary.tm_registration_counter_seat_quota",
Alias: "tmrcsq",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "tmrcsq.fk_ms_registration_counter_id", Operator: queryUtils.OpEqual, Value: "mrc.id"},
{Column: "tmrcsq.date", Operator: queryUtils.OpEqual, Value: currentDate},
},
},
},
{
Type: "LEFT",
Table: "master.ms_healthcare_service_schedule",
Alias: "mhss3",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mhss3.fk_ms_healthcare_service_id", Operator: queryUtils.OpEqual, Value: "mhs.id"},
},
},
},
{
Type: "INNER",
Table: "master.ms_kiosk_payment_type",
Alias: "mkpt",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mkpt.fk_ms_kiosk_id", Operator: queryUtils.OpEqual, Value: "mk.id"},
},
},
},
{
Type: "INNER",
Table: "master.ms_healthcare_service_payment_type",
Alias: "mhspt",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mhspt.fk_ms_healthcare_service_id", Operator: queryUtils.OpEqual, Value: "mhs.id"},
{Column: "mkpt.fk_ref_payment_type_id", Operator: queryUtils.OpEqual, Value: "mhspt.fk_ref_payment_type_id"},
},
},
},
{
Type: "INNER",
Table: "reference.ref_payment_type",
Alias: "rpt",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mkpt.fk_ref_payment_type_id", Operator: queryUtils.OpEqual, Value: "rpt.id"},
},
},
},
},
Filters: []queryUtils.FilterGroup{
{
Filters: []queryUtils.DynamicFilter{
{
Column: "mk.name",
Operator: queryUtils.OpEqual,
Value: tipeKlinik,
},
},
},
},
}
log.Println(query)
ListPoli, total, err := h.GetdataSpesialis(ctx, dbConn, query)
if err != nil {
if err == sql.ErrNoRows {
c.JSON(http.StatusOK, modelsMaster.PoliklinikResponse{
Message: "Data spesialis tidak ditemukan",
Data: []*modelsMaster.PoliklinikGrouped{},
Meta: map[string]interface{}{
"total": 0,
"count": 0,
},
})
return
}
logger.Error("Failed to get spesialis data", map[string]interface{}{"error": err.Error()})
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve data"})
return
}
groupedData := h.groupByKlinik(ListPoli)
response := modelsMaster.PoliklinikResponse{
Message: "Data Poli berhasil diambil",
Data: groupedData,
Meta: map[string]interface{}{
"total": total,
"count": len(ListPoli),
},
}
c.JSON(http.StatusOK, response)
}
func (h *PoliHandler) GetdataSpesialis(ctx context.Context, dbConn *sqlx.DB, query queryUtils.DynamicQuery) ([]*modelsMaster.ListPoli, int, error) {
var List []*modelsMaster.ListPoli
queryCtx, queryCancel := context.WithTimeout(ctx, 30*time.Second)
defer queryCancel()
logger.Info("Executing spesialis query")
err := h.queryBuilder.ExecuteQuery(queryCtx, dbConn, query, &List)
if err != nil {
logger.Error("Failed to execute spesialis query", map[string]interface{}{
"error": err.Error(),
"query": query,
})
return nil, 0, fmt.Errorf("failed to execute spesialis query: %w", err)
}
total := len(List)
logger.Info("Query execution completed", map[string]interface{}{
"totalRecords": total,
"returnedRecords": len(List),
})
// Check if no data found
if total == 0 {
return nil, 0, sql.ErrNoRows
}
return List, total, nil
}
//func (h *PoliHandler) groupByKlinik(data []*modelsMaster.ListPoli) []*modelsMaster.PoliklinikGrouped {
// klinikMap := make(map[string]*modelsMaster.PoliklinikGrouped)
// spesialisMap := make(map[string]map[int]bool)
// jadwalMap := make(map[string]map[string]bool)
// loketMap := make(map[string]map[string]bool)
// paymentMap := make(map[string]map[string]bool)
//
// for _, item := range data {
// // Skip jika IDKlinik tidak valid atau kosong
// if !item.IDKlinik.Valid || item.IDKlinik.String == "" {
// continue
// }
//
// idKlinik := item.IDKlinik.String
//
// // Jika klinik belum ada di map, buat entry baru
// if _, exists := klinikMap[idKlinik]; !exists {
// namaKlinik := ""
// if item.NamaKlinik.Valid {
// namaKlinik = item.NamaKlinik.String
// }
//
// klinikMap[idKlinik] = &modelsMaster.PoliklinikGrouped{
// TipeAnjungan: item.TipeAnjungan,
// Active: item.Active,
// IDKlinik: idKlinik,
// NamaKlinik: namaKlinik,
// Shift: item.Shift,
// Kuota: item.Kuota,
// Code: item.Code,
// Jadwal: []modelsMaster.ScheduleDetail{},
// Spesialis: []modelsMaster.SpesialisDetail{},
// Loket: []modelsMaster.LoketDetail{},
// Payment: []modelsMaster.PaymentDetail{},
// }
//
// // Inisialisasi map untuk tracking
// spesialisMap[idKlinik] = make(map[int]bool)
// jadwalMap[idKlinik] = make(map[string]bool)
// loketMap[idKlinik] = make(map[string]bool)
// paymentMap[idKlinik] = make(map[string]bool)
// }
//
// // Tambahkan jadwal jika belum ada
// if item.Hari != "" && item.JamMulai != "" && item.JamSelesai != "" {
// jamOperasional := fmt.Sprintf("%s - %s", item.JamMulai, item.JamSelesai)
// jadwalKey := fmt.Sprintf("%s_%s", item.Hari, jamOperasional)
//
// // Cek apakah jadwal dengan hari dan jam operasional ini sudah ada
// if !jadwalMap[idKlinik][jadwalKey] {
// schedule := modelsMaster.ScheduleDetail{
// Hari: item.Hari,
// JamOperasional: jamOperasional,
// }
//
// klinikMap[idKlinik].Jadwal = append(klinikMap[idKlinik].Jadwal, schedule)
// jadwalMap[idKlinik][jadwalKey] = true
// }
// }
//
// // Tambahkan spesialis jika belum ada
// if item.IDSpesialis.Valid && item.IDSpesialis.Int64 != 0 && item.Spesialis.Valid && item.Spesialis.String != "" {
// idSpesialis := int(item.IDSpesialis.Int64)
//
// if !spesialisMap[idKlinik][idSpesialis] {
// spesialisDetail := modelsMaster.SpesialisDetail{
// IDSpesialis: idSpesialis,
// Spesialis: item.Spesialis.String,
// }
//
// klinikMap[idKlinik].Spesialis = append(klinikMap[idKlinik].Spesialis, spesialisDetail)
// spesialisMap[idKlinik][idSpesialis] = true
// }
// }
//
// if item.IDloket.Valid && item.IDloket.String != "" {
// idLoket := item.IDloket.String
//
// if !loketMap[idKlinik][idLoket] {
// namaLoket := ""
// if item.Namaloket.Valid {
// namaLoket = item.Namaloket.String
// }
//
// kodeLoket := ""
// if item.KodeLoket.Valid {
// kodeLoket = item.KodeLoket.String
// }
//
// loketDetail := modelsMaster.LoketDetail{
// IDLoket: idLoket,
// NamaLoket: namaLoket,
// KodeLoket: kodeLoket,
// }
//
// klinikMap[idKlinik].Loket = append(klinikMap[idKlinik].Loket, loketDetail)
// loketMap[idKlinik][idLoket] = true
// }
// }
// if item.PaymentType.Valid && item.PaymentType.String != "" {
// idPayment := int(item.IdPayment.Int64)
// paymentType := item.PaymentType.String
//
// if !paymentMap[idKlinik][paymentType] {
// paymentDetail := modelsMaster.PaymentDetail{
// IDPayment: idPayment,
// PaymentType: paymentType,
// }
//
// klinikMap[idKlinik].Payment = append(klinikMap[idKlinik].Payment, paymentDetail)
// paymentMap[idKlinik][paymentType] = true
// }
// }
// }
//
// result := make([]*modelsMaster.PoliklinikGrouped, 0, len(klinikMap))
// for _, klinik := range klinikMap {
// result = append(result, klinik)
// }
//
// return result
//}
func (h *PoliHandler) groupByKlinik(data []*modelsMaster.ListPoli) []*modelsMaster.PoliklinikGrouped {
klinikMap := make(map[string]*modelsMaster.PoliklinikGrouped)
spesialisMap := make(map[string]map[int]bool)
jadwalMap := make(map[string]map[string]bool)
loketMap := make(map[string][]modelsMaster.LoketDetail) // ✅ Array untuk menyimpan semua loket
paymentMap := make(map[string]map[string]bool)
for _, item := range data {
// Skip jika IDKlinik tidak valid atau kosong
if !item.IDKlinik.Valid || item.IDKlinik.String == "" {
continue
}
idKlinik := item.IDKlinik.String
// Jika klinik belum ada di map, buat entry baru
if _, exists := klinikMap[idKlinik]; !exists {
namaKlinik := ""
if item.NamaKlinik.Valid {
namaKlinik = item.NamaKlinik.String
}
klinikMap[idKlinik] = &modelsMaster.PoliklinikGrouped{
TipeAnjungan: item.TipeAnjungan,
Active: item.Active,
IDKlinik: idKlinik,
NamaKlinik: namaKlinik,
Shift: item.Shift,
Kuota: item.Kuota,
Code: item.Code,
Jadwal: []modelsMaster.ScheduleDetail{},
Spesialis: []modelsMaster.SpesialisDetail{},
Loket: []modelsMaster.LoketDetail{},
Payment: []modelsMaster.PaymentDetail{},
}
// Inisialisasi map untuk tracking
spesialisMap[idKlinik] = make(map[int]bool)
jadwalMap[idKlinik] = make(map[string]bool)
loketMap[idKlinik] = []modelsMaster.LoketDetail{}
paymentMap[idKlinik] = make(map[string]bool)
}
// Tambahkan jadwal jika belum ada
if item.Hari != "" && item.JamMulai != "" && item.JamSelesai != "" {
jamOperasional := fmt.Sprintf("%s - %s", item.JamMulai, item.JamSelesai)
jadwalKey := fmt.Sprintf("%s_%s", item.Hari, jamOperasional)
if !jadwalMap[idKlinik][jadwalKey] {
schedule := modelsMaster.ScheduleDetail{
Hari: item.Hari,
JamOperasional: jamOperasional,
}
klinikMap[idKlinik].Jadwal = append(klinikMap[idKlinik].Jadwal, schedule)
jadwalMap[idKlinik][jadwalKey] = true
}
}
// Tambahkan spesialis jika belum ada
if item.IDSpesialis.Valid && item.IDSpesialis.Int64 != 0 && item.Spesialis.Valid && item.Spesialis.String != "" {
idSpesialis := int(item.IDSpesialis.Int64)
if !spesialisMap[idKlinik][idSpesialis] {
spesialisDetail := modelsMaster.SpesialisDetail{
IDSpesialis: idSpesialis,
Spesialis: item.Spesialis.String,
}
klinikMap[idKlinik].Spesialis = append(klinikMap[idKlinik].Spesialis, spesialisDetail)
spesialisMap[idKlinik][idSpesialis] = true
}
}
// ✅ LOGIC BARU: Kumpulkan semua loket dulu
if item.IDloket.Valid && item.IDloket.String != "" {
idLoket := item.IDloket.String
// Cek apakah loket ini sudah ada di array
loketExists := false
for _, loket := range loketMap[idKlinik] {
if loket.IDLoket == idLoket {
loketExists = true
break
}
}
if !loketExists {
namaLoket := ""
if item.Namaloket.Valid {
namaLoket = item.Namaloket.String
}
kodeLoket := ""
if item.KodeLoket.Valid {
kodeLoket = item.KodeLoket.String
}
availableQuota := 0
if item.AvailableQuota.Valid {
availableQuota = int(item.AvailableQuota.Int64)
} else if item.QuotaLoket.Valid {
// Kalau available_quota NULL, berarti belum ada yang pakai (awal hari)
availableQuota = int(item.QuotaLoket.Int64)
}
loketDetail := modelsMaster.LoketDetail{
IDLoket: idLoket,
NamaLoket: namaLoket,
KodeLoket: kodeLoket,
AvailableQuota: availableQuota,
}
loketMap[idKlinik] = append(loketMap[idKlinik], loketDetail)
}
}
// Payment (tetap sama)
if item.PaymentType.Valid && item.PaymentType.String != "" {
idPayment := int(item.IdPayment.Int64)
paymentType := item.PaymentType.String
if !paymentMap[idKlinik][paymentType] {
paymentDetail := modelsMaster.PaymentDetail{
IDPayment: idPayment,
PaymentType: paymentType,
}
klinikMap[idKlinik].Payment = append(klinikMap[idKlinik].Payment, paymentDetail)
paymentMap[idKlinik][paymentType] = true
}
}
}
// ✅ LOGIC PEMILIHAN LOKET
for idKlinik, loketList := range loketMap {
if len(loketList) == 0 {
continue
}
// Cek apakah ada loket yang sudah terpakai (ada record quota hari ini)
adaYangTerpakai := false
for _, loket := range loketList {
// Kalau available_quota < quota_loket, berarti sudah ada yang pakai
for _, item := range data {
if item.IDKlinik.Valid && item.IDKlinik.String == idKlinik &&
item.IDloket.Valid && item.IDloket.String == loket.IDLoket {
if item.AvailableQuota.Valid && item.QuotaLoket.Valid {
if item.AvailableQuota.Int64 < item.QuotaLoket.Int64 {
adaYangTerpakai = true
break
}
}
}
}
if adaYangTerpakai {
break
}
}
if adaYangTerpakai {
// ✅ Sudah ada yang pakai tiket: Pilih 1 loket dengan quota terbanyak
var selectedLoket *modelsMaster.LoketDetail
maxQuota := -1
for i := range loketList {
if loketList[i].AvailableQuota > maxQuota {
maxQuota = loketList[i].AvailableQuota
selectedLoket = &loketList[i]
}
}
if selectedLoket != nil {
klinikMap[idKlinik].Loket = []modelsMaster.LoketDetail{*selectedLoket}
}
} else {
// ✅ Awal hari (belum ada yang pakai): Tampilkan semua loket
klinikMap[idKlinik].Loket = loketList
}
}
result := make([]*modelsMaster.PoliklinikGrouped, 0, len(klinikMap))
for _, klinik := range klinikMap {
result = append(result, klinik)
}
return result
}
func (h *PoliHandler) GetLoket(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
}
query := queryUtils.DynamicQuery{
From: "master.ms_registration_counter",
Aliases: "mrc",
Fields: []queryUtils.SelectField{
{Expression: "mrc.id", Alias: "idloket"},
{Expression: "mrc.name", Alias: "namaloket"},
{Expression: "mrc.code", Alias: "kodeloket"},
{Expression: "mrc.quota", Alias: "kuotaloket"},
{Expression: "mrc.active", Alias: "loketaktif"},
{Expression: "rht.name", Alias: "jenisloket"},
{Expression: "rst.name", Alias: "tipeloket"},
{Expression: "rvt.name", Alias: "tipevisit"},
{Expression: "rpt.name", Alias: "pembayaran"},
{Expression: "mhs.name", Alias: "namaklinik"},
{Expression: "mhs.id", Alias: "idklinik"},
},
Joins: []queryUtils.Join{
{
Type: "LEFT",
Table: "reference.ref_healthcare_type",
Alias: "rht",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrc.fk_ref_service_type_id", Operator: queryUtils.OpEqual, Value: "rht.id"},
},
},
},
{
Type: "LEFT",
Table: "reference.ref_service_type",
Alias: "rst",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrc.fk_ref_service_type_id", Operator: queryUtils.OpEqual, Value: "rst.id"},
},
},
},
{
Type: "INNER",
Table: "master.ms_registration_counter_visit_type",
Alias: "mrcvt",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrcvt.fk_ms_registration_counter_id", Operator: queryUtils.OpEqual, Value: "mrc.id"},
},
},
},
{
Type: "LEFT",
Table: "reference.ref_visit_type",
Alias: "rvt",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrcvt.fk_ref_visit_type_id", Operator: queryUtils.OpEqual, Value: "rvt.id"},
},
},
},
{
Type: "INNER",
Table: "master.ms_registration_counter_payment_type",
Alias: "mrcpt",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrcpt.fk_ms_registration_counter_id", Operator: queryUtils.OpEqual, Value: "mrc.id"},
},
},
},
{
Type: "LEFT",
Table: "reference.ref_payment_type",
Alias: "rpt",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrcpt.fk_ref_payment_type_id", Operator: queryUtils.OpEqual, Value: "rpt.id"},
},
},
},
{
Type: "INNER",
Table: "master.ms_registration_counter_healthcare_service",
Alias: "mrchs",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrchs.fk_ms_registration_counter_id", Operator: queryUtils.OpEqual, Value: "mrc.id"},
},
},
},
{
Type: "LEFT",
Table: "master.ms_healthcare_service",
Alias: "mhs",
OnConditions: queryUtils.FilterGroup{
Filters: []queryUtils.DynamicFilter{
{Column: "mrchs.fk_ms_healthcare_service_id", Operator: queryUtils.OpEqual, Value: "mhs.id"},
},
},
},
},
}
log.Println(query)
ListLoket, total, err := h.GetdataLoket(ctx, dbConn, query)
if err != nil {
if err == sql.ErrNoRows {
c.JSON(http.StatusOK, modelsMaster.LoketResponse{
Message: "Data loket tidak ditemukan",
Data: []*modelsMaster.LoketGrouped{},
Meta: map[string]interface{}{
"total": 0,
"count": 0,
},
})
return
}
logger.Error("Failed to get loket data", map[string]interface{}{"error": err.Error()})
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to retrieve data"})
return
}
// GROUPING DATA DISINI
groupedData := h.groupByLoket(ListLoket)
response := modelsMaster.LoketResponse{
Message: "Data Loket berhasil diambil",
Data: groupedData,
Meta: map[string]interface{}{
"total_loket": len(groupedData),
"total_records": total,
},
}
c.JSON(http.StatusOK, response)
}
func (h *PoliHandler) GetdataLoket(ctx context.Context, dbConn *sqlx.DB, query queryUtils.DynamicQuery) ([]*modelsMaster.ListLoket, int, error) {
var List []*modelsMaster.ListLoket
queryCtx, queryCancel := context.WithTimeout(ctx, 30*time.Second)
defer queryCancel()
logger.Info("Executing loket query")
err := h.queryBuilder.ExecuteQuery(queryCtx, dbConn, query, &List)
if err != nil {
logger.Error("Failed to execute loket query", map[string]interface{}{
"error": err.Error(),
"query": query,
})
return nil, 0, fmt.Errorf("failed to execute loket query: %w", err)
}
total := len(List)
logger.Info("Query execution completed", map[string]interface{}{
"totalRecords": total,
"returnedRecords": len(List),
})
if total == 0 {
return nil, 0, sql.ErrNoRows
}
return List, total, nil
}
// FUNGSI UNTUK GROUPING - Group by Loket
func (h *PoliHandler) groupByLoket(data []*modelsMaster.ListLoket) []*modelsMaster.LoketGrouped {
loketMap := make(map[string]*modelsMaster.LoketGrouped)
visitMap := make(map[string]map[string]bool)
klinikMap := make(map[string]map[string]bool) // Ubah jadi tracking by IDKlinik
pembayaranMap := make(map[string]map[string]bool)
for _, item := range data {
if item.KodeLoket == "" {
continue
}
// Jika loket belum ada di map, buat entry baru
if _, exists := loketMap[item.KodeLoket]; !exists {
loketMap[item.KodeLoket] = &modelsMaster.LoketGrouped{
IdLoket: item.IdLoket,
TipeAnjungan: item.NamaLoket,
KodeLoket: item.KodeLoket,
KuotaLoket: item.KuotaLoket,
LoketAktif: item.LoketAktif,
JenisLoket: item.JenisLoket,
TipeLoket: item.TipeLoket,
TipeVisit: []modelsMaster.ListVisit{},
NamaKlinik: []modelsMaster.ListKlinik{},
NamaPembayaran: []modelsMaster.ListPembayaran{},
}
// Inisialisasi map untuk tracking
visitMap[item.KodeLoket] = make(map[string]bool)
klinikMap[item.KodeLoket] = make(map[string]bool)
pembayaranMap[item.KodeLoket] = make(map[string]bool)
}
if item.TipeVisit != "" {
if !visitMap[item.KodeLoket][item.TipeVisit] {
visit := modelsMaster.ListVisit{
TipeVisit: item.TipeVisit,
}
loketMap[item.KodeLoket].TipeVisit = append(loketMap[item.KodeLoket].TipeVisit, visit)
visitMap[item.KodeLoket][item.TipeVisit] = true
}
}
if item.IDKlinik != "" && item.NamaKlinik != "" {
if !klinikMap[item.KodeLoket][item.IDKlinik] {
klinik := modelsMaster.ListKlinik{
IDKlinik: item.IDKlinik,
NamaKlinik: item.NamaKlinik,
}
loketMap[item.KodeLoket].NamaKlinik = append(loketMap[item.KodeLoket].NamaKlinik, klinik)
klinikMap[item.KodeLoket][item.IDKlinik] = true
}
}
if item.Pembayaran != "" {
if !pembayaranMap[item.KodeLoket][item.Pembayaran] {
pembayaran := modelsMaster.ListPembayaran{
NamaPembayaran: item.Pembayaran,
}
loketMap[item.KodeLoket].NamaPembayaran = append(loketMap[item.KodeLoket].NamaPembayaran, pembayaran)
pembayaranMap[item.KodeLoket][item.Pembayaran] = true
}
}
}
// Convert map ke slice
result := make([]*modelsMaster.LoketGrouped, 0, len(loketMap))
for _, loket := range loketMap {
result = append(result, loket)
}
return result
}