feat: add CPONE package endpoints
This commit is contained in:
456
internal/repository/paket.go
Normal file
456
internal/repository/paket.go
Normal file
@@ -0,0 +1,456 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type PaketListParams struct {
|
||||
Search string
|
||||
Code string
|
||||
CareType string
|
||||
Status int
|
||||
Active string
|
||||
StartDate string
|
||||
EndDate string
|
||||
Page int
|
||||
PerPage int
|
||||
}
|
||||
|
||||
type PaketPage struct {
|
||||
Items []PaketSummary
|
||||
Total int
|
||||
}
|
||||
|
||||
type PaketSummary struct {
|
||||
PaketID string `json:"PaketID"`
|
||||
KodeCorp *string `json:"KodeCorp"`
|
||||
Nama string `json:"Nama"`
|
||||
JenisPaketID string `json:"JenisPaketID"`
|
||||
JenisPaket *string `json:"JenisPaket"`
|
||||
Harga int64 `json:"Harga"`
|
||||
RawatInap bool `json:"RawatInap"`
|
||||
RawatJalan bool `json:"RawatJalan"`
|
||||
MultiVisit bool `json:"MultiVisit"`
|
||||
TglDari string `json:"TglDari"`
|
||||
TglSampai string `json:"TglSampai"`
|
||||
KelasID string `json:"KelasID"`
|
||||
Kelas *string `json:"Kelas"`
|
||||
JumlahHari int `json:"JumlahHari"`
|
||||
StatusID int `json:"StatusID"`
|
||||
Status *string `json:"Status"`
|
||||
NA string `json:"NA"`
|
||||
Aktif bool `json:"Aktif"`
|
||||
Keterangan string `json:"Keterangan"`
|
||||
}
|
||||
|
||||
type PaketDetail struct {
|
||||
PaketID string `json:"PaketID"`
|
||||
Nama string `json:"Nama"`
|
||||
JenisPaket string `json:"JenisPaket"`
|
||||
RJ int `json:"RJ"`
|
||||
RI int `json:"RI"`
|
||||
TglDari string `json:"TglDari"`
|
||||
TglSampai string `json:"TglSampai"`
|
||||
MultiVisit int `json:"MultiVisit"`
|
||||
HeaderPaket string `json:"HeaderPaket"`
|
||||
KelasID string `json:"KelasID"`
|
||||
JumlahHari int `json:"JumlahHari"`
|
||||
TotalHarga string `json:"TotalHarga"`
|
||||
Keterangan string `json:"Keterangan"`
|
||||
StatusID int `json:"StatusID"`
|
||||
LoginBuat string `json:"LoginBuat"`
|
||||
TanggalBuat string `json:"TanggalBuat"`
|
||||
LoginEdit string `json:"LoginEdit"`
|
||||
TanggalEdit string `json:"TanggalEdit"`
|
||||
NA string `json:"NA"`
|
||||
Layanan []PaketLayanan `json:"layanan"`
|
||||
Operasi []PaketOperasi `json:"operasi"`
|
||||
Item []PaketItem `json:"item"`
|
||||
Kamar []PaketKamar `json:"kamar"`
|
||||
}
|
||||
|
||||
type PaketLayanan struct {
|
||||
PaketLayananID int64 `json:"PaketLayananID"`
|
||||
LayananID string `json:"LayananID"`
|
||||
NamaLayanan *string `json:"NamaLayanan"`
|
||||
NamaDepartemen *string `json:"NamaDepartemen"`
|
||||
Jumlah string `json:"Jumlah"`
|
||||
JumlahHari int `json:"JumlahHari"`
|
||||
Harga string `json:"Harga"`
|
||||
Diskon string `json:"Diskon"`
|
||||
TipeDiskon string `json:"TipeDiskon"`
|
||||
ShareRS string `json:"ShareRS"`
|
||||
ShareDokter string `json:"ShareDokter"`
|
||||
Subtotal string `json:"Subtotal"`
|
||||
}
|
||||
|
||||
type PaketOperasi struct {
|
||||
PaketOperasiHeaderID int64 `json:"PaketOperasiHeaderID"`
|
||||
NamaTindakan *string `json:"NamaTindakan"`
|
||||
NamaGolongan *string `json:"NamaGolongan"`
|
||||
NamaJenisAnestesi *string `json:"NamaJenisAnestesi"`
|
||||
NamaKasusOperasi *string `json:"NamaKasusOperasi"`
|
||||
EstimasiWaktu string `json:"EstimasiWaktu"`
|
||||
Keterangan string `json:"Keterangan"`
|
||||
}
|
||||
|
||||
type PaketItem struct {
|
||||
PaketItemID int64 `json:"PaketItemID"`
|
||||
ItemID string `json:"ItemID"`
|
||||
NamaItem *string `json:"NamaItem"`
|
||||
NamaDepartemen *string `json:"NamaDepartemen"`
|
||||
Jumlah string `json:"Jumlah"`
|
||||
NamaSatuan *string `json:"NamaSatuan"`
|
||||
Harga string `json:"Harga"`
|
||||
Diskon string `json:"Diskon"`
|
||||
TipeDiskon string `json:"TipeDiskon"`
|
||||
SubTotal string `json:"SubTotal"`
|
||||
}
|
||||
|
||||
type PaketKamar struct {
|
||||
PaketTTID int64 `json:"PaketTTID"`
|
||||
NamaDepartemen *string `json:"NamaDepartemen"`
|
||||
NamaKelas *string `json:"NamaKelas"`
|
||||
Jumlah string `json:"Jumlah"`
|
||||
Harga string `json:"Harga"`
|
||||
TipeDiskon string `json:"TipeDiskon"`
|
||||
Diskon float64 `json:"Diskon"`
|
||||
SubTotal string `json:"SubTotal"`
|
||||
}
|
||||
|
||||
func (r MySQLLayananRepository) ListPaket(ctx context.Context, params PaketListParams) (PaketPage, error) {
|
||||
where, args := paketListWhere(params)
|
||||
var total int
|
||||
if err := r.db.QueryRowContext(ctx, "SELECT COUNT(*) FROM paket_header ph "+where, args...).Scan(&total); err != nil {
|
||||
return PaketPage{}, err
|
||||
}
|
||||
|
||||
query := `
|
||||
SELECT ph.PaketID,
|
||||
(SELECT pch.KodeCorp FROM paket_corp_header pch
|
||||
WHERE pch.PaketID = ph.PaketID ORDER BY pch.TanggalBuat DESC LIMIT 1),
|
||||
ph.Nama, ph.JenisPaket, jp.Nama, ph.TotalHarga, ph.RI, ph.RJ,
|
||||
ph.MultiVisit, CAST(ph.TglDari AS CHAR), CAST(ph.TglSampai AS CHAR),
|
||||
ph.KelasID, ph.JumlahHari, ph.StatusID, ph.NA, ph.Keterangan
|
||||
FROM paket_header ph
|
||||
LEFT JOIN jenispaket jp ON jp.JenisPaketID = ph.JenisPaket
|
||||
` + where + `
|
||||
ORDER BY ph.TanggalBuat DESC, ph.PaketID DESC
|
||||
LIMIT ? OFFSET ?`
|
||||
queryArgs := append(append([]any{}, args...), params.PerPage, (params.Page-1)*params.PerPage)
|
||||
rows, err := r.db.QueryContext(ctx, query, queryArgs...)
|
||||
if err != nil {
|
||||
return PaketPage{}, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
items := make([]PaketSummary, 0)
|
||||
for rows.Next() {
|
||||
var item PaketSummary
|
||||
var kodeCorp, jenisPaket sql.NullString
|
||||
var harga float64
|
||||
var ri, rj, multiVisit int
|
||||
if err := rows.Scan(
|
||||
&item.PaketID, &kodeCorp, &item.Nama, &item.JenisPaketID, &jenisPaket,
|
||||
&harga, &ri, &rj, &multiVisit, &item.TglDari, &item.TglSampai,
|
||||
&item.KelasID, &item.JumlahHari, &item.StatusID, &item.NA, &item.Keterangan,
|
||||
); err != nil {
|
||||
return PaketPage{}, err
|
||||
}
|
||||
item.KodeCorp = nullStringPtr(kodeCorp)
|
||||
item.JenisPaket = nullStringPtr(jenisPaket)
|
||||
item.Harga = int64(math.Round(harga))
|
||||
item.RawatInap = ri != 0
|
||||
item.RawatJalan = rj != 0
|
||||
item.MultiVisit = multiVisit != 0
|
||||
item.Status = paketStatus(item.StatusID)
|
||||
item.Aktif = item.NA == "N"
|
||||
items = append(items, item)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return PaketPage{}, err
|
||||
}
|
||||
return PaketPage{Items: items, Total: total}, nil
|
||||
}
|
||||
|
||||
func paketListWhere(params PaketListParams) (string, []any) {
|
||||
conditions := []string{"ph.StatusID = ?"}
|
||||
args := []any{params.Status}
|
||||
if params.Active != "" {
|
||||
conditions = append(conditions, "ph.NA = ?")
|
||||
args = append(args, params.Active)
|
||||
}
|
||||
if params.Search != "" {
|
||||
conditions = append(conditions, "ph.Nama LIKE ?")
|
||||
args = append(args, "%"+params.Search+"%")
|
||||
}
|
||||
if params.Code != "" {
|
||||
conditions = append(conditions, "ph.PaketID LIKE ?")
|
||||
args = append(args, "%"+params.Code+"%")
|
||||
}
|
||||
if params.CareType != "" {
|
||||
conditions = append(conditions, "ph."+params.CareType+" = 1")
|
||||
}
|
||||
if params.StartDate != "" {
|
||||
conditions = append(conditions, "ph.TglSampai >= ?")
|
||||
args = append(args, params.StartDate)
|
||||
}
|
||||
if params.EndDate != "" {
|
||||
conditions = append(conditions, "ph.TglDari <= ?")
|
||||
args = append(args, params.EndDate)
|
||||
}
|
||||
return "WHERE " + strings.Join(conditions, " AND "), args
|
||||
}
|
||||
|
||||
func paketStatus(status int) *string {
|
||||
statuses := map[int]string{-1: "rejected", 0: "draft", 1: "submitted", 2: "approved"}
|
||||
value, ok := statuses[status]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return &value
|
||||
}
|
||||
|
||||
func (r MySQLLayananRepository) GetPaket(ctx context.Context, id string) (*PaketDetail, error) {
|
||||
row := r.db.QueryRowContext(ctx, `
|
||||
SELECT PaketID, Nama, JenisPaket, RJ, RI, CAST(TglDari AS CHAR), CAST(TglSampai AS CHAR),
|
||||
MultiVisit, HeaderPaket, KelasID, JumlahHari, CAST(TotalHarga AS CHAR), Keterangan,
|
||||
StatusID, LoginBuat, CAST(TanggalBuat AS CHAR), LoginEdit, CAST(TanggalEdit AS CHAR), NA
|
||||
FROM paket_header WHERE PaketID = ? LIMIT 1`, id)
|
||||
var result PaketDetail
|
||||
var tanggalBuat, tanggalEdit string
|
||||
if err := row.Scan(
|
||||
&result.PaketID, &result.Nama, &result.JenisPaket, &result.RJ, &result.RI,
|
||||
&result.TglDari, &result.TglSampai, &result.MultiVisit, &result.HeaderPaket,
|
||||
&result.KelasID, &result.JumlahHari, &result.TotalHarga, &result.Keterangan,
|
||||
&result.StatusID, &result.LoginBuat, &tanggalBuat, &result.LoginEdit, &tanggalEdit, &result.NA,
|
||||
); err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
return nil, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
result.TanggalBuat = laravelDateJSON(tanggalBuat)
|
||||
result.TanggalEdit = laravelDateJSON(tanggalEdit)
|
||||
|
||||
var err error
|
||||
if result.Layanan, err = r.paketLayanan(ctx, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Operasi, err = r.paketOperasi(ctx, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Item, err = r.paketItem(ctx, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if result.Kamar, err = r.paketKamar(ctx, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
func (r MySQLLayananRepository) paketLayanan(ctx context.Context, id string) ([]PaketLayanan, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT pl.PaketLayananID, pl.LayananID, ml.Nama, d.Nama,
|
||||
CAST(pl.Jumlah AS CHAR), pl.JumlahHari, CAST(pl.Harga AS CHAR),
|
||||
CAST(pl.Diskon AS CHAR), pl.TipeDiskon, CAST(pl.ShareRS AS CHAR),
|
||||
CAST(pl.ShareDokter AS CHAR), CAST(pl.Subtotal AS CHAR)
|
||||
FROM paket_layanan pl
|
||||
LEFT JOIN masterlayanan ml ON ml.LayananID = pl.LayananID
|
||||
LEFT JOIN departemen d ON d.DepartemenID = pl.DepartemenID AND d.NA = 'N'
|
||||
WHERE pl.PaketID = ? AND pl.NA = 'N'
|
||||
ORDER BY pl.PaketLayananID`, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
result := make([]PaketLayanan, 0)
|
||||
for rows.Next() {
|
||||
var item PaketLayanan
|
||||
var namaLayanan, namaDepartemen sql.NullString
|
||||
var harga, shareRS, shareDokter, subtotal string
|
||||
if err := rows.Scan(
|
||||
&item.PaketLayananID, &item.LayananID, &namaLayanan, &namaDepartemen,
|
||||
&item.Jumlah, &item.JumlahHari, &harga, &item.Diskon, &item.TipeDiskon,
|
||||
&shareRS, &shareDokter, &subtotal,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.NamaLayanan = nullStringPtr(namaLayanan)
|
||||
item.NamaDepartemen = nullStringPtr(namaDepartemen)
|
||||
item.Harga = phpNumberFormat(parseDecimal(harga), 0)
|
||||
item.ShareRS = phpNumberFormat(parseDecimal(shareRS), 2)
|
||||
item.ShareDokter = phpNumberFormat(parseDecimal(shareDokter), 0)
|
||||
item.Subtotal = phpNumberFormat(parseDecimal(subtotal), 0)
|
||||
result = append(result, item)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (r MySQLLayananRepository) paketOperasi(ctx context.Context, id string) ([]PaketOperasi, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT po.PaketOperasiHeaderID, mt.Nama, go.Nama, ja.Nama, ko.Nama,
|
||||
po.EstimasiWaktu, po.Keterangan
|
||||
FROM paket_operasi_header po
|
||||
LEFT JOIN mastertindakan mt ON mt.TindakanID = po.TindakanID
|
||||
LEFT JOIN golonganoperasi go ON go.GolonganOperasiID = po.GolonganOperasiID
|
||||
LEFT JOIN jenisanestesi ja ON ja.JenisAnestesiID = po.JenisAnestesiID
|
||||
LEFT JOIN kasusoperasi ko ON ko.KasusOperasiID = po.KasusOperasiID
|
||||
WHERE po.PaketID = ? AND po.NA = 'N'
|
||||
ORDER BY po.PaketOperasiHeaderID`, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
result := make([]PaketOperasi, 0)
|
||||
for rows.Next() {
|
||||
var item PaketOperasi
|
||||
var tindakan, golongan, anestesi, kasus sql.NullString
|
||||
if err := rows.Scan(
|
||||
&item.PaketOperasiHeaderID, &tindakan, &golongan, &anestesi, &kasus,
|
||||
&item.EstimasiWaktu, &item.Keterangan,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.NamaTindakan = nullStringPtr(tindakan)
|
||||
item.NamaGolongan = nullStringPtr(golongan)
|
||||
item.NamaJenisAnestesi = nullStringPtr(anestesi)
|
||||
item.NamaKasusOperasi = nullStringPtr(kasus)
|
||||
result = append(result, item)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (r MySQLLayananRepository) paketItem(ctx context.Context, id string) ([]PaketItem, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT pi.PaketItemID, pi.ItemID, mi.Nama, d.Nama, CAST(pi.Jumlah AS CHAR), s.Nama,
|
||||
CAST(pi.Harga AS CHAR), CAST(pi.Diskon AS CHAR), pi.TipeDiskon,
|
||||
pi.SatuanID, pi.SatuanDasarID,
|
||||
COALESCE((SELECT ks.NilaiKonversi FROM konversisatuan ks
|
||||
WHERE ks.ItemID = pi.ItemID AND ks.SatuanTujuan = pi.SatuanDasarID
|
||||
AND ks.SatuanAsal = pi.SatuanID AND ks.NA = 'N' LIMIT 1), 1)
|
||||
FROM paket_item pi
|
||||
LEFT JOIN masteritem mi ON mi.ItemID = pi.ItemID AND mi.NA = 'N'
|
||||
LEFT JOIN departemen d ON d.DepartemenID = pi.DepartemenID AND d.NA = 'N'
|
||||
LEFT JOIN satuan s ON s.SatuanID = pi.SatuanID
|
||||
WHERE pi.PaketID = ? AND pi.NA = 'N'
|
||||
ORDER BY pi.PaketItemID`, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
result := make([]PaketItem, 0)
|
||||
for rows.Next() {
|
||||
var item PaketItem
|
||||
var namaItem, namaDepartemen, namaSatuan sql.NullString
|
||||
var harga, satuanID, satuanDasarID string
|
||||
var konversi float64
|
||||
if err := rows.Scan(
|
||||
&item.PaketItemID, &item.ItemID, &namaItem, &namaDepartemen, &item.Jumlah,
|
||||
&namaSatuan, &harga, &item.Diskon, &item.TipeDiskon,
|
||||
&satuanID, &satuanDasarID, &konversi,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.NamaItem = nullStringPtr(namaItem)
|
||||
item.NamaDepartemen = nullStringPtr(namaDepartemen)
|
||||
item.NamaSatuan = nullStringPtr(namaSatuan)
|
||||
hargaValue := parseDecimal(harga)
|
||||
diskon := parseDecimal(item.Diskon)
|
||||
nDisc := diskon
|
||||
if item.TipeDiskon == "Percentage" {
|
||||
nDisc = hargaValue * (diskon / 100)
|
||||
}
|
||||
if satuanDasarID == satuanID {
|
||||
konversi = 1
|
||||
}
|
||||
item.Harga = phpNumberFormat(hargaValue, 2)
|
||||
item.SubTotal = phpNumberFormat((hargaValue-nDisc)*parseDecimal(item.Jumlah)*konversi, 2)
|
||||
result = append(result, item)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func (r MySQLLayananRepository) paketKamar(ctx context.Context, id string) ([]PaketKamar, error) {
|
||||
rows, err := r.db.QueryContext(ctx, `
|
||||
SELECT pt.PaketTTID, d.Nama, k.Nama, CAST(pt.Jumlah AS CHAR),
|
||||
CAST(pt.Harga AS CHAR), pt.TipeDiskon, CAST(pt.Diskon AS CHAR)
|
||||
FROM paket_tt pt
|
||||
LEFT JOIN departemen d ON d.DepartemenID = pt.DepartemenID AND d.NA = 'N'
|
||||
LEFT JOIN kelas k ON k.KelasID = pt.KelasID AND k.NA = 'N'
|
||||
WHERE pt.PaketID = ?
|
||||
ORDER BY pt.PaketTTID`, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
result := make([]PaketKamar, 0)
|
||||
for rows.Next() {
|
||||
var item PaketKamar
|
||||
var namaDepartemen, namaKelas sql.NullString
|
||||
var harga, diskon string
|
||||
if err := rows.Scan(
|
||||
&item.PaketTTID, &namaDepartemen, &namaKelas, &item.Jumlah,
|
||||
&harga, &item.TipeDiskon, &diskon,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
item.NamaDepartemen = nullStringPtr(namaDepartemen)
|
||||
item.NamaKelas = nullStringPtr(namaKelas)
|
||||
hargaValue := parseDecimal(harga)
|
||||
diskonValue := parseDecimal(diskon)
|
||||
item.Diskon = diskonValue
|
||||
if item.TipeDiskon == "Percentage" {
|
||||
item.Diskon = hargaValue * (diskonValue / 100)
|
||||
}
|
||||
item.Harga = phpNumberFormat(hargaValue, 2)
|
||||
item.SubTotal = phpNumberFormat((hargaValue-item.Diskon)*parseDecimal(item.Jumlah), 0)
|
||||
result = append(result, item)
|
||||
}
|
||||
return result, rows.Err()
|
||||
}
|
||||
|
||||
func parseDecimal(value string) float64 {
|
||||
parsed, _ := strconv.ParseFloat(value, 64)
|
||||
return parsed
|
||||
}
|
||||
|
||||
func phpNumberFormat(value float64, decimals int) string {
|
||||
power := math.Pow10(decimals)
|
||||
value = math.Round(value*power) / power
|
||||
formatted := strconv.FormatFloat(value, 'f', decimals, 64)
|
||||
parts := strings.SplitN(formatted, ".", 2)
|
||||
integer := parts[0]
|
||||
sign := ""
|
||||
if strings.HasPrefix(integer, "-") {
|
||||
sign = "-"
|
||||
integer = strings.TrimPrefix(integer, "-")
|
||||
}
|
||||
for index := len(integer) - 3; index > 0; index -= 3 {
|
||||
integer = integer[:index] + "," + integer[index:]
|
||||
}
|
||||
if len(parts) == 2 {
|
||||
return sign + integer + "." + parts[1]
|
||||
}
|
||||
return sign + integer
|
||||
}
|
||||
|
||||
func laravelDateJSON(value string) string {
|
||||
if strings.HasPrefix(value, "0000-00-00") {
|
||||
return "-000001-11-29T16:52:48.000000Z"
|
||||
}
|
||||
location, err := time.LoadLocation("Asia/Jakarta")
|
||||
if err != nil {
|
||||
return value
|
||||
}
|
||||
parsed, err := time.ParseInLocation("2006-01-02 15:04:05", value, location)
|
||||
if err != nil {
|
||||
return value
|
||||
}
|
||||
return parsed.UTC().Format("2006-01-02T15:04:05.000000Z")
|
||||
}
|
||||
Reference in New Issue
Block a user