feat: add search and pagination to tariff period services

This commit is contained in:
2026-09-02 09:27:23 +07:00
parent 1d5bc309c0
commit 181d53af45
8 changed files with 560 additions and 8 deletions

View File

@@ -23,6 +23,21 @@ Authorization: Bearer <your-secure-token>
GET /api/cpone/tarif-kerjasama/{id}?group_tarif_id=&active=N&effective_date=2026-08-22 GET /api/cpone/tarif-kerjasama/{id}?group_tarif_id=&active=N&effective_date=2026-08-22
Authorization: Bearer <your-secure-token> Authorization: Bearer <your-secure-token>
GET /api/cpone/periode-tarif
Authorization: Bearer <your-secure-token>
X-RS-Code: <kode-rs>
Endpoint ini menampilkan seluruh periode tarif dari HIS dengan `NA = 'N'`.
GET /api/cpone/periode-tarif/{PeriodeTarifID}?search=&page=1
Authorization: Bearer <your-secure-token>
X-RS-Code: <kode-rs>
Endpoint ini mengambil detail seluruh tarif layanan dari tabel dinamis
`harga_layanan_{PeriodeTarifID}` beserta layanan, departemen, dan kelas tarif.
Parameter `search` mencari berdasarkan `LayananID` atau `NamaLayanan`; hasil
dipaginasi 20 data per halaman.
GET /api/cpone/layanan-mapping-harga?periodeTarifID=10&layananID=6413,6414,1309 GET /api/cpone/layanan-mapping-harga?periodeTarifID=10&layananID=6413,6414,1309
Authorization: Bearer <your-secure-token> Authorization: Bearer <your-secure-token>

View File

