diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fac1da..1a24f9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/). +## [0.6.5] — 2026-11-06 — "qr-custom-delete" + +### Ditambahkan +- **Hapus satu QR custom** dari detail (wajib password admin). +- **Reset seluruh histori QR custom** dari header tabel (wajib password admin, konfirmasi). + ## [0.6.4] — 2026-11-06 — "qr-custom-ux" ### Diperbaiki diff --git a/README.md b/README.md index 704cb73..e474680 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@
-
+
diff --git a/package.json b/package.json
index ae247c3..4fe0624 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "open-isp",
- "version": "0.6.4",
+ "version": "0.6.5",
"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 bc4ddfb..9106f62 100644
--- a/routes/adminPortal.js
+++ b/routes/adminPortal.js
@@ -4062,6 +4062,30 @@ router.post('/custom-qr/:id/manual-paid', requireAdminSession, requireSidebarMen
res.redirect('/admin/custom-qr?id=' + Number(req.params.id));
});
+function _checkAdminPassword(req) {
+ const pass = String((req.body && req.body.password) || '');
+ const expected = String(getSetting('admin_password', 'demo-isp123') || 'demo-isp123');
+ return !!expected && pass === expected;
+}
+
+router.post('/custom-qr/:id/delete', requireAdminSession, restrictToAdmin, requireSidebarMenuAccess('custom_qr'), express.urlencoded({ extended: true }), (req, res) => {
+ try {
+ if (!_checkAdminPassword(req)) throw new Error('Password admin salah.');
+ const n = require('../services/customQrService').remove(req.params.id);
+ req.session._msg = n ? { type: 'success', text: 'QR custom dihapus.' } : { type: 'error', text: 'Data tidak ditemukan.' };
+ } catch (e) { req.session._msg = { type: 'error', text: e.message }; }
+ res.redirect('/admin/custom-qr');
+});
+
+router.post('/custom-qr/reset', requireAdminSession, restrictToAdmin, requireSidebarMenuAccess('custom_qr'), express.urlencoded({ extended: true }), (req, res) => {
+ try {
+ if (!_checkAdminPassword(req)) throw new Error('Password admin salah.');
+ const n = require('../services/customQrService').clearAll();
+ req.session._msg = { type: 'success', text: `Histori QR custom direset (${n} data dihapus).` };
+ } catch (e) { req.session._msg = { type: 'error', text: e.message }; }
+ res.redirect('/admin/custom-qr');
+});
+
router.post('/custom-qr/:id/regenerate', requireAdminSession, requireSidebarMenuAccess('custom_qr'), express.urlencoded({ extended: true }), (req, res) => {
try {
const row = require('../services/customQrService').regenerate(req.params.id, resolvePaidByName(req, 'Admin'));
diff --git a/services/customQrService.js b/services/customQrService.js
index 8b218ba..bd4a9ba 100644
--- a/services/customQrService.js
+++ b/services/customQrService.js
@@ -118,6 +118,16 @@ function acceptWarning(id, actor, note) {
return getById(id);
}
+function remove(id) {
+ const r = db.prepare('DELETE FROM custom_qr_payments WHERE id=?').run(Number(id) || 0);
+ return r.changes || 0;
+}
+
+function clearAll() {
+ const r = db.prepare('DELETE FROM custom_qr_payments').run();
+ return r.changes || 0;
+}
+
function regenerate(id, actor) {
const old = getById(id);
if (!old) throw new Error('Data tidak ditemukan.');
@@ -196,6 +206,8 @@ module.exports = {
markManualPaid,
acceptWarning,
regenerate,
+ remove,
+ clearAll,
reject,
markPaidFromWebhook,
markWarningFromWebhook,
diff --git a/version.txt b/version.txt
index eb514eb..e0ea44c 100644
--- a/version.txt
+++ b/version.txt
@@ -1 +1 @@
-0.6.4
\ No newline at end of file
+0.6.5
\ No newline at end of file
diff --git a/views/admin/custom_qr.ejs b/views/admin/custom_qr.ejs
index cacc8c4..f87a07c 100644
--- a/views/admin/custom_qr.ejs
+++ b/views/admin/custom_qr.ejs
@@ -110,13 +110,23 @@
<% } %>
+
| Judul | Kategori | Nominal | Status | Dibuat | Kedaluwarsa | Aksi |
|---|