727 lines
23 KiB
Go
727 lines
23 KiB
Go
package repository
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"fmt"
|
|
"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"`
|
|
PeriodeTarif *PaketPeriodeTarif `json:"PeriodeTarif"`
|
|
Layanan []PaketLayanan `json:"layanan"`
|
|
Operasi []PaketOperasi `json:"operasi"`
|
|
Item []PaketItem `json:"item"`
|
|
Kamar []PaketKamar `json:"kamar"`
|
|
}
|
|
|
|
type PaketPeriodeTarif struct {
|
|
PeriodeTarifID int64 `json:"PeriodeTarifID"`
|
|
Keterangan *string `json:"Keterangan"`
|
|
TanggalBerlaku string `json:"TanggalBerlaku"`
|
|
TanggalBerakhir string `json:"TanggalBerakhir"`
|
|
}
|
|
|
|
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"`
|
|
HargaDasar string `json:"HargaDasar"`
|
|
HargaDasarSource string `json:"HargaDasarSource"`
|
|
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"`
|
|
HargaDasar string `json:"HargaDasar"`
|
|
}
|
|
|
|
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"`
|
|
HargaDasar string `json:"HargaDasar"`
|
|
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"`
|
|
HargaDasar string `json:"HargaDasar"`
|
|
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)
|
|
|
|
periodeTarif, tariffTable, err := r.activePaketTariff(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
result.PeriodeTarif = periodeTarif
|
|
pricing := newPaketBasePricing()
|
|
if result.Layanan, err = r.paketLayanan(ctx, id, result.KelasID, periodeTarif, tariffTable, pricing); err != nil {
|
|
return nil, err
|
|
}
|
|
if result.Item, err = r.paketItem(ctx, id, pricing); err != nil {
|
|
return nil, err
|
|
}
|
|
if result.Operasi, err = r.paketOperasi(ctx, id, pricing); err != nil {
|
|
return nil, err
|
|
}
|
|
if result.Kamar, err = r.paketKamar(ctx, id); err != nil {
|
|
return nil, err
|
|
}
|
|
return &result, nil
|
|
}
|
|
|
|
type paketBasePricing struct {
|
|
layananByOperation map[string]float64
|
|
itemByOperation map[string]float64
|
|
}
|
|
|
|
func newPaketBasePricing() *paketBasePricing {
|
|
return &paketBasePricing{
|
|
layananByOperation: make(map[string]float64),
|
|
itemByOperation: make(map[string]float64),
|
|
}
|
|
}
|
|
|
|
func (r MySQLLayananRepository) activePaketTariff(ctx context.Context) (*PaketPeriodeTarif, string, error) {
|
|
var period PaketPeriodeTarif
|
|
var keterangan sql.NullString
|
|
err := r.db.QueryRowContext(ctx, `
|
|
SELECT PeriodeTarifID, Keterangan,
|
|
CAST(TanggalBerlaku AS CHAR), CAST(TanggalBerakhir AS CHAR)
|
|
FROM periode_tarif
|
|
WHERE DATE(TanggalBerlaku) <= CURRENT_DATE()
|
|
AND DATE(TanggalBerakhir) >= CURRENT_DATE()
|
|
AND NA = 'N'
|
|
ORDER BY PeriodeTarifID DESC
|
|
LIMIT 1`).Scan(
|
|
&period.PeriodeTarifID,
|
|
&keterangan,
|
|
&period.TanggalBerlaku,
|
|
&period.TanggalBerakhir,
|
|
)
|
|
if err == sql.ErrNoRows {
|
|
return nil, "", nil
|
|
}
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
period.Keterangan = nullStringPtr(keterangan)
|
|
|
|
tableName := "harga_layanan_" + strconv.FormatInt(period.PeriodeTarifID, 10)
|
|
exists, err := r.tableExists(ctx, tableName)
|
|
if err != nil || !exists {
|
|
return &period, "", err
|
|
}
|
|
return &period, tableName, nil
|
|
}
|
|
|
|
func (r MySQLLayananRepository) paketLayanan(
|
|
ctx context.Context,
|
|
id string,
|
|
classID string,
|
|
periodeTarif *PaketPeriodeTarif,
|
|
tariffTable string,
|
|
pricing *paketBasePricing,
|
|
) ([]PaketLayanan, error) {
|
|
tariffPriceSelect := "NULL"
|
|
args := make([]any, 0, 3)
|
|
if tariffTable != "" {
|
|
tariffPriceSelect = fmt.Sprintf(`(
|
|
SELECT tarif.Harga
|
|
FROM %s tarif
|
|
INNER JOIN layanandepartemen ld
|
|
ON ld.LayananDepartemenID = tarif.LayananDepartemenID
|
|
AND ld.NA = 'N'
|
|
WHERE ld.LayananID = pl.LayananID
|
|
AND tarif.KelasID IN (?, 'Default')
|
|
AND tarif.NA = 'N'
|
|
AND tarif.Harga > 0
|
|
ORDER BY CASE
|
|
WHEN tarif.KelasID = ? AND ld.DepartemenID <=> pl.DepartemenID THEN 1
|
|
WHEN tarif.KelasID = ? THEN 2
|
|
WHEN tarif.KelasID = 'Default' AND ld.DepartemenID <=> pl.DepartemenID THEN 3
|
|
ELSE 4
|
|
END, tarif.HargaLayananID
|
|
LIMIT 1
|
|
)`, tariffTable)
|
|
args = append(args, classID, classID, classID)
|
|
}
|
|
args = append(args, id)
|
|
query := fmt.Sprintf(`
|
|
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),
|
|
%s, COALESCE(ml.DefaultHarga, 0), COALESCE(ml.LayananKode, ''),
|
|
CAST(pl.HeaderLayanan 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`, tariffPriceSelect)
|
|
rows, err := r.db.QueryContext(ctx, query, args...)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
result := make([]PaketLayanan, 0)
|
|
type servicePricing struct {
|
|
tariffPrice sql.NullFloat64
|
|
defaultPrice float64
|
|
serviceCode string
|
|
operationHeader sql.NullString
|
|
}
|
|
pricingRows := make([]servicePricing, 0)
|
|
needsAdministrationPrice := false
|
|
for rows.Next() {
|
|
var item PaketLayanan
|
|
var namaLayanan, namaDepartemen sql.NullString
|
|
var harga, shareRS, shareDokter, subtotal string
|
|
var servicePrice servicePricing
|
|
if err := rows.Scan(
|
|
&item.PaketLayananID, &item.LayananID, &namaLayanan, &namaDepartemen,
|
|
&item.Jumlah, &item.JumlahHari, &harga, &item.Diskon, &item.TipeDiskon,
|
|
&shareRS, &shareDokter, &subtotal, &servicePrice.tariffPrice,
|
|
&servicePrice.defaultPrice, &servicePrice.serviceCode, &servicePrice.operationHeader,
|
|
); 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)
|
|
if servicePrice.tariffPrice.Float64 <= 0 && servicePrice.defaultPrice <= 0 && isAdministrationService(servicePrice.serviceCode) {
|
|
needsAdministrationPrice = true
|
|
}
|
|
result = append(result, item)
|
|
pricingRows = append(pricingRows, servicePrice)
|
|
}
|
|
if err := rows.Err(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
administrationPrice := 0.0
|
|
administrationSource := "NOT_AVAILABLE"
|
|
if needsAdministrationPrice {
|
|
var err error
|
|
administrationPrice, administrationSource, err = r.paketAdministrationBasePrice(ctx, periodeTarif)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
for index := range result {
|
|
servicePrice := pricingRows[index]
|
|
basePrice, source := paketServiceBasePrice(
|
|
servicePrice.tariffPrice,
|
|
servicePrice.defaultPrice,
|
|
servicePrice.serviceCode,
|
|
administrationPrice,
|
|
administrationSource,
|
|
)
|
|
result[index].HargaDasar = phpNumberFormat(basePrice, 0)
|
|
result[index].HargaDasarSource = source
|
|
if servicePrice.operationHeader.Valid {
|
|
pricing.layananByOperation[servicePrice.operationHeader.String] += basePrice * parseDecimal(result[index].Jumlah)
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func paketServiceBasePrice(
|
|
tariffPrice sql.NullFloat64,
|
|
defaultPrice float64,
|
|
serviceCode string,
|
|
administrationPrice float64,
|
|
administrationSource string,
|
|
) (float64, string) {
|
|
if tariffPrice.Valid && tariffPrice.Float64 > 0 {
|
|
return tariffPrice.Float64, "TARIF_PERIODE"
|
|
}
|
|
if defaultPrice > 0 {
|
|
return defaultPrice, "MASTER_DEFAULT"
|
|
}
|
|
if isAdministrationService(serviceCode) && administrationPrice > 0 {
|
|
return administrationPrice, administrationSource
|
|
}
|
|
return 0, "NOT_AVAILABLE"
|
|
}
|
|
|
|
func isAdministrationService(serviceCode string) bool {
|
|
switch strings.ToUpper(strings.TrimSpace(serviceCode)) {
|
|
case "AN001", "AD001":
|
|
return true
|
|
default:
|
|
return false
|
|
}
|
|
}
|
|
|
|
func (r MySQLLayananRepository) paketAdministrationBasePrice(
|
|
ctx context.Context,
|
|
periodeTarif *PaketPeriodeTarif,
|
|
) (float64, string, error) {
|
|
if periodeTarif != nil {
|
|
exists, err := r.tableExists(ctx, "adm_periode")
|
|
if err != nil {
|
|
return 0, "", err
|
|
}
|
|
if exists {
|
|
var price sql.NullFloat64
|
|
err = r.db.QueryRowContext(ctx, `
|
|
SELECT NilaiAdministrasi
|
|
FROM adm_periode
|
|
WHERE JenisRegID = 0 AND PeriodeTarifID = ?
|
|
ORDER BY AdmPeriodeID DESC
|
|
LIMIT 1`, periodeTarif.PeriodeTarifID).Scan(&price)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return 0, "", err
|
|
}
|
|
if price.Valid && price.Float64 > 0 {
|
|
return price.Float64, "ADM_PERIODE", nil
|
|
}
|
|
}
|
|
}
|
|
|
|
var price sql.NullFloat64
|
|
err := r.db.QueryRowContext(ctx, `
|
|
SELECT NilaiAdministrasi
|
|
FROM jenisreg
|
|
WHERE JenisRegID = 0
|
|
LIMIT 1`).Scan(&price)
|
|
if err != nil && err != sql.ErrNoRows {
|
|
return 0, "", err
|
|
}
|
|
if price.Valid && price.Float64 > 0 {
|
|
return price.Float64, "BIAYA_ADMIN", nil
|
|
}
|
|
return 0, "NOT_AVAILABLE", nil
|
|
}
|
|
|
|
func (r MySQLLayananRepository) paketOperasi(ctx context.Context, id string, pricing *paketBasePricing) ([]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)
|
|
headerID := strconv.FormatInt(item.PaketOperasiHeaderID, 10)
|
|
item.HargaDasar = phpNumberFormat(
|
|
pricing.layananByOperation[headerID]+pricing.itemByOperation[headerID],
|
|
2,
|
|
)
|
|
result = append(result, item)
|
|
}
|
|
return result, rows.Err()
|
|
}
|
|
|
|
func (r MySQLLayananRepository) paketItem(ctx context.Context, id string, pricing *paketBasePricing) ([]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),
|
|
COALESCE(mi.HET, 'N'), COALESCE(mi.DefaultHarga, 0),
|
|
COALESCE(mi.CostDasar, 0), COALESCE(mi.KategoriItemID, 0),
|
|
(SELECT mo.Margin FROM margin_obat mo
|
|
WHERE mo.Dari <= mi.CostDasar AND mo.Sampai >= mi.CostDasar
|
|
AND mo.JenisReg = 'RJ' AND mo.NA = 'N' LIMIT 1),
|
|
(SELECT ss.Value_ FROM sys_setting ss
|
|
WHERE ss.Setting = 'Margin_RJ' AND ss.NA = 'N' LIMIT 1),
|
|
(SELECT ma.Margin FROM margin_alkes ma
|
|
WHERE ma.Dari <= mi.CostDasar AND ma.Sampai >= mi.CostDasar
|
|
AND ma.NA = 'N' LIMIT 1),
|
|
CAST(pi.HeaderID AS CHAR)
|
|
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
|
|
var het string
|
|
var defaultPrice, baseCost float64
|
|
var categoryID int
|
|
var medicineMargin, settingMargin, medicalSupplyMargin sql.NullFloat64
|
|
var operationHeader sql.NullString
|
|
if err := rows.Scan(
|
|
&item.PaketItemID, &item.ItemID, &namaItem, &namaDepartemen, &item.Jumlah,
|
|
&namaSatuan, &harga, &item.Diskon, &item.TipeDiskon,
|
|
&satuanID, &satuanDasarID, &konversi,
|
|
&het, &defaultPrice, &baseCost, &categoryID,
|
|
&medicineMargin, &settingMargin, &medicalSupplyMargin, &operationHeader,
|
|
); 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)
|
|
basePrice := paketItemBasePrice(
|
|
het, defaultPrice, baseCost, categoryID,
|
|
medicineMargin, settingMargin, medicalSupplyMargin,
|
|
)
|
|
item.HargaDasar = phpNumberFormat(basePrice, 2)
|
|
item.SubTotal = phpNumberFormat((hargaValue-nDisc)*parseDecimal(item.Jumlah)*konversi, 2)
|
|
if operationHeader.Valid {
|
|
pricing.itemByOperation[operationHeader.String] += basePrice * parseDecimal(item.Jumlah) * konversi
|
|
}
|
|
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),
|
|
COALESCE(k.HargaTT, 0)
|
|
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
|
|
var basePrice float64
|
|
if err := rows.Scan(
|
|
&item.PaketTTID, &namaDepartemen, &namaKelas, &item.Jumlah,
|
|
&harga, &item.TipeDiskon, &diskon, &basePrice,
|
|
); 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.HargaDasar = phpNumberFormat(basePrice, 2)
|
|
item.SubTotal = phpNumberFormat((hargaValue-item.Diskon)*parseDecimal(item.Jumlah), 0)
|
|
result = append(result, item)
|
|
}
|
|
return result, rows.Err()
|
|
}
|
|
|
|
func paketItemBasePrice(
|
|
het string,
|
|
defaultPrice float64,
|
|
baseCost float64,
|
|
categoryID int,
|
|
medicineMargin sql.NullFloat64,
|
|
settingMargin sql.NullFloat64,
|
|
medicalSupplyMargin sql.NullFloat64,
|
|
) float64 {
|
|
if het == "Y" {
|
|
return defaultPrice
|
|
}
|
|
|
|
switch categoryID {
|
|
case 1:
|
|
margin := settingMargin.Float64
|
|
if medicineMargin.Valid {
|
|
margin = medicineMargin.Float64
|
|
}
|
|
return math.Round(baseCost + baseCost*margin/100)
|
|
case 2:
|
|
return math.Round(baseCost + baseCost*medicalSupplyMargin.Float64/100)
|
|
default:
|
|
return math.Round(defaultPrice)
|
|
}
|
|
}
|
|
|
|
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")
|
|
}
|