﻿# GLIMS — Operational Lab Schema & Module Planning Guide

> **Aplikasi:** `glims` — Laboratory Information Management System  
> **Konteks:** Operational lab: sampling, analisa, QC/monitoring, LHU  
> **Standar:** ISO/IEC 17025:2017 + Persyaratan KAN  
> **Arsitektur:** Laravel Modular Monolith + DDD + CQRS

---

## Status Modul yang Sudah Ada

```
✅ Master Data     → glims_m_* (sample types, parameters, methods, regulations, quality standards)
✅ Sales Order  → glims_t_sales_orders, _items, _item_params, _payments
🔲 Operational     → Belum ada (dibuat dalam guide ini)
```

---

## Konvensi Tabel

| Prefix | Kegunaan | Contoh |
|--------|----------|--------|
| `glims_m_*` | Master data | `glims_m_parameters` ✅ |
| `glims_t_*` | Transaksi | `glims_t_work_orders` |
| `glims_h_*` | History / log | `glims_h_sample_chain_of_custody` |
| `glims_cfg_*` | Konfigurasi | `glims_cfg_instruments` |
| `glims_p_*` | Pivot | `glims_p_method_parameters` ✅ |
| `glims_r_*` | Reference (file/attachment) | `glims_r_result_attachments` |

---

## Alur Operasional Lab (End-to-End)

```
SO Customer (sudah ada)
    │
    ▼
[1] SampleRegistration     ← penerimaan sampel fisik, kondisi, chain of custody
    │
    ▼
[2] WorkOrder (Job Card)   ← pembagian ke divisi/analis, prioritas, deadline
    │
    ├──► [3] SamplePreparation  ← preparasi (ekstraksi, destruksi, pengenceran)
    │
    ▼
[4] AnalysisResult         ← entry raw data + perhitungan hasil
    │
    ├──► [5] QCSample           ← blanko, duplikat, spike, standar, CRM
    │
    ▼
[6] ResultVerification     ← cek analis ke-2 / supervisor approval
    │
    ▼
[7] LHU                    ← Laporan Hasil Uji (generate PDF + digital sign)
    │
    └──► [gfin] SalesInvoice  ← trigger invoice ke customer
```

---

## Arsitektur Data Hasil Analisa

### Kenapa perlu sampai rumus (bukan hanya logbook)?

ISO 17025 §7.5 mewajibkan **technical records** yang cukup untuk *mereproduksi ulang* pengukuran.  
KAN auditor akan trace: `raw reading → rumus → hasil antara → hasil akhir → LHU`.

```
Contoh: Parameter BOD₅

Raw data yang disimpan:
  D1 = 8.2 mg/L    (DO awal sampel)
  D2 = 2.4 mg/L    (DO akhir setelah 5 hari inkubasi)
  B1 = 8.1 mg/L    (DO awal blanko)
  B2 = 7.3 mg/L    (DO akhir blanko)
  f  = 1.0          (faktor koreksi seeding)
  P  = 0.02         (faktor pengenceran)

Rumus (dari glims_cfg_calculation_formulas):
  BOD = [(D1 - D2) - (B1 - B2) × f] / P

Hasil antara:
  delta_sample = 8.2 - 2.4 = 5.8
  delta_blank  = 8.1 - 7.3 = 0.8
  correction   = 5.8 - (0.8 × 1.0) = 5.0
  BOD          = 5.0 / 0.02 = 250 mg/L

Yang disimpan di database:
  → semua variable input (D1, D2, B1, B2, f, P)
  → formula_id (referensi rumus)
  → hasil antara (JSON)
  → hasil akhir: 250 mg/L
```

### Parameter Sederhana (tidak perlu rumus)

```
Contoh: pH, suhu, kekeruhan, DO meter langsung

  reading_value = 7.2       ← langsung dari alat
  result_value  = 7.2       ← sama dengan reading
  no formula needed
```

---

## Phase 1 — Sample Registration

### Module: `SampleRegistration`

**Tujuan:** Penerimaan dan registrasi sampel fisik dari customer / sampling mandiri.

