# SPRINT 3 — PRODUCTION DEPLOYMENT GUIDE
**PT. Sarana Gemilang Finance System**
Tanggal: 18 Mei 2026
Target: 8 KRITIS findings + 3 HIGH related fixes

---

## RINGKASAN PERUBAHAN

### File Migration Baru (2)
- `prisma/migrations/20260518000001_partial_unique_konsolidasi/migration.sql`
- `prisma/migrations/20260518000002_unique_nomor_faktur_pajak/migration.sql`

### File Backend Modified (5)
- `src/server.js` — Health check logic, user-aktif cache, uncaughtException handler
- `src/routes/invoices.js` — Pre-flight konsolidasi, auto-materai defensive, faktur pajak unique check, withRetry logger
- `src/routes/auth.js` — Import invalidateUserAktifCache, call saat deactivate user
- `src/routes/efaktur.js` — DPP summary fix, CSV escape, format tanggal locale-independent
- `prisma/seed.js` — Password default lulus validator

### File Schema Modified (1)
- `prisma/schema.prisma` — Komentar di nomorFakturPajak (constraint via migration)

### File Script Baru (1)
- `scripts/sprint3-deploy.js` — Verifikasi deployment

---

## MAPPING TEMUAN → FIX

| Temuan | Severity | File yang Diubah | Status |
|--------|----------|------------------|--------|
| KRITIS-VERIF-01 | 🔴 | migration baru | ✅ |
| KRITIS-VERIF-02 | 🔴 | invoices.js (pre-flight check) | ✅ |
| KRITIS-VERIF-03 | 🔴 | efaktur.js (summary endpoint) | ✅ |
| KRITIS-VERIF-04 | 🔴 | server.js (status logic) | ✅ |
| KRITIS-VERIF-05 | 🔴 | seed.js (Admin@123! / Staff@123!) | ✅ |
| KRITIS-VERIF-06 | 🔴 | invoices.js (auto-materai defensive) | ✅ |
| KRITIS-VERIF-07 | 🔴 | migration baru + invoices.js (dup check) | ✅ |
| KRITIS-VERIF-08 | 🔴 | server.js (user aktif cache) + auth.js | ✅ |
| HIGH-V1 | 🟡 | efaktur.js (CSV escape) | ✅ |
| HIGH-V2 | 🟡 | efaktur.js (format tanggal) | ✅ |
| HIGH-V4 | 🟡 | invoices.js (dup check faktur pajak) | ✅ |
| HIGH-V10 | 🟡 | server.js (uncaughtException) | ✅ |
| HIGH-V12 | 🟡 | invoices.js (withRetry logger) | ✅ |
| MED-V4 | 🟢 | server.js (memory threshold env) | ✅ |

---

## LANGKAH DEPLOYMENT

### 1. Persiapan (di local development)

```powershell
cd C:\Users\pdplb\sarana-gemilang-finance\backend

# Backup .env existing
copy .env .env.backup

# Generate Prisma client untuk schema baru
npx prisma generate
```

### 2. Push ke Repository

```powershell
cd C:\Users\pdplb\sarana-gemilang-finance
git add backend/prisma/migrations/20260518000001_partial_unique_konsolidasi
git add backend/prisma/migrations/20260518000002_unique_nomor_faktur_pajak
git add backend/prisma/schema.prisma
git add backend/prisma/seed.js
git add backend/src/server.js
git add backend/src/routes/invoices.js
git add backend/src/routes/auth.js
git add backend/src/routes/efaktur.js
git add backend/scripts/sprint3-deploy.js
git commit -m "Sprint 3: Fix 8 KRITIS + 6 HIGH/MED findings from re-audit"
git push origin main
```

### 3. Di Hosting Production

```bash
# 3a. Pull perubahan
cd /path/to/sarana-gemilang-finance/backend
git pull origin main

# 3b. Install dependencies (jika belum)
npm install

# 3c. BACKUP DATABASE PRODUCTION DULU
pg_dump $DATABASE_URL > backup_pre_sprint3_$(date +%Y%m%d_%H%M%S).sql

# 3d. Apply migration baru
npx prisma migrate deploy

# 3e. Regenerate Prisma client
npx prisma generate

# 3f. Verifikasi deployment
node scripts/sprint3-deploy.js

# 3g. Restart aplikasi (sesuaikan dengan setup hosting Anda)
pm2 restart sarana-gemilang-backend
# atau
systemctl restart sarana-gemilang
# atau
docker compose up -d --build
```

### 4. Environment Variables Baru (Opsional)

Tambahkan ke `.env` di production jika ingin override default:

```env
# Sprint 3 — Health check thresholds
HEALTH_MEM_WARN_MB=400
HEALTH_MEM_DOWN_MB=700

# MED-06 — Audit log retention untuk LOGIN/LOGOUT (default 365)
OPERATIONAL_LOG_RETENTION_DAYS=365

# HIGH-05 — Swagger di production (default OFF di production)
ENABLE_DOCS=false
```

### 5. Wajib Sebelum Production Go-Live

