diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b00ffa..6017082 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/). +## [0.6.1] — 2026-11-06 — "qr-custom-gate" + +### Diperbaiki +- Ikon menu **QR-Custom Payment** memakai ikon yang tersedia (`bi-qr-code`). +- Fitur QR-Custom **dinonaktifkan otomatis** bila QRIS Statis (Semi-Otomatis) belum aktif atau `qris_static_payload` kosong: + - menu disembunyikan, + - form pembuatan dinonaktifkan, + - request pembuatan ditolak (tidak ada data/list dibuat tanpa QR). + ## [0.6.0] — 2026-11-06 — "qr-custom" ### Ditambahkan diff --git a/README.md b/README.md index 42618aa..b3ee0a7 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

Lisensi: ISC - Versi 0.6.0 (qr-custom) + Versi 0.6.1 (qr-custom-gate) Node.js >= 20 Docker Demo diff --git a/package.json b/package.json index c7548c4..6989a76 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-isp", - "version": "0.6.0", + "version": "0.6.1", "description": "OPEN-ISP \u2014 sistem billing & manajemen ISP open-source (PWA pelanggan, multi-OLT, RADIUS, WhatsApp, QRIS)", "main": "app-customer.js", "scripts": { diff --git a/routes/adminPortal.js b/routes/adminPortal.js index d4cb9f4..e382112 100644 --- a/routes/adminPortal.js +++ b/routes/adminPortal.js @@ -4031,7 +4031,8 @@ router.get('/custom-qr', requireAdminSession, requireSidebarMenuAccess('custom_q detail, baseUrl, durationMinutes: svc.DURATION_MINUTES, - hasQrisPayload: !!String(settings.qris_static_payload || '').trim() + hasQrisPayload: !!String(settings.qris_static_payload || '').trim(), + ready: svc.isReady() }); }); diff --git a/services/customQrService.js b/services/customQrService.js index 6856750..5a3b375 100644 --- a/services/customQrService.js +++ b/services/customQrService.js @@ -7,9 +7,19 @@ const crypto = require('crypto'); const db = require('../config/database'); const { isQrisAmountTaken } = require('./invoiceQrisService'); const { logger } = require('../config/logger'); +const { getSetting } = require('../config/settingsManager'); const DURATION_MINUTES = 20; +function isReady() { + try { + const enabled = getSetting('qris_static_enabled', true); + const off = enabled === false || enabled === 'false' || enabled === 0 || enabled === '0'; + const payload = String(getSetting('qris_static_payload', '') || '').trim(); + return !off && payload.length > 0; + } catch (e) { return false; } +} + function nowIso() { return new Date().toISOString(); } function sweepExpired() { @@ -27,6 +37,7 @@ function isTaken(amount, excludeId) { } function create({ title, category, amount, note, createdBy }) { + if (!isReady()) throw new Error('QRIS Statis (Semi-Otomatis) belum aktif/berisi payload. Atur dulu di Pengaturan → QRIS.'); const base = Math.max(1, Math.floor(Number(amount) || 0)); if (!title || !String(title).trim()) throw new Error('Judul wajib diisi.'); if (!base) throw new Error('Nominal wajib diisi.'); @@ -161,6 +172,7 @@ function isExpired(row) { module.exports = { DURATION_MINUTES, + isReady, create, list, stats, diff --git a/services/sidebarMenuService.js b/services/sidebarMenuService.js index eaed894..ee4bfe8 100644 --- a/services/sidebarMenuService.js +++ b/services/sidebarMenuService.js @@ -32,7 +32,7 @@ const MENU_DEFINITIONS = [ { key: 'map', section: 'main', href: '/admin/map', icon: 'bi bi-map', labelKey: 'admin.nav.network_map', labelDefault: 'Peta Jaringan', roles: ['admin', 'cashier'], bottomNav: true, bottomNavOrder: 4, activePages: ['map'] }, { key: 'whatsapp', section: 'main', href: '/admin/whatsapp', icon: 'bi bi-whatsapp', labelKey: 'admin.nav.whatsapp', labelDefault: 'WhatsApp', roles: ['admin', 'cashier'], activePages: ['whatsapp','broadcast','whatsapp_live_chat','whatsapp_templates'] }, { key: 'automation', section: 'main', href: '/admin/automation', icon: 'bi bi-robot', labelKey: 'admin.nav.automation', labelDefault: 'Automation', roles: ['admin'], activePages: ['automation'] }, - { key: 'custom_qr', section: 'main', href: '/admin/custom-qr', icon: 'bi bi-qr-code-simple', labelKey: 'admin.nav.custom_qr', labelDefault: 'QR-Custom Payment', roles: ['admin'], activePages: ['custom_qr'] }, + { key: 'custom_qr', section: 'main', href: '/admin/custom-qr', icon: 'bi bi-qr-code', labelKey: 'admin.nav.custom_qr', labelDefault: 'QR-Custom Payment', roles: ['admin'], activePages: ['custom_qr'] }, { key: 'grp_customers', section: 'billing', href: '/admin/customers', icon: 'bi bi-people', labelKey: 'admin.nav.customers', labelDefault: 'Pelanggan', roles: ['admin', 'cashier'], bottomNav: true, bottomNavOrder: 2, activePages: ['customers', 'onu_stickers'], children: [ { key: 'customers', section: 'billing', href: '/admin/customers', icon: 'bi bi-people', labelKey: 'admin.nav.customers_data', labelDefault: 'Data Pelanggan', roles: ['admin', 'cashier'], activePages: ['customers'] }, @@ -269,12 +269,21 @@ function enrichMenu(menu, states) { return out; } +function isCustomQrReady() { + try { + const enabled = getSetting('qris_static_enabled', true); + const off = enabled === false || enabled === 'false' || enabled === 0 || enabled === '0'; + return !off && String(getSetting('qris_static_payload', '') || '').trim().length > 0; + } catch (e) { return false; } +} + function getSidebarSections(session) { const states = getStoredMenuStates(); return SECTION_DEFINITIONS.map((section) => { const items = MENU_DEFINITIONS .filter((menu) => menu.section === section.key) .filter((menu) => isMenuAllowedForSession(menu, session)) + .filter((menu) => !(menu.key === 'custom_qr' && !isCustomQrReady())) .map((menu) => { const enriched = enrichMenu(menu, states); if (Array.isArray(enriched.children)) { diff --git a/version.txt b/version.txt index 09a3acf..7ceb040 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.6.0 \ No newline at end of file +0.6.1 \ No newline at end of file diff --git a/views/admin/custom_qr.ejs b/views/admin/custom_qr.ejs index abfdaab..40591fc 100644 --- a/views/admin/custom_qr.ejs +++ b/views/admin/custom_qr.ejs @@ -23,10 +23,11 @@ <% } %> - <% if (typeof hasQrisPayload !== 'undefined' && !hasQrisPayload) { %> -

QRIS statis belum diatur. Isi QRIS static payload di Pengaturan agar QR bisa dibuat.
+ <% if (typeof ready !== 'undefined' && !ready) { %> +
QR-Custom Payment dinonaktifkan. Aktifkan QRIS Statis (Semi-Otomatis) dan isi QRIS static payload di Pengaturan terlebih dahulu.
<% } %> + <% if (typeof ready === 'undefined' || ready) { %>
Buat QR Nominal Custom
@@ -43,6 +44,7 @@
+ <% } %>
Pembayaran masuk
<%= stats.paidCount %>
Rp <%= Number(stats.paidTotal||0).toLocaleString('id-ID') %>