```sql
-- glims_t_sample_registrations
id, uuid
registration_number     VARCHAR(50) UNIQUE NOT NULL  -- nomor penerimaan sampel
sales_order_id       BIGINT UNSIGNED FK → glims_t_sales_orders
sales_order_item_id  BIGINT UNSIGNED NULL FK → glims_t_sales_order_items
received_date           DATE NOT NULL
received_time           TIME NULL
sample_type_id          BIGINT UNSIGNED FK → glims_m_sample_types
sample_label            VARCHAR(200) NULL            -- label/kode yang ada di wadah sampel
sampling_date           DATE NULL                    -- tanggal pengambilan di lapangan
sampling_location       VARCHAR(300) NULL            -- lokasi sampling
sampling_point          VARCHAR(200) NULL            -- titik/koordinat
sampling_method         VARCHAR(200) NULL            -- SNI, SOP internal
sampler_name            VARCHAR(200) NULL            -- nama petugas sampling
sampler_type            ENUM('gmp_staff','customer','third_party')
is_gmp_sampling         BOOLEAN DEFAULT false

-- Kondisi dan preservasi
container_type          VARCHAR(100) NULL            -- botol plastik, botol gelap, dll
container_count         INT DEFAULT 1
volume_received         DECIMAL(10,4) NULL           -- volume yang diterima (ml/L)
volume_unit             VARCHAR(20) NULL
condition_on_receive    ENUM('good','turbid','unusual_color','unusual_odor','container_broken','other') DEFAULT 'good'
condition_notes         TEXT NULL
preservative_used       VARCHAR(200) NULL            -- H2SO4, NaOH, dll
preservation_date       DATE NULL
storage_temperature     DECIMAL(5,2) NULL            -- suhu penyimpanan (°C)

-- Batas waktu
holding_time_hours      INT NULL                     -- dari glims_m_sample_types atau metode
must_analyze_by         TIMESTAMP NULL               -- deadline analisa (auto-hitung dari holding time)

-- Status
status                  ENUM('received','accepted','rejected','in_analysis','completed') DEFAULT 'received'
rejected_reason         TEXT NULL
accepted_by             BIGINT UNSIGNED NULL
accepted_at             TIMESTAMP NULL
notes                   TEXT NULL
[standard cols]

-- glims_h_sample_chain_of_custody  (log setiap perpindahan/perubahan kondisi sampel)
id, uuid
sample_registration_id  BIGINT UNSIGNED FK
event_type              ENUM('received','accepted','transferred','stored','prepared','analyzed','disposed')
event_time              TIMESTAMP NOT NULL
from_location           VARCHAR(200) NULL
to_location             VARCHAR(200) NULL            -- kulkas A, bench B, dll
handler_id              BIGINT UNSIGNED NOT NULL     -- user yang menangani
temperature             DECIMAL(5,2) NULL            -- suhu saat perpindahan
condition_notes         TEXT NULL
created_at              TIMESTAMP
created_by              BIGINT UNSIGNED
```

---

## Phase 2 — Work Order

### Module: `WorkOrder`

**Tujuan:** Pembagian pekerjaan analisa ke divisi dan analis, tracking status per parameter.

```sql
-- glims_t_work_orders
id, uuid
work_order_number       VARCHAR(50) UNIQUE NOT NULL
sample_registration_id  BIGINT UNSIGNED FK → glims_t_sample_registrations  -- 1 sampel = 1 WO per divisi
sales_order_id       BIGINT UNSIGNED FK
sales_order_item_id  BIGINT UNSIGNED NULL FK → glims_t_sales_order_items  -- link ke item SO
lab_division            ENUM('fisika_kimia','mikrobiologi','biologi','lapangan','instrumen')
assigned_to_id          BIGINT UNSIGNED NULL         -- analis utama
priority                ENUM('normal','urgent','rush') DEFAULT 'normal'
received_date           DATE NOT NULL
target_completion_date  DATE NULL
status                  ENUM('queued','in_preparation','in_analysis','review','completed','cancelled') DEFAULT 'queued'
completed_at            TIMESTAMP NULL
notes                   TEXT NULL
[standard cols]

-- glims_t_work_order_parameters  (satu baris per parameter yang harus dianalisa)
id, uuid
work_order_id           BIGINT UNSIGNED FK → glims_t_work_orders
parameter_id            BIGINT UNSIGNED FK → glims_m_parameters
method_id               BIGINT UNSIGNED FK → glims_m_methods
analyst_id              BIGINT UNSIGNED NULL          -- analis yang mengerjakan parameter ini
instrument_id           BIGINT UNSIGNED NULL FK → glims_cfg_instruments
status                  ENUM('pending','in_progress','resulted','verified','cancelled') DEFAULT 'pending'
started_at              TIMESTAMP NULL
resulted_at             TIMESTAMP NULL
verified_at             TIMESTAMP NULL
verified_by             BIGINT UNSIGNED NULL
notes                   TEXT NULL
[standard cols]
```

---

## Phase 3 — Sample Preparation (Prep Lab)

### Module: `SamplePreparation`

**Tujuan:** Mencatat langkah preparasi sampel sebelum analisa (ekstraksi, destruksi, pengenceran). Ini bagian dari **technical records** ISO 17025.

