Files
open-isp/services/registrationService.js
T
oxpoe eaf05f2811 OPEN-ISP v0.5 (pickle-lime) — rilis open-source perdana
Aplikasi billing & manajemen ISP (fiber optic) — PWA pelanggan, portal teknisi & kolektor,
admin panel lengkap, multi-vendor OLT, RADIUS, WhatsApp/Telegram, pembayaran QRIS.

Kredit & lisensi (ISC):
- Base kode & repo: alijayanet/billing-rtrw oleh Alijaya Net — terima kasih banyak
  atas base kode dan repo yang bermanfaat.
- Add-on: GoWA, LibreSpeed/OpenSpeedTest, Uptime Kuma, notification-forwarder.
- Dikembangkan menjadi OPEN-ISP oleh oxpoe — https://github.com/oxpoe/open-isp
2026-10-09 05:51:21 +07:00

73 lines
2.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'use strict';
/**
* registrationService.js — Perhitungan biaya awal pendaftaran (prorata, acuan tgl 5).
* biaya pasang + prorata paket − diskon voucher = total bayar awal.
*
* Aturan acuan tgl 5:
* - daftar SEBELUM tgl 5 → paket dihitung dari tgl daftar sampai tgl 5 BULAN INI
* - daftar tgl 5 atau SESUDAH → dihitung sampai tgl 5 BULAN DEPAN
*/
const { getSetting } = require('../config/settingsManager');
const voucherService = require('./voucherService');
function localParts(date) {
const tz = getSetting('timezone', 'Asia/Jakarta');
try {
const fmt = new Intl.DateTimeFormat('en-CA', { timeZone: tz, year: 'numeric', month: '2-digit', day: '2-digit' });
const o = {};
fmt.formatToParts(date).forEach(p => { o[p.type] = p.value; });
return { y: Number(o.year), m: Number(o.month), d: Number(o.day) };
} catch (e) {
return { y: date.getFullYear(), m: date.getMonth() + 1, d: date.getDate() };
}
}
function pad(n) { return String(n).padStart(2, '0'); }
function computeBill(pkgPrice, opts = {}) {
const now = opts.now ? new Date(opts.now) : new Date();
const { y, m, d } = localParts(now);
const price = Math.max(0, Math.round(Number(pkgPrice) || 0));
const installFee = Math.max(0, Math.round(Number(opts.installFee != null ? opts.installFee : getSetting('registration_install_fee', 0)) || 0));
let dueY = y, dueM = m;
if (d >= 5) {
if (m === 12) { dueY = y + 1; dueM = 1; } else { dueM = m + 1; }
}
const start = new Date(y, m - 1, d);
const due = new Date(dueY, dueM - 1, 5);
const diffDays = Math.max(1, Math.round((due.getTime() - start.getTime()) / 86400000));
const daysInMonth = new Date(dueY, dueM, 0).getDate();
const costPerDay = price / daysInMonth;
const prorata = Math.round(diffDays * costPerDay);
const gross = prorata + installFee;
let discount = 0;
let voucher = null;
if (opts.voucherCode) {
const v = voucherService.getByCode(opts.voucherCode);
if (v) {
const chk = voucherService.validate(opts.voucherCode, gross);
if (chk.ok) { discount = chk.discount; voucher = v; }
}
}
const total = Math.max(0, gross - discount);
return {
price,
installFee,
startDate: `${y}-${pad(m)}-${pad(d)}`,
dueDate: `${dueY}-${pad(dueM)}-05`,
diffDays,
daysInMonth,
costPerDay: Math.round(costPerDay),
prorata,
gross,
discount,
total,
voucherCode: voucher ? voucher.code : '',
voucherValid: !!voucher
};
}
module.exports = { computeBill, localParts };