Skip to content

[TASK-SET-EXPIRED-BILLING-V3] Implementasi Endpoint Set Expired Billing V3 dan Tracking Pembatalan Single Billing

Modul: Single Billing | Tipe: Feature | Stage Saat Ini: DEVELOPMENT | Tanggal Dokumen: 2026-08-27


1. Informasi Tugas & Referensi Bisnis

  • Task ID / Nomor Referensi: TASK-SET-EXPIRED-BILLING-V3

  • Sumber Instruksi: Instruksi Penambahan Endpoint Set Expired Billing V3

  • Dokumen Referensi / Lampiran: -

  • Task / Dependensi Terkait (Cross-Service): TASK-SPLIT-BILLING-202608 — Dikonsumsi oleh modul Pengangkut saat void & reset split billing

  • Modul Terkait: Single Billing

  • Service / Repository: single-billing-BE

  • PIC Pengembang: Hermansyah Efendi

  • Latar Belakang & Ringkasan Kebutuhan: Implementasi endpoint baru POST /v3/set-expired-billing pada layanan Single Billing untuk mengakomodasi kebutuhan pembatalan dan pembaruan status billing menjadi kedaluwarsa (expired) sebelum waktu jatuh tempo reguler tercapai. Endpoint ini dilengkapi dengan proteksi validasi skema Joi anti-SQL injection, penjagaan status billing (fl_paid dan fl_expired), pembaruan flag dan timestamp kedaluwarsa pada database, pencatatan event tracking status code 312, serta audit logging terpadu. Selain itu, penyesuaian dilakukan pada query pengecekan billing eksisting agar mengabaikan billing yang telah berstatus expired.

  • Ruang Lingkup & Rincian Sub-Pekerjaan (Technical Scope & Milestones):

    • [x] Milestone 1: Validation Schema Definition (Definisi skema validasi Joi schemasSetExpiredBillingV3 dengan proteksi anti-SQL Injection).
    • [x] Milestone 2: Route Registration & Security Guard (Implementasi route handler POST /v3/set-expired-billing yang diamankan middleware doIntrospection dan validateSchemaSimponi).
    • [x] Milestone 3: Core Service Logic & Orchestration (Implementasi business logic setExpiredBillingHelper untuk validasi status billing fl_paid/fl_expired, update database, insert tracking 312, dan logging audit).
    • [x] Milestone 4: Database Model Query (Implementasi query model setBillingExpired pada tabel billing.subbiller_hdr).
    • [x] Milestone 5: Existing Billing Filter Adjustment (Penyesuaian query checkExistingBilling agar mengabaikan billing yang telah berstatus expired dengan kondisi fl_expired is not true).

2. Deployment Lifecycle

#EnvironmentTanggal & WaktuPICStatusCatatan
1Development2026-08-27 12:18Hermansyah EfendiDEPLOYEDBranch: development (Commit: ec51278e)
2Staging (UAT)PENDING
3ProductionPENDING

Riwayat Update Log

TanggalEnvironmentPerubahanPIC
2026-08-27DevelopmentInisialisasi draft audit log dari context block backend (commit ec51278e)Hermansyah Efendi

3. Analisis Teknis & Rincian Perubahan

  • Alur Logika & Bisnis:

    • Validasi Kondisi Billing: Memeriksa keberadaan data billing berdasarkan trx_id_insw (mengembalikan 404 jika tidak ditemukan). Memastikan billing belum dibayar (fl_paid !== true), jika sudah dibayar ditolak dengan status 400. Memastikan billing belum berstatus expired (fl_expired !== true), jika sudah expired ditolak dengan status 400.
    • Eksekusi Update Database: Mengubah flag fl_expired menjadi true dan memperbarui expired_date dengan timestamp sistem saat ini pada tabel billing.subbiller_hdr.
    • Pencatatan Audit & Tracking: Menambahkan event tracking baru ke tabel tracking dengan status code 312 pada timestamp zona waktu Asia/Jakarta, serta mencatat raw audit log ke tabel logging sistem INSW.
    • Penyesuaian Inquiry & Existing Billing Check: Penyesuaian query checkExistingBilling pada app/models/tagihan_models.js dengan menyertakan filter fl_expired is not true sehingga billing yang telah dibatalkan/expired tidak menghambat pembentukan siklus billing baru.
    • Daftar File yang Berubah:
      • app/routes/v3/index.js [Controller / API Route]
      • app/controllers/v3/billing_controller.js [Controller / API Route]
      • app/helpers/billing_helper.js [Service / Business Logic]
      • app/models/billing_models.js [Model / Database Access]
      • app/models/tagihan_models.js [Validation / Schema & Model]
      • tests/helpers/billing_helper.test.js [Unit Test]
  • Perubahan Database (DDL / DML):

    sql
    -- DML Update Status Expired pada tabel billing.subbiller_hdr
    UPDATE billing.subbiller_hdr
    SET fl_expired = true, 
        expired_date = to_char(now(), 'YYYY-MM-DD HH24:MI:SS')
    WHERE trx_id_insw = $1 
    RETURNING *;
  • Kontrak API & Response Payload:

    POST /api/v3/set-expired-billing

    • Deskripsi: Mengubah status billing yang terdaftar di Single Billing menjadi expired sebelum jatuh tempo reguler.
    • Autentikasi / Guard: Bearer Token (doIntrospection, scope: single_billing, menu: /billing, validateSchemaSimponi)
    • Request Header / Content-Type:
      • Authorization: Bearer <token>
      • Content-Type: application/json
    • Request Body:
      json
      {
        "trx_id_insw": "TRX-INSW-20260827-0001",
        "alasan": "Permintaan pembatalan layanan oleh pengguna jasa"
      }
    • Response Payload (200 OK):
      json
      {
        "success": true,
        "statusCode": 200,
        "message": "Berhasil mengubah status billing menjadi expired",
        "data": {
          "trx_id_insw": "TRX-INSW-20260827-0001",
          "kode_billing_induk": "8202608270001",
          "fl_expired": true,
          "expired_date": "2026-08-27 14:45:00.000",
          "alasan": "Permintaan pembatalan layanan oleh pengguna jasa"
        }
      }
    • Response Error (400 Bad Request / 404 Not Found / 500 Server Error):
      json
      {
        "success": false,
        "statusCode": 400,
        "message": "Billing sudah dibayar dan tidak dapat diubah statusnya menjadi expired"
      }
      json
      {
        "success": false,
        "statusCode": 404,
        "message": "Data billing dengan aju tersebut tidak ditemukan"
      }