```sql
-- glims_t_sample_preparations
id, uuid
work_order_parameter_id BIGINT UNSIGNED FK → glims_t_work_order_parameters
prep_type               ENUM('dilution','extraction','digestion','filtration','distillation','other')
steps                   JSON NULL                    -- array langkah preparasi dengan timestamp

-- Detail pengenceran (paling umum)
dilution_factor         DECIMAL(10,6) NULL
initial_volume_ml       DECIMAL(10,4) NULL
final_volume_ml         DECIMAL(10,4) NULL

-- Reagen yang digunakan
reagents_used           JSON NULL                    -- [{item_id, batch_no, volume_used, unit}]

-- Kondisi
prep_start_at           TIMESTAMP NULL
prep_end_at             TIMESTAMP NULL
prep_by                 BIGINT UNSIGNED NOT NULL
temperature             DECIMAL(5,2) NULL
ph_prep                 DECIMAL(5,2) NULL
notes                   TEXT NULL
[standard cols]
```

---

## Phase 4 — Analysis Result & Logbook Analis

### Module: `AnalysisResult`

**Ini inti dari ISO 17025.** Menyimpan dua level:
- **Level A (semua parameter):** raw reading + hasil akhir + info instrumen + analis
- **Level B (parameter kalkulasi):** semua variable input + formula + hasil antara

```sql
-- glims_cfg_instruments  (master instrumen lab)
id, uuid
code                    VARCHAR(30) UNIQUE NOT NULL
name                    VARCHAR(200) NOT NULL
type                    VARCHAR(100) NULL           -- 'Spektrofotometer', 'pH Meter', dll
brand                   VARCHAR(100) NULL
model                   VARCHAR(100) NULL
serial_number           VARCHAR(100) NULL
calibration_due_date    DATE NULL
calibration_certificate VARCHAR(500) NULL
location                VARCHAR(200) NULL
status                  ENUM('active','maintenance','out_of_service','retired') DEFAULT 'active'
-- link ke gwin Asset Register
wh_asset_id             BIGINT UNSIGNED NULL        -- referensi ke gwin wh_m_assets
[standard cols]

-- glims_cfg_calculation_formulas  (definisi rumus per metode per parameter)
id, uuid
method_id               BIGINT UNSIGNED FK → glims_m_methods
parameter_id            BIGINT UNSIGNED FK → glims_m_parameters
formula_expression      TEXT NOT NULL               -- string rumus, e.g.: '((D1-D2)-(B1-B2)*f)/P'
formula_description     TEXT NULL                   -- keterangan rumus
variables               JSON NOT NULL               -- definisi variabel: [{"key":"D1","label":"Initial DO","unit":"mg/L","type":"float"}, ...]
unit_result             VARCHAR(30) NULL             -- satuan hasil
decimal_places          TINYINT DEFAULT 2            -- jumlah desimal hasil
version                 VARCHAR(20) DEFAULT '1.0'
is_active               BOOLEAN DEFAULT true
reference_standard      VARCHAR(200) NULL           -- 'SNI 6989.72:2009'
[standard cols]

-- glims_t_analysis_results  (hasil analisa per parameter — LEVEL A: semua parameter)
id, uuid
work_order_parameter_id BIGINT UNSIGNED FK → glims_t_work_order_parameters
sample_registration_id  BIGINT UNSIGNED FK          -- denormalized for easy query
parameter_id            BIGINT UNSIGNED FK → glims_m_parameters
method_id               BIGINT UNSIGNED FK → glims_m_methods
instrument_id           BIGINT UNSIGNED NULL FK → glims_cfg_instruments
analyst_id              BIGINT UNSIGNED NOT NULL
analysis_date           DATE NOT NULL
analysis_time           TIME NULL

-- Raw measurement (untuk parameter sederhana langsung baca alat)
reading_value           DECIMAL(18,6) NULL          -- nilai bacaan alat (sebelum koreksi)
reading_unit            VARCHAR(30) NULL

-- Hasil akhir
result_value            DECIMAL(18,6) NULL          -- hasil akhir (setelah rumus/koreksi)
result_unit             VARCHAR(30) NULL
result_qualifier        ENUM('normal','<','>')  DEFAULT 'normal'  -- untuk bdl/bdq
is_below_detection      BOOLEAN DEFAULT false        -- < LOD
is_below_quantification BOOLEAN DEFAULT false        -- < LOQ
lod_value               DECIMAL(18,6) NULL          -- Limit of Detection
loq_value               DECIMAL(18,6) NULL          -- Limit of Quantification

-- Apakah pakai formula kalkulasi?
has_calculation         BOOLEAN DEFAULT false        -- true = ada di glims_t_analysis_calculations
-- calculation_id di-set setelah calculation dibuat (tidak FK karena hindari circular)

-- Kondisi analisa
room_temperature        DECIMAL(5,2) NULL
room_humidity           DECIMAL(5,2) NULL
wavelength_nm           DECIMAL(7,2) NULL           -- untuk spektro

-- Quality control flag
is_qc_sample            BOOLEAN DEFAULT false
qc_type                 ENUM('blank','duplicate','spike','standard','crm') NULL -- jika ini QC sample

-- Kepatuhan baku mutu
quality_standard_param_id BIGINT UNSIGNED NULL FK → glims_m_quality_standard_params
limit_value             DECIMAL(18,6) NULL          -- snapshot nilai batas
compliance_status       ENUM('compliant','non_compliant','na') DEFAULT 'na'

-- Status & verifikasi
status                  ENUM('draft','submitted','verified','rejected') DEFAULT 'draft'
rejection_reason        TEXT NULL
notes                   TEXT NULL
[standard cols]

-- glims_t_analysis_calculations  (LEVEL B: detail variabel + perhitungan untuk rumus)
id, uuid
analysis_result_id      BIGINT UNSIGNED FK → glims_t_analysis_results UNIQUE
calculation_formula_id  BIGINT UNSIGNED FK → glims_cfg_calculation_formulas
input_variables         JSON NOT NULL               -- {"D1": 8.2, "D2": 2.4, "B1": 8.1, "B2": 7.3, "f": 1.0, "P": 0.02}
intermediate_steps      JSON NULL                   -- {"delta_sample": 5.8, "delta_blank": 0.8, "correction": 5.0}
raw_formula             TEXT NULL                   -- snapshot rumus saat digunakan (audit trail)
calculated_value        DECIMAL(18,6) NOT NULL      -- hasil hitungan sebelum round
final_value             DECIMAL(18,6) NOT NULL      -- hasil setelah round ke decimal_places
calculation_notes       TEXT NULL
calculated_by           BIGINT UNSIGNED NOT NULL    -- analis
calculated_at           TIMESTAMP NOT NULL
[standard cols]

-- glims_h_analyst_logbook  (ringkasan aktivitas harian analis — untuk rekap logbook)
id, uuid
analyst_id              BIGINT UNSIGNED NOT NULL
log_date                DATE NOT NULL
lab_division            VARCHAR(50) NULL
work_order_parameter_id BIGINT UNSIGNED FK
activity_type           ENUM('preparation','analysis','calibration','maintenance','qc_check','other')
start_time              TIME NULL
end_time                TIME NULL
instrument_id           BIGINT UNSIGNED NULL FK
description             TEXT NULL
created_at              TIMESTAMP
```