@@ -56,6 +56,8 @@ func main() {
mux.Handle("GET /api/cpone/perusahaan/{id}/group-tarif", protected(http.HandlerFunc(handler.GetPerusahaanGroupTarif))) mux.Handle("GET /api/cpone/perusahaan/{id}/group-tarif", protected(http.HandlerFunc(handler.GetPerusahaanGroupTarif)))
mux.Handle("GET /api/cpone/tarif-kerjasama", protected(http.HandlerFunc(handler.ListTarifKerjasama))) mux.Handle("GET /api/cpone/tarif-kerjasama", protected(http.HandlerFunc(handler.ListTarifKerjasama)))
mux.Handle("GET /api/cpone/tarif-kerjasama/{id}", protected(http.HandlerFunc(handler.GetTarifKerjasama))) mux.Handle("GET /api/cpone/tarif-kerjasama/{id}", protected(http.HandlerFunc(handler.GetTarifKerjasama)))
mux.Handle("GET /api/cpone/periode-tarif", protected(http.HandlerFunc(handler.ListPeriodeTarif)))
mux.Handle("GET /api/cpone/periode-tarif/{periodeTarifID}", protected(http.HandlerFunc(handler.ListPeriodeTarifLayanan)))
mux.Handle("GET /api/cpone/layanan-mapping-harga", protected(http.HandlerFunc(handler.MappingHarga))) mux.Handle("GET /api/cpone/layanan-mapping-harga", protected(http.HandlerFunc(handler.MappingHarga)))
mux.Handle("POST /api/cpone/patients/medrec", protected(http.HandlerFunc(handler.StoreMedrec))) mux.Handle("POST /api/cpone/patients/medrec", protected(http.HandlerFunc(handler.StoreMedrec)))
mux.Handle("POST /api/cpone/patients/lab-registration", protected(http.HandlerFunc(handler.RegisterLab))) mux.Handle("POST /api/cpone/patients/lab-registration", protected(http.HandlerFunc(handler.RegisterLab)))

View File

@@ -41,6 +41,14 @@ type TarifKerjasamaRepository interface {
GetTarifKerjasama(ctx context.Context, id int64, params repository.TarifKerjasamaDetailParams) (*repository.TarifKerjasamaDetail, error) GetTarifKerjasama(ctx context.Context, id int64, params repository.TarifKerjasamaDetailParams) (*repository.TarifKerjasamaDetail, error)
} }
type PeriodeTarifRepository interface {
ListPeriodeTarif(ctx context.Context) ([]repository.PeriodeTarif, error)
}
type PeriodeTarifDetailRepository interface {
ListPeriodeTarifLayanan(ctx context.Context, params repository.PeriodeTarifLayananListParams) (repository.PeriodeTarifLayananPage, error)
}
type PerusahaanRepository interface { type PerusahaanRepository interface {
ListPerusahaan(ctx context.Context, params repository.PerusahaanListParams) (repository.PerusahaanPage, error) ListPerusahaan(ctx context.Context, params repository.PerusahaanListParams) (repository.PerusahaanPage, error)
GetPerusahaan(ctx context.Context, id string) (*repository.PerusahaanDetail, error) GetPerusahaan(ctx context.Context, id string) (*repository.PerusahaanDetail, error)
@@ -54,6 +62,8 @@ type Handler struct {
labResultRepo LabResultRepository labResultRepo LabResultRepository
paketRepo PaketRepository paketRepo PaketRepository
tarifRepo TarifKerjasamaRepository tarifRepo TarifKerjasamaRepository
periodeTarifRepo PeriodeTarifRepository
periodeTarifDetailRepo PeriodeTarifDetailRepository
perusahaanRepo PerusahaanRepository perusahaanRepo PerusahaanRepository
} }
@@ -64,13 +74,16 @@ type CPONERepository interface {
LabResultRepository LabResultRepository
PaketRepository PaketRepository
TarifKerjasamaRepository TarifKerjasamaRepository
PeriodeTarifRepository
PeriodeTarifDetailRepository
PerusahaanRepository PerusahaanRepository
} }
func NewHandler(repo CPONERepository) Handler { func NewHandler(repo CPONERepository) Handler {
return Handler{ return Handler{
layananRepo: repo, labRepo: repo, patientRepo: repo, layananRepo: repo, labRepo: repo, patientRepo: repo,
labResultRepo: repo, paketRepo: repo, tarifRepo: repo, perusahaanRepo: repo, labResultRepo: repo, paketRepo: repo, tarifRepo: repo,
periodeTarifRepo: repo, periodeTarifDetailRepo: repo, perusahaanRepo: repo,
} }
} }
@@ -118,6 +131,20 @@ func (h Handler) tarifRepository(ctx context.Context) TarifKerjasamaRepository {
return h.tarifRepo return h.tarifRepo
} }
func (h Handler) periodeTarifRepository(ctx context.Context) PeriodeTarifRepository {
if repo, ok := selectedRepository(ctx); ok {
return repo
}
return h.periodeTarifRepo
}
func (h Handler) periodeTarifDetailRepository(ctx context.Context) PeriodeTarifDetailRepository {
if repo, ok := selectedRepository(ctx); ok {
return repo
}
return h.periodeTarifDetailRepo
}
func (h Handler) perusahaanRepository(ctx context.Context) PerusahaanRepository { func (h Handler) perusahaanRepository(ctx context.Context) PerusahaanRepository {
if repo, ok := selectedRepository(ctx); ok { if repo, ok := selectedRepository(ctx); ok {
return repo return repo

View File

@@ -0,0 +1,29 @@
package http
import (
"net/http"
"primaya-api/cpone-middleware/internal/repository"
)
func (h Handler) ListPeriodeTarif(w http.ResponseWriter, r *http.Request) {
items, err := h.periodeTarifRepository(r.Context()).ListPeriodeTarif(r.Context())
if err != nil {
WriteJSON(w, http.StatusInternalServerError, Response{
Success: false,
Message: "internal server error",
Data: nil,
})
return
}
if items == nil {
items = []repository.PeriodeTarif{}
}
WriteJSON(w, http.StatusOK, Response{
Success: true,
Message: "ok",
Data: items,
})
}

View File

@@ -0,0 +1,118 @@
package http
import (
"errors"
"net/http"
"net/url"
"strconv"
"strings"
"primaya-api/cpone-middleware/internal/repository"
)
const periodeTarifLayananPerPage = 20
type PeriodeTarifLayananPaginationResponse struct {
CurrentPage int `json:"current_page"`
Data []repository.PeriodeTarifLayanan `json:"data"`
FirstPageURL string `json:"first_page_url"`
From *int `json:"from"`
LastPage int `json:"last_page"`
LastPageURL string `json:"last_page_url"`
Links map[string]any `json:"links"`
NextPageURL *string `json:"next_page_url"`
Total int `json:"total"`
PerPage int `json:"per_page"`
}
func (h Handler) ListPeriodeTarifLayanan(w http.ResponseWriter, r *http.Request) {
rawID := strings.TrimSpace(r.PathValue("periodeTarifID"))
if rawID == "" {
rawID = strings.TrimSpace(r.PathValue("PeriodeTarifID"))
}
periodeTarifID, err := strconv.ParseInt(rawID, 10, 64)
if err != nil || periodeTarifID < 1 {
WriteJSON(w, http.StatusUnprocessableEntity, Response{
Success: false,
Message: "PeriodeTarifID harus berupa angka positif",
Data: nil,
})
return
}
pageNumber := periodeTarifLayananPageNumber(r)
search := strings.TrimSpace(r.URL.Query().Get("search"))
page, err := h.periodeTarifDetailRepository(r.Context()).ListPeriodeTarifLayanan(r.Context(), repository.PeriodeTarifLayananListParams{
PeriodeTarifID: periodeTarifID,
Page: pageNumber,
PerPage: periodeTarifLayananPerPage,
Search: search,
})
if errors.Is(err, repository.ErrTarifTableNotFound) {
WriteJSON(w, http.StatusNotFound, Response{
Success: false,
Message: "Tabel tarif tidak ditemukan: harga_layanan_" + strconv.FormatInt(periodeTarifID, 10),
Data: nil,
})
return
}
if err != nil {
WriteJSON(w, http.StatusInternalServerError, Response{
Success: false,
Message: "internal server error",
Data: nil,
})
return
}
if page.Items == nil {
page.Items = []repository.PeriodeTarifLayanan{}
}
lastPage := (page.Total + periodeTarifLayananPerPage - 1) / periodeTarifLayananPerPage
if lastPage < 1 {
lastPage = 1
}
var from *int
if len(page.Items) > 0 {
value := (pageNumber-1)*periodeTarifLayananPerPage + 1
from = &value
}
var nextPageURL *string
if pageNumber < lastPage {
value := periodeTarifLayananPageURL(r, pageNumber+1)
nextPageURL = &value
}
WriteJSON(w, http.StatusOK, PeriodeTarifLayananPaginationResponse{
CurrentPage: pageNumber, Data: page.Items, FirstPageURL: "", From: from,
LastPage: lastPage, LastPageURL: "", Links: map[string]any{},
NextPageURL: nextPageURL, Total: page.Total, PerPage: periodeTarifLayananPerPage,
})
}
func periodeTarifLayananPageNumber(r *http.Request) int {
page, err := strconv.Atoi(r.URL.Query().Get("page"))
if err != nil || page < 1 {
return 1
}
return page
}
func periodeTarifLayananPageURL(r *http.Request, page int) string {
scheme := "http"
if r.TLS != nil {
scheme = "https"
}
if forwarded := r.Header.Get("X-Forwarded-Proto"); forwarded != "" {
scheme = strings.TrimSpace(strings.Split(forwarded, ",")[0])
}
host := r.Host
if host == "" {
host = "localhost"
}
query := r.URL.Query()
query.Set("page", strconv.Itoa(page))
query.Set("per_page", strconv.Itoa(periodeTarifLayananPerPage))
return (&url.URL{Scheme: scheme, Host: host, Path: r.URL.Path, RawQuery: query.Encode()}).String()
}

View File

@@ -0,0 +1,113 @@
package http
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"primaya-api/cpone-middleware/internal/repository"
)
type fakePeriodeTarifDetailRepository struct {
items []repository.PeriodeTarifLayanan
err error
id int64
params repository.PeriodeTarifLayananListParams
total int
}
func (f *fakePeriodeTarifDetailRepository) ListPeriodeTarifLayanan(_ context.Context, params repository.PeriodeTarifLayananListParams) (repository.PeriodeTarifLayananPage, error) {
f.id = params.PeriodeTarifID
f.params = params
total := f.total
if total == 0 {
total = len(f.items)
}
return repository.PeriodeTarifLayananPage{Items: f.items, Total: total}, f.err
}
func TestListPeriodeTarifLayananReturnsRows(t *testing.T) {
layananID, nama, departemen, kelas, namaKelas, harga := "6413", "Darah Lengkap", "LAB", "Default", "Default", "125000.00"
repo := &fakePeriodeTarifDetailRepository{items: []repository.PeriodeTarifLayanan{{
TarifLayananID: 10, LayananID: &layananID, NamaLayanan: &nama,
LayananDepartemenID: 20, Departemen: &departemen, Kelas: kelas, NamaKelas: &namaKelas,
Harga: &harga,
}}}
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/periode-tarif/12?search=Darah&page=2", nil)
request.SetPathValue("periodeTarifID", "12")
Handler{periodeTarifDetailRepo: repo}.ListPeriodeTarifLayanan(recorder, request)
if recorder.Code != http.StatusOK || repo.id != 12 {
t.Fatalf("status=%d id=%d body=%s", recorder.Code, repo.id, recorder.Body.String())
}
for _, expected := range []string{
`"LayananID":"6413"`, `"NamaLayanan":"Darah Lengkap"`,
`"LayananDepartemenID":20`, `"Departemen":"LAB"`,
`"Kelas":"Default"`, `"Harga":"125000.00"`,
} {
if !strings.Contains(recorder.Body.String(), expected) {
t.Fatalf("response missing %s: %s", expected, recorder.Body.String())
}
}
}
func TestListPeriodeTarifLayananUsesSearchAndPagination(t *testing.T) {
repo := &fakePeriodeTarifDetailRepository{total: 41}
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "http://localhost/api/cpone/periode-tarif/12?search=Admin&page=1", nil)
request.SetPathValue("periodeTarifID", "12")
Handler{periodeTarifDetailRepo: repo}.ListPeriodeTarifLayanan(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
if repo.params.Page != 1 || repo.params.PerPage != periodeTarifLayananPerPage || repo.params.Search != "Admin" {
t.Fatalf("params=%+v", repo.params)
}
for _, expected := range []string{
`"current_page":1`, `"last_page":3`, `"per_page":20`, `"total":41`,
`"next_page_url":"http://localhost/api/cpone/periode-tarif/12?page=2\u0026per_page=20\u0026search=Admin"`,
} {
if !strings.Contains(recorder.Body.String(), expected) {
t.Fatalf("response missing %s: %s", expected, recorder.Body.String())
}
}
}
func TestListPeriodeTarifLayananValidationAndMissingTable(t *testing.T) {
for _, rawID := range []string{"", "abc", "0", "1.5"} {
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/periode-tarif/"+rawID, nil)
request.SetPathValue("periodeTarifID", rawID)
Handler{periodeTarifDetailRepo: &fakePeriodeTarifDetailRepository{}}.ListPeriodeTarifLayanan(recorder, request)
if recorder.Code != http.StatusUnprocessableEntity {
t.Fatalf("id=%q status=%d body=%s", rawID, recorder.Code, recorder.Body.String())
}
}
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/periode-tarif/12", nil)
request.SetPathValue("periodeTarifID", "12")
Handler{periodeTarifDetailRepo: &fakePeriodeTarifDetailRepository{err: repository.ErrTarifTableNotFound}}.
ListPeriodeTarifLayanan(recorder, request)
if recorder.Code != http.StatusNotFound || !strings.Contains(recorder.Body.String(), "harga_layanan_12") {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestListPeriodeTarifLayananInternalError(t *testing.T) {
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/periode-tarif/12", nil)
request.SetPathValue("periodeTarifID", "12")
Handler{periodeTarifDetailRepo: &fakePeriodeTarifDetailRepository{err: errors.New("query failed")}}.
ListPeriodeTarifLayanan(recorder, request)
if recorder.Code != http.StatusInternalServerError {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}

View File

@@ -0,0 +1,72 @@
package http
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"primaya-api/cpone-middleware/internal/repository"
)
type fakePeriodeTarifRepository struct {
items []repository.PeriodeTarif
err error
}
func (f *fakePeriodeTarifRepository) ListPeriodeTarif(_ context.Context) ([]repository.PeriodeTarif, error) {
return f.items, f.err
}
func (f *fakePeriodeTarifRepository) ListPeriodeTarifLayanan(_ context.Context, _ repository.PeriodeTarifLayananListParams) (repository.PeriodeTarifLayananPage, error) {
return repository.PeriodeTarifLayananPage{}, f.err
}
func TestListPeriodeTarifReturnsAvailablePeriods(t *testing.T) {
keterangan := "Tarif Dasar 2026"
repo := &fakePeriodeTarifRepository{items: []repository.PeriodeTarif{{
PeriodeTarifID: 42, Keterangan: &keterangan,
TanggalBerlaku: "2026-01-01", TanggalBerakhir: "2026-12-31",
}}}
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/periode-tarif", nil)
Handler{periodeTarifRepo: repo}.ListPeriodeTarif(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
for _, expected := range []string{
`"success":true`, `"message":"ok"`, `"PeriodeTarifID":42`,
`"Keterangan":"Tarif Dasar 2026"`, `"TanggalBerlaku":"2026-01-01"`,
`"TanggalBerakhir":"2026-12-31"`,
} {
if !strings.Contains(recorder.Body.String(), expected) {
t.Fatalf("response missing %s: %s", expected, recorder.Body.String())
}
}
}
func TestListPeriodeTarifReturnsEmptyArray(t *testing.T) {
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/periode-tarif", nil)
Handler{periodeTarifRepo: &fakePeriodeTarifRepository{items: []repository.PeriodeTarif{}}}.
ListPeriodeTarif(recorder, request)
if recorder.Code != http.StatusOK || !strings.Contains(recorder.Body.String(), `"data":[]`) {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}
func TestListPeriodeTarifInternalError(t *testing.T) {
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/periode-tarif", nil)
Handler{periodeTarifRepo: &fakePeriodeTarifRepository{err: errors.New("query failed")}}.
ListPeriodeTarif(recorder, request)
if recorder.Code != http.StatusInternalServerError {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}

View File

@@ -0,0 +1,176 @@
package repository
import (
"context"
"database/sql"
"fmt"
"strconv"
)
type PeriodeTarif struct {
PeriodeTarifID int64 `json:"PeriodeTarifID"`
Keterangan *string `json:"Keterangan"`
TanggalBerlaku string `json:"TanggalBerlaku"`
TanggalBerakhir string `json:"TanggalBerakhir"`
}
func (r MySQLLayananRepository) ListPeriodeTarif(ctx context.Context) ([]PeriodeTarif, error) {
rows, err := r.db.QueryContext(ctx, `
SELECT PeriodeTarifID, Keterangan,
CAST(TanggalBerlaku AS CHAR), CAST(TanggalBerakhir AS CHAR)
FROM periode_tarif
WHERE NA = 'N'
ORDER BY Keterangan ASC, PeriodeTarifID ASC
`)
if err != nil {
return nil, err
}
defer rows.Close()
result := make([]PeriodeTarif, 0)
for rows.Next() {
var item PeriodeTarif
var keterangan sql.NullString
if err := rows.Scan(&item.PeriodeTarifID, &keterangan,
&item.TanggalBerlaku, &item.TanggalBerakhir); err != nil {
return nil, err
}
item.Keterangan = nullStringPtr(keterangan)
result = append(result, item)
}
if err := rows.Err(); err != nil {
return nil, err
}
return result, nil
}
type PeriodeTarifLayanan struct {
TarifLayananID int64 `json:"TarifLayananID"`
LayananID *string `json:"LayananID"`
NamaLayanan *string `json:"NamaLayanan"`
LayananDepartemenID int64 `json:"LayananDepartemenID"`
Departemen *string `json:"Departemen"`
Kelas string `json:"Kelas"`
NamaKelas *string `json:"NamaKelas"`
Harga *string `json:"Harga"`
TipeShare *string `json:"TipeShare"`
ShareDokter *string `json:"ShareDokter"`
ShareRS *string `json:"ShareRS"`
ShareExternal *string `json:"ShareExternal"`
ShareLain *string `json:"ShareLain"`
}
type PeriodeTarifLayananListParams struct {
PeriodeTarifID int64
Page int
PerPage int
Search string
}
type PeriodeTarifLayananPage struct {
Items []PeriodeTarifLayanan
Total int
}
func (r MySQLLayananRepository) ListPeriodeTarifLayanan(ctx context.Context, params PeriodeTarifLayananListParams) (PeriodeTarifLayananPage, error) {
tableName := "harga_layanan_" + strconv.FormatInt(params.PeriodeTarifID, 10)
exists, err := r.tableExists(ctx, tableName)
if err != nil {
return PeriodeTarifLayananPage{}, err
}
if !exists {
return PeriodeTarifLayananPage{}, ErrTarifTableNotFound
}
where := `
WHERE tarif.NA = 'N'
AND layanan_dept.NA = 'N'`
args := make([]any, 0, 4)
if params.Search != "" {
where += `
AND (layanan_dept.LayananID LIKE ? OR layanan.Nama LIKE ?)`
like := "%" + params.Search + "%"
args = append(args, like, like)
}
countQuery := fmt.Sprintf(`
SELECT COUNT(*)
FROM %s AS tarif
INNER JOIN layanandepartemen AS layanan_dept
ON layanan_dept.LayananDepartemenID = tarif.LayananDepartemenID
LEFT JOIN masterlayanan AS layanan
ON layanan.LayananID = layanan_dept.LayananID
%s
`, tableName, where)
var total int
if err := r.db.QueryRowContext(ctx, countQuery, args...).Scan(&total); err != nil {
return PeriodeTarifLayananPage{}, err
}
query := fmt.Sprintf(`
SELECT
tarif.HargaLayananID,
layanan_dept.LayananID,
layanan.Nama,
tarif.LayananDepartemenID,
departemen.Nama,
tarif.KelasID,
kelas.Nama,
CAST(tarif.Harga AS CHAR),
tarif.ShareTipe,
CAST(tarif.ShareDokter AS CHAR),
CAST(tarif.ShareRS AS CHAR),
CAST(tarif.ShareExternal AS CHAR),
CAST(tarif.ShareLain AS CHAR)
FROM %s AS tarif
INNER JOIN layanandepartemen AS layanan_dept
ON layanan_dept.LayananDepartemenID = tarif.LayananDepartemenID
LEFT JOIN masterlayanan AS layanan
ON layanan.LayananID = layanan_dept.LayananID
LEFT JOIN departemen AS departemen
ON departemen.DepartemenID = layanan_dept.DepartemenID
LEFT JOIN kelas AS kelas
ON kelas.KelasID = tarif.KelasID
%s
ORDER BY layanan.Nama, layanan_dept.LayananID, tarif.KelasID, tarif.HargaLayananID
LIMIT ? OFFSET ?
`, tableName, where)
args = append(args, params.PerPage, (params.Page-1)*params.PerPage)
rows, err := r.db.QueryContext(ctx, query, args...)
if err != nil {
return PeriodeTarifLayananPage{}, err
}
defer rows.Close()
result := make([]PeriodeTarifLayanan, 0)
for rows.Next() {
var item PeriodeTarifLayanan
var layananID, namaLayanan, departemen, namaKelas, harga sql.NullString
var tipeShare, shareDokter, shareRS, shareExternal, shareLain sql.NullString
if err := rows.Scan(
&item.TarifLayananID, &layananID, &namaLayanan,
&item.LayananDepartemenID, &departemen, &item.Kelas, &namaKelas,
&harga, &tipeShare, &shareDokter, &shareRS, &shareExternal, &shareLain,
); err != nil {
return PeriodeTarifLayananPage{}, err
}
item.LayananID = nullStringPtr(layananID)
item.NamaLayanan = nullStringPtr(namaLayanan)
item.Departemen = nullStringPtr(departemen)
item.NamaKelas = nullStringPtr(namaKelas)
item.Harga = nullStringPtr(harga)
item.TipeShare = nullStringPtr(tipeShare)
item.ShareDokter = nullStringPtr(shareDokter)
item.ShareRS = nullStringPtr(shareRS)
item.ShareExternal = nullStringPtr(shareExternal)
item.ShareLain = nullStringPtr(shareLain)
result = append(result, item)
}
if err := rows.Err(); err != nil {
return PeriodeTarifLayananPage{}, err
}
return PeriodeTarifLayananPage{Items: result, Total: total}, nil
}