4. Bukti Teknis (Technical Proof & Audit Trail)

  • Repository: single-billing-BE
  • Branch Utama: development
  • Merge Request / PR: -

Daftar Commit Terkait (Multi-Commit Trail)

Commit IDBranchAuthorTanggal & JamRingkasan Perubahan
ec51278e1c156bfbc5048155996bc14e2a6b21e9developmentHermansyah Efendi2026-08-27 12:18feat(billing): implement set expired billing endpoint v3

Cuplikan Kode / Implementasi Inti

javascript
// app/helpers/billing_helper.js
export const setExpiredBillingHelper = async ({ trx_id_insw, alasan }) => {
  let logging = [
    null, null, Date.now() / 1000, null, 1, trx_id_insw,
    { trx_id_insw, alasan }, null, null, "Set Expired Billing",
    null, true, null, null, "set_expired", "INSW"
  ];
  try {
    const dataSubbillerHdr = await getDataSubbillerByTrxIdINSW(trx_id_insw);
    if (!dataSubbillerHdr) {
      return {
        success: false,
        statusCode: status.notfound,
        message: "Data billing dengan aju tersebut tidak ditemukan",
      };
    }
    if (dataSubbillerHdr.fl_paid === true) {
      return {
        success: false,
        statusCode: status.bad,
        message: "Billing sudah dibayar dan tidak dapat diubah statusnya menjadi expired",
      };
    }
    if (dataSubbillerHdr.fl_expired === true) {
      return {
        success: false,
        statusCode: status.bad,
        message: "Billing sudah dalam status expired",
      };
    }
    const updatedBilling = await setBillingExpired(trx_id_insw);
    if (!updatedBilling) {
      return {
        success: false,
        statusCode: status.error,
        message: "Gagal memperbarui status expired di database",
      };
    }
    const recordTime = moment().tz("Asia/Jakarta").format("YYYY-MM-DD HH:mm:ss.SSS");
    await insertTableTracking(trx_id_insw, recordTime, "312");
    logging[0] = dataSubbillerHdr.id_subbiller_hdr;
    logging[1] = dataSubbillerHdr.kode_billing_induk || null;
    logging[9] = "Success Set Expired Billing >> setExpiredBilling_V3";
    await insertLogRawQuery(logging);
    return {
      success: true,
      statusCode: status.success,
      message: "Berhasil mengubah status billing menjadi expired",
      data: {
        trx_id_insw: trx_id_insw,
        kode_billing_induk: dataSubbillerHdr.kode_billing_induk || null,
        fl_expired: true,
        expired_date: updatedBilling.expired_date || recordTime,
        alasan: alasan || null,
      },
    };
  } catch (error) {
    logging[3] = Date.now() / 1000;
    logging[9] = "Error Set Expired Billing";
    await insertLogRawQuery(logging);
    return {
      success: false,
      statusCode: status.error,
      message: "Internal server error",
      error: error.message || error,
    };
  }
};

5. Bukti Verifikasi Pengujian (QA / Unit Test / API Verification)

A. Verifikasi Unit Test (Development Stage)

  • Test Suite File: tests/helpers/billing_helper.test.js
  • Status Eksekusi CLI: PASSED (7/7 tests passed)
text
 RUN  v4.1.11 D:/WORK/LNSW/single-billing-BE
 ✓ tests/helpers/billing_helper.test.js (7 tests) 41ms
 Test Files  1 passed (1)
      Tests  7 passed (7)
   Start at  15:22:21
   Duration  705ms (transform 159ms, setup 138ms, import 120ms, tests 41ms, environment 0ms)

B. Verifikasi Integrasi Staging / Production

Diisi saat naik ke Staging atau Production.

json
{
  "test_suites": [
    "tests/helpers/billing_helper.test.js (PASS)"
  ],
  "verification_status": "DEV_PASSED"
}
  • Hasil Pengujian UAT: PENDING
  • Catatan QA: Unit test lokal (7 tests) telah lulus secara empiris. Menunggu jadwal deployment dan verifikasi staging.

Single Source of Truth (SSOT) Arsitektur & Perubahan Teknis Backend