---

## Phase 5 — QC Sample & Quality Monitoring

### Module: `QualityControl`

**Tujuan:** ISO 17025 §7.7 — setiap batch analisa wajib menyertakan QC samples untuk memvalidasi proses.

```sql
-- glims_cfg_qc_requirements  (aturan QC per metode/parameter)
id, uuid
method_id               BIGINT UNSIGNED FK → glims_m_methods
parameter_id            BIGINT UNSIGNED NULL FK     -- null = berlaku untuk semua param di metode
qc_type                 ENUM('blank','duplicate','spike','standard','crm')
frequency               ENUM('per_batch','per_10_samples','per_20_samples','daily','weekly')
acceptance_criteria     TEXT NULL                   -- keterangan kriteria keberterimaan
-- untuk spike: % recovery
spike_recovery_min      DECIMAL(5,2) NULL           -- e.g., 70
spike_recovery_max      DECIMAL(5,2) NULL           -- e.g., 130
-- untuk duplikat: RPD (Relative Percent Difference)
rpd_max_percent         DECIMAL(5,2) NULL           -- e.g., 10
-- untuk blanko: <LOD
blank_limit             DECIMAL(18,6) NULL
is_active               BOOLEAN DEFAULT true
[standard cols]

-- glims_t_qc_batches  (satu batch analisa = satu set QC)
id, uuid
work_order_id           BIGINT UNSIGNED FK → glims_t_work_orders
batch_date              DATE NOT NULL
lab_division            VARCHAR(50) NULL
analyst_id              BIGINT UNSIGNED NOT NULL
parameter_id            BIGINT UNSIGNED FK → glims_m_parameters
method_id               BIGINT UNSIGNED FK → glims_m_methods
status                  ENUM('open','completed','accepted','failed') DEFAULT 'open'
overall_qc_status       ENUM('pass','fail','warning') NULL
notes                   TEXT NULL
[standard cols]

-- glims_t_qc_results  (hasil QC samples per batch)
id, uuid
qc_batch_id             BIGINT UNSIGNED FK → glims_t_qc_batches
qc_type                 ENUM('blank','duplicate','spike','standard','crm')
sequence_no             TINYINT DEFAULT 1           -- urutan jika lebih dari 1 blanko, dll
instrument_id           BIGINT UNSIGNED NULL FK
analyst_id              BIGINT UNSIGNED NOT NULL
analysis_date           DATE NOT NULL

-- Hasil QC
result_value            DECIMAL(18,6) NULL
result_unit             VARCHAR(30) NULL

-- Untuk duplikat
original_result_id      BIGINT UNSIGNED NULL FK → glims_t_analysis_results
duplicate_value         DECIMAL(18,6) NULL
rpd_percent             DECIMAL(8,4) NULL           -- |(A-B)|/((A+B)/2) × 100
rpd_status              ENUM('pass','fail') NULL

-- Untuk spike
spike_concentration     DECIMAL(18,6) NULL          -- konsentrasi yang ditambahkan
unspiked_value          DECIMAL(18,6) NULL
spiked_value            DECIMAL(18,6) NULL
recovery_percent        DECIMAL(8,4) NULL           -- (spiked - unspiked)/spike × 100
recovery_status         ENUM('pass','fail') NULL

-- Untuk standar/CRM
certified_value         DECIMAL(18,6) NULL
certified_uncertainty   DECIMAL(18,6) NULL
z_score                 DECIMAL(8,4) NULL           -- (result - certified) / uncertainty
z_status                ENUM('satisfactory','questionable','unsatisfactory') NULL

-- Acceptance
is_accepted             BOOLEAN NULL
rejection_reason        TEXT NULL
reviewed_by             BIGINT UNSIGNED NULL
reviewed_at             TIMESTAMP NULL
notes                   TEXT NULL
[standard cols]

-- glims_h_control_charts  (data untuk control chart per parameter per instrumen)
id, uuid
parameter_id            BIGINT UNSIGNED FK
method_id               BIGINT UNSIGNED FK
instrument_id           BIGINT UNSIGNED NULL FK
chart_type              ENUM('x_bar','range','cusum','ewma')
measurement_date        DATE NOT NULL
value                   DECIMAL(18,6) NOT NULL
mean                    DECIMAL(18,6) NULL          -- mean dari periode kalkulasi
ucl                     DECIMAL(18,6) NULL          -- Upper Control Limit (mean + 3σ)
lcl                     DECIMAL(18,6) NULL          -- Lower Control Limit (mean - 3σ)
uwl                     DECIMAL(18,6) NULL          -- Upper Warning Limit (mean + 2σ)
lwl                     DECIMAL(18,6) NULL          -- Lower Warning Limit (mean - 2σ)
status                  ENUM('in_control','warning','out_of_control') DEFAULT 'in_control'
qc_result_id            BIGINT UNSIGNED NULL FK → glims_t_qc_results
created_at              TIMESTAMP
```

