From 458edce0ca34144a02616c93f666c29ac62577b9 Mon Sep 17 00:00:00 2001 From: fabiodalez-dev Date: Tue, 10 Nov 2026 00:36:06 +0100 Subject: [PATCH 1/4] fines-ledger: re-anchor loan C on today before the late return The loan seeded three days late in test 11 became four days late when the library's midnight passed before test 13, and the fine rose to 2.00. --- tests/fines-ledger.spec.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/fines-ledger.spec.js b/tests/fines-ledger.spec.js index 9d92d9b8d..8e2b0221b 100644 --- a/tests/fines-ledger.spec.js +++ b/tests/fines-ledger.spec.js @@ -421,6 +421,9 @@ test.describe.serial('Fines and patron ledger', () => { await saveFineSettings(page, { email: true }); expect(readSetting('loans', 'fine_charged_email')).toBe('1'); + // Loan C was seeded 3 days late by an earlier test: if the library's midnight + // passed since then it is 4 days late now. Re-anchor it on today's date. + dbQuery(`UPDATE prestiti SET data_prestito='${appDateOffsetISO(-17)}', data_scadenza='${appDateOffsetISO(-3)}' WHERE id=${state.loans.C}`); await returnFromDesk(page, state.loans.C); expect(dbQuery(`SELECT importo FROM movimenti_lettore WHERE prestito_id=${state.loans.C} AND tipo='multa_ritardo'`)).toBe('1.50'); From 2fe9fb9dcf4873ca34a1a23395516962e6d6bbc5 Mon Sep 17 00:00:00 2001 From: fabiodalez-dev Date: Tue, 10 Nov 2026 01:59:58 +0100 Subject: [PATCH 2/4] fines-ledger: price loan C's fine on the recorded return day If the library's midnight falls during the return itself, the loan is four days late: the fine, the email subject, the days and the balance are now derived from DATEDIFF(data_restituzione, data_scadenza). --- tests/fines-ledger.spec.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/tests/fines-ledger.spec.js b/tests/fines-ledger.spec.js index 8e2b0221b..c68185681 100644 --- a/tests/fines-ledger.spec.js +++ b/tests/fines-ledger.spec.js @@ -422,10 +422,14 @@ test.describe.serial('Fines and patron ledger', () => { expect(readSetting('loans', 'fine_charged_email')).toBe('1'); // Loan C was seeded 3 days late by an earlier test: if the library's midnight - // passed since then it is 4 days late now. Re-anchor it on today's date. + // passed since then it is 4 days late now. Re-anchor it on today's date, and + // price the fine on the return day the desk recorded, in case midnight falls + // during the return itself (0,50 a day, every day charged past the grace). dbQuery(`UPDATE prestiti SET data_prestito='${appDateOffsetISO(-17)}', data_scadenza='${appDateOffsetISO(-3)}' WHERE id=${state.loans.C}`); await returnFromDesk(page, state.loans.C); - expect(dbQuery(`SELECT importo FROM movimenti_lettore WHERE prestito_id=${state.loans.C} AND tipo='multa_ritardo'`)).toBe('1.50'); + const daysLate = Number(dbQuery(`SELECT DATEDIFF(data_restituzione, data_scadenza) FROM prestiti WHERE id=${state.loans.C}`)); + expect([3, 4]).toContain(daysLate); + expect(dbQuery(`SELECT importo FROM movimenti_lettore WHERE prestito_id=${state.loans.C} AND tipo='multa_ritardo'`)).toBe((daysLate * 0.5).toFixed(2)); const query = `to:${PATRON_EMAIL} subject:"Multa per ritardo"`; let message = null; @@ -437,10 +441,10 @@ test.describe.serial('Fines and patron ledger', () => { } expect(message, 'fine_charged email in Mailpit').not.toBeNull(); const full = await (await fetch(`${MAILPIT_API}/message/${message.ID}`)).json(); - expect(full.Subject).toContain(euro(150)); + expect(full.Subject).toContain(euro(daysLate * 50)); expect(full.HTML).toContain(`${TITLE_PREFIX}_C`); - expect(full.HTML).toContain('3'); // days late - expect(full.HTML).toContain(euro(650)); // balance: 5,00 + 1,50 + expect(full.HTML).toContain(String(daysLate)); // days late + expect(full.HTML).toContain(euro(500 + daysLate * 50)); // balance: 5,00 + this fine }); test('13b. a fine waived at the desk: the return email has no fine line and no fine email is sent (Mailpit)', async () => { From dee281f2f36a6b1725ebbb898d883ae6afdd838c Mon Sep 17 00:00:00 2001 From: fabiodalez-dev Date: Wed, 11 Nov 2026 17:22:29 +0100 Subject: [PATCH 3/4] fines-ledger: check the days late in the email together with their label A bare digit could match the book title or an amount, so the test passed with a wrong day count. --- tests/fines-ledger.spec.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/fines-ledger.spec.js b/tests/fines-ledger.spec.js index c68185681..3a1ce5565 100644 --- a/tests/fines-ledger.spec.js +++ b/tests/fines-ledger.spec.js @@ -443,7 +443,8 @@ test.describe.serial('Fines and patron ledger', () => { const full = await (await fetch(`${MAILPIT_API}/message/${message.ID}`)).json(); expect(full.Subject).toContain(euro(daysLate * 50)); expect(full.HTML).toContain(`${TITLE_PREFIX}_C`); - expect(full.HTML).toContain(String(daysLate)); // days late + // With its label: a bare digit could match the book title or an amount. + expect(full.HTML).toMatch(new RegExp(`Giorni di ritardo:\\s*(?:]*>\\s*)*${daysLate}(?!\\d)`)); expect(full.HTML).toContain(euro(500 + daysLate * 50)); // balance: 5,00 + this fine }); From 6acb6c588b95a975907f99307390e43ee11c18a8 Mon Sep 17 00:00:00 2001 From: fabiodalez-dev Date: Wed, 11 Nov 2026 19:37:20 +0100 Subject: [PATCH 4/4] fines-ledger: switch the closures calendar off for the run With closed days configured the ledger charges open days only, while these tests price fines on calendar days. The suite now disables the module in its setup and restores what was there. --- tests/fines-ledger.spec.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/fines-ledger.spec.js b/tests/fines-ledger.spec.js index 3a1ce5565..23dc321fa 100644 --- a/tests/fines-ledger.spec.js +++ b/tests/fines-ledger.spec.js @@ -14,8 +14,18 @@ const { test, expect } = require('@playwright/test'); // fines, so it switches it on for its run and puts back what was there. const featureToggles = require('./helpers/feature-toggles'); let featureSnapshot; -test.beforeAll(async () => { featureSnapshot = await featureToggles.enableFeatures(['fines']); }); -test.afterAll(async () => { await featureToggles.restoreFeatures(featureSnapshot); }); +let calendarSnapshot; +// The closures calendar is switched off for the run: with closed days the +// ledger charges open days only, and these tests price fines on calendar +// days. Closed days have their own suite (fines-closed-days.spec.js). +test.beforeAll(async () => { + featureSnapshot = await featureToggles.enableFeatures(['fines']); + calendarSnapshot = await featureToggles.disableFeatures(['closures_calendar']); +}); +test.afterAll(async () => { + await featureToggles.restoreFeatures(calendarSnapshot); + await featureToggles.restoreFeatures(featureSnapshot); +}); const { execFileSync } = require('child_process'); const fs = require('fs'); const path = require('path');