feat: add package creation and clone validation

This commit is contained in:
2026-09-16 13:55:56 +07:00
parent 94c3dd1a70
commit 19fe8c5d94
12 changed files with 1034 additions and 10 deletions

View File

@@ -22,6 +22,47 @@ type PaketListParams struct {
PerPage int
}
// CreatePaketParams contains values already validated by the HTTP layer.
// Monetary values are stored as cents to keep calculations exact without float.
type CreatePaketParams struct {
Nama string
JenisPaket string
RawatInap bool
RawatJalan bool
MultiVisit bool
TanggalMulai string
TanggalSelesai string
KelasID string
HeaderPaketID string
Keterangan string
JumlahHari int64
Channel string
Layanan []CreatePaketLayananParams
}
type CreatePaketLayananParams struct {
LayananID string
DepartemenID string
Jumlah int64
JumlahHari int64
HargaCents int64
TipeDiskon string
DiskonCents int64
TipeShare string
ShareRSCents int64
ShareDokterCents int64
ShareExternalCents int64
ShareLainCents int64
}
type CreatePaketResult struct {
PaketID string
Nama string
TotalHarga string
TotalDiskon string
JumlahLayanan int
}
type PaketPage struct {
Items []PaketSummary
Total int
@@ -86,6 +127,7 @@ type PaketPeriodeTarif struct {
type PaketLayanan struct {
PaketLayananID int64 `json:"PaketLayananID"`
LayananID string `json:"LayananID"`
DepartemenID string `json:"DepartemenID"`
NamaLayanan *string `json:"NamaLayanan"`
NamaDepartemen *string `json:"NamaDepartemen"`
Jumlah string `json:"Jumlah"`
@@ -95,8 +137,11 @@ type PaketLayanan struct {
HargaDasarSource string `json:"HargaDasarSource"`
Diskon string `json:"Diskon"`
TipeDiskon string `json:"TipeDiskon"`
TipeShare string `json:"TipeShare"`
ShareRS string `json:"ShareRS"`
ShareDokter string `json:"ShareDokter"`
ShareExternal string `json:"ShareExternal"`
ShareLain string `json:"ShareLain"`
Subtotal string `json:"Subtotal"`
}
@@ -350,10 +395,11 @@ func (r MySQLLayananRepository) paketLayanan(
}
args = append(args, id)
query := fmt.Sprintf(`
SELECT pl.PaketLayananID, pl.LayananID, ml.Nama, d.Nama,
SELECT pl.PaketLayananID, pl.LayananID, pl.DepartemenID, 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),
CAST(pl.ShareDokter AS CHAR), pl.ShareTipe, CAST(pl.ShareExternal AS CHAR),
CAST(pl.ShareLain AS CHAR), CAST(pl.Subtotal AS CHAR),
%s, COALESCE(ml.DefaultHarga, 0), COALESCE(ml.LayananKode, ''),
CAST(pl.HeaderLayanan AS CHAR)
FROM paket_layanan pl
@@ -378,21 +424,26 @@ func (r MySQLLayananRepository) paketLayanan(
for rows.Next() {
var item PaketLayanan
var namaLayanan, namaDepartemen sql.NullString
var harga, shareRS, shareDokter, subtotal string
var harga, shareRS, shareDokter, tipeShare, shareExternal, shareLain, subtotal string
var departemenID string
var servicePrice servicePricing
if err := rows.Scan(
&item.PaketLayananID, &item.LayananID, &namaLayanan, &namaDepartemen,
&item.PaketLayananID, &item.LayananID, &departemenID, &namaLayanan, &namaDepartemen,
&item.Jumlah, &item.JumlahHari, &harga, &item.Diskon, &item.TipeDiskon,
&shareRS, &shareDokter, &subtotal, &servicePrice.tariffPrice,
&shareRS, &shareDokter, &tipeShare, &shareExternal, &shareLain, &subtotal, &servicePrice.tariffPrice,
&servicePrice.defaultPrice, &servicePrice.serviceCode, &servicePrice.operationHeader,
); err != nil {
return nil, err
}
item.NamaLayanan = nullStringPtr(namaLayanan)
item.NamaDepartemen = nullStringPtr(namaDepartemen)
item.DepartemenID = departemenID
item.TipeShare = tipeShare
item.Harga = phpNumberFormat(parseDecimal(harga), 0)
item.ShareRS = phpNumberFormat(parseDecimal(shareRS), 2)
item.ShareDokter = phpNumberFormat(parseDecimal(shareDokter), 0)
item.ShareExternal = phpNumberFormat(parseDecimal(shareExternal), 2)
item.ShareLain = phpNumberFormat(parseDecimal(shareLain), 2)
item.Subtotal = phpNumberFormat(parseDecimal(subtotal), 0)
if servicePrice.tariffPrice.Float64 <= 0 && servicePrice.defaultPrice <= 0 && isAdministrationService(servicePrice.serviceCode) {
needsAdministrationPrice = true