---

## Phase 6 — Result Verification

### Module: `ResultVerification`

**Tujuan:** ISO 17025 §7.8 — hasil harus di-review sebelum dilaporkan ke customer.

```sql
-- glims_t_result_reviews
id, uuid
work_order_id           BIGINT UNSIGNED FK → glims_t_work_orders
reviewer_id             BIGINT UNSIGNED NOT NULL    -- analis senior / supervisor
review_date             DATE NOT NULL
review_type             ENUM('analyst_check','supervisor_approval','final_approval')
status                  ENUM('pending','in_review','approved','rejected') DEFAULT 'pending'
note                    TEXT NULL
approved_at             TIMESTAMP NULL
rejected_at             TIMESTAMP NULL
[standard cols]

-- glims_t_result_review_items  (per parameter yang direview)
id, uuid
result_review_id        BIGINT UNSIGNED FK → glims_t_result_reviews
analysis_result_id      BIGINT UNSIGNED FK → glims_t_analysis_results
decision                ENUM('accept','reject','flag') NULL
comment                 TEXT NULL
[standard cols]
```

---

## Phase 7 — LHU (Laporan Hasil Uji)

### Module: `LHU`

**Tujuan:** Generate laporan hasil uji resmi yang diberikan ke customer.

```sql
-- glims_t_lhu  (Laporan Hasil Uji)
id, uuid
lhu_number              VARCHAR(50) UNIQUE NOT NULL  -- nomor LHU resmi
sales_order_id       BIGINT UNSIGNED FK → glims_t_sales_orders
sample_registration_id  BIGINT UNSIGNED FK → glims_t_sample_registrations  -- 1 LHU = 1 sampel
version                 TINYINT DEFAULT 1           -- revisi LHU (1 = original, 2+ = amended)
previous_lhu_id         BIGINT UNSIGNED NULL FK      -- jika ini revisi dari LHU sebelumnya
amendment_reason        TEXT NULL

-- Data sampel
sample_description      TEXT NULL                   -- deskripsi sampel
sampling_date           DATE NULL
sampling_location       TEXT NULL
received_date           DATE NULL

-- Signatory (penanda tangan)
prepared_by             BIGINT UNSIGNED NULL         -- analis
verified_by             BIGINT UNSIGNED NULL         -- reviewer
approved_by             BIGINT UNSIGNED NULL         -- kepala lab / authorized signatory
issued_by               BIGINT UNSIGNED NULL

-- Status & tanggal
status                  ENUM('draft','waiting_approval','approved','issued','amended','cancelled') DEFAULT 'draft'
issued_date             DATE NULL
issued_at               TIMESTAMP NULL

-- Akreditasi
is_accredited           BOOLEAN DEFAULT false        -- apakah sampel ini termasuk lingkup akreditasi KAN
accreditation_logo      BOOLEAN DEFAULT false        -- apakah logo KAN dicantumkan

-- File
pdf_path                VARCHAR(500) NULL
digital_signature       TEXT NULL                   -- hash / e-signature
[standard cols]

-- glims_t_lhu_parameters  (detail hasil di LHU — snapshot saat LHU diterbitkan)
id, uuid
lhu_id                  BIGINT UNSIGNED FK → glims_t_lhu
parameter_id            BIGINT UNSIGNED FK → glims_m_parameters
method_id               BIGINT UNSIGNED FK → glims_m_methods
analysis_result_id      BIGINT UNSIGNED FK → glims_t_analysis_results
result_value            DECIMAL(18,6) NULL           -- snapshot hasil
result_unit             VARCHAR(30) NULL
result_qualifier        VARCHAR(10) NULL             -- '<', '>', dsb
is_accredited           BOOLEAN DEFAULT false        -- parameter ini akreditasi atau tidak
quality_standard        VARCHAR(200) NULL            -- nama baku mutu acuan
limit_value             DECIMAL(18,6) NULL           -- nilai batas
compliance_status       ENUM('compliant','non_compliant','na') DEFAULT 'na'
uncertainty             DECIMAL(18,6) NULL           -- uncertainty of measurement (U, k=2)
sort_order              INT DEFAULT 0
notes                   TEXT NULL
[standard cols]
```

