feat: enhance date formatting in rental documents and receipts

This commit is contained in:
nessar committed 2026-08-17 14:18:15 +02:00
1 parent d7a7aff3ee
commit 3bbbf773f4
5 files changed
+46 -15

No files matched your search

@@ -8,13 +8,17 @@ import org.thymeleaf.context.Context;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.time.format.DateTimeFormatter;
import java.time.LocalDate;
import java.time.Month;
import java.time.YearMonth;
import java.time.format.TextStyle;
import java.time.temporal.TemporalAccessor;
import java.util.List;
import java.util.Locale;
@Component
public class ThymeleafRentalDocumentRenderer implements RentalDocumentRenderer {
private static final DateTimeFormatter MONTH = DateTimeFormatter.ofPattern("MMMM yyyy", Locale.FRANCE);
private static final FrenchDates DATES = new FrenchDates();
private final TemplateEngine templates;
@@ -45,13 +49,13 @@ public class ThymeleafRentalDocumentRenderer implements RentalDocumentRenderer {
Context context = context(document);
context.setVariable("tenantNames", names(document.contract().tenants().stream()
.map(tenant -> tenant.firstName() + " " + tenant.lastName()).toList()));
context.setVariable("receiptMonth", document.month().format(MONTH));
return render("rental/rent-receipt", context);
}
private Context context(Object document) {
Context context = new Context(Locale.FRANCE);
context.setVariable("document", document);
context.setVariable("dates", DATES);
return context;
}
@@ -68,4 +72,22 @@ public class ThymeleafRentalDocumentRenderer implements RentalDocumentRenderer {
private static String names(List<String> names) {
return names.isEmpty() ? "Aucun" : String.join(", ", names);
}
public static final class FrenchDates {
private FrenchDates() {
}
public String date(LocalDate date) {
return date.getDayOfMonth() + " " + monthName(date) + " " + date.getYear();
}
public String month(YearMonth month) {
return monthName(month) + " " + month.getYear();
}
private static String monthName(TemporalAccessor date) {
String month = Month.from(date).getDisplayName(TextStyle.FULL, Locale.FRANCE);
return Character.toUpperCase(month.charAt(0)) + month.substring(1);
}
}
}
@@ -24,7 +24,7 @@
<tr><th>Téléphone du bailleur</th><td th:text="${document.landlord.phone}">+33612345678</td></tr>
<tr><th>Adresse du bailleur</th><td th:text="${document.landlord.address}">1 rue de Paris</td></tr>
<tr><th>Locataire(s)</th><td th:text="${tenantNames}">Camille Martin</td></tr>
<tr><th>Période</th><td><span th:text="${document.period.startDate}">2026-09-01</span> au <span th:text="${document.period.endDate}">2027-08-31</span></td></tr>
<tr><th>Période</th><td><span th:text="${dates.date(document.period.startDate)}">1 Septembre 2026</span> au <span th:text="${dates.date(document.period.endDate)}">31 Août 2027</span></td></tr>
<tr><th>Loyer mensuel</th><td><span th:text="${document.terms.monthlyRent}">850.00</span> EUR</td></tr>
<tr><th>Charges mensuelles</th><td><span th:text="${document.terms.monthlyCharges}">50.00</span> EUR</td></tr>
<tr><th>Dépôt de garantie</th><td><span th:text="${document.terms.securityDeposit}">850.00</span> EUR</td></tr>
@@ -18,7 +18,7 @@
<tr><th>Bailleur</th><td th:text="${document.contract.landlord.firstName + ' ' + document.contract.landlord.lastName}">Léa Dubois</td></tr>
<tr><th>Logement</th><td th:text="${document.contract.apartment.address}">12 rue de la République</td></tr>
<tr><th>Locataire(s)</th><td th:text="${tenantNames}">Camille Martin</td></tr>
<tr><th>Reçu le</th><td th:text="${document.receivedAt}">2026-07-05</td></tr>
<tr><th>Reçu le</th><td th:text="${dates.date(document.receivedAt)}">5 Juillet 2026</td></tr>
<tr><th>Montant reçu</th><td><span th:text="${document.amount}">1200.00</span> EUR</td></tr>
<tr><th>Montant remboursé</th><td><span th:text="${document.refundedAmount}">0.00</span> EUR</td></tr>
<tr><th>Solde non affecté</th><td><span th:text="${document.unallocatedBalance}">300.00</span> EUR</td></tr>
@@ -28,9 +28,9 @@
<thead><tr><th>Mois</th><th>Montant</th><th>Date</th></tr></thead>
<tbody>
<tr th:each="allocation : ${document.allocations}">
<td th:text="${allocation.month}">2026-07</td>
<td th:text="${dates.month(allocation.month)}">Juillet 2026</td>
<td><span th:text="${allocation.amount}">900.00</span> EUR</td>
<td th:text="${allocation.allocatedAt}">2026-07-05</td>
<td th:text="${dates.date(allocation.allocatedAt)}">5 Juillet 2026</td>
</tr>
<tr th:if="${document.allocations.empty}"><td colspan="3">Aucune affectation</td></tr>
</tbody>
@@ -42,7 +42,7 @@
<tr th:each="refund : ${document.refunds}">
<td th:text="${refund.refundId.value}">refund-1</td>
<td><span th:text="${refund.amount}">100.00</span> EUR</td>
<td th:text="${refund.refundedAt}">2026-07-06</td>
<td th:text="${dates.date(refund.refundedAt)}">6 Juillet 2026</td>
</tr>
<tr th:if="${document.refunds.empty}"><td colspan="3">Aucun remboursement</td></tr>
</tbody>
@@ -21,11 +21,11 @@
<tr><th>Logement</th><td th:text="${document.contract.apartment.name}">Studio République</td></tr>
<tr><th>Adresse</th><td th:text="${document.contract.apartment.address}">12 rue de la République</td></tr>
<tr><th>Locataire(s)</th><td th:text="${tenantNames}">Camille Martin</td></tr>
<tr><th>Période</th><td th:text="${receiptMonth}">juillet 2026</td></tr>
<tr><th>Période</th><td th:text="${dates.month(document.month)}">Juillet 2026</td></tr>
<tr><th>Loyer</th><td><span th:text="${document.rent}">850.00</span> EUR</td></tr>
<tr><th>Charges</th><td><span th:text="${document.charges}">50.00</span> EUR</td></tr>
<tr><th>Total réglé</th><td><span th:text="${document.total}">900.00</span> EUR</td></tr>
</table>
<footer>Quittance générée le <span th:text="${document.issuedAt}">2026-07-18</span>. Le surplus non affecté n'est pas inclus.</footer>
<footer>Quittance générée le <span th:text="${dates.date(document.issuedAt)}">18 Juillet 2026</span>. Le surplus non affecté n'est pas inclus.</footer>
</body>
</html>
@@ -48,8 +48,8 @@ class ThymeleafRentalDocumentRendererTest {
ThymeleafRentalDocumentRenderer renderer = new ThymeleafRentalDocumentRenderer(templateEngine());
LeaseContractVersion contract = contract();
ReceiptAllocation allocation = new ReceiptAllocation(new PaymentAllocationId("allocation-1"),
new RentDueId("due-1"), YearMonth.of(2026, 7), new BigDecimal("950.00"),
LocalDate.of(2026, 7, 5));
new RentDueId("due-1"), YearMonth.of(2026, 6), new BigDecimal("950.00"),
LocalDate.of(2026, 8, 6));
byte[] lease = renderer.renderLease(new LeaseDocument(new LeaseId("lease-1"), contract.landlord(),
contract.apartment(), contract.tenants(), contract.guarantors(), contract.period(), contract.terms()));
@@ -57,18 +57,27 @@ class ThymeleafRentalDocumentRendererTest {
LocalDate.of(2026, 7, 5), LocalDate.of(2026, 7, 6), new BigDecimal("1200.00"),
new BigDecimal("50.00"), new BigDecimal("200.00"), contract, List.of(allocation),
List.of(new ReceiptRefund(new RentPaymentRefundId("refund-1"), new BigDecimal("50.00"),
LocalDate.of(2026, 7, 6)))));
LocalDate.of(2026, 9, 7)))));
byte[] rentReceipt = renderer.renderRentReceipt(new RentReceipt(new RentDueId("due-1"),
YearMonth.of(2026, 7), new BigDecimal("900.00"), new BigDecimal("50.00"),
new BigDecimal("950.00"), LocalDate.of(2026, 7, 18), contract));
YearMonth.of(2026, 10), new BigDecimal("900.00"), new BigDecimal("50.00"),
new BigDecimal("950.00"), LocalDate.of(2026, 7, 19), contract));
assertAll(() -> assertPdf(lease), () -> assertPdf(paymentReceipt), () -> assertPdf(rentReceipt));
String leaseText = pdfText(lease);
assertTrue(leaseText.contains("1 Janvier 2026"));
assertTrue(leaseText.contains("31 Décembre 2026"));
String paymentText = pdfText(paymentReceipt);
assertTrue(paymentText.contains("5 Juillet 2026"));
assertTrue(paymentText.contains("Juin 2026"));
assertTrue(paymentText.contains("6 Août 2026"));
assertTrue(paymentText.contains("7 Septembre 2026"));
assertTrue(paymentText.contains("1200.00"));
assertTrue(paymentText.contains("950.00"));
assertTrue(paymentText.contains("200.00"));
assertTrue(paymentText.contains("refund-1"));
String rentText = pdfText(rentReceipt);
assertTrue(rentText.contains("Octobre 2026"));
assertTrue(rentText.contains("19 Juillet 2026"));
assertTrue(rentText.contains("900.00"));
assertTrue(rentText.contains("50.00"));
assertFalse(rentText.contains("1200.00"));