Keep finalization violations domain-shaped (#53)
This commit is contained in:
1 parent
ce53bab698
commit
603b508aec
6 files changed
+144
-70
No files matched your search
-1
@@ -53,7 +53,6 @@ record GuaranteeDeedRequest(
|
||||
String attestationPath() {
|
||||
return "guaranteeRequirements[" + requirementIndex + "].attestation";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record DeedResponse(
|
||||
|
||||
+32
-16
@@ -146,27 +146,43 @@ private static PdfUpload upload(MultipartFile file) {
|
||||
private static FinalizationValidationFailure deedValidationFailure(
|
||||
LeaseFinalizationApi.GuaranteeDeedRequest request,
|
||||
LeaseFinalizationValidationFailure failure) {
|
||||
if (LeaseFinalizationValidationFailure.ATTESTATION.equals(failure.field())) {
|
||||
return new FinalizationValidationFailure(
|
||||
List.of(
|
||||
new LeaseFinalizationApi.FieldValidationError(
|
||||
request.attestationPath(), failure.code())));
|
||||
}
|
||||
if (LeaseFinalizationValidationFailure.FILE.equals(failure.field())) {
|
||||
return new FinalizationValidationFailure(
|
||||
List.of(new LeaseFinalizationApi.FieldValidationError(request.filePath(), failure.code())));
|
||||
}
|
||||
throw failure;
|
||||
return switch (failure.rule()) {
|
||||
case ATTESTATION_REQUIRED -> validationFailure(request.attestationPath(), "required");
|
||||
case PDF_REQUIRED -> validationFailure(request.filePath(), "required");
|
||||
case PDF_INVALID -> validationFailure(request.filePath(), "invalid");
|
||||
default -> throw failure;
|
||||
};
|
||||
}
|
||||
|
||||
private static FinalizationValidationFailure finalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure failure) {
|
||||
String field =
|
||||
LeaseFinalizationValidationFailure.FILE.equals(failure.field())
|
||||
? "signedLease"
|
||||
: failure.field();
|
||||
return switch (failure.rule()) {
|
||||
case ATTESTATION_REQUIRED -> validationFailure("attestation", "required");
|
||||
case PDF_REQUIRED -> validationFailure("signedLease", "required");
|
||||
case PDF_INVALID -> validationFailure("signedLease", "invalid");
|
||||
case ALL_PARTIES_MUST_SIGN -> validationFailure("allPartiesSigned", "required");
|
||||
case REVISION_MUST_MATCH -> validationFailure("revisionMatches", "required");
|
||||
case SIGNATURE_DATE_REQUIRED -> validationFailure("lastSignatureDate", "required");
|
||||
case GUARANTEE_DEEDS_REQUIRED -> validationFailure("guaranteeRequirements", "required");
|
||||
case WARNING_CONFIRMATION_INVALID -> validationFailure("confirmedWarningCodes", "invalid");
|
||||
case WARNING_CONFIRMATION_REQUIRED -> validationFailure("confirmedWarningCodes", "required");
|
||||
case INVENTORY_CORRECTIONS_INCONSISTENT ->
|
||||
validationFailure("entryInventoryDiscrepancies", "invalid");
|
||||
case INVENTORY_CORRECTIONS_REQUIRED -> validationFailure("inventoryCorrections", "required");
|
||||
case INVENTORY_CORRECTIONS_MUST_BE_INITIALED ->
|
||||
validationFailure("allInventoryCorrectionsInitialed", "required");
|
||||
case UNKNOWN_CORRECTION_PRIVATE_AREA ->
|
||||
validationFailure(
|
||||
"inventoryCorrections[" + failure.correctionIndex() + "].privateAreaId", "invalid");
|
||||
case UNKNOWN_CORRECTION_INVENTORY_LINE ->
|
||||
validationFailure(
|
||||
"inventoryCorrections[" + failure.correctionIndex() + "].inventoryLineId", "invalid");
|
||||
};
|
||||
}
|
||||
|
||||
private static FinalizationValidationFailure validationFailure(String field, String code) {
|
||||
return new FinalizationValidationFailure(
|
||||
List.of(new LeaseFinalizationApi.FieldValidationError(field, failure.code())));
|
||||
List.of(new LeaseFinalizationApi.FieldValidationError(field, code)));
|
||||
}
|
||||
|
||||
private static final class FinalizationValidationFailure extends RuntimeException {
|
||||
|
||||
+24
-15
@@ -120,7 +120,7 @@ public LeaseGuaranteeDeed uploadGuaranteeDeed(
|
||||
requireAuthor(authorId);
|
||||
if (!attestation) {
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.ATTESTATION, "required");
|
||||
LeaseFinalizationValidationFailure.Rule.ATTESTATION_REQUIRED);
|
||||
}
|
||||
requirePair(lease, guarantorId, tenantId);
|
||||
byte[] content = pdf(upload);
|
||||
@@ -242,13 +242,16 @@ public Result finalizeLease(
|
||||
}
|
||||
requireDraftForFinalization(lease);
|
||||
if (!request.allPartiesSigned()) {
|
||||
throw new LeaseFinalizationValidationFailure("allPartiesSigned", "required");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.ALL_PARTIES_MUST_SIGN);
|
||||
}
|
||||
if (!request.revisionMatches()) {
|
||||
throw new LeaseFinalizationValidationFailure("revisionMatches", "required");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.REVISION_MUST_MATCH);
|
||||
}
|
||||
if (request.lastSignatureDate() == null) {
|
||||
throw new LeaseFinalizationValidationFailure("lastSignatureDate", "required");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.SIGNATURE_DATE_REQUIRED);
|
||||
}
|
||||
requireInventoryCorrectionAttestation(request);
|
||||
byte[] content = pdf(signedLease);
|
||||
@@ -258,7 +261,8 @@ public Result finalizeLease(
|
||||
List<GuaranteeRequirement> requirements =
|
||||
requirements(landlordId, lease, guaranteeDeeds.findAll(landlordId, leaseId));
|
||||
if (requirements.stream().anyMatch(requirement -> requirement.deed() == null)) {
|
||||
throw new LeaseFinalizationValidationFailure("guaranteeRequirements", "required");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.GUARANTEE_DEEDS_REQUIRED);
|
||||
}
|
||||
List<LeaseFinalization.LegalOverride> overrides =
|
||||
overrides(landlordId, lease, revision, request.confirmedWarningCodes(), authorId);
|
||||
@@ -373,14 +377,16 @@ private List<LeaseFinalization.LegalOverride> overrides(
|
||||
List<LeaseDocumentWarning> warnings = warnings(landlordId, lease, revision);
|
||||
if (confirmed.stream()
|
||||
.anyMatch(code -> warnings.stream().noneMatch(warning -> warning.code().equals(code)))) {
|
||||
throw new LeaseFinalizationValidationFailure("confirmedWarningCodes", "invalid");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.WARNING_CONFIRMATION_INVALID);
|
||||
}
|
||||
Instant now = Instant.now(clock);
|
||||
return warnings.stream()
|
||||
.map(
|
||||
warning -> {
|
||||
if (!confirmed.contains(warning.code())) {
|
||||
throw new LeaseFinalizationValidationFailure("confirmedWarningCodes", "required");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.WARNING_CONFIRMATION_REQUIRED);
|
||||
}
|
||||
return new LeaseFinalization.LegalOverride(
|
||||
warning.code(), warning.ruleVersion(), authorId, now);
|
||||
@@ -443,12 +449,12 @@ private static void validateInventoryCorrections(
|
||||
LeaseFinalization.InventoryCorrection correction = corrections.get(index);
|
||||
if (!privateAreaIds.contains(correction.privateAreaId())) {
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
"inventoryCorrections[" + index + "].privateAreaId", "invalid");
|
||||
LeaseFinalizationValidationFailure.Rule.UNKNOWN_CORRECTION_PRIVATE_AREA, index);
|
||||
}
|
||||
if (correction.inventoryLineId() != null
|
||||
&& !inventoryLineIds.contains(correction.inventoryLineId())) {
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
"inventoryCorrections[" + index + "].inventoryLineId", "invalid");
|
||||
LeaseFinalizationValidationFailure.Rule.UNKNOWN_CORRECTION_INVENTORY_LINE, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -519,12 +525,12 @@ private Lease lease(LandlordId landlordId, LeaseId leaseId) {
|
||||
private byte[] pdf(PdfUpload upload) {
|
||||
if (upload == null) {
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.FILE, "required");
|
||||
LeaseFinalizationValidationFailure.Rule.PDF_REQUIRED);
|
||||
}
|
||||
byte[] content = upload.content();
|
||||
if (content.length == 0) {
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.FILE, "required");
|
||||
LeaseFinalizationValidationFailure.Rule.PDF_REQUIRED);
|
||||
}
|
||||
if (content.length > maximumPdfSize) {
|
||||
throw new AttachmentTooLarge(maximumPdfSize);
|
||||
@@ -533,7 +539,7 @@ private byte[] pdf(PdfUpload upload) {
|
||||
|| content.length < 5
|
||||
|| !Arrays.equals(Arrays.copyOf(content, 5), "%PDF-".getBytes(StandardCharsets.US_ASCII))) {
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.FILE, "invalid");
|
||||
LeaseFinalizationValidationFailure.Rule.PDF_INVALID);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
@@ -557,13 +563,16 @@ private static void requireInventoryCorrectionAttestation(FinalizationRequest re
|
||||
if (!request.entryInventoryDiscrepancies()
|
||||
&& (!request.inventoryCorrections().isEmpty()
|
||||
|| request.allInventoryCorrectionsInitialed())) {
|
||||
throw new LeaseFinalizationValidationFailure("entryInventoryDiscrepancies", "invalid");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.INVENTORY_CORRECTIONS_INCONSISTENT);
|
||||
}
|
||||
if (request.entryInventoryDiscrepancies() && request.inventoryCorrections().isEmpty()) {
|
||||
throw new LeaseFinalizationValidationFailure("inventoryCorrections", "required");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.INVENTORY_CORRECTIONS_REQUIRED);
|
||||
}
|
||||
if (request.entryInventoryDiscrepancies() && !request.allInventoryCorrectionsInitialed()) {
|
||||
throw new LeaseFinalizationValidationFailure("allInventoryCorrectionsInitialed", "required");
|
||||
throw new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.INVENTORY_CORRECTIONS_MUST_BE_INITIALED);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
-13
@@ -1,22 +1,44 @@
|
||||
package fr.nessar.lmnporganizer.rental.application.lease;
|
||||
|
||||
public final class LeaseFinalizationValidationFailure extends RuntimeException {
|
||||
public static final String FILE = "file";
|
||||
public static final String ATTESTATION = "attestation";
|
||||
|
||||
private final String field;
|
||||
private final String code;
|
||||
|
||||
public LeaseFinalizationValidationFailure(String field, String code) {
|
||||
this.field = field;
|
||||
this.code = code;
|
||||
public enum Rule {
|
||||
ATTESTATION_REQUIRED,
|
||||
PDF_REQUIRED,
|
||||
PDF_INVALID,
|
||||
ALL_PARTIES_MUST_SIGN,
|
||||
REVISION_MUST_MATCH,
|
||||
SIGNATURE_DATE_REQUIRED,
|
||||
GUARANTEE_DEEDS_REQUIRED,
|
||||
WARNING_CONFIRMATION_INVALID,
|
||||
WARNING_CONFIRMATION_REQUIRED,
|
||||
INVENTORY_CORRECTIONS_INCONSISTENT,
|
||||
INVENTORY_CORRECTIONS_REQUIRED,
|
||||
INVENTORY_CORRECTIONS_MUST_BE_INITIALED,
|
||||
UNKNOWN_CORRECTION_PRIVATE_AREA,
|
||||
UNKNOWN_CORRECTION_INVENTORY_LINE
|
||||
}
|
||||
|
||||
public String field() {
|
||||
return field;
|
||||
private final Rule rule;
|
||||
private final Integer correctionIndex;
|
||||
|
||||
public LeaseFinalizationValidationFailure(Rule rule) {
|
||||
this(rule, null);
|
||||
}
|
||||
|
||||
public String code() {
|
||||
return code;
|
||||
public LeaseFinalizationValidationFailure(Rule rule, int correctionIndex) {
|
||||
this(rule, Integer.valueOf(correctionIndex));
|
||||
}
|
||||
|
||||
private LeaseFinalizationValidationFailure(Rule rule, Integer correctionIndex) {
|
||||
this.rule = rule;
|
||||
this.correctionIndex = correctionIndex;
|
||||
}
|
||||
|
||||
public Rule rule() {
|
||||
return rule;
|
||||
}
|
||||
|
||||
public int correctionIndex() {
|
||||
return correctionIndex;
|
||||
}
|
||||
}
|
||||
+21
-3
@@ -235,10 +235,29 @@ void guaranteeAndFinalizationValidationUseVisibleStableFieldPaths() throws Excep
|
||||
.andExpect(jsonPath("$.errors[1].field").value("guaranteeRequirements[0].tenantId"))
|
||||
.andExpect(jsonPath("$.errors[2].field").value("guaranteeRequirements[0].attestation"));
|
||||
|
||||
when(finalizations.uploadGuaranteeDeed(
|
||||
any(), any(), any(), any(), any(Boolean.class), any(), any()))
|
||||
.thenThrow(
|
||||
new LeaseFinalizationValidationFailure(
|
||||
LeaseFinalizationValidationFailure.Rule.PDF_INVALID));
|
||||
mockMvc
|
||||
.perform(
|
||||
multipart("/api/leases/lease-1/guarantee-deeds")
|
||||
.file(pdf("file"))
|
||||
.param("requirementIndex", "0")
|
||||
.param("guarantorId", "guarantor-1")
|
||||
.param("tenantId", "tenant-1")
|
||||
.param("attestation", "true")
|
||||
.with(landlord())
|
||||
.with(csrf()))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.errors[0].field").value("guaranteeRequirements[0].file"))
|
||||
.andExpect(jsonPath("$.errors[0].code").value("invalid"));
|
||||
|
||||
when(finalizations.finalizeLease(any(), any(), any(), any(), any()))
|
||||
.thenThrow(
|
||||
new LeaseFinalizationValidationFailure(
|
||||
"inventoryCorrections[0].privateAreaId", "invalid"));
|
||||
LeaseFinalizationValidationFailure.Rule.UNKNOWN_CORRECTION_PRIVATE_AREA, 0));
|
||||
mockMvc
|
||||
.perform(
|
||||
multipart("/api/leases/lease-1/finalization")
|
||||
@@ -248,8 +267,7 @@ void guaranteeAndFinalizationValidationUseVisibleStableFieldPaths() throws Excep
|
||||
.with(csrf()))
|
||||
.andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("$.code").value("validation_failed"))
|
||||
.andExpect(
|
||||
jsonPath("$.errors[0].field").value("inventoryCorrections[0].privateAreaId"))
|
||||
.andExpect(jsonPath("$.errors[0].field").value("inventoryCorrections[0].privateAreaId"))
|
||||
.andExpect(jsonPath("$.errors[0].code").value("invalid"));
|
||||
}
|
||||
|
||||
|
||||
+32
-22
@@ -503,28 +503,38 @@ void
|
||||
successfulFinalizationSetup(draft, revision);
|
||||
|
||||
LeaseFinalizationService service = service(clock("2026-07-19T12:00:00Z"));
|
||||
assertThrows(
|
||||
LeaseFinalizationValidationFailure.class,
|
||||
() ->
|
||||
service.finalizeLease(
|
||||
LANDLORD_ID,
|
||||
LEASE_ID,
|
||||
correctionRequest(
|
||||
new LeaseFinalization.InventoryCorrection(
|
||||
"correction-1", "line-1", "other-area", "Chaise", 0, "Absente")),
|
||||
upload(PDF),
|
||||
"subject-1"));
|
||||
assertThrows(
|
||||
LeaseFinalizationValidationFailure.class,
|
||||
() ->
|
||||
service.finalizeLease(
|
||||
LANDLORD_ID,
|
||||
LEASE_ID,
|
||||
correctionRequest(
|
||||
new LeaseFinalization.InventoryCorrection(
|
||||
"correction-1", "other-line", "area-1", "Chaise", 0, "Absente")),
|
||||
upload(PDF),
|
||||
"subject-1"));
|
||||
LeaseFinalizationValidationFailure privateAreaFailure =
|
||||
assertThrows(
|
||||
LeaseFinalizationValidationFailure.class,
|
||||
() ->
|
||||
service.finalizeLease(
|
||||
LANDLORD_ID,
|
||||
LEASE_ID,
|
||||
correctionRequest(
|
||||
new LeaseFinalization.InventoryCorrection(
|
||||
"correction-1", "line-1", "other-area", "Chaise", 0, "Absente")),
|
||||
upload(PDF),
|
||||
"subject-1"));
|
||||
assertEquals(
|
||||
LeaseFinalizationValidationFailure.Rule.UNKNOWN_CORRECTION_PRIVATE_AREA,
|
||||
privateAreaFailure.rule());
|
||||
assertEquals(0, privateAreaFailure.correctionIndex());
|
||||
LeaseFinalizationValidationFailure inventoryLineFailure =
|
||||
assertThrows(
|
||||
LeaseFinalizationValidationFailure.class,
|
||||
() ->
|
||||
service.finalizeLease(
|
||||
LANDLORD_ID,
|
||||
LEASE_ID,
|
||||
correctionRequest(
|
||||
new LeaseFinalization.InventoryCorrection(
|
||||
"correction-1", "other-line", "area-1", "Chaise", 0, "Absente")),
|
||||
upload(PDF),
|
||||
"subject-1"));
|
||||
assertEquals(
|
||||
LeaseFinalizationValidationFailure.Rule.UNKNOWN_CORRECTION_INVENTORY_LINE,
|
||||
inventoryLineFailure.rule());
|
||||
assertEquals(0, inventoryLineFailure.correctionIndex());
|
||||
|
||||
LeaseFinalizationService.Result result =
|
||||
service.finalizeLease(
|
||||
|
||||
Reference in new issue
Block a user