---

## Rangkuman Persyaratan ISO 17025 yang Diakomodir

| Klausul ISO 17025 | Kebutuhan | Module |
|-------------------|-----------|--------|
| 7.3 — Sampling | Dokumentasi metode & kondisi sampling | `SampleRegistration` |
| 7.4 — Penanganan item | CoC, kondisi wadah, suhu, holding time | `SampleRegistration` + `chain_of_custody` |
| 7.5 — Technical records | Raw data, rumus, hasil antara, audit trail | `AnalysisResult` + `AnalysisCalculation` |
| 7.5 — Logbook | Aktivitas harian analis | `analyst_logbook` |
| 7.6 — Uncertainty | Menyimpan nilai U per parameter | `LHUParameters.uncertainty` |
| 7.7 — QC internal | Blank, duplikat, spike, standar | `QCBatch` + `QCResult` |
| 7.7 — Control chart | Monitoring statistik per periode | `ControlChart` |
| 7.8 — Reporting | LHU berversi, snapshot hasil, signatory | `LHU` + `LHUParameters` |
| 7.8.6 — Amended report | Revisi LHU dengan nomor versi | `LHU.version` + `previous_lhu_id` |
| 6.4 — Equipment | Kalibrasi instrumen, status, sertifikat | `Instrument` → link ke `gwin.AssetMaintenance` |
| 6.4 — Traceability | Instrumen → kalibrasi → sertifikat → hasil | `AnalysisResult.instrument_id` |
| 6.6 — Subcontracting | Rujukan ke lab lain jika parameter tidak dikerjakan sendiri | `SubcontractedTest` |
| 7.9 — Complaints | Keluhan customer, penanganan, corrective action | `Complaint` |
| 7.10 — Non-conforming work | Pengelolaan ketidaksesuaian internal | `NonConformance` |
| 7.7.2 — Proficiency testing | Uji profisiensi / ILC antar lab | `ProficiencyTest` |
| 7.4.4 — Sample disposal | Catatan pembuangan sampel setelah selesai | `SampleDisposal` |

---

## Keputusan Arsitektur: Dua Level Data Hasil

```
Level A — Wajib untuk SEMUA parameter:
  glims_t_analysis_results
    reading_value, result_value, analyst_id, instrument_id,
    analysis_date, conditions, compliance_status

Level B — Wajib untuk parameter KALKULASI (BOD, COD, Hardness, dll.):
  glims_t_analysis_calculations
    input_variables (JSON)  → semua variable yang dimasukkan analis
    intermediate_steps (JSON) → hasil antara
    raw_formula (snapshot)
    calculated_value, final_value
```

