Add MCU report endpoint and deployment tooling

This commit is contained in:
2026-09-25 06:49:58 +07:00
parent d881872206
commit dd62728316
14 changed files with 3841 additions and 2 deletions

View File

@@ -64,6 +64,10 @@ type DokterRepository interface {
ListDokter(ctx context.Context, params repository.DokterListParams) (repository.DokterPage, error)
}
type LaporanMCURepository interface {
ListLaporanMCU(ctx context.Context, params repository.LaporanMCUParams) ([]repository.LaporanMCURow, error)
}
type Handler struct {
layananRepo LayananRepository
labRepo LabRegistrationRepository
@@ -76,6 +80,7 @@ type Handler struct {
periodeTarifDetailRepo PeriodeTarifDetailRepository
perusahaanRepo PerusahaanRepository
dokterRepo DokterRepository
laporanMCURepo LaporanMCURepository
hospitalCode string
departmentID string
}
@@ -95,12 +100,19 @@ type CPONERepository interface {
}
func NewHandler(repo CPONERepository) Handler {
return Handler{
handler := Handler{
layananRepo: repo, labRepo: repo, patientRepo: repo, registrationRepo: repo,
labResultRepo: repo, paketRepo: repo, tarifRepo: repo,
periodeTarifRepo: repo, periodeTarifDetailRepo: repo, perusahaanRepo: repo,
dokterRepo: repo,
}
// Keep NewHandler source-compatible with repositories created before the
// MCU report was added. The concrete MySQL repository implements this
// optional capability and the dynamic production handler uses it.
if reportRepo, ok := any(repo).(LaporanMCURepository); ok {
handler.laporanMCURepo = reportRepo
}
return handler
}
func NewDynamicHandler(hospitalCode string, departmentID string) Handler {
@@ -187,6 +199,13 @@ func (h Handler) dokterRepository(ctx context.Context) DokterRepository {
return h.dokterRepo
}
func (h Handler) laporanMCURepository(ctx context.Context) LaporanMCURepository {
if repo, ok := selectedRepository(ctx); ok {
return repo
}
return h.laporanMCURepo
}
func (h Handler) MappingHarga(w http.ResponseWriter, r *http.Request) {
layananIDs := parseLayananIDs(r)
if len(layananIDs) == 0 {

View File

@@ -0,0 +1,72 @@
package http
import (
"net/http"
"strings"
"time"
"primaya-api/cpone-middleware/internal/repository"
)
// ListLaporanMCU exposes the data formerly produced as mcu.xls by
// docs/lap_mcu.php. The API keeps the same lower-case date parameters as the
// PHP script and returns the spreadsheet rows as JSON data.
func (h Handler) ListLaporanMCU(w http.ResponseWriter, r *http.Request) {
tanggal, ok := firstQueryValue(r, "tanggal", "Tanggal")
if !ok || strings.TrimSpace(tanggal) == "" {
WriteJSON(w, http.StatusUnprocessableEntity, Response{
Success: false, Message: "Parameter tanggal wajib diisi", Data: nil,
})
return
}
tanggal2, ok := firstQueryValue(r, "tanggal2", "Tanggal2")
if !ok || strings.TrimSpace(tanggal2) == "" {
WriteJSON(w, http.StatusUnprocessableEntity, Response{
Success: false, Message: "Parameter tanggal2 wajib diisi", Data: nil,
})
return
}
start, err := parseReportDate(tanggal)
if err != nil {
WriteJSON(w, http.StatusUnprocessableEntity, Response{
Success: false, Message: "Parameter tanggal harus format Y-m-d", Data: nil,
})
return
}
end, err := parseReportDate(tanggal2)
if err != nil {
WriteJSON(w, http.StatusUnprocessableEntity, Response{
Success: false, Message: "Parameter tanggal2 harus format Y-m-d", Data: nil,
})
return
}
if end.Before(start) {
WriteJSON(w, http.StatusUnprocessableEntity, Response{
Success: false, Message: "Parameter tanggal2 tidak boleh lebih kecil dari tanggal", Data: nil,
})
return
}
rows, err := h.laporanMCURepository(r.Context()).ListLaporanMCU(r.Context(), repository.LaporanMCUParams{
Tanggal: start.Format("2006-01-02"), Tanggal2: end.Format("2006-01-02"),
})
if err != nil {
WriteJSON(w, http.StatusInternalServerError, Response{
Success: false, Message: "internal server error", Data: nil,
})
return
}
WriteJSON(w, http.StatusOK, Response{Success: true, Message: "ok", Data: rows})
}
// GetLaporanMCU is an explicit alias for callers that use the Get naming
// convention for report endpoints.
func (h Handler) GetLaporanMCU(w http.ResponseWriter, r *http.Request) {
h.ListLaporanMCU(w, r)
}
func parseReportDate(value string) (time.Time, error) {
return time.Parse("2006-01-02", strings.TrimSpace(value))
}

View File

@@ -0,0 +1,74 @@
package http
import (
"context"
"errors"
"net/http"
"net/http/httptest"
"strings"
"testing"
"primaya-api/cpone-middleware/internal/repository"
)
type fakeLaporanMCURepository struct {
rows []repository.LaporanMCURow
err error
params repository.LaporanMCUParams
}
func (f *fakeLaporanMCURepository) ListLaporanMCU(_ context.Context, params repository.LaporanMCUParams) ([]repository.LaporanMCURow, error) {
f.params = params
return f.rows, f.err
}
func TestListLaporanMCUResponseAndDateRange(t *testing.T) {
regID := "MCU260900001"
repo := &fakeLaporanMCURepository{rows: []repository.LaporanMCURow{{No: 1, RegID: &regID}}}
handler := Handler{laporanMCURepo: repo}
recorder := httptest.NewRecorder()
request := httptest.NewRequest(http.MethodGet, "/api/cpone/reports/mcu?tanggal=2026-09-01&tanggal2=2026-09-07", nil)
handler.ListLaporanMCU(recorder, request)
if recorder.Code != http.StatusOK {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
if repo.params.Tanggal != "2026-09-01" || repo.params.Tanggal2 != "2026-09-07" {
t.Fatalf("params=%+v", repo.params)
}
for _, expected := range []string{`"success":true`, `"message":"ok"`, `"No":1`, `"RegID":"MCU260900001"`} {
if !strings.Contains(recorder.Body.String(), expected) {
t.Fatalf("response missing %s: %s", expected, recorder.Body.String())
}
}
}
func TestListLaporanMCUValidatesDateRange(t *testing.T) {
repo := &fakeLaporanMCURepository{}
handler := Handler{laporanMCURepo: repo}
for target := range map[string]struct{}{
"/api/cpone/reports/mcu": {},
"/api/cpone/reports/mcu?tanggal=01-09-2026&tanggal2=2026-09-07": {},
"/api/cpone/reports/mcu?tanggal=2026-09-08&tanggal2=2026-09-07": {},
"/api/cpone/reports/mcu?tanggal=2026-09-01&tanggal2=not-a-date": {},
} {
recorder := httptest.NewRecorder()
handler.ListLaporanMCU(recorder, httptest.NewRequest(http.MethodGet, target, nil))
if recorder.Code != http.StatusUnprocessableEntity {
t.Fatalf("target=%s status=%d body=%s", target, recorder.Code, recorder.Body.String())
}
}
if repo.params != (repository.LaporanMCUParams{}) {
t.Fatalf("repository should not be called for invalid dates: %+v", repo.params)
}
}
func TestListLaporanMCUInternalError(t *testing.T) {
handler := Handler{laporanMCURepo: &fakeLaporanMCURepository{err: errors.New("database unavailable")}}
recorder := httptest.NewRecorder()
handler.ListLaporanMCU(recorder, httptest.NewRequest(http.MethodGet, "/api/cpone/reports/mcu?tanggal=2026-09-01&tanggal2=2026-09-01", nil))
if recorder.Code != http.StatusInternalServerError {
t.Fatalf("status=%d body=%s", recorder.Code, recorder.Body.String())
}
}

View File

@@ -0,0 +1,326 @@
package repository
import (
"context"
"database/sql"
)
// LaporanMCUParams is the date range used by docs/lap_mcu.php. The bounds are
// deliberately second-based because the legacy report queried
// "tanggal 00:00:01" through "tanggal2 23:59:59".
type LaporanMCUParams struct {
Tanggal string
Tanggal2 string
}
// LaporanMCURow contains the columns that the legacy Excel report writes.
// Nullable values remain pointers so an absent HIS record is represented as
// null by the API, while values for which the PHP report supplied a fallback
// are always non-null.
type LaporanMCURow struct {
No int `json:"No"`
RegID *string `json:"RegID"`
MEDRECID *string `json:"MEDRECID"`
NamaPasien *string `json:"NamaPasien"`
JenisKelamin *string `json:"JenisKelamin"`
TanggalLahir *string `json:"TanggalLahir"`
Umur *int64 `json:"Umur"`
TanggalMCU *string `json:"TanggalMCU"`
NamaPenanggung *string `json:"NamaPenanggung"`
DokterMCU *string `json:"DokterMCU"`
InstansiKerja *string `json:"InstansiKerja"`
TinggiBadan *string `json:"TinggiBadan"`
BeratBadan *string `json:"BeratBadan"`
TekananDarah *string `json:"TekananDarah"`
Nadi *string `json:"Nadi"`
Suhu *string `json:"Suhu"`
Pernafasan *string `json:"Pernafasan"`
Diabetes *string `json:"Diabetes"`
Hipertensi *string `json:"Hipertensi"`
Dyslipidemia *string `json:"Dyslipidemia"`
Jantung *string `json:"Jantung"`
Stroke *string `json:"Stroke"`
Asma *string `json:"Asma"`
Paru *string `json:"Paru"`
RiwayatCidera *string `json:"RiwayatCidera"`
RiwayatRawatInap *string `json:"RiwayatRawatInap"`
RiwayatOperasi *string `json:"RiwayatOperasi"`
LainLain *string `json:"LainLain"`
DiabetesKeluarga *string `json:"DiabetesKeluarga"`
HipertensiKeluarga *string `json:"HipertensiKeluarga"`
DyslipidemiaKeluarga *string `json:"DyslipidemiaKeluarga"`
JantungKeluarga *string `json:"JantungKeluarga"`
StrokeKeluarga *string `json:"StrokeKeluarga"`
AsmaKeluarga *string `json:"AsmaKeluarga"`
ParuKeluarga *string `json:"ParuKeluarga"`
RiwayatCideraKeluarga *string `json:"RiwayatCideraKeluarga"`
RiwayatRawatInapKeluarga *string `json:"RiwayatRawatInapKeluarga"`
RiwayatOperasiKeluarga *string `json:"RiwayatOperasiKeluarga"`
LainLainKeluarga *string `json:"LainLainKeluarga"`
AlergiObat *string `json:"AlergiObat"`
AlergiMakanan *string `json:"AlergiMakanan"`
AlergiLain *string `json:"AlergiLain"`
Merokok *string `json:"Merokok"`
Alkohol *string `json:"Alkohol"`
Olahraga *string `json:"Olahraga"`
KeadaanUmum *string `json:"KeadaanUmum"`
KeadaanSakit *string `json:"KeadaanSakit"`
StatusGizi *string `json:"StatusGizi"`
Kesadaran *string `json:"Kesadaran"`
ColourBlindness *string `json:"ColourBlindness"`
VisusDekat *string `json:"VisusDekat"`
KelainananMataLain *string `json:"KelainananMataLain"`
Faring *string `json:"Faring"`
Tonsil *string `json:"Tonsil"`
MulutLainLain *string `json:"MulutLainLain"`
KelainanGigi *string `json:"KelainanGigi"`
ParuLain *string `json:"ParuLain"`
JantungLain *string `json:"JantungLain"`
Hernia *string `json:"Hernia"`
KeluhanUtama *string `json:"KeluhanUtama"`
PenyakitSekarang *string `json:"PenyakitSekarang"`
PenyakitDahulu *string `json:"PenyakitDahulu"`
ECG *string `json:"ECG"`
Audiometri *string `json:"Audiometri"`
Spirometri *string `json:"Spirometri"`
PemeriksaanLainLain *string `json:"PemeriksaanLainLain"`
SaranKesehatanKerja *string `json:"SaranKesehatanKerja"`
Kebisingan *string `json:"Kebisingan"`
SuhuPanas *string `json:"SuhuPanas"`
SuhuDingin *string `json:"SuhuDingin"`
RadiasiBukanPengion *string `json:"RadiasiBukanPengion"`
RadiasiPengion *string `json:"RadiasiPengion"`
GetaranLokal *string `json:"GetaranLokal"`
GetaranSeluruhTubuh *string `json:"GetaranSeluruhTubuh"`
Ketinggian *string `json:"Ketinggian"`
LainLainAkibatKerja *string `json:"LainLainAkibatKerja"`
DebuOrganik *string `json:"DebuOrganik"`
DebuAnorganik *string `json:"DebuAnorganik"`
Asap *string `json:"Asap"`
LogamBerat *string `json:"LogamBerat"`
PelarutOrganik *string `json:"PelarutOrganik"`
IritanAsam *string `json:"IritanAsam"`
IritanBasa *string `json:"IritanBasa"`
CairanPembersih *string `json:"CairanPembersih"`
Pestisida *string `json:"Pestisida"`
UapLogam *string `json:"UapLogam"`
BahayaKimiaLain *string `json:"BahayaKimiaLain"`
Bakteri *string `json:"Bakteri"`
CairanTubuh *string `json:"CairanTubuh"`
Serangga *string `json:"Serangga"`
Limbah *string `json:"Limbah"`
BiologiLain *string `json:"BiologiLain"`
Overload *string `json:"Overload"`
Mismatch *string `json:"Mismatch"`
KetidakJelasan *string `json:"KetidakJelasan"`
HambatanJenjangKarir *string `json:"HambatanJenjangKarir"`
KerjaShift *string `json:"KerjaShift"`
KonflikTeman *string `json:"KonflikTeman"`
KonflikKeluarga *string `json:"KonflikKeluarga"`
PsikologisLain *string `json:"PsikologisLain"`
TanganBergerakUlang *string `json:"TanganBergerakUlang"`
AngkatBerat *string `json:"AngkatBerat"`
DudukLama *string `json:"DudukLama"`
BerdiriLama *string `json:"BerdiriLama"`
PosisiTubuhTidakErgonomis *string `json:"PosisiTubuhTidakErgonomis"`
PencahayaanTidakSesuai *string `json:"PencahayaanTidakSesuai"`
LihatMonitorLama *string `json:"LihatMonitorLama"`
ErgonomisLain *string `json:"ErgonomisLain"`
VisusJauhTanpaKoreksiVOD *string `json:"VisusJauhTanpaKoreksiVOD"`
VisusJauhTanpaKoreksiVOS *string `json:"VisusJauhTanpaKoreksiVOS"`
VisusJauhDenganKoreksiVOD *string `json:"VisusJauhDenganKoreksiVOD"`
VisusJauhDenganKoreksiVOS *string `json:"VisusJauhDenganKoreksiVOS"`
Pekerjaan *string `json:"Pekerjaan"`
DokterPemeriksa *string `json:"DokterPemeriksa"`
}
func (r MySQLLayananRepository) ListLaporanMCU(ctx context.Context, params LaporanMCUParams) ([]LaporanMCURow, error) {
rows, err := r.db.QueryContext(ctx, laporanMCUQuery, params.Tanggal+" 00:00:01", params.Tanggal2+" 23:59:59")
if err != nil {
return nil, err
}
defer rows.Close()
result := make([]LaporanMCURow, 0)
for rows.Next() {
row, err := scanLaporanMCURow(rows)
if err != nil {
return nil, err
}
row.No = len(result) + 1
result = append(result, row)
}
if err := rows.Err(); err != nil {
return nil, err
}
return result, nil
}
const laporanMCUQuery = `
SELECT
r.RegID, r.MEDRECID, r.Nama,
CASE WHEN m.Kelamin = 'L' THEN 'Laki - Laki' ELSE 'Perempuan' END,
DATE_FORMAT(m.TanggalLahir, '%Y-%m-%d'), TIMESTAMPDIFF(YEAR, m.TanggalLahir, CURDATE()),
DATE_FORMAT(r.Tanggal, '%Y-%m-%d %H:%i:%s'),
COALESCE(NULLIF(p.Nama, ''), 'UMUM'), d.Nama, pm.DepartemenKerja,
rd.TinggiBadan, rd.BeratBadan, tv.Tekanandarah, tv.Nadi, tv.Suhu, tv.Pernafasan,
COALESCE(NULLIF(rpd.DiabetesDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.HipertensiDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.KelebihanLemakDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.JantungDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.StrokeDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.AsmaDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.ParuDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.CideraDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.RawatInapDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.OperasiDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpd.LainLainDahuluAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.DiabetesAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.HypertensiAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.KelebihanLemakAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.JantungAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.StrokeAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.AsmaAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.ParuAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.CideraAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.RawatInapAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.OperasiAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(rpk.LainLainAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(ra.AlergiObatAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(ra.AlergiMakananAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(ra.AlergiLainAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(gl.SejakKapanMerokok, ''), 'Tidak Merokok'),
COALESCE(NULLIF(gl.SejakKapanAlkohol, ''), 'Tidak Minum ALkohol'),
COALESCE(NULLIF(gl.KebiasaanOlahragaAda, ''), 'Tidak Olahraga'),
pfu.KeadaanUmum, pfu.KeadaanSakit, pfu.StatusGizi, pfu.Kesadaran,
pmata.ColourBlindness,
COALESCE(NULLIF(pmata.VisusDekatKelainan, ''), 'Normal'),
COALESCE(NULLIF(pmata.MataLainLainAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(ptg.FaringKelainan, ''), 'Normal'),
COALESCE(NULLIF(ptg.TonsilKelainan, ''), 'Tidak Ada'),
COALESCE(NULLIF(pmulut.MulutLainLainAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(pgigi.KelainanGigiAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(pparu.ParuLainLainAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(pjantung.JantungLainLainAda, ''), 'Tidak Ada'),
COALESCE(NULLIF(pabd.HerniaAda, ''), 'Tidak Ada'),
pm.KeluhanUtamaRingkasan, pm.PenyakitSekarangRingkasan, pm.PenyakitDahuluRingkasan,
ph.ECG, ph.Audiometri, ph.Spirometri, pm.PemeriksaanLainLainRingkasan,
pm.SaranKesehatanKerjaRingkasan,
COALESCE(NULLIF(pak.KebisinganAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.SuhuPanasAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.SuhuDinginAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.RadiasiBukanPengionAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.RadiasiPengionAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.GetaranLokalAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.GetaranSeluruhTubuhAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.KetinggianAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.FisikLainLainAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.DebuOrganikAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.DebuAnorganikAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.AsapAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.LogamBeratAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.PelarutOrganikAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.IritanAsamAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.IritanBasaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.CairanPembersihAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.PestisidaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.UapLogamAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.BahanKimiaBerbahayaLainLainAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.BakteriAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.CairanTubuhAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.SeranggaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.LimbahAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.BiologiLainLainAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.OverLoadKerjaanAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.MismatchKerjaanAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.KetidakjelasanTugasAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.HambatanJenjangKarirAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.KerjaShiftAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.KonflikTemanKerjaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.KonflikKeluargaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.PsikologisLainLainAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.TanganBergerakBerulangAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.AngkatBebanBeratAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.DudukLamaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.BerdiriLamaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.PosisiTubuhTidakErgonomisAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.PencahayaanTidakSesuaiAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.LihatMonitorLamaAda, ''), 'TidakAda'),
COALESCE(NULLIF(pak.ErgonomisLainLainAda, ''), 'TidakAda'),
pmata.VisusJauhTanpaKoreksiVOD, pmata.VisusJauhTanpaKoreksiVOS,
pmata.VisusJauhDenganKoreksiVOD, pmata.VisusJauhDenganKoreksiVOS,
m.Pekerjaan, dp.Nama
FROM regpas AS r
LEFT JOIN medrec AS m ON m.MEDRECID = r.MEDRECID
LEFT JOIN pengkajianmcu AS pm ON pm.RegID = r.RegID
LEFT JOIN dokter AS d ON d.DokterID = r.DokterID
LEFT JOIN dokter AS dp ON dp.DokterID = pm.DokterIDPengkaji
LEFT JOIN perusahaan AS p ON p.PerusahaanID = r.AsuransiID
LEFT JOIN tandavital_pasien AS tv ON tv.RegID = r.RegID AND tv.TekananDarah NOT LIKE ''
LEFT JOIN regpas_detail AS rd ON rd.RegID = r.RegID
LEFT JOIN gayahidup AS gl ON gl.RegID = r.RegID
LEFT JOIN riwayatpenyakitdahulu AS rpd ON rpd.RegID = r.RegID
LEFT JOIN riwayatpenyakitkeluarga AS rpk ON rpk.RegID = r.RegID
LEFT JOIN riwayatalergi AS ra ON ra.MEDRECID = r.MEDRECID
LEFT JOIN pemeriksaanfisik_umum AS pfu ON pfu.RegID = r.RegID
LEFT JOIN pemeriksaanfisik_mata AS pmata ON pmata.RegID = r.RegID
LEFT JOIN pemeriksaanfisik_tenggorokan AS ptg ON ptg.RegID = r.RegID
LEFT JOIN pemeriksaanfisik_mulut AS pmulut ON pmulut.RegID = r.RegID
LEFT JOIN pemeriksaanfisik_gigi AS pgigi ON pgigi.RegID = r.RegID
LEFT JOIN pemeriksaanfisik_paru AS pparu ON pparu.RegID = r.RegID
LEFT JOIN pemeriksaanfisik_jantung AS pjantung ON pjantung.RegID = r.RegID
LEFT JOIN pemeriksaanfisik_abdomen AS pabd ON pabd.RegID = r.RegID
LEFT JOIN pemeriksaankhusus AS ph ON ph.RegID = r.RegID
LEFT JOIN penyakitakibatkerja AS pak ON pak.RegID = r.RegID
WHERE r.Tanggal BETWEEN ? AND ? AND r.JenisRegID = '2' AND r.NA = 'N'
ORDER BY r.Tanggal, r.RegID`
func scanLaporanMCURow(scanner rowScanner) (LaporanMCURow, error) {
var row LaporanMCURow
var regID, medrecID, nama, jenisKelamin, tanggalLahir, tanggalMCU, namaPenanggung, dokterMCU, instansiKerja sql.NullString
var umur sql.NullInt64
var fields [103]sql.NullString
args := []any{
&regID, &medrecID, &nama, &jenisKelamin, &tanggalLahir, &umur, &tanggalMCU,
&namaPenanggung, &dokterMCU, &instansiKerja,
}
for i := range fields {
args = append(args, &fields[i])
}
if err := scanner.Scan(args...); err != nil {
return LaporanMCURow{}, err
}
row.RegID = nullStringPtr(regID)
row.MEDRECID = nullStringPtr(medrecID)
row.NamaPasien = nullStringPtr(nama)
row.JenisKelamin = nullStringPtr(jenisKelamin)
row.TanggalLahir = nullStringPtr(tanggalLahir)
row.Umur = nullInt64Ptr(umur)
row.TanggalMCU = nullStringPtr(tanggalMCU)
row.NamaPenanggung = nullStringPtr(namaPenanggung)
row.DokterMCU = nullStringPtr(dokterMCU)
row.InstansiKerja = nullStringPtr(instansiKerja)
values := make([]*string, len(fields))
for i := range fields {
values[i] = nullStringPtr(fields[i])
}
assignLaporanMCUFields(&row, values)
return row, nil
}
func assignLaporanMCUFields(row *LaporanMCURow, v []*string) {
// The SELECT order intentionally mirrors the legacy spreadsheet column
// order after the ten patient/header columns above.
row.TinggiBadan, row.BeratBadan, row.TekananDarah, row.Nadi, row.Suhu, row.Pernafasan = v[0], v[1], v[2], v[3], v[4], v[5]
row.Diabetes, row.Hipertensi, row.Dyslipidemia, row.Jantung, row.Stroke, row.Asma, row.Paru, row.RiwayatCidera, row.RiwayatRawatInap, row.RiwayatOperasi, row.LainLain = v[6], v[7], v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15], v[16]
row.DiabetesKeluarga, row.HipertensiKeluarga, row.DyslipidemiaKeluarga, row.JantungKeluarga, row.StrokeKeluarga, row.AsmaKeluarga, row.ParuKeluarga, row.RiwayatCideraKeluarga, row.RiwayatRawatInapKeluarga, row.RiwayatOperasiKeluarga, row.LainLainKeluarga = v[17], v[18], v[19], v[20], v[21], v[22], v[23], v[24], v[25], v[26], v[27]
row.AlergiObat, row.AlergiMakanan, row.AlergiLain, row.Merokok, row.Alkohol, row.Olahraga = v[28], v[29], v[30], v[31], v[32], v[33]
row.KeadaanUmum, row.KeadaanSakit, row.StatusGizi, row.Kesadaran, row.ColourBlindness, row.VisusDekat, row.KelainananMataLain = v[34], v[35], v[36], v[37], v[38], v[39], v[40]
row.Faring, row.Tonsil, row.MulutLainLain, row.KelainanGigi, row.ParuLain, row.JantungLain, row.Hernia = v[41], v[42], v[43], v[44], v[45], v[46], v[47]
row.KeluhanUtama, row.PenyakitSekarang, row.PenyakitDahulu, row.ECG, row.Audiometri, row.Spirometri, row.PemeriksaanLainLain, row.SaranKesehatanKerja = v[48], v[49], v[50], v[51], v[52], v[53], v[54], v[55]
row.Kebisingan, row.SuhuPanas, row.SuhuDingin, row.RadiasiBukanPengion, row.RadiasiPengion, row.GetaranLokal, row.GetaranSeluruhTubuh, row.Ketinggian, row.LainLainAkibatKerja, row.DebuOrganik, row.DebuAnorganik, row.Asap, row.LogamBerat, row.PelarutOrganik, row.IritanAsam, row.IritanBasa, row.CairanPembersih, row.Pestisida, row.UapLogam, row.BahayaKimiaLain = v[56], v[57], v[58], v[59], v[60], v[61], v[62], v[63], v[64], v[65], v[66], v[67], v[68], v[69], v[70], v[71], v[72], v[73], v[74], v[75]
row.Bakteri, row.CairanTubuh, row.Serangga, row.Limbah, row.BiologiLain, row.Overload, row.Mismatch, row.KetidakJelasan, row.HambatanJenjangKarir, row.KerjaShift, row.KonflikTeman, row.KonflikKeluarga, row.PsikologisLain, row.TanganBergerakUlang, row.AngkatBerat, row.DudukLama, row.BerdiriLama, row.PosisiTubuhTidakErgonomis, row.PencahayaanTidakSesuai, row.LihatMonitorLama, row.ErgonomisLain = v[76], v[77], v[78], v[79], v[80], v[81], v[82], v[83], v[84], v[85], v[86], v[87], v[88], v[89], v[90], v[91], v[92], v[93], v[94], v[95], v[96]
row.VisusJauhTanpaKoreksiVOD, row.VisusJauhTanpaKoreksiVOS, row.VisusJauhDenganKoreksiVOD, row.VisusJauhDenganKoreksiVOS, row.Pekerjaan, row.DokterPemeriksa = v[97], v[98], v[99], v[100], v[101], v[102]
}