first commit

This commit is contained in:
2026-02-13 15:57:28 +07:00
parent d4248b62e1
commit 7ae8c76a20
26 changed files with 5040 additions and 32 deletions

No files matched your search

+228
View File
@@ -0,0 +1,228 @@
package master
import (
"api-service/internal/config"
"api-service/internal/database"
"api-service/internal/models"
modelsMaster "api-service/internal/models/master"
"api-service/pkg/logger"
"context"
"fmt"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/jmoiron/sqlx"
"net/http"
"strconv"
"strings"
"time"
)
func init() {
once.Do(func() {
db = database.New(config.LoadConfig())
validate = validator.New()
// Register custom validations if needed
validate.RegisterValidation("retribusi_status", validateRetribusiStatus)
if db == nil {
logger.Fatal("Failed to initialize database connection")
}
})
}
func validateRetribusiStatus(fl validator.FieldLevel) bool {
return models.IsValidStatus(fl.Field().String())
}
type DokterHandler struct {
db database.Service
}
func NewDokterHandler() *DokterHandler {
return &DokterHandler{
db: db,
}
}
func (h *DokterHandler) GetDokterbyspesialis(c *gin.Context) {
ctx, cancel := context.WithTimeout(c.Request.Context(), 120*time.Second)
defer cancel()
klinikIDStr := c.Param("idklinik")
klinikID, err := strconv.Atoi(klinikIDStr)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid klinik ID"})
return
}
dbAntrean, err := h.db.GetSQLXDB("postgres_antrean")
if err != nil {
logger.Error("Failed to connect postgres_antrean", map[string]interface{}{"error": err.Error()})
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
return
}
spesialisIDs, err := h.GetSpesialisIDsByKlinik(ctx, dbAntrean, klinikID)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
return
}
if len(spesialisIDs) == 0 {
c.JSON(http.StatusOK, modelsMaster.DokterResponse{
Message: "Tidak ada dokter untuk klinik ini",
Data: []*modelsMaster.DokterFormatted{},
Meta: map[string]interface{}{"total": 0},
})
return
}
logger.Info("Spesialis IDs found", map[string]interface{}{
"klinikID": klinikID,
"spesialisIDs": spesialisIDs,
"count": len(spesialisIDs),
})
dbConn, err := h.db.GetSQLXDB("postgres_satudata")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Database connection failed"})
return
}
dokterList, err := h.GetDokterBySpesialisIDs(ctx, dbConn, spesialisIDs)
if err != nil {
logger.Error("Failed to get dokter data", map[string]interface{}{"error": err.Error()})
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to get dokter data"})
return
}
formattedData := h.FormatDokterData(dokterList)
c.JSON(http.StatusOK, modelsMaster.DokterResponse{
Message: "Data dokter berhasil diambil",
Data: formattedData,
Meta: map[string]interface{}{
"total": len(formattedData),
"klinikID": klinikID,
"jumlahSpesialis": len(spesialisIDs),
},
})
}
func (h *DokterHandler) GetSpesialisIDsByKlinik(ctx context.Context, dbConn *sqlx.DB, klinikID int) ([]int, error) {
query := `
SELECT fk_sd_spesialis_id
FROM master.ms_healthcare_service_spesialis
WHERE fk_ms_healthcare_service_id = $1
`
var spesialisIDs []int
err := dbConn.SelectContext(ctx, &spesialisIDs, query, klinikID)
if err != nil {
return nil, fmt.Errorf("failed to get spesialis IDs: %w", err)
}
return spesialisIDs, nil
}
func (h *DokterHandler) FormatDokterData(data []*modelsMaster.ListDokter) []*modelsMaster.DokterFormatted {
result := make([]*modelsMaster.DokterFormatted, 0, len(data))
for _, dokter := range data {
namaLengkap := ""
if dokter.GelarDepan != "" {
gelarDepanList := strings.Split(dokter.GelarDepan, "|")
var formattedGelar []string
for _, gelar := range gelarDepanList {
gelar = strings.TrimSpace(gelar)
if gelar != "" && !strings.HasSuffix(gelar, ".") {
gelar += "."
}
if gelar != "" {
formattedGelar = append(formattedGelar, gelar)
}
}
if len(formattedGelar) > 0 {
namaLengkap = strings.Join(formattedGelar, " ") + " "
}
}
namaLengkap += strings.TrimSpace(dokter.NamaLengkap)
if dokter.GelarBelakang != "" {
gelarBelakangList := strings.Split(dokter.GelarBelakang, "|")
var formattedGelar []string
for _, gelar := range gelarBelakangList {
gelar = strings.TrimSpace(gelar)
if gelar != "" {
formattedGelar = append(formattedGelar, gelar)
}
}
if len(formattedGelar) > 0 {
namaLengkap += ", " + strings.Join(formattedGelar, ", ")
}
}
formatted := &modelsMaster.DokterFormatted{
NamaLengkapFormatted: strings.TrimSpace(namaLengkap),
Spesialis: strings.TrimSpace(dokter.Spesialis),
Subspesialis: strings.TrimSpace(dokter.Subspesialis),
}
result = append(result, formatted)
}
return result
}
func (h *DokterHandler) GetDokterBySpesialisIDs(ctx context.Context, dbConn *sqlx.DB, spesialisIDs []int) ([]*modelsMaster.ListDokter, error) {
query, args, err := sqlx.In(`
SELECT
ARRAY_TO_STRING(
ARRAY(
SELECT dgd."Gelar_depan"
FROM data_pegawai_daftar_gelar_depan dpdgd
LEFT JOIN daftar_gelar_depan dgd ON dpdgd.daftar_gelar_depan_id = dgd.id
WHERE dpdgd.data_pegawai_id = dp.id
), '|'
) AS gelardepan,
COALESCE(dk."Nama_lengkap", '') AS namalengkap,
ARRAY_TO_STRING(
ARRAY(
SELECT dgb."Gelar_belakang"
FROM data_pegawai_daftar_gelar_belakang dpdgb
LEFT JOIN daftar_gelar_belakang dgb ON dpdgb.daftar_gelar_belakang_id = dgb.id
WHERE dpdgb.data_pegawai_id = dp.id
), '|'
) AS gelarbelakang,
ds."Subspesialis",
dss."Spesialis"
FROM "data_pegawai" dp
LEFT JOIN data_ktp dk ON dp."KTP" = dk.id
LEFT JOIN daftar_subspesialis ds ON dp."Subspesialis" = ds.id
LEFT JOIN daftar_spesialis dss ON ds."FK_daftar_spesialis_ID" = dss.id
WHERE dss.id IN (?)
ORDER BY dk."Nama_lengkap" ASC
`, spesialisIDs)
if err != nil {
return nil, fmt.Errorf("failed to build query: %w", err)
}
query = dbConn.Rebind(query)
var dokterList []*modelsMaster.ListDokter
err = dbConn.SelectContext(ctx, &dokterList, query, args...)
if err != nil {
return nil, fmt.Errorf("failed to execute query: %w", err)
}
return dokterList, nil
}
+403
View File
@@ -0,0 +1,403 @@
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
}
+949
View File
@@ -0,0 +1,949 @@
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
}