nodebb-plugin-calendar-onekite 11.1.63 → 11.1.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/admin.js CHANGED
@@ -348,6 +348,7 @@ admin.getAccounting = async function (req, res) {
348
348
  const r = await dbLayer.getReservation(rid);
349
349
  if (!r) continue;
350
350
  if (String(r.status) !== 'paid') continue;
351
+ if (r.accPurgedAt) continue;
351
352
  const start = parseInt(r.start, 10);
352
353
  if (!Number.isFinite(start)) continue;
353
354
  if (start < minTs || start >= maxTs) continue;
@@ -434,4 +435,45 @@ admin.exportAccountingCsv = async function (req, res) {
434
435
  return res.send(csv);
435
436
  };
436
437
 
438
+
439
+ admin.purgeAccounting = async function (req, res) {
440
+ const qFrom = String((req.query && req.query.from) || '').trim();
441
+ const qTo = String((req.query && req.query.to) || '').trim();
442
+ const parseDay = (s) => {
443
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(s)) return null;
444
+ const [y, m, d] = s.split('-').map((x) => parseInt(x, 10));
445
+ const dt = new Date(Date.UTC(y, m - 1, d));
446
+ const ts = dt.getTime();
447
+ return Number.isFinite(ts) ? ts : null;
448
+ };
449
+ const fromTs = parseDay(qFrom);
450
+ const toTs = parseDay(qTo);
451
+
452
+ const now = new Date();
453
+ const defaultTo = Date.UTC(now.getUTCFullYear() + 100, 0, 1); // far future
454
+ const defaultFrom = Date.UTC(1970, 0, 1);
455
+ const minTs = fromTs ?? defaultFrom;
456
+ const maxTs = toTs ?? defaultTo;
457
+
458
+ const ids = await dbLayer.listAllReservationIds(200000);
459
+ let purged = 0;
460
+ const ts = Date.now();
461
+
462
+ for (const rid of ids) {
463
+ const r = await dbLayer.getReservation(rid);
464
+ if (!r) continue;
465
+ if (String(r.status) !== 'paid') continue;
466
+ const start = parseInt(r.start, 10);
467
+ if (!Number.isFinite(start)) continue;
468
+ if (start < minTs || start >= maxTs) continue;
469
+
470
+ r.accPurgedAt = ts;
471
+ await dbLayer.saveReservation(r);
472
+ purged++;
473
+ }
474
+
475
+ return res.json({ ok: true, purged });
476
+ };
477
+
478
+
437
479
  module.exports = admin;
@@ -295,7 +295,20 @@ async function handler(req, res, next) {
295
295
  return res.json({ ok: true, ignored: true });
296
296
  }
297
297
 
298
+ // Ignore incomplete Payment events (HelloAsso sometimes omits metadata and checkoutIntentId on Payment webhooks).
299
+ // We rely on Order events (or checkoutIntent mappings) for reliable reconciliation.
300
+ const _eventType = String((payload && payload.eventType) || '').toLowerCase();
301
+ const _rid = getReservationIdFromPayload(payload);
302
+ const _checkoutIntentId = getCheckoutIntentIdFromPayload(payload);
303
+ if (_eventType === 'payment' && !_rid && !_checkoutIntentId) {
304
+ return res.json({ ok: true, ignored: true, incompletePayment: true });
305
+ }
306
+
298
307
  const paymentId = payload && payload.data ? (payload.data.id || payload.data.paymentId) : null;
308
+ // If we can't identify the payment, acknowledge and ignore (prevents accidental crashes).
309
+ if (!paymentId) {
310
+ return res.json({ ok: true, ignored: true, missingPaymentId: true });
311
+ }
299
312
  if (await alreadyProcessed(paymentId)) {
300
313
  return res.json({ ok: true, duplicate: true });
301
314
  }
package/library.js CHANGED
@@ -97,6 +97,7 @@ Plugin.init = async function (params) {
97
97
  // Accounting / exports
98
98
  router.get(`${base}/accounting`, ...adminMws, admin.getAccounting);
99
99
  router.get(`${base}/accounting.csv`, ...adminMws, admin.exportAccountingCsv);
100
+ router.post(`${base}/accounting/purge`, ...adminMws, admin.purgeAccounting);
100
101
  });
101
102
 
102
103
  // HelloAsso callback endpoint (hardened)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nodebb-plugin-calendar-onekite",
3
- "version":"11.1.63",
3
+ "version": "11.1.65",
4
4
  "description": "FullCalendar-based equipment reservation workflow with admin approval & HelloAsso payment for NodeBB",
5
5
  "main": "library.js",
6
6
  "license": "MIT",
@@ -8,4 +8,4 @@
8
8
  "node": ">=18"
9
9
  },
10
10
  "dependencies": {}
11
- }
11
+ }
package/plugin.json CHANGED
@@ -27,5 +27,5 @@
27
27
  "acpScripts": [
28
28
  "public/admin.js"
29
29
  ],
30
- "version": "1.0.31"
31
- }
30
+ "version": "1.0.33"
31
+ }
package/public/admin.js CHANGED
@@ -516,6 +516,7 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
516
516
  const accTo = document.getElementById('onekite-acc-to');
517
517
  const accRefresh = document.getElementById('onekite-acc-refresh');
518
518
  const accExport = document.getElementById('onekite-acc-export');
519
+ const accPurge = document.getElementById('onekite-acc-purge');
519
520
  const accSummary = document.querySelector('#onekite-acc-summary tbody');
520
521
  const accRows = document.querySelector('#onekite-acc-rows tbody');
521
522
 
@@ -584,6 +585,32 @@ define('admin/plugins/calendar-onekite', ['alerts', 'bootbox'], function (alerts
584
585
  const url = `/api/v3/admin/plugins/calendar-onekite/accounting.csv${qs ? `?${qs}` : ''}`;
585
586
  window.open(url, '_blank');
586
587
  });
588
+
589
+ if (accPurge) {
590
+ accPurge.addEventListener('click', async () => {
591
+ const ok = window.confirm('Purger la comptabilité pour la période sélectionnée ?
592
+ Cela masquera ces réservations dans l\'onglet Comptabilisation (sans modifier leur statut payé).');
593
+ if (!ok) return;
594
+ try {
595
+ const params = new URLSearchParams();
596
+ if (accFrom && accFrom.value) params.set('from', accFrom.value);
597
+ if (accTo && accTo.value) params.set('to', accTo.value);
598
+ const qs = params.toString();
599
+ const url = `/api/v3/admin/plugins/calendar-onekite/accounting/purge${qs ? `?${qs}` : ''}`;
600
+ const res = await fetchJson(url, { method: 'POST' });
601
+ if (res && res.ok) {
602
+ showAlert('success', `Compta purgée : ${res.purged || 0} réservation(s).`);
603
+ const data = await fetchAccounting();
604
+ renderAccounting(data);
605
+ } else {
606
+ showAlert('error', 'Purge impossible.');
607
+ }
608
+ } catch (e) {
609
+ showAlert('error', 'Purge impossible.');
610
+ }
611
+ });
612
+ }
613
+
587
614
  }
588
615
  }
589
616
 
@@ -122,6 +122,7 @@
122
122
  <div>
123
123
  <button type="button" class="btn btn-primary" id="onekite-acc-refresh">Rafraîchir</button>
124
124
  <button type="button" class="btn btn-outline-secondary" id="onekite-acc-export">Exporter CSV</button>
125
+ <button type="button" class="btn btn-outline-danger" id="onekite-acc-purge">Purger la compta</button>
125
126
  </div>
126
127
  </div>
127
128