**Manfaat pendekatan ini:**
- Parameter sederhana (pH, suhu): tidak ada overhead, hanya Level A
- Parameter kompleks: full traceability Level A + B
- Formula bisa diupdate tanpa ubah data historis (karena formula di-snapshot)

---

## Integrasi Antar Modul

```
glims ↔ gwin (Warehouse)
  SampleRegistration.preparation
    → event: SamplePreparationStarted
    → gwin: issue bahan kimia / consumable (StockIssue auto-create)
    → gwin: link ke work_order untuk konsumsi per pengujian

  Instrument (glims_cfg_instruments)
    → referensi ke gwin.wh_m_assets (wh_asset_id)
    → kalibrasi instrument → update dari gwin.AssetMaintenance

glims ↔ gfin (Finance)
  LHU.status = 'issued'
    → event: LHUissued
    → gfin: create SalesInvoice (draft) dari SO yang terkait

  SO payment dari gfin
    → event: CustomerPaymentReceived
    → glims: update glims_t_sales_orders.payment_status

glims → auth (SSO)
  analyst_id, reviewer_id, approved_by
    → semua referensi ke user dari sistem auth terpusat
```

---

## Roadmap Development

```
Sprint 1: SampleRegistration + WorkOrder
          → Bisa mulai terima sampel, assign ke analis

Sprint 2: AnalysisResult (Level A) + Instrument master
          → Analis bisa input hasil langsung

Sprint 3: CalculationFormula + AnalysisCalculation (Level B)
          → Parameter kompleks (BOD, COD, dll.) bisa dihitung

Sprint 4: QCBatch + QCResult
          → Monitoring QC per batch

Sprint 5: ResultVerification
          → Workflow approval sebelum LHU

Sprint 6: LHU generation + PDF
          → Laporan resmi ke customer

Sprint 7: ControlChart + Reporting
          → Dashboard QC, statistik trending

Sprint 8: Integration
          → gwin (bahan habis pakai), gfin (invoice trigger)
```

---

## Module Tambahan ISO 17025 Compliance

### Module: `SampleDisposal` (ISO 17025 §7.4.4)

**Tujuan:** Dokumentasi pembuangan sampel setelah pengujian selesai atau masa simpan habis.

```sql
-- glims_t_sample_disposals
id, uuid
sample_registration_id  BIGINT UNSIGNED FK → glims_t_sample_registrations
disposal_method         ENUM('chemical_treatment','incineration','return_to_customer','landfill','other')
disposal_date           DATE NOT NULL
disposed_by             BIGINT UNSIGNED NOT NULL
witness_by              BIGINT UNSIGNED NULL         -- saksi (untuk B3/kimia)
quantity_disposed       DECIMAL(10,4) NULL
unit                    VARCHAR(20) NULL
reason                  ENUM('analysis_completed','expired','customer_request','damaged','other') DEFAULT 'analysis_completed'
is_hazardous            BOOLEAN DEFAULT false
hazardous_notes         TEXT NULL
disposal_certificate    VARCHAR(500) NULL            -- path file sertifikat pemusnahan
notes                   TEXT NULL
[standard cols]
```

---

### Module: `SubcontractedTest` (ISO 17025 §6.6)

**Tujuan:** Pengelolaan pengujian yang dirujuk ke lab eksternal.

```sql
-- glims_t_subcontracted_tests
id, uuid
work_order_parameter_id BIGINT UNSIGNED FK → glims_t_work_order_parameters
external_lab_name       VARCHAR(200) NOT NULL
external_lab_address    TEXT NULL
external_lab_accreditation VARCHAR(100) NULL      -- nomor akreditasi KAN lab tujuan
reason                  TEXT NULL                   -- alasan rujukan
sample_sent_date        DATE NULL
sample_sent_by          BIGINT UNSIGNED NULL
result_received_date    DATE NULL
result_value            DECIMAL(18,6) NULL
result_unit             VARCHAR(30) NULL
certificate_number      VARCHAR(100) NULL
certificate_path        VARCHAR(500) NULL            -- path file sertifikat
status                  ENUM('draft','sent','result_received','accepted','rejected') DEFAULT 'draft'
notes                   TEXT NULL
[standard cols]
```

---

### Module: `Complaint` (ISO 17025 §7.9)

**Tujuan:** Pencatatan dan penanganan keluhan customer terkait hasil pengujian.