- [ ] Ganti password default 3 akun:
  - `admin@saranagemilang.com` (default: `Admin@123!`)
  - `supervisor@saranagemilang.com` (default: `Staff@123!`)
  - `staff@saranagemilang.com` (default: `Staff@123!`)
- [ ] Set `NODE_ENV=production`
- [ ] Set `JWT_SECRET` ke nilai unik 64+ char (generate: `node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"`)
- [ ] Set `FRONTEND_URLS` ke domain production
- [ ] Verify health check: `curl https://api.yourdomain.com/health/detailed`
- [ ] Setup monitoring (UptimeRobot/Pingdom) ke `/health/detailed`

---

## TESTING POST-DEPLOY

### Test 1: Constraint partial unique konsolidasi
```sql
-- Coba buat 2 invoice aktif untuk satu konsolidasi → harus gagal di yang kedua
-- (di-test via UI: terbitkan invoice 2x untuk Daftar Konsolidasi yang sama)
```

### Test 2: Auto-materai defensive
```javascript
// POST /api/invoices dengan grandTotal=5,510,000 (sudah include 10k materai)
// → Backend TIDAK menambah 10k lagi
// Verify: invoice.grandTotal di response = 5,510,000 (bukan 5,520,000)
```

### Test 3: Faktur pajak duplikat
```bash
# Set nomor faktur pajak yang sama di 2 invoice berbeda
curl -X PATCH /api/invoices/INV-1/faktur-pajak -d '{"nomorFakturPajak":"010.001-26.12345678"}'
curl -X PATCH /api/invoices/INV-2/faktur-pajak -d '{"nomorFakturPajak":"010.001-26.12345678"}'
# Yang kedua harus return 409 Conflict
```

### Test 4: User deactivation
```bash
# 1. Login sebagai admin
# 2. Login di tab lain sebagai user X (dapat token)
# 3. Admin nonaktifkan user X
# 4. User X coba akses endpoint → harus 401 dalam 30 detik
```

### Test 5: Health check status
```bash
curl https://api.yourdomain.com/health/detailed
# Saat DB normal → status: "ok"
# Saat DB down → status: "down" (return 503)
# Saat memory > 400MB → status: "degraded"
```

### Test 6: e-Faktur DPP konsistensi
```bash
# Export dan summary harus return DPP yang sama
curl /api/efaktur/summary?bulan=5&tahun=2026
curl /api/efaktur/export?bulan=5&tahun=2026
# totalDPP di /summary === sum DPP di kolom JUMLAH_DPP di CSV /export
```

---

## ROLLBACK PLAN

Jika ada masalah setelah deploy:

```bash
# 1. Restart ke versi sebelumnya via git
git revert HEAD
git push origin main

# 2. Rollback migration di DB
# IMPORTANT: PostgreSQL tidak punya DROP INDEX IF EXISTS dengan auto-rollback dari Prisma.
# Manual rollback (jalankan di psql):
DROP INDEX IF EXISTS "invoices_konsolidasi_active_unique";
DROP INDEX IF EXISTS "invoices_nomor_faktur_pajak_unique";
DROP INDEX IF EXISTS "invoices_nomor_faktur_pajak_idx";

# 3. Update _prisma_migrations untuk mark migration as rolled-back
DELETE FROM _prisma_migrations WHERE migration_name IN (
  '20260518000001_partial_unique_konsolidasi',
  '20260518000002_unique_nomor_faktur_pajak'
);

# 4. Pulih ke backup jika perlu
psql $DATABASE_URL < backup_pre_sprint3_*.sql
```

---

## CATATAN PENTING

1. **Migration partial unique konsolidasi** — Jika sudah ada data duplikat di production (2+ invoice aktif untuk konsolidasi yang sama), migration akan FAIL. Cek dulu:
   ```sql
   SELECT "konsolidasiDokumenId", COUNT(*) FROM invoices
   WHERE "deletedAt" IS NULL AND "konsolidasiDokumenId" IS NOT NULL
   GROUP BY "konsolidasiDokumenId" HAVING COUNT(*) > 1;
   ```
   Kalau ada hasil, hapus/soft-delete duplikatnya dulu sebelum migrate.

2. **Migration unique nomorFakturPajak** — Sama: cek duplikat dulu:
   ```sql
   SELECT "nomorFakturPajak", COUNT(*) FROM invoices
   WHERE "nomorFakturPajak" IS NOT NULL
   GROUP BY "nomorFakturPajak" HAVING COUNT(*) > 1;
   ```

3. **User-aktif cache** — TTL 30 detik. Jika ingin instant invalidation, panggil `invalidateUserAktifCache(userId)` dari mana saja (sudah dipanggil di route deactivate user).

4. **uncaughtException** — Sekarang akan force-exit setelah 5 detik. Pastikan ada process manager (PM2, systemd) yang auto-restart.

---

**Status Sprint 3: SIAP DEPLOY ✅**
Skor verifikasi: 86.5% → estimasi 93%+ setelah deploy
