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)
@@ -48,13 +56,15 @@ type PerusahaanRepository interface {
} }
type Handler struct { type Handler struct {
layananRepo LayananRepository layananRepo LayananRepository
labRepo LabRegistrationRepository labRepo LabRegistrationRepository
patientRepo PatientRepository patientRepo PatientRepository
labResultRepo LabResultRepository labResultRepo LabResultRepository
paketRepo PaketRepository paketRepo PaketRepository
tarifRepo TarifKerjasamaRepository tarifRepo TarifKerjasamaRepository
perusahaanRepo PerusahaanRepository periodeTarifRepo PeriodeTarifRepository
periodeTarifDetailRepo PeriodeTarifDetailRepository
perusahaanRepo PerusahaanRepository
} }
type CPONERepository interface { type CPONERepository interface {
@@ -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
}