```sql
-- glims_t_complaints
id, uuid
complaint_number        VARCHAR(50) UNIQUE NOT NULL
complainant_name        VARCHAR(200) NOT NULL
complainant_type        ENUM('customer','internal','regulator','other')
complainant_contact     VARCHAR(200) NULL
complaint_date          DATE NOT NULL
received_by             BIGINT UNSIGNED NOT NULL
-- referensi transaksi terkait
lhu_id                  BIGINT UNSIGNED NULL FK → glims_t_lhu
sales_order_id       BIGINT UNSIGNED NULL FK
category                ENUM('result_accuracy','turnaround_time','sample_handling','report_format','service','other')
description             TEXT NOT NULL
root_cause              TEXT NULL
corrective_action       TEXT NULL
preventive_action       TEXT NULL
resolution              TEXT NULL
resolved_date           DATE NULL
resolved_by             BIGINT UNSIGNED NULL
status                  ENUM('open','investigating','resolved','closed') DEFAULT 'open'
customer_notified       BOOLEAN DEFAULT false
customer_notified_at    TIMESTAMP NULL
notes                   TEXT NULL
[standard cols]
```

---

### Module: `NonConformance` (ISO 17025 §7.10)

**Tujuan:** Pengelolaan pekerjaan yang tidak sesuai (out-of-spec QC, pelanggaran SOP, dll).

```sql
-- glims_t_non_conformances
id, uuid
nc_number               VARCHAR(50) UNIQUE NOT NULL
detection_date          DATE NOT NULL
detected_by             BIGINT UNSIGNED NOT NULL
source                  ENUM('qc_failure','audit_finding','complaint','process_deviation','equipment_failure','other')
-- referensi terkait
work_order_id           BIGINT UNSIGNED NULL FK
qc_batch_id             BIGINT UNSIGNED NULL FK → glims_t_qc_batches
analysis_result_id      BIGINT UNSIGNED NULL FK
complaint_id            BIGINT UNSIGNED NULL FK → glims_t_complaints
-- detail
description             TEXT NOT NULL
impact_assessment       TEXT NULL                   -- dampak terhadap hasil yang sudah dikeluarkan
immediate_action        TEXT NULL
root_cause              TEXT NULL
corrective_action       TEXT NULL
preventive_action       TEXT NULL
deadline                DATE NULL
responsible_person_id   BIGINT UNSIGNED NULL
status                  ENUM('open','investigating','action_taken','verified','closed') DEFAULT 'open'
verified_by             BIGINT UNSIGNED NULL
verified_at             TIMESTAMP NULL
closed_at               TIMESTAMP NULL
notes                   TEXT NULL
[standard cols]
```

---

### Module: `ProficiencyTest` (ISO 17025 §7.7.2)

**Tujuan:** Uji profisiensi (PT) dan Inter-Laboratory Comparison (ILC).

```sql
-- glims_t_proficiency_tests
id, uuid
pt_number               VARCHAR(50) UNIQUE NOT NULL
provider                VARCHAR(200) NOT NULL       -- penyedia PT (BATAN, lembaga lain)
parameter_id            BIGINT UNSIGNED FK → glims_m_parameters
method_id               BIGINT UNSIGNED FK → glims_m_methods
round_date              DATE NOT NULL               -- tanggal putaran PT
result_submitted_date   DATE NULL
lab_result_value        DECIMAL(18,6) NULL           -- hasil lab kita
assigned_value          DECIMAL(18,6) NULL           -- nilai acuan
uncertainty             DECIMAL(18,6) NULL
z_score                 DECIMAL(8,4) NULL
en_number               DECIMAL(8,4) NULL            -- En number (untuk ILC)
performance_status      ENUM('satisfactory','questionable','unsatisfactory') NULL
analyst_id              BIGINT UNSIGNED NULL
instrument_id           BIGINT UNSIGNED NULL FK → glims_cfg_instruments
certificate_path        VARCHAR(500) NULL
corrective_action       TEXT NULL                   -- jika unsatisfactory
status                  ENUM('registered','in_progress','submitted','result_received','closed') DEFAULT 'registered'
notes                   TEXT NULL
[standard cols]
```

---

## Urutan Development per Sprint (DDD)

```
1. Migration
2. Domain/Models + Domain/Enums (status, qc_type, dll.)
3. Domain/Events (SampleReceived, ResultSubmitted, LHUIssued, dll.)
4. Domain/Contracts (RepositoryInterface)
5. Domain/Services (CalculationEngineService, QCValidationService, HoldingTimeService)
6. Application/Actions (Commands)
7. Application/Queries
8. Infrastructure/Repositories
9. Infrastructure/Routes + Controllers
10. Resources/Pages (React + Inertia)
11. Tests (Feature test wajib untuk calculation engine & QC validation)
```
