diff --git a/dossierfacile-common-library/pom.xml b/dossierfacile-common-library/pom.xml
index 7d516f40e..c71216fd1 100644
--- a/dossierfacile-common-library/pom.xml
+++ b/dossierfacile-common-library/pom.xml
@@ -41,7 +41,7 @@
1.4.2.Final
- 19
+ 21
${java.version}
${java.version}
1.18.30
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/converter/ParsedFileConverter.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/converter/ParsedFileConverter.java
index 3776f1e30..ccdb7eac4 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/converter/ParsedFileConverter.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/converter/ParsedFileConverter.java
@@ -4,6 +4,7 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import fr.dossierfacile.common.entity.ocr.ParsedFile;
import fr.dossierfacile.common.enums.ParsedFileClassification;
+import fr.dossierfacile.common.utils.MapperUtil;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@@ -11,7 +12,7 @@
@Converter
public class ParsedFileConverter implements AttributeConverter {
- private static final ObjectMapper objectMapper = new ObjectMapper();
+ private static final ObjectMapper objectMapper = MapperUtil.newObjectMapper();
@Override
public String convertToDatabaseColumn(ParsedFile object) {
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/DocumentRule.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/DocumentRule.java
index b66605145..ec609bd27 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/DocumentRule.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/DocumentRule.java
@@ -7,18 +7,23 @@
@AllArgsConstructor
public enum DocumentRule {
- R_TAX_PARSE(Level.WARN,"La lecture des informations de l'avis a échoué"),
- R_TAX_FAKE(Level.CRITICAL,"Les informations sont floues ou corrompues"),
- R_TAX_N1( Level.CRITICAL, "L'avis d'imposition sur les revenus N-1 doit etre fournis"),
- R_TAX_LEAF( Level.CRITICAL, "Veuillez fournir les feuillets des avis"),
- R_TAX_ALL_LEAF( Level.WARN, "Veuillez fournir tous les feuillets des avis"),// feuillet 1 founi
- R_TAX_N3( Level.CRITICAL, "Les avis d'imposition antérieur à N-3 ne sont pas autorisé"),
- R_TAX_NAMES( Level.CRITICAL, "Les noms et prénoms ne correspondent pas"),
-
- R_GUARANTEE_NAMES( Level.CRITICAL, "Les noms et prénoms ne correspondent pas"),
- R_GUARANTEE_EXIRED( Level.CRITICAL, "La garantie a expiré");
-
- public enum Level{
+ R_TAX_PARSE(Level.WARN, "La lecture des informations de l'avis a échoué"),
+ R_TAX_FAKE(Level.CRITICAL, "Les informations sont floues ou corrompues"),
+ R_TAX_N1(Level.CRITICAL, "L'avis d'imposition sur les revenus N-1 doit etre fournis"),
+ R_TAX_LEAF(Level.CRITICAL, "Veuillez fournir les feuillets des avis"),
+ R_TAX_ALL_LEAF(Level.WARN, "Veuillez fournir tous les feuillets des avis"),// feuillet 1 founi
+ R_TAX_N3(Level.CRITICAL, "Les avis d'imposition antérieur à N-3 ne sont pas autorisé"),
+ R_TAX_NAMES(Level.CRITICAL, "Les noms et prénoms ne correspondent pas"),
+
+ R_GUARANTEE_NAMES(Level.CRITICAL, "Les noms et prénoms ne correspondent pas"),
+ R_GUARANTEE_EXPIRED(Level.CRITICAL, "La garantie a expiré"),
+
+ R_PAYSLIP_QRCHECK(Level.CRITICAL, "La lecture des informations et du QR Code ne correspondent pas"),
+ R_PAYSLIP_NAME(Level.CRITICAL, "Nom/prénoms ne correspondent pas"),
+ R_PAYSLIP_MONTHS(Level.CRITICAL, "Les 3 derniers bulletins de salaire doivent être transmis"),
+ R_PAYSLIP_AMOUNT_MISMATCHES(Level.CRITICAL, "Le montant specifié ne correspond pas au montant des bulletins");
+
+ public enum Level {
CRITICAL, WARN
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/Tenant.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/Tenant.java
index 1da4cdc46..6dff73656 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/Tenant.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/Tenant.java
@@ -183,8 +183,8 @@ public boolean isAllCategories() {
if (guarantor.getDocuments() == null || guarantor.getDocuments().isEmpty()) {
return false;
} else if (guarantor.getTypeGuarantor() == TypeGuarantor.ORGANISM) {
- // Must have exactly one Document of type IDENTIFICATION
- if (guarantor.getDocuments().size() != 1 || !guarantor.getDocuments().get(0).getDocumentCategory().equals(DocumentCategory.IDENTIFICATION)) {
+ // Must have exactly one Document of type GUARANTEE_PROVIDER_CERTIFICATE
+ if (guarantor.getDocuments().size() != 1 || !guarantor.getDocuments().get(0).getDocumentCategory().equals(DocumentCategory.GUARANTEE_PROVIDER_CERTIFICATE)) {
return false;
}
} else if (guarantor.getTypeGuarantor() == TypeGuarantor.NATURAL_PERSON) {
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/ParsedFile.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/ParsedFile.java
index 4308fd04d..e700a9e79 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/ParsedFile.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/ParsedFile.java
@@ -2,6 +2,8 @@
import fr.dossierfacile.common.enums.ParsedFileClassification;
-public interface ParsedFile {
+import java.io.Serializable;
+
+public interface ParsedFile extends Serializable {
ParsedFileClassification getClassification();
}
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/PublicPayslipFile.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/PublicPayslipFile.java
new file mode 100644
index 000000000..119264a9c
--- /dev/null
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/PublicPayslipFile.java
@@ -0,0 +1,24 @@
+package fr.dossierfacile.common.entity.ocr;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import fr.dossierfacile.common.enums.ParsedFileClassification;
+import fr.dossierfacile.common.enums.ParsedStatus;
+import lombok.*;
+
+import java.time.YearMonth;
+
+@Data
+@Builder
+@ToString
+@NoArgsConstructor
+@AllArgsConstructor
+public class PublicPayslipFile implements ParsedFile {
+ @Builder.Default
+ ParsedFileClassification classification = ParsedFileClassification.PUBLIC_PAYSLIP;
+ ParsedStatus status;
+ String fullname;
+ @JsonFormat(pattern = "yyyy-MM")
+ YearMonth month;
+ double netTaxableIncome;
+ double cumulativeNetTaxableIncome;
+}
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeLeaf.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeLeaf.java
index 4f96789b1..a0d6968b0 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeLeaf.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeLeaf.java
@@ -14,7 +14,7 @@
@ToString
@NoArgsConstructor
@AllArgsConstructor
-public class TaxIncomeLeaf implements ParsedFile, Serializable {
+public class TaxIncomeLeaf implements ParsedFile {
@Builder.Default
ParsedFileClassification classification = ParsedFileClassification.TAX_INCOME_LEAF;
String numeroFiscal; // declarant1
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeMainFile.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeMainFile.java
index 8c751131b..22c68fbd3 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeMainFile.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/entity/ocr/TaxIncomeMainFile.java
@@ -16,7 +16,7 @@
@ToString
@NoArgsConstructor
@AllArgsConstructor
-public class TaxIncomeMainFile implements ParsedFile, Serializable {
+public class TaxIncomeMainFile implements ParsedFile {
@Builder.Default
ParsedFileClassification classification = ParsedFileClassification.TAX_INCOME;
String declarant1NumFiscal;
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentCategory.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentCategory.java
index 4648820ba..67ded1312 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentCategory.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentCategory.java
@@ -4,12 +4,16 @@
@Getter
public enum DocumentCategory {
+
IDENTIFICATION("tenant.profile.link1.v2"),
RESIDENCY("tenant.profile.link2.v2"),
PROFESSIONAL("tenant.profile.link3.v2"),
FINANCIAL("tenant.profile.link5.v2"),
TAX("tenant.profile.link4.v2"),
+
IDENTIFICATION_LEGAL_PERSON("tenant.profile.link1.v2"),
+ GUARANTEE_PROVIDER_CERTIFICATE("tenant.profile.link9.v2"),
+
NULL("");
String label;
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentSubCategory.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentSubCategory.java
index 0c4f61a6c..0ef65efbd 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentSubCategory.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/DocumentSubCategory.java
@@ -14,8 +14,8 @@ public enum DocumentSubCategory {
FRENCH_PASSPORT("document_sub_category.FRENCH_PASSPORT"),
FRENCH_RESIDENCE_PERMIT("document_sub_category.FRENCH_RESIDENCE_PERMIT"),
DRIVERS_LICENSE("document_sub_category.DRIVERS_LICENSE"),
+ FRANCE_IDENTITE("document_sub_category.FRANCE_IDENTITE"),
OTHER_IDENTIFICATION("document_sub_category.OTHER_IDENTIFICATION"),
- CERTIFICATE_VISA("document_sub_category.CERTIFICATE_VISA"),
// Residency
TENANT("document_sub_category.TENANT"),
@@ -58,9 +58,14 @@ public enum DocumentSubCategory {
LESS_THAN_YEAR("document_sub_category.LESS_THAN_YEAR"),
OTHER_TAX("document_sub_category.OTHER_TAX"),
+ // Guarantor
+ CERTIFICATE_VISA("document_sub_category.CERTIFICATE_VISA"),
+ VISALE("document_sub_category.VISALE"),
+ OTHER_GUARANTEE("document_sub_category.OTHER_GUARANTEE"),
LEGAL_PERSON("document_sub_category.LEGAL_PERSON"),
- UNDEFINED("document_sub_category.UNDEFINED"),
- OTHER_PROFESSION_GUARANTOR("document_sub_category.OTHER_PROFESSION_GUARANTOR");
+ OTHER_PROFESSION_GUARANTOR("document_sub_category.OTHER_PROFESSION_GUARANTOR"),
+
+ UNDEFINED("document_sub_category.UNDEFINED");
final String label;
@@ -74,14 +79,4 @@ public static List alphabeticallySortedValues() {
return values;
}
- public DocumentSubCategory getOnlyOldCategories() {
- return switch (this) {
- case INTERMITTENT, STAY_AT_HOME_PARENT, NO_ACTIVITY, ARTIST -> OTHER;
- case DRIVERS_LICENSE -> OTHER_IDENTIFICATION;
- case SHORT_TERM_RENTAL -> TENANT;
- case GUEST_ORGANISM -> GUEST;
- default -> this;
- };
- }
-
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/ParsedFileClassification.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/ParsedFileClassification.java
index fa390578a..e37eaec01 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/ParsedFileClassification.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/enums/ParsedFileClassification.java
@@ -1,9 +1,6 @@
package fr.dossierfacile.common.enums;
-import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile;
-import fr.dossierfacile.common.entity.ocr.ParsedFile;
-import fr.dossierfacile.common.entity.ocr.TaxIncomeLeaf;
-import fr.dossierfacile.common.entity.ocr.TaxIncomeMainFile;
+import fr.dossierfacile.common.entity.ocr.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
@@ -12,7 +9,8 @@
public enum ParsedFileClassification {
TAX_INCOME(TaxIncomeMainFile.class),
TAX_INCOME_LEAF(TaxIncomeLeaf.class),
- GUARANTEE_PROVIDER(GuaranteeProviderFile.class);
+ GUARANTEE_PROVIDER(GuaranteeProviderFile.class),
+ PUBLIC_PAYSLIP(PublicPayslipFile.class),;
Class extends ParsedFile> classificationClass;
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/exceptions/OldKeyException.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/exceptions/UnsupportedKeyException.java
similarity index 65%
rename from dossierfacile-common-library/src/main/java/fr/dossierfacile/common/exceptions/OldKeyException.java
rename to dossierfacile-common-library/src/main/java/fr/dossierfacile/common/exceptions/UnsupportedKeyException.java
index 31d63b9f7..3aef6787d 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/exceptions/OldKeyException.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/exceptions/UnsupportedKeyException.java
@@ -4,9 +4,9 @@
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(HttpStatus.BAD_REQUEST)
-public class OldKeyException extends RuntimeException {
+public class UnsupportedKeyException extends RuntimeException {
- public OldKeyException(String message) {
+ public UnsupportedKeyException(String message) {
super(message);
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationFullMapper.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationFullMapper.java
index aa228d677..fd298d045 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationFullMapper.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationFullMapper.java
@@ -13,6 +13,7 @@
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@@ -24,13 +25,19 @@ public abstract class ApplicationFullMapper implements ApartmentSharingMapper {
@Value("${application.domain:default}")
protected String domain;
+ protected CategoriesMapper categoriesMapper;
+
+ @Autowired
+ public void setCategoriesMapper(CategoriesMapper categoriesMapper) {
+ this.categoriesMapper = categoriesMapper;
+ }
+
public abstract ApplicationModel toApplicationModel(ApartmentSharing apartmentSharing);
public abstract ApplicationModel toApplicationModel(ApartmentSharing apartmentSharing, @Context UserApi userApi);
@Mapping(target = "name", expression = "java((document.getWatermarkFile() != null )? domain + \"/\" + PATH + \"/\" + document.getName() : null)")
- @Mapping(target = "subCategory", source = "documentSubCategory")
- @HideNewSubCategories
+ @MapDocumentCategories
@Mapping(target = "authenticityStatus", expression = "java(fr.dossierfacile.common.entity.AuthenticityStatus.isAuthentic(document))")
public abstract DocumentModel toDocumentModel(Document document);
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationLightMapper.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationLightMapper.java
index c3a607fb9..7ad071b7e 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationLightMapper.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/ApplicationLightMapper.java
@@ -6,17 +6,24 @@
import fr.dossierfacile.common.model.apartment_sharing.DocumentModel;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Mapper(componentModel = "spring")
public abstract class ApplicationLightMapper implements ApartmentSharingMapper {
+ protected CategoriesMapper categoriesMapper;
+
+ @Autowired
+ public void setCategoriesMapper(CategoriesMapper categoriesMapper) {
+ this.categoriesMapper = categoriesMapper;
+ }
+
public abstract ApplicationModel toApplicationModel(ApartmentSharing apartmentSharing);
@Mapping(target = "name", ignore = true)
- @Mapping(target = "subCategory", source = "documentSubCategory")
- @HideNewSubCategories
+ @MapDocumentCategories
@Mapping(target = "authenticityStatus", expression = "java(fr.dossierfacile.common.entity.AuthenticityStatus.isAuthentic(document))")
public abstract DocumentModel documentToDocumentModel(Document document);
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/CategoriesMapper.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/CategoriesMapper.java
new file mode 100644
index 000000000..ae7d1cdbf
--- /dev/null
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/CategoriesMapper.java
@@ -0,0 +1,12 @@
+package fr.dossierfacile.common.mapper;
+
+import fr.dossierfacile.common.enums.DocumentCategory;
+import fr.dossierfacile.common.enums.DocumentSubCategory;
+
+public interface CategoriesMapper {
+
+ DocumentCategory mapCategory(DocumentCategory category);
+
+ DocumentSubCategory mapSubCategory(DocumentSubCategory subCategory);
+
+}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/DeletedTenantCommonMapper.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/DeletedTenantCommonMapper.java
index a19b91833..3c9034d55 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/DeletedTenantCommonMapper.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/DeletedTenantCommonMapper.java
@@ -20,9 +20,9 @@ public interface DeletedTenantCommonMapper {
@Mapping(target = "hashedFirstname", source = "firstName", qualifiedByName = "firstnameToHashedFirstname")
@Mapping(target = "hashedPreferredName", source = "preferredName", qualifiedByName = "preferredNameToHashedPreferredName")
public abstract DeletedTenantModel toDeletedTenantModel(Tenant tenant);
+
@Mapping(target = "name", expression = "java((document.getWatermarkFile() != null )? document.getWatermarkFile().getName() : null)")
@Mapping(target = "subCategory", source = "documentSubCategory")
- @HideNewSubCategories
public abstract DocumentModel toDocumentModel(Document document);
@Named("emailToHashedEmail")
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/DummyCategoriesMapper.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/DummyCategoriesMapper.java
new file mode 100644
index 000000000..24877881d
--- /dev/null
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/DummyCategoriesMapper.java
@@ -0,0 +1,20 @@
+package fr.dossierfacile.common.mapper;
+
+import fr.dossierfacile.common.enums.DocumentCategory;
+import fr.dossierfacile.common.enums.DocumentSubCategory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DummyCategoriesMapper implements CategoriesMapper {
+
+ @Override
+ public DocumentCategory mapCategory(DocumentCategory category) {
+ return category;
+ }
+
+ @Override
+ public DocumentSubCategory mapSubCategory(DocumentSubCategory subCategory) {
+ return subCategory;
+ }
+
+}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/HideNewSubCategories.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/MapDocumentCategories.java
similarity index 51%
rename from dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/HideNewSubCategories.java
rename to dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/MapDocumentCategories.java
index fdb3d1a0f..65f26d277 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/HideNewSubCategories.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/MapDocumentCategories.java
@@ -7,11 +7,9 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-/**
- * TODO remove this behavior 6 months from now
- */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
-@Mapping(target = "documentSubCategory", expression = "java(document.getDocumentSubCategory().getOnlyOldCategories())")
-public @interface HideNewSubCategories {
+@Mapping(target = "documentCategory", expression = "java(categoriesMapper.mapCategory(document.getDocumentCategory()))")
+@Mapping(target = "subCategory", expression = "java(categoriesMapper.mapSubCategory(document.getDocumentSubCategory()))")
+public @interface MapDocumentCategories {
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/TenantCommonMapper.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/TenantCommonMapper.java
deleted file mode 100644
index af5a6ee5c..000000000
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/mapper/TenantCommonMapper.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package fr.dossierfacile.common.mapper;
-
-import fr.dossierfacile.common.entity.Document;
-import fr.dossierfacile.common.entity.Tenant;
-import fr.dossierfacile.common.model.apartment_sharing.DocumentModel;
-import fr.dossierfacile.common.model.apartment_sharing.TenantModel;
-import org.mapstruct.Mapper;
-import org.mapstruct.Mapping;
-import org.springframework.stereotype.Component;
-
-@Component
-@Mapper(componentModel = "spring")
-public abstract class TenantCommonMapper {
- public abstract TenantModel toTenantModel(Tenant tenant);
- @Mapping(target = "name", expression = "java((document.getWatermarkFile() != null )? document.getWatermarkFile().getName() : null)")
- @Mapping(target = "subCategory", source = "documentSubCategory")
- @HideNewSubCategories
- public abstract DocumentModel toDocumentModel(Document document);
-}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/model/apartment_sharing/DocumentModel.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/model/apartment_sharing/DocumentModel.java
index 919c505fe..63ab171b3 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/model/apartment_sharing/DocumentModel.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/model/apartment_sharing/DocumentModel.java
@@ -18,7 +18,6 @@
public class DocumentModel {
private Long id;
private DocumentCategory documentCategory;
- private DocumentSubCategory documentSubCategory; // TODO delete this field 6 months from now
private DocumentSubCategory subCategory;
private String customText;
private Integer monthlySum;
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/repository/TenantCommonRepository.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/repository/TenantCommonRepository.java
index 37da0b878..53a6900d4 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/repository/TenantCommonRepository.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/repository/TenantCommonRepository.java
@@ -98,9 +98,6 @@ default Tenant findNextApplicationByProfessional(LocalDateTime localDateTime, Li
)
List listIdTenantsAccountCompletedPendingToSendCallBack(@Param("id") Long userApiId, @Param("since") LocalDateTime lastUpdateSince);
- @Query("SELECT t FROM Tenant t ORDER BY t.id DESC")
- Page findAllTenants(Pageable page);
-
@Query(value = """
SELECT COUNT(DISTINCT t.id)
FROM tenant t
@@ -275,12 +272,4 @@ List findTenantUpdateByLastUpdateAndPartner(@Param("lastUpdateFrom
)
List findTenantUpdateByCreationDateAndPartner(@Param("creationDateFrom") LocalDateTime from, @Param("partnerId") Long id, @Param("limit") Long limit);
- @Query("""
- SELECT DISTINCT t
- FROM Tenant t
- JOIN Log l ON t.id = l.tenantId
- WHERE l.logType = 'ACCOUNT_VALIDATED' OR l.logType = 'ACCOUNT_DENIED'
- """
- )
- List findTenantsToExtract(Pageable pageable);
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/CallbackLogServiceImpl.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/CallbackLogServiceImpl.java
index eecab15c2..9b81bbed3 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/CallbackLogServiceImpl.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/CallbackLogServiceImpl.java
@@ -1,13 +1,13 @@
package fr.dossierfacile.common.service;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import fr.dossierfacile.common.entity.CallbackLog;
import fr.dossierfacile.common.entity.Tenant;
import fr.dossierfacile.common.enums.TenantFileStatus;
import fr.dossierfacile.common.model.apartment_sharing.ApplicationModel;
import fr.dossierfacile.common.repository.CallbackLogRepository;
import fr.dossierfacile.common.service.interfaces.CallbackLogService;
+import fr.dossierfacile.common.utils.MapperUtil;
import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
@@ -18,13 +18,12 @@
@Slf4j
public class CallbackLogServiceImpl implements CallbackLogService {
private final CallbackLogRepository callbackLogRepository;
+ private static final ObjectMapper objectMapper = MapperUtil.newObjectMapper();
@SneakyThrows
@Override
public void createCallbackLogForPartnerModel(Tenant tenant, Long partnerId, TenantFileStatus tenantFileStatus, ApplicationModel applicationModel) {
- ObjectMapper objectMapper = new ObjectMapper();
- objectMapper.registerModule(new JavaTimeModule());
String jsonContent = objectMapper.writeValueAsString(applicationModel);
- callbackLogRepository.save(new CallbackLog(tenant.getId(),partnerId,tenantFileStatus, jsonContent));
+ callbackLogRepository.save(new CallbackLog(tenant.getId(), partnerId, tenantFileStatus, jsonContent));
}
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/LogServiceImpl.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/LogServiceImpl.java
index 0ccd227bc..113dd688c 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/LogServiceImpl.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/LogServiceImpl.java
@@ -16,6 +16,7 @@
import fr.dossierfacile.common.model.log.EditionType;
import fr.dossierfacile.common.repository.LogRepository;
import fr.dossierfacile.common.service.interfaces.LogService;
+import fr.dossierfacile.common.utils.MapperUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -30,11 +31,7 @@ public class LogServiceImpl implements LogService {
private final LogRepository repository;
private final DeletedTenantCommonMapper deletedTenantCommonMapper;
- private ObjectMapper objectMapper = new ObjectMapper();
-
- {
- objectMapper.registerModule(new JavaTimeModule());
- }
+ private final ObjectMapper objectMapper = MapperUtil.newObjectMapper();
private void saveLog(Log log) {
repository.save(log);
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/MockStorage.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/MockStorage.java
index 65f4de2e1..e0602738b 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/MockStorage.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/MockStorage.java
@@ -1,8 +1,10 @@
package fr.dossierfacile.common.service;
+import fr.dossierfacile.common.entity.EncryptionKey;
import fr.dossierfacile.common.entity.ObjectStorageProvider;
import fr.dossierfacile.common.entity.StorageFile;
import fr.dossierfacile.common.exceptions.RetryableOperationException;
+import fr.dossierfacile.common.exceptions.UnsupportedKeyException;
import fr.dossierfacile.common.repository.StorageFileRepository;
import fr.dossierfacile.common.service.interfaces.FileStorageService;
import lombok.extern.slf4j.Slf4j;
@@ -62,9 +64,12 @@ public void delete(StorageFile storageFile) {
}
}
- private InputStream download(String filename, Key key) throws IOException {
+ private InputStream download(String filename, EncryptionKey key) throws IOException {
InputStream in = Files.newInputStream(Path.of(filePath + filename));
if (key != null) {
+ if (key.getVersion() != 1){
+ throw new UnsupportedKeyException("Unsupported key version " + key.getVersion());
+ }
try {
byte[] iv = DigestUtils.md5(filename);
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OutscaleFileStorageServiceImpl.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OutscaleFileStorageServiceImpl.java
index 94ae4f7d2..d6ae0b525 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OutscaleFileStorageServiceImpl.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OutscaleFileStorageServiceImpl.java
@@ -2,19 +2,13 @@
import com.amazonaws.AmazonServiceException;
import com.amazonaws.services.s3.AmazonS3;
-import com.amazonaws.services.s3.model.GetObjectRequest;
-import com.amazonaws.services.s3.model.ListObjectsRequest;
-import com.amazonaws.services.s3.model.ObjectMetadata;
-import com.amazonaws.services.s3.model.PutObjectResult;
-import com.amazonaws.services.s3.model.S3Object;
-import com.amazonaws.services.s3.model.S3ObjectSummary;
+import com.amazonaws.services.s3.model.*;
import fr.dossierfacile.common.config.ThreeDSOutscaleConfig;
import fr.dossierfacile.common.entity.EncryptionKey;
import fr.dossierfacile.common.entity.ObjectStorageProvider;
-import fr.dossierfacile.common.exceptions.OldKeyException;
import fr.dossierfacile.common.exceptions.RetryableOperationException;
+import fr.dossierfacile.common.exceptions.UnsupportedKeyException;
import fr.dossierfacile.common.service.interfaces.FileStorageProviderService;
-import io.sentry.Sentry;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
@@ -47,17 +41,16 @@ public class OutscaleFileStorageServiceImpl implements FileStorageProviderServic
@Value("${threeds.s3.bucket:dossierfacile-preprod}")
private String bucket;
- private static InputStream cipherInputStream(String path, Key key, InputStream in) throws IOException {
+ private static InputStream cipherInputStream(String path, EncryptionKey key, InputStream in) throws IOException {
try {
Cipher aes;
- if (key instanceof EncryptionKey && ((EncryptionKey) key).getVersion() == 0) {
- Sentry.captureMessage("Wrong key version used");
- throw new OldKeyException("Wrong key version used");
- } else {
+ if (key.getVersion() == 1) {
byte[] iv = DigestUtils.sha256(path); // arbitrary set the filename to build IV
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
aes = Cipher.getInstance("AES/GCM/NoPadding");
aes.init(Cipher.DECRYPT_MODE, key, gcmParamSpec);
+ } else {
+ throw new UnsupportedKeyException("Unsupported Key version " + key.getVersion());
}
in = new CipherInputStream(in, aes);
} catch (Exception e) {
@@ -79,7 +72,7 @@ public void delete(String name) {
}
@Override
- public InputStream download(String path, Key key) throws IOException {
+ public InputStream download(String path, EncryptionKey key) throws IOException {
AmazonS3 s3client = threeDSOutscaleConfig.getAmazonS3Client();
S3Object fullObject;
try {
@@ -95,8 +88,11 @@ public InputStream download(String path, Key key) throws IOException {
}
@Override
- public void upload(String name, InputStream inputStream, Key key, String contentType) throws RetryableOperationException, IOException {
+ public void upload(String name, InputStream inputStream, EncryptionKey key, String contentType) throws RetryableOperationException, IOException {
if (key != null) {
+ if (key.getVersion() != 1){
+ throw new UnsupportedKeyException("Unsupported key version " + key.getVersion());
+ }
try {
byte[] iv = DigestUtils.sha256(name);
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OvhFileStorageServiceImpl.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OvhFileStorageServiceImpl.java
index 7801bd39a..c056a3bf1 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OvhFileStorageServiceImpl.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/OvhFileStorageServiceImpl.java
@@ -4,6 +4,7 @@
import fr.dossierfacile.common.entity.ObjectStorageProvider;
import fr.dossierfacile.common.exceptions.OvhConnectionFailedException;
import fr.dossierfacile.common.exceptions.RetryableOperationException;
+import fr.dossierfacile.common.exceptions.UnsupportedKeyException;
import fr.dossierfacile.common.service.interfaces.FileStorageProviderService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
@@ -101,7 +102,7 @@ public void delete(String path) {
}
@Override
- public InputStream download(String path, Key key) throws IOException {
+ public InputStream download(String path, EncryptionKey key) throws IOException {
SwiftObject object;
try {
object = getClient().objectStorage().objects().get(ovhContainerName, path);
@@ -116,14 +117,13 @@ public InputStream download(String path, Key key) throws IOException {
if (key != null) {
try {
Cipher aes;
- if (key instanceof EncryptionKey && ((EncryptionKey) key).getVersion() == 0) {
- aes = Cipher.getInstance("AES/ECB/PKCS5Padding");
- aes.init(Cipher.DECRYPT_MODE, key);
- } else {
+ if (key.getVersion() == 1) {
byte[] iv = DigestUtils.md5(path); // arbitrary set the filename to build IV
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
aes = Cipher.getInstance("AES/GCM/NoPadding");
aes.init(Cipher.DECRYPT_MODE, key, gcmParamSpec);
+ } else {
+ throw new UnsupportedKeyException("Unsupported key version " + key.getVersion());
}
in = new CipherInputStream(in, aes);
} catch (Exception e) {
@@ -134,8 +134,11 @@ public InputStream download(String path, Key key) throws IOException {
}
@Override
- public void upload(String ovhPath, InputStream inputStream, Key key, String contentType) throws RetryableOperationException, IOException {
+ public void upload(String ovhPath, InputStream inputStream, EncryptionKey key, String contentType) throws RetryableOperationException, IOException {
if (key != null) {
+ if (key.getVersion() != 1){
+ throw new UnsupportedKeyException("Unsupported key version " + key.getVersion());
+ }
try {
byte[] iv = DigestUtils.md5(ovhPath);
GCMParameterSpec gcmParamSpec = new GCMParameterSpec(128, iv);
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/PartnerCallBackServiceImpl.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/PartnerCallBackServiceImpl.java
index b8d0de29f..de3d6afa4 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/PartnerCallBackServiceImpl.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/PartnerCallBackServiceImpl.java
@@ -98,7 +98,7 @@ public void sendCallBack(Tenant tenant, WebhookDTO webhookDTO) {
log.warn("UserApi call has not effect for " + userApi.getName());
return;
}
- if (userApi.getVersion() != 2) {
+ if (userApi.getVersion() < 2) {
log.error("Unable to send callback to tenant " + tenant.getId() + " due to userApi version" + userApi.getVersion());
return;
}
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/interfaces/FileStorageProviderService.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/interfaces/FileStorageProviderService.java
index 1a83bcd85..7d5eacbda 100644
--- a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/interfaces/FileStorageProviderService.java
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/service/interfaces/FileStorageProviderService.java
@@ -1,5 +1,6 @@
package fr.dossierfacile.common.service.interfaces;
+import fr.dossierfacile.common.entity.EncryptionKey;
import fr.dossierfacile.common.entity.ObjectStorageProvider;
import fr.dossierfacile.common.exceptions.RetryableOperationException;
@@ -15,9 +16,9 @@ public interface FileStorageProviderService {
void delete(String name);
- InputStream download(String path, Key key) throws IOException;
+ InputStream download(String path, EncryptionKey key) throws IOException;
- void upload(String ovhPath, InputStream inputStream, Key key, String contentType) throws RetryableOperationException, IOException;
+ void upload(String ovhPath, InputStream inputStream, EncryptionKey key, String contentType) throws RetryableOperationException, IOException;
List listObjectNames(@Nullable String marker, int maxObjects);
diff --git a/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/utils/MapperUtil.java b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/utils/MapperUtil.java
new file mode 100644
index 000000000..226f0d3f5
--- /dev/null
+++ b/dossierfacile-common-library/src/main/java/fr/dossierfacile/common/utils/MapperUtil.java
@@ -0,0 +1,14 @@
+package fr.dossierfacile.common.utils;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
+import lombok.experimental.UtilityClass;
+
+@UtilityClass
+public class MapperUtil {
+ public static ObjectMapper newObjectMapper() {
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.registerModule(new JavaTimeModule());
+ return objectMapper;
+ }
+}
diff --git a/dossierfacile-common-library/src/main/resources/db/changelog/databaseChangeLog.xml b/dossierfacile-common-library/src/main/resources/db/changelog/databaseChangeLog.xml
index de97525c3..a3028066d 100644
--- a/dossierfacile-common-library/src/main/resources/db/changelog/databaseChangeLog.xml
+++ b/dossierfacile-common-library/src/main/resources/db/changelog/databaseChangeLog.xml
@@ -137,5 +137,8 @@
+
+
+
diff --git a/dossierfacile-common-library/src/main/resources/db/migration/202401260000-rename-sub-category-for-guarantor-organism.xml b/dossierfacile-common-library/src/main/resources/db/migration/202401260000-rename-sub-category-for-guarantor-organism.xml
new file mode 100644
index 000000000..ac8e0e14c
--- /dev/null
+++ b/dossierfacile-common-library/src/main/resources/db/migration/202401260000-rename-sub-category-for-guarantor-organism.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/main/resources/db/migration/202401260001-rename-category-for-guarantor-organism.xml b/dossierfacile-common-library/src/main/resources/db/migration/202401260001-rename-category-for-guarantor-organism.xml
new file mode 100644
index 000000000..766046f30
--- /dev/null
+++ b/dossierfacile-common-library/src/main/resources/db/migration/202401260001-rename-category-for-guarantor-organism.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/main/resources/db/migration/202401300000-change-denied-options-sub-category.xml b/dossierfacile-common-library/src/main/resources/db/migration/202401300000-change-denied-options-sub-category.xml
new file mode 100644
index 000000000..7584a7096
--- /dev/null
+++ b/dossierfacile-common-library/src/main/resources/db/migration/202401300000-change-denied-options-sub-category.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationFullMapperTest.java b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationFullMapperTest.java
index 2a9cba91d..c806bc887 100644
--- a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationFullMapperTest.java
+++ b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationFullMapperTest.java
@@ -3,11 +3,13 @@
import fr.dossierfacile.common.entity.Document;
import fr.dossierfacile.common.model.apartment_sharing.DocumentModel;
-class ApplicationFullMapperTest implements DocumentMappingTest, AuthenticityStatusMappingTest {
+class ApplicationFullMapperTest implements AuthenticityStatusMappingTest {
@Override
public DocumentModel mapDocument(Document document) {
- return new ApplicationFullMapperImpl().toDocumentModel(document);
+ ApplicationFullMapperImpl mapper = new ApplicationFullMapperImpl();
+ mapper.setCategoriesMapper(new DummyCategoriesMapper());
+ return mapper.toDocumentModel(document);
}
}
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationLightMapperTest.java b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationLightMapperTest.java
index 30d26ca1e..d14d25c15 100644
--- a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationLightMapperTest.java
+++ b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/ApplicationLightMapperTest.java
@@ -3,11 +3,13 @@
import fr.dossierfacile.common.entity.Document;
import fr.dossierfacile.common.model.apartment_sharing.DocumentModel;
-class ApplicationLightMapperTest implements DocumentMappingTest, AuthenticityStatusMappingTest {
+class ApplicationLightMapperTest implements AuthenticityStatusMappingTest {
@Override
public DocumentModel mapDocument(Document document) {
- return new ApplicationLightMapperImpl().documentToDocumentModel(document);
+ ApplicationLightMapperImpl mapper = new ApplicationLightMapperImpl();
+ mapper.setCategoriesMapper(new DummyCategoriesMapper());
+ return mapper.documentToDocumentModel(document);
}
}
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/DocumentMappingTest.java b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/DocumentMappingTest.java
deleted file mode 100644
index 3d21ff44a..000000000
--- a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/DocumentMappingTest.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package fr.dossierfacile.common.mapper;
-
-import fr.dossierfacile.common.entity.Document;
-import fr.dossierfacile.common.enums.DocumentSubCategory;
-import fr.dossierfacile.common.model.apartment_sharing.DocumentModel;
-import org.junit.jupiter.api.DisplayName;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.params.ParameterizedTest;
-import org.junit.jupiter.params.provider.CsvSource;
-import org.junit.jupiter.params.provider.EnumSource;
-
-import static fr.dossierfacile.common.enums.DocumentSubCategory.DRIVERS_LICENSE;
-import static fr.dossierfacile.common.enums.DocumentSubCategory.OTHER;
-import static fr.dossierfacile.common.enums.DocumentSubCategory.OTHER_IDENTIFICATION;
-import static org.assertj.core.api.Assertions.assertThat;
-
-interface DocumentMappingTest {
-
- DocumentModel mapDocument(Document document);
-
- @ParameterizedTest
- @EnumSource(value = DocumentSubCategory.class,
- names = {"INTERMITTENT", "STAY_AT_HOME_PARENT", "NO_ACTIVITY", "ARTIST"})
- @DisplayName("New professional categories are replaced by 'OTHER' in field 'documentSubCategory'")
- default void should_replace_new_professional_categories_by_other(DocumentSubCategory subCategory) {
- DocumentModel documentModel = mapDocumentWithSubCategory(subCategory);
-
- assertThat(documentModel.getDocumentSubCategory()).isEqualTo(OTHER);
- }
-
- @ParameterizedTest
- @CsvSource({
- "GUEST_ORGANISM, GUEST",
- "SHORT_TERM_RENTAL, TENANT"
- })
- @DisplayName("New residency categories are replaced by existing ones in field 'documentSubCategory'")
- default void should_replace_new_residency_categories(DocumentSubCategory newSubCategory, DocumentSubCategory existingSubCategory) {
- DocumentModel documentModel = mapDocumentWithSubCategory(newSubCategory);
-
- assertThat(documentModel.getDocumentSubCategory()).isEqualTo(existingSubCategory);
- }
-
- @Test
- @DisplayName("Driver's license is replaced by 'OTHER_IDENTIFICATION' in field 'documentSubCategory'")
- default void should_replace_drivers_license_by_other() {
- DocumentModel documentModel = mapDocumentWithSubCategory(DRIVERS_LICENSE);
-
- assertThat(documentModel.getDocumentSubCategory()).isEqualTo(OTHER_IDENTIFICATION);
- }
-
- @ParameterizedTest
- @EnumSource(value = DocumentSubCategory.class)
- @DisplayName("All categories are mapped as is in field 'subCategory'")
- default void should_expose_all_categories(DocumentSubCategory subCategory) {
- DocumentModel documentModel = mapDocumentWithSubCategory(subCategory);
-
- assertThat(documentModel.getSubCategory()).isEqualTo(subCategory);
- }
-
- private DocumentModel mapDocumentWithSubCategory(DocumentSubCategory subCategory) {
- Document document = Document.builder()
- .documentSubCategory(subCategory)
- .build();
-
- return mapDocument(document);
- }
-
-}
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/TenantCommonMapperTest.java b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/TenantCommonMapperTest.java
deleted file mode 100644
index ab67f2749..000000000
--- a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/mapper/TenantCommonMapperTest.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package fr.dossierfacile.common.mapper;
-
-import fr.dossierfacile.common.entity.Document;
-import fr.dossierfacile.common.model.apartment_sharing.DocumentModel;
-
-class TenantCommonMapperTest implements DocumentMappingTest {
-
- @Override
- public DocumentModel mapDocument(Document document) {
- return new TenantCommonMapperImpl().toDocumentModel(document);
- }
-
-}
\ No newline at end of file
diff --git a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/LogServiceImplTest.java b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/LogServiceImplTest.java
index c023c8fc5..2a50a8b8f 100644
--- a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/LogServiceImplTest.java
+++ b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/LogServiceImplTest.java
@@ -27,7 +27,7 @@
class LogServiceImplTest {
private final LogRepository logRepository = mock(LogRepository.class);
- private final LogService logService = new LogServiceImpl(logRepository, null, new ObjectMapper());
+ private final LogService logService = new LogServiceImpl(logRepository, null);
@Test
void should_save_edition_log_for_tenant_document() {
diff --git a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/ThreeDSOutscaleFileStorageServiceImplTest.java b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/ThreeDSOutscaleFileStorageServiceImplTest.java
index 77fd92ed7..eb897598c 100644
--- a/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/ThreeDSOutscaleFileStorageServiceImplTest.java
+++ b/dossierfacile-common-library/src/test/java/fr/dossierfacile/common/service/ThreeDSOutscaleFileStorageServiceImplTest.java
@@ -28,7 +28,7 @@ void connect3DS() throws IOException, NoSuchAlgorithmException, RetryableOperati
InputStream fileInputStream = ThreeDSOutscaleFileStorageServiceImplTest.class.getClassLoader().getResourceAsStream("hello.txt");
KeyGenerator keygen = KeyGenerator.getInstance("AES");
SecretKey key = keygen.generateKey();
- Key encryptionKey = EncryptionKey.builder()
+ EncryptionKey encryptionKey = EncryptionKey.builder()
.algorithm(key.getAlgorithm())
.format(key.getFormat())
.encodedSecret(key.getEncoded())
diff --git a/dossierfacile-pdf-generator/pom.xml b/dossierfacile-pdf-generator/pom.xml
index 50f283d3e..d35e3a394 100644
--- a/dossierfacile-pdf-generator/pom.xml
+++ b/dossierfacile-pdf-generator/pom.xml
@@ -15,7 +15,7 @@
Generate tenant's PDF documents
- 19
+ 21
${java.version}
${java.version}
1.18.30
diff --git a/dossierfacile-pdf-generator/src/main/resources/messages.properties b/dossierfacile-pdf-generator/src/main/resources/messages.properties
index 1d3e6b22d..daa0e2fef 100644
--- a/dossierfacile-pdf-generator/src/main/resources/messages.properties
+++ b/dossierfacile-pdf-generator/src/main/resources/messages.properties
@@ -23,6 +23,7 @@ tenant.profile.link5.v2=Justificatif de ressources
tenant.profile.link6.v2=Attestation de garantie
tenant.profile.link7.v2=Identification de la personne morale
tenant.profile.link8.v2=Pièce d'identité du représentant de la personne morale
+tenant.profile.link9.v2=Certificat de caution
tenant.profile.certified=Revenu fiscal certifié
tenant.profile.help=(de 1 Ã 3 documents)
diff --git a/dossierfacile-process-file/pom.xml b/dossierfacile-process-file/pom.xml
index 96e5b5cfe..ed34300c0 100644
--- a/dossierfacile-process-file/pom.xml
+++ b/dossierfacile-process-file/pom.xml
@@ -17,7 +17,7 @@
ProcessFile for DossierFacile
- 19
+ 21
1.18.30
3.0.0
3.0.3
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/AnalyzeFile.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/AnalyzeFile.java
index 2ad7c162e..bcd3916a3 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/AnalyzeFile.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/AnalyzeFile.java
@@ -2,7 +2,7 @@
import fr.dossierfacile.process.file.repository.FileRepository;
import fr.dossierfacile.process.file.service.processors.BarCodeFileProcessor;
-import fr.dossierfacile.process.file.service.processors.OcrParserFileProcessor;
+import fr.dossierfacile.process.file.service.processors.FileParserProcessor;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@@ -15,13 +15,13 @@
@AllArgsConstructor
public class AnalyzeFile {
private final BarCodeFileProcessor barCodeFileProcessor;
- private final OcrParserFileProcessor ocrParserFileProcessor;
+ private final FileParserProcessor fileParserProcessor;
private final FileRepository fileRepository;
public void processFile(Long fileId) {
Optional.ofNullable(fileRepository.findById(fileId).orElse(null))
.filter(Objects::nonNull)
.map(barCodeFileProcessor::process)
- .map(ocrParserFileProcessor::process);
+ .map(fileParserProcessor::process);
}
}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationService.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationService.java
index b184f5741..053de9f58 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationService.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationService.java
@@ -13,6 +13,10 @@
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+import static fr.dossierfacile.common.enums.DocumentSubCategory.OTHER_GUARANTEE;
+import static fr.dossierfacile.common.enums.DocumentSubCategory.VISALE;
@Service
@RequiredArgsConstructor
@@ -20,8 +24,8 @@
public class GuaranteeProviderRulesValidationService implements RulesValidationService {
@Override
public boolean shouldBeApplied(Document document) {
- return document.getDocumentCategory() == DocumentCategory.IDENTIFICATION
- && document.getDocumentSubCategory() == DocumentSubCategory.CERTIFICATE_VISA
+ return document.getDocumentCategory() == DocumentCategory.GUARANTEE_PROVIDER_CERTIFICATE
+ && List.of(OTHER_GUARANTEE, VISALE).contains(document.getDocumentSubCategory())
&& !CollectionUtils.isEmpty(document.getFiles())
&& document.getFiles().stream().anyMatch((f) -> f.getParsedFileAnalysis() != null
&& f.getParsedFileAnalysis().getParsedFile() != null);
@@ -29,7 +33,7 @@ public boolean shouldBeApplied(Document document) {
private boolean checkNamesRule(Document document, GuaranteeProviderFile parsedFile) {
- Tenant tenant = document.getTenant();
+ Tenant tenant = document.getGuarantor().getTenant();
return parsedFile.getNames().stream().anyMatch(
(fullname) -> PersonNameComparator.bearlyEqualsTo(fullname.firstName(), tenant.getFirstName())
&& PersonNameComparator.bearlyEqualsTo(fullname.lastName(), tenant.getLastName()));
@@ -53,20 +57,20 @@ public DocumentAnalysisReport process(Document document, DocumentAnalysisReport
GuaranteeProviderFile parsedFile = (GuaranteeProviderFile) document.getFiles().get(0).getParsedFileAnalysis().getParsedFile();
if (parsedFile.getStatus() == ParsedStatus.INCOMPLETE) {
- log.error("Document was not correctly parsed :" + document.getTenant().getId());
+ log.error("Document was not correctly parsed :" + document.getGuarantor().getTenant().getId());
report.setAnalysisStatus(DocumentAnalysisStatus.UNDEFINED);
} else if (!checkNamesRule(document, parsedFile)) {
- log.error("Document names mismatches :" + document.getTenant().getId());
+ log.error("Document names mismatches :" + document.getGuarantor().getTenant().getId());
report.getBrokenRules().add(DocumentBrokenRule.builder()
.rule(DocumentRule.R_GUARANTEE_NAMES)
.message(DocumentRule.R_GUARANTEE_NAMES.getDefaultMessage())
.build());
report.setAnalysisStatus(DocumentAnalysisStatus.DENIED);
} else if (!checkValidityRule(parsedFile)) {
- log.error("Document is expired :" + document.getTenant().getId());
+ log.error("Document is expired :" + document.getGuarantor().getTenant().getId());
report.getBrokenRules().add(DocumentBrokenRule.builder()
- .rule(DocumentRule.R_GUARANTEE_EXIRED)
- .message(DocumentRule.R_GUARANTEE_EXIRED.getDefaultMessage())
+ .rule(DocumentRule.R_GUARANTEE_EXPIRED)
+ .message(DocumentRule.R_GUARANTEE_EXPIRED.getDefaultMessage())
.build());
report.setAnalysisStatus(DocumentAnalysisStatus.DENIED);
} else {
@@ -74,7 +78,7 @@ public DocumentAnalysisReport process(Document document, DocumentAnalysisReport
}
} catch (Exception e) {
- log.error("Error during the rules validation execution pocess");
+ log.error("Error during the rules validation execution pocess",e);
report.setAnalysisStatus(DocumentAnalysisStatus.UNDEFINED);
}
return report;
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/documentrules/PublicPayslipRulesValidationService.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/documentrules/PublicPayslipRulesValidationService.java
new file mode 100644
index 000000000..a77914106
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/documentrules/PublicPayslipRulesValidationService.java
@@ -0,0 +1,179 @@
+package fr.dossierfacile.process.file.service.documentrules;
+
+import fr.dossierfacile.common.entity.*;
+import fr.dossierfacile.common.entity.ocr.PublicPayslipFile;
+import fr.dossierfacile.common.enums.DocumentCategory;
+import fr.dossierfacile.common.enums.DocumentSubCategory;
+import fr.dossierfacile.common.enums.ParsedFileAnalysisStatus;
+import fr.dossierfacile.common.enums.ParsedFileClassification;
+import fr.dossierfacile.process.file.barcode.twoddoc.parsing.TwoDDocDataType;
+import fr.dossierfacile.process.file.util.PersonNameComparator;
+import fr.dossierfacile.process.file.util.TwoDDocUtil;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@Slf4j
+public class PublicPayslipRulesValidationService implements RulesValidationService {
+ @Override
+ public boolean shouldBeApplied(Document document) {
+ return document.getDocumentCategory() == DocumentCategory.FINANCIAL
+ && document.getDocumentSubCategory() == DocumentSubCategory.SALARY
+ && !CollectionUtils.isEmpty(document.getFiles())
+ && document.getFiles().stream().anyMatch((f) -> f.getParsedFileAnalysis() != null
+ && f.getParsedFileAnalysis().getParsedFile() != null
+ && f.getParsedFileAnalysis().getParsedFile().getClassification() == ParsedFileClassification.PUBLIC_PAYSLIP);
+ }
+
+ private PublicPayslipFile fromQR(BarCodeFileAnalysis barCodeFileAnalysis) {
+ Map dataWithLabel = (Map) barCodeFileAnalysis.getVerifiedData();
+ return PublicPayslipFile.builder()
+ .fullname(dataWithLabel.get(TwoDDocDataType.ID_10.getLabel()))
+ .month(YearMonth.from(TwoDDocUtil.getLocalDateFrom2DDocHexDate(dataWithLabel.get(TwoDDocDataType.ID_54.getLabel()))))
+ .netTaxableIncome(Double.parseDouble(dataWithLabel.get(TwoDDocDataType.ID_58.getLabel()).replace(" ", "").replace(',', '.')))
+ .cumulativeNetTaxableIncome(Double.parseDouble(dataWithLabel.get(TwoDDocDataType.ID_59.getLabel()).replace(" ", "").replace(',', '.')))
+ .build();
+ }
+
+ private boolean checkQRCode(Document document) {
+ for (File dfFile : document.getFiles()) {
+ ParsedFileAnalysis analysis = dfFile.getParsedFileAnalysis();
+ if (analysis == null || dfFile.getFileAnalysis() == null || analysis.getAnalysisStatus() == ParsedFileAnalysisStatus.FAILED) {
+ continue;
+ }
+ if (analysis.getClassification() == ParsedFileClassification.PUBLIC_PAYSLIP) {
+ PublicPayslipFile qrDocument = fromQR(dfFile.getFileAnalysis());
+ PublicPayslipFile parsedDocument = (PublicPayslipFile) analysis.getParsedFile();
+
+ if (qrDocument == null
+ || qrDocument.getFullname() == null
+ || qrDocument.getMonth() == null
+ || qrDocument.getCumulativeNetTaxableIncome() == 0
+ || !PersonNameComparator.equalsWithNormalization(qrDocument.getFullname(), parsedDocument.getFullname())
+ || !qrDocument.getMonth().equals(parsedDocument.getMonth())
+ || qrDocument.getNetTaxableIncome() != parsedDocument.getNetTaxableIncome()
+ || qrDocument.getCumulativeNetTaxableIncome() != parsedDocument.getCumulativeNetTaxableIncome()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private boolean checkNamesRule(Document document) {
+ Person documentOwner = Optional.ofNullable((Person) document.getTenant()).orElseGet(() -> document.getGuarantor());
+ for (File dfFile : document.getFiles()) {
+ ParsedFileAnalysis analysis = dfFile.getParsedFileAnalysis();
+ PublicPayslipFile parsedFile = (PublicPayslipFile) analysis.getParsedFile();
+
+ String fullname = parsedFile.getFullname().toUpperCase().replaceFirst("^(MR |MME |MLLE )", "");
+
+ if (!PersonNameComparator.bearlyEqualsTo(fullname, documentOwner.getLastName(), documentOwner.getFirstName())
+ && !PersonNameComparator.bearlyEqualsTo(fullname, documentOwner.getPreferredName(), documentOwner.getFirstName())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private List> getExpectedMonthsLists() {
+ LocalDate localDate = LocalDate.now();
+ YearMonth yearMonth = YearMonth.now();
+ return (localDate.getDayOfMonth() <= 15) ?
+ List.of(
+ List.of(yearMonth.minusMonths(1), yearMonth.minusMonths(2), yearMonth.minusMonths(3)),
+ List.of(yearMonth.minusMonths(2), yearMonth.minusMonths(3), yearMonth.minusMonths(4))) :
+ List.of(
+ List.of(yearMonth, yearMonth.minusMonths(1), yearMonth.minusMonths(2)),
+ List.of(yearMonth.minusMonths(1), yearMonth.minusMonths(2), yearMonth.minusMonths(3)));
+ }
+
+ private boolean checkMonthsValidityRule(Document document) {
+ List> expectedMonthsList = getExpectedMonthsLists();
+
+ List presentMonths = document.getFiles().stream()
+ .map(file -> ((PublicPayslipFile) file.getParsedFileAnalysis().getParsedFile()).getMonth())
+ .toList();
+
+ return expectedMonthsList.stream().anyMatch(
+ expectedMonths -> expectedMonths.stream().allMatch(month -> presentMonths.contains(month))
+ );
+ }
+
+ private boolean checkAmountValidityRule(Document document) {
+ List recentFiles = document.getFiles().stream()
+ .map(file -> (PublicPayslipFile) file.getParsedFileAnalysis().getParsedFile())
+ .sorted(Comparator.comparing(PublicPayslipFile::getMonth).reversed())
+ .limit(3)
+ .collect(Collectors.toList());
+
+ double monthlyAverage = recentFiles.stream()
+ .mapToDouble(PublicPayslipFile::getNetTaxableIncome)
+ .sum() / recentFiles.size();
+
+ // Check percentage difference
+ double diffPercentage = Math.abs((monthlyAverage - document.getMonthlySum()) / document.getMonthlySum());
+ return (diffPercentage <= 0.2);
+ }
+
+ @Override
+ public DocumentAnalysisReport process(Document document, DocumentAnalysisReport report) {
+
+ try {
+ if (CollectionUtils.isEmpty(document.getFiles()) || document.getFiles().stream()
+ .anyMatch(f -> f.getParsedFileAnalysis() == null || f.getParsedFileAnalysis().getParsedFile() == null)
+ ) {
+ report.setAnalysisStatus(DocumentAnalysisStatus.UNDEFINED);
+ return report;
+ }
+
+ if (!checkQRCode(document)) {
+ log.error("Document mismatch to QR CODE :" + document.getId());
+ report.getBrokenRules().add(DocumentBrokenRule.builder()
+ .rule(DocumentRule.R_PAYSLIP_QRCHECK)
+ .message(DocumentRule.R_PAYSLIP_QRCHECK.getDefaultMessage())
+ .build());
+ report.setAnalysisStatus(DocumentAnalysisStatus.DENIED);
+ } else if (!checkNamesRule(document)) {
+ log.error("Document names mismatches :" + document.getId());
+ report.getBrokenRules().add(DocumentBrokenRule.builder()
+ .rule(DocumentRule.R_PAYSLIP_NAME)
+ .message(DocumentRule.R_PAYSLIP_NAME.getDefaultMessage())
+ .build());
+ report.setAnalysisStatus(DocumentAnalysisStatus.DENIED);
+ } else if (!checkMonthsValidityRule(document)) {
+ log.error("Document is expired :" + document.getId());
+ report.getBrokenRules().add(DocumentBrokenRule.builder()
+ .rule(DocumentRule.R_PAYSLIP_MONTHS)
+ .message(DocumentRule.R_PAYSLIP_MONTHS.getDefaultMessage())
+ .build());
+ report.setAnalysisStatus(DocumentAnalysisStatus.DENIED);
+ } else if (!checkAmountValidityRule(document)) {
+ log.error("Amount specified on document mismatch :" + document.getId());
+ report.getBrokenRules().add(DocumentBrokenRule.builder()
+ .rule(DocumentRule.R_PAYSLIP_AMOUNT_MISMATCHES)
+ .message(DocumentRule.R_PAYSLIP_AMOUNT_MISMATCHES.getDefaultMessage())
+ .build());
+ report.setAnalysisStatus(DocumentAnalysisStatus.DENIED);
+ } else {
+ report.setAnalysisStatus(DocumentAnalysisStatus.CHECKED);
+ }
+
+ } catch (Exception e) {
+ log.error("Error during the rules validation execution pocess", e);
+ report.setAnalysisStatus(DocumentAnalysisStatus.UNDEFINED);
+ }
+ return report;
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/OcrParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/OcrParser.java
deleted file mode 100644
index 92c0b625e..000000000
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/OcrParser.java
+++ /dev/null
@@ -1,10 +0,0 @@
-package fr.dossierfacile.process.file.service.ocr;
-
-
-import fr.dossierfacile.common.entity.ocr.ParsedFile;
-
-import java.io.File;
-
-public interface OcrParser {
- T parse(File file);
-}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/AbstractPDFParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/AbstractPDFParser.java
new file mode 100644
index 000000000..c53af6509
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/AbstractPDFParser.java
@@ -0,0 +1,50 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+
+import fr.dossierfacile.common.entity.ocr.ParsedFile;
+import fr.dossierfacile.process.file.service.parsers.tools.PageExtractorModel;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.pdfbox.Loader;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.text.PDFTextStripperByArea;
+
+import java.io.File;
+import java.io.IOException;
+
+@Slf4j
+@RequiredArgsConstructor
+public abstract class AbstractPDFParser implements FileParser {
+
+ protected abstract String getJsonModelFile();
+
+ protected abstract T getResultFromExtraction(PDFTextStripperByArea stripper);
+
+
+ @Override
+ public T parse(File file) {
+ try (PDDocument document = Loader.loadPDF(file)) {
+ PDPage page = document.getPage(0);
+
+ PageExtractorModel model = new PageExtractorModel(getJsonModelFile());
+ double scale = page.getMediaBox().getWidth() / model.getDefaultWidth();
+
+ if (modelMatches(page, model)) {
+ PDFTextStripperByArea stripper = new PDFTextStripperByArea();
+ model.getNamedZones(scale).forEach((name, rect) -> stripper.addRegion(name, rect));
+ stripper.extractRegions(page);
+
+ return getResultFromExtraction(stripper);
+ }
+ } catch (Exception e) {
+ log.error("Unable to parse");
+ throw new RuntimeException(e);
+ }
+ return null;
+ }
+
+ protected boolean modelMatches(PDPage page, PageExtractorModel model) throws IOException {
+ return true;
+ }
+}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/AbstractSinglePageImageOcrParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/AbstractSinglePageImageOcrParser.java
similarity index 93%
rename from dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/AbstractSinglePageImageOcrParser.java
rename to dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/AbstractSinglePageImageOcrParser.java
index a6527beee..5ddbaf2f9 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/AbstractSinglePageImageOcrParser.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/AbstractSinglePageImageOcrParser.java
@@ -1,4 +1,4 @@
-package fr.dossierfacile.process.file.service.ocr;
+package fr.dossierfacile.process.file.service.parsers;
import fr.dossierfacile.common.entity.ocr.ParsedFile;
import fr.dossierfacile.common.utils.FileUtility;
@@ -11,7 +11,7 @@
import java.io.IOException;
@Slf4j
-public abstract class AbstractSinglePageImageOcrParser implements OcrParser {
+public abstract class AbstractSinglePageImageOcrParser implements FileParser {
private BufferedImage getImage(File file) throws IOException {
BufferedImage image;
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/FileParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/FileParser.java
new file mode 100644
index 000000000..00fe1ac04
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/FileParser.java
@@ -0,0 +1,11 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+import fr.dossierfacile.common.entity.ocr.ParsedFile;
+
+import java.io.File;
+
+public interface FileParser {
+ T parse(File file);
+
+ boolean shouldTryToApply(fr.dossierfacile.common.entity.File file);
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttParser.java
new file mode 100644
index 000000000..3cb74843a
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttParser.java
@@ -0,0 +1,75 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+
+import fr.dossierfacile.common.entity.File;
+import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile;
+import fr.dossierfacile.common.enums.DocumentCategory;
+import fr.dossierfacile.process.file.service.parsers.tools.PageExtractorModel;
+import fr.dossierfacile.process.file.util.ImageUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.graphics.PDXObject;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.text.PDFTextStripperByArea;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.List;
+
+import static fr.dossierfacile.common.enums.DocumentSubCategory.OTHER_GUARANTEE;
+
+/**
+ * A Parsing POC
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+@Order(1)
+public class GuaranteeFasttParser extends AbstractPDFParser implements FileParser {
+
+ @Override
+ protected String getJsonModelFile() {
+ return "/parsers/fasttN1ZT.json";
+ }
+
+ @Override
+ protected GuaranteeProviderFile getResultFromExtraction(PDFTextStripperByArea stripper) {
+ return GuaranteeProviderFile.builder()
+ .names(List.of(new GuaranteeProviderFile.FullName(
+ stripper.getTextForRegion("firstName").trim(),
+ stripper.getTextForRegion("lastName").trim()
+ )))
+ .visaNumber(stripper.getTextForRegion("visaNumber").trim())
+ .validityDate(stripper.getTextForRegion("validityDate").trim())
+ .build();
+ }
+ @Override
+ protected boolean modelMatches(PDPage page, PageExtractorModel model) throws IOException {
+ PDResources resources = page.getResources();
+ for (COSName xObjectName : resources.getXObjectNames()) {
+ PDXObject xObject = resources.getXObject(xObjectName);
+ if (xObject instanceof PDImageXObject xImage) {
+ BufferedImage image = xImage.getImage();
+ String md5 = ImageUtils.md5(image);
+ boolean result = model.getBackgroundImageMD5().equals(md5);
+ if (!result) {
+ log.debug("MD5 mismatches " + md5);
+ }
+ return result;
+ }
+ }
+ return false;
+ }
+ @Override
+ public boolean shouldTryToApply(File file) {
+ return (file.getDocument().getDocumentCategory() == DocumentCategory.GUARANTEE_PROVIDER_CERTIFICATE
+ && file.getDocument().getDocumentSubCategory() == OTHER_GUARANTEE
+ && MediaType.APPLICATION_PDF_VALUE.equalsIgnoreCase(file.getStorageFile().getContentType()));
+ }
+}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttV2Parser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttV2Parser.java
new file mode 100644
index 000000000..39ec113cb
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttV2Parser.java
@@ -0,0 +1,84 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+
+import fr.dossierfacile.common.entity.File;
+import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile;
+import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile.FullName;
+import fr.dossierfacile.common.enums.DocumentCategory;
+import fr.dossierfacile.process.file.service.parsers.tools.PageExtractorModel;
+import fr.dossierfacile.process.file.util.ImageUtils;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.pdfbox.cos.COSName;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.pdmodel.PDResources;
+import org.apache.pdfbox.pdmodel.graphics.PDXObject;
+import org.apache.pdfbox.pdmodel.graphics.image.PDImageXObject;
+import org.apache.pdfbox.text.PDFTextStripperByArea;
+import org.springframework.core.annotation.Order;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.util.List;
+
+import static fr.dossierfacile.common.enums.DocumentSubCategory.OTHER_GUARANTEE;
+
+/**
+ * A Parsing POC
+ */
+@Service
+@Slf4j
+@RequiredArgsConstructor
+@Order(2)
+public class GuaranteeFasttV2Parser extends AbstractPDFParser implements FileParser {
+
+ @Override
+ protected String getJsonModelFile() {
+ return "/parsers/fasttN2ZT.json";
+ }
+
+ @Override
+ protected GuaranteeProviderFile getResultFromExtraction(PDFTextStripperByArea stripper) {
+
+ return GuaranteeProviderFile.builder()
+ .names(List.of(new FullName(
+ stripper.getTextForRegion("firstName").trim(),
+ stripper.getTextForRegion("lastName").trim()),
+ new FullName(
+ stripper.getTextForRegion("firstName2").trim(),
+ stripper.getTextForRegion("lastName2").trim())
+ )
+ )
+ .visaNumber(stripper.getTextForRegion("visaNumber").trim())
+ .validityDate(stripper.getTextForRegion("validityDate").trim())
+ .build();
+ }
+
+ // Currenlty FASTT PDF can be identified thanks to their background image
+ @Override
+ protected boolean modelMatches(PDPage page, PageExtractorModel model) throws IOException {
+ PDResources resources = page.getResources();
+ for (COSName xObjectName : resources.getXObjectNames()) {
+ PDXObject xObject = resources.getXObject(xObjectName);
+ if (xObject instanceof PDImageXObject xImage) {
+ BufferedImage image = xImage.getImage();
+ String md5 = ImageUtils.md5(image);
+ boolean result = model.getBackgroundImageMD5().equals(md5);
+ if (!result) {
+ log.debug("MD5 mismatches " + md5);
+ }
+ return result;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean shouldTryToApply(File file) {
+ return (file.getDocument().getDocumentCategory() == DocumentCategory.GUARANTEE_PROVIDER_CERTIFICATE
+ && file.getDocument().getDocumentSubCategory() == OTHER_GUARANTEE
+ && MediaType.APPLICATION_PDF_VALUE.equalsIgnoreCase(file.getStorageFile().getContentType()));
+ }
+}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/GuaranteeVisaleParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeVisaleParser.java
similarity index 81%
rename from dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/GuaranteeVisaleParser.java
rename to dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeVisaleParser.java
index 35db6db0f..30c160dbe 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/GuaranteeVisaleParser.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/GuaranteeVisaleParser.java
@@ -1,13 +1,15 @@
-package fr.dossierfacile.process.file.service.ocr;
+package fr.dossierfacile.process.file.service.parsers;
-import com.google.common.annotations.VisibleForTesting;
+import fr.dossierfacile.common.entity.File;
import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile;
+import fr.dossierfacile.common.enums.DocumentCategory;
import fr.dossierfacile.common.enums.ParsedStatus;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.ITessAPI;
import net.sourceforge.tess4j.Tesseract;
+import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import java.awt.*;
@@ -17,13 +19,17 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import static fr.dossierfacile.common.enums.DocumentSubCategory.OTHER_GUARANTEE;
+import static fr.dossierfacile.common.enums.DocumentSubCategory.VISALE;
+
/**
* A Parsing POC
*/
@Service
@Slf4j
@RequiredArgsConstructor
-public class GuaranteeVisaleParser extends AbstractSinglePageImageOcrParser implements OcrParser {
+@Order(3)
+public class GuaranteeVisaleParser extends AbstractSinglePageImageOcrParser implements FileParser {
static final Zones ZONES = new Zones(
new Rectangle(120, 150, 370, 30),
new Rectangle(35, 205, 530, 40),
@@ -71,8 +77,8 @@ public GuaranteeProviderFile parse(BufferedImage image) {
String[] firstNames = zonesId[0].split("Pr[éeè]nom[:\\s\\d]+");
String[] lastNames = zonesId[1].split("Nom[:\\s\\d]+");
List fullNames = new LinkedList<>();
- for ( int i = 1 ; i < firstNames.length && i < lastNames.length ; i++){
- fullNames.add(new GuaranteeProviderFile.FullName(firstNames[i], lastNames[i]));
+ for (int i = 1; i < firstNames.length && i < lastNames.length; i++) {
+ fullNames.add(new GuaranteeProviderFile.FullName(firstNames[i].trim(), lastNames[i].trim()));
}
if (fullNames.isEmpty()) {
log.error("Firstname/Lastname not found");
@@ -96,6 +102,13 @@ public GuaranteeProviderFile parse(BufferedImage image) {
return result;
}
+ @Override
+ public boolean shouldTryToApply(File file) {
+ return (file.getDocument().getDocumentCategory() == DocumentCategory.GUARANTEE_PROVIDER_CERTIFICATE
+ && (file.getDocument().getDocumentSubCategory() == VISALE
+ || file.getDocument().getDocumentSubCategory() == OTHER_GUARANTEE));// TODO currently there is not way to selection VISALE subcategory on UI
+ }
+
public record Zones(Rectangle zoneTitle, Rectangle zoneIdentification, Rectangle zoneValidityDate) {
public static Rectangle scale(Rectangle rectangle, double scale) {
return new Rectangle((int) (rectangle.x * scale), (int) (rectangle.y * scale), (int) (rectangle.width * scale), (int) (rectangle.height * scale));
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/PublicPayslipParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/PublicPayslipParser.java
new file mode 100644
index 000000000..cc4e183fc
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/PublicPayslipParser.java
@@ -0,0 +1,91 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+import fr.dossierfacile.common.entity.ocr.PublicPayslipFile;
+import fr.dossierfacile.common.enums.DocumentCategory;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.pdfbox.Loader;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.pdmodel.PDDocumentInformation;
+import org.apache.pdfbox.pdmodel.PDPage;
+import org.apache.pdfbox.text.PDFTextStripperByArea;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Service;
+
+import java.awt.*;
+import java.io.File;
+import java.time.YearMonth;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.Locale;
+
+import static fr.dossierfacile.common.enums.DocumentSubCategory.SALARY;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+public class PublicPayslipParser implements FileParser {
+
+ private static final DateTimeFormatter YEAR_MONTH_FORMATTER = new DateTimeFormatterBuilder()
+ .parseCaseInsensitive().appendPattern("MMMM yyyy").toFormatter(Locale.FRENCH);
+
+ @Override
+ public PublicPayslipFile parse(File file) {
+ PublicPayslipFile result = PublicPayslipFile.builder().build();
+ // file is a pdf
+ try (PDDocument document = Loader.loadPDF(file)) {
+ PDDocumentInformation info = document.getDocumentInformation();
+
+ if (info == null || info.getTitle() == null || !info.getTitle().equalsIgnoreCase("PAY18E")) {
+ log.info("This document is not in PAY18E format");
+ return null;
+ }
+
+ PDPage page = document.getPage(0);
+ double scale = page.getMediaBox().getWidth() / 595;
+
+ Rectangle titleRect = new Rectangle((int) (273 * scale), (int) (10 * scale), (int) (134 * scale), (int) (8 * scale));
+ Rectangle monthRect = new Rectangle((int) (315 * scale), (int) (22 * scale), (int) (100 * scale), (int) (11 * scale));
+ Rectangle netTaxableIncomeRect = new Rectangle((int) (125 * scale), (int) (711 * scale), (int) (80 * scale), (int) (13 * scale));
+ Rectangle cumulativeNetIncomeRect = new Rectangle((int) (25 * scale), (int) (711 * scale), (int) (84 * scale), (int) (13 * scale));
+ Rectangle fullnameRect = new Rectangle((int) (215 * scale), (int) (680 * scale), (int) (266 * scale), (int) (35 * scale));
+
+ PDFTextStripperByArea stripper = new PDFTextStripperByArea();
+ stripper.addRegion("title", titleRect);
+ stripper.addRegion("month", monthRect);
+ stripper.addRegion("netTaxableIncome", netTaxableIncomeRect);
+ stripper.addRegion("cumulativeNetIncome", cumulativeNetIncomeRect);
+ stripper.addRegion("fullname", fullnameRect);
+
+ stripper.extractRegions(page);
+
+ String title = stripper.getTextForRegion("title");
+ if ("BULLETIN DE PAYE".equalsIgnoreCase(title)) {
+ log.info("This document is not a public payslip");
+ return null;
+ }
+ String month = stripper.getTextForRegion("month").trim();
+ String netTaxableIncome = stripper.getTextForRegion("netTaxableIncome").trim();
+ String cumulativeNetIncome = stripper.getTextForRegion("cumulativeNetIncome").trim();
+ String fullname = stripper.getTextForRegion("fullname").replaceAll("\n.*", "").trim();
+
+ result.setNetTaxableIncome(Double.parseDouble(netTaxableIncome.replace(" ", "").replace(',', '.')));
+ result.setCumulativeNetTaxableIncome(Double.parseDouble(cumulativeNetIncome.replace(" ", "").replace(',', '.')));
+ result.setFullname(fullname);
+
+ result.setMonth(YearMonth.parse(month, YEAR_MONTH_FORMATTER));
+
+ return result;
+ } catch (Exception e) {
+ log.error("Unable to parse");
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean shouldTryToApply(fr.dossierfacile.common.entity.File file) {
+ return (file.getDocument().getDocumentCategory() == DocumentCategory.FINANCIAL
+ && file.getDocument().getDocumentSubCategory() == SALARY
+ && MediaType.APPLICATION_PDF_VALUE.equalsIgnoreCase(file.getStorageFile().getContentType()));
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeLeafParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeLeafParser.java
similarity index 90%
rename from dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeLeafParser.java
rename to dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeLeafParser.java
index 1b2970f45..d97c4cf59 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeLeafParser.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeLeafParser.java
@@ -1,12 +1,15 @@
-package fr.dossierfacile.process.file.service.ocr;
+package fr.dossierfacile.process.file.service.parsers;
import com.google.common.annotations.VisibleForTesting;
+import fr.dossierfacile.common.entity.File;
import fr.dossierfacile.common.entity.ocr.TaxIncomeLeaf;
+import fr.dossierfacile.common.enums.DocumentCategory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.ITessAPI;
import net.sourceforge.tess4j.Tesseract;
+import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import java.awt.*;
@@ -21,7 +24,8 @@
@Service
@Slf4j
@RequiredArgsConstructor
-public class TaxIncomeLeafParser extends AbstractSinglePageImageOcrParser implements OcrParser {
+@Order(2)
+public class TaxIncomeLeafParser extends AbstractSinglePageImageOcrParser implements FileParser {
static final TaxIncomeLeafParser.TaxIncomeLeafZones TEMPLATE_2023 =
new TaxIncomeLeafParser.TaxIncomeLeafZones(
@@ -96,6 +100,11 @@ public TaxIncomeLeaf parse(BufferedImage image) {
return result;
}
+ @Override
+ public boolean shouldTryToApply(File file) {
+ return (file.getDocument().getDocumentCategory() == DocumentCategory.TAX);
+ }
+
public record TaxIncomeLeafZones(Rectangle headerLeft, Rectangle headerRight) {
public static Rectangle scale(Rectangle rectangle, double scale) {
return new Rectangle((int) (rectangle.x * scale), (int) (rectangle.y * scale), (int) (rectangle.width * scale), (int) (rectangle.height * scale));
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeParser.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeParser.java
similarity index 94%
rename from dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeParser.java
rename to dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeParser.java
index 56cb0b43d..8f5d49df2 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeParser.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeParser.java
@@ -1,13 +1,15 @@
-package fr.dossierfacile.process.file.service.ocr;
+package fr.dossierfacile.process.file.service.parsers;
import fr.dossierfacile.common.entity.ocr.TaxIncomeMainFile;
+import fr.dossierfacile.common.enums.DocumentCategory;
import fr.dossierfacile.common.utils.FileUtility;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sourceforge.tess4j.ITessAPI;
import net.sourceforge.tess4j.Tesseract;
import org.apache.commons.io.FilenameUtils;
+import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import javax.imageio.ImageIO;
@@ -26,7 +28,8 @@
@Service
@Slf4j
@RequiredArgsConstructor
-public class TaxIncomeParser implements OcrParser {
+@Order(1)
+public class TaxIncomeParser implements FileParser {
static final TaxIncomeZones TEMPLATE_2023 = new TaxIncomeZones(new Rectangle(120, 10, 400, 65), new Rectangle(17, 176, 200, 180), new Rectangle(250, 170, 300, 100), new Rectangle(226, 595, 325, 55));
static final TaxIncomeZones TEMPLATE_2020 = new TaxIncomeZones(new Rectangle(140, 40, 325, 65), new Rectangle(40, 155, 200, 180), new Rectangle(270, 130, 300, 100), new Rectangle(255, 570, 315, 55));
private final Pattern incomeYearPattern = Pattern.compile("revenus de (\\d{4})");
@@ -38,6 +41,9 @@ public class TaxIncomeParser implements OcrParser {
private final TaxIncomeLeafParser taxIncomeLeafParser;
private transient volatile Tesseract tesseract;
+
+
+
String fixNumber(String str){
return str.replaceAll("O", "0").replaceAll("[lI|]", "1").replaceAll("S", "5").replaceAll("\\s", "");
}
@@ -140,6 +146,11 @@ public TaxIncomeMainFile parse(File file) {
return result;
}
+ @Override
+ public boolean shouldTryToApply(fr.dossierfacile.common.entity.File file) {
+ return (file.getDocument().getDocumentCategory() == DocumentCategory.TAX);
+ }
+
public record TaxIncomeZones(Rectangle zoneTitle, Rectangle zoneRef, Rectangle zoneAddress,
Rectangle zoneRevenuPart) {
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/tools/PageExtractorModel.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/tools/PageExtractorModel.java
new file mode 100644
index 000000000..6d9d4ca95
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/parsers/tools/PageExtractorModel.java
@@ -0,0 +1,37 @@
+package fr.dossierfacile.process.file.service.parsers.tools;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import fr.dossierfacile.common.utils.MapperUtil;
+import fr.dossierfacile.process.file.service.parsers.AbstractPDFParser;
+
+import java.awt.*;
+import java.io.IOException;
+import java.util.Map;
+import java.util.TreeMap;
+
+public class PageExtractorModel {
+ private final JsonNode jsonNode;
+
+ public PageExtractorModel(String jsonResourceModelPath) throws IOException {
+ this.jsonNode = MapperUtil.newObjectMapper().readTree(AbstractPDFParser.class.getResource(jsonResourceModelPath));
+ }
+
+ public double getDefaultWidth() {
+ return jsonNode.get("page").get("width").asDouble();
+ }
+ public String getBackgroundImageMD5() {
+ return jsonNode.get("classification").get("background-image-md5").asText();
+ }
+ public Map getNamedZones(double scale) {
+ Map map = new TreeMap();
+ JsonNode attributesNode = jsonNode.get("zones");
+ for (JsonNode attributeNode : attributesNode) {
+ String rectStr = attributeNode.get("rect").asText();
+ String[] rectSplit = rectStr.split(",");
+ Rectangle rectangle = new Rectangle((int) (Integer.parseInt(rectSplit[0]) * scale), (int) (Integer.parseInt(rectSplit[1]) * scale), (int) (Integer.parseInt(rectSplit[2]) * scale), (int) (Integer.parseInt(rectSplit[3]) * scale));
+
+ map.put(attributeNode.get("name").asText(), rectangle);
+ }
+ return map;
+ }
+}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/processors/FileParserProcessor.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/processors/FileParserProcessor.java
new file mode 100644
index 000000000..b8dee9e6d
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/processors/FileParserProcessor.java
@@ -0,0 +1,89 @@
+package fr.dossierfacile.process.file.service.processors;
+
+import fr.dossierfacile.common.entity.File;
+import fr.dossierfacile.common.entity.ParsedFileAnalysis;
+import fr.dossierfacile.common.entity.ocr.ParsedFile;
+import fr.dossierfacile.common.enums.ParsedFileAnalysisStatus;
+import fr.dossierfacile.common.repository.ParsedFileAnalysisRepository;
+import fr.dossierfacile.process.file.repository.FileRepository;
+import fr.dossierfacile.process.file.service.StorageFileLoaderService;
+import fr.dossierfacile.process.file.service.parsers.FileParser;
+import lombok.AllArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.core.annotation.Order;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+import javax.annotation.PostConstruct;
+import java.util.Comparator;
+import java.util.List;
+
+@Slf4j
+@Service
+@AllArgsConstructor
+public class FileParserProcessor implements Processor {
+ private StorageFileLoaderService storageFileLoaderService;
+ private final FileRepository fileRepository;
+ private final ParsedFileAnalysisRepository parsedFileAnalysisRepository;
+
+ private final List> fileParsers;
+
+ @PostConstruct
+ public void init() {
+ fileParsers.sort(Comparator.comparingInt(parser -> {
+ Order order = AnnotationUtils.findAnnotation(parser.getClass(), Order.class);
+ return order == null ? Integer.MAX_VALUE : order.value();
+ }));
+ }
+
+ /**
+ * Gets configured parsers list for the specified type of dffile
+ */
+ private List> getParsers(File file) {
+ return fileParsers.stream().filter(parser -> parser.shouldTryToApply(file)).toList();
+ }
+
+ public File process(File dfFile) {
+
+ List> parsers = getParsers(dfFile);
+ if (CollectionUtils.isEmpty(parsers)) {
+ log.debug("There is not parser associated to this kind of document");
+ return dfFile;
+ }
+
+ java.io.File file = storageFileLoaderService.getTemporaryFilePath(dfFile.getStorageFile());
+ if (file == null) {
+ log.error("File reading Error");
+ return dfFile;
+ }
+ try {
+ for (FileParser parser : parsers) {
+ try {
+ ParsedFile parsedDocument = parser.parse(file);
+ if (parsedDocument == null) {
+ log.warn("File {} has not been parsed", dfFile.getId());
+ } else {
+ ParsedFileAnalysis parsedFileAnalysis = ParsedFileAnalysis.builder()
+ .analysisStatus(ParsedFileAnalysisStatus.COMPLETED)
+ .parsedFile(parsedDocument)
+ .file(dfFile)
+ .classification(parsedDocument.getClassification())
+ .build();
+
+ parsedFileAnalysisRepository.save(parsedFileAnalysis);
+ dfFile.setParsedFileAnalysis(parsedFileAnalysis);
+ fileRepository.save(dfFile);
+ log.info("Successfully parse file {}", dfFile.getId());
+ break;
+ }
+ } catch (Exception e) {
+ log.warn("Unable to parse file {}", dfFile.getId(), e);
+ }
+ }
+ } finally {
+ storageFileLoaderService.removeFileIfExist(file);
+ }
+ return dfFile;
+ }
+}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/processors/OcrParserFileProcessor.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/processors/OcrParserFileProcessor.java
deleted file mode 100644
index a167f27b9..000000000
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/service/processors/OcrParserFileProcessor.java
+++ /dev/null
@@ -1,88 +0,0 @@
-package fr.dossierfacile.process.file.service.processors;
-
-import fr.dossierfacile.common.entity.File;
-import fr.dossierfacile.common.entity.ParsedFileAnalysis;
-import fr.dossierfacile.common.entity.ocr.ParsedFile;
-import fr.dossierfacile.common.enums.DocumentCategory;
-import fr.dossierfacile.common.enums.ParsedFileAnalysisStatus;
-import fr.dossierfacile.common.repository.ParsedFileAnalysisRepository;
-import fr.dossierfacile.process.file.repository.FileRepository;
-import fr.dossierfacile.process.file.service.ocr.GuaranteeVisaleParser;
-import fr.dossierfacile.process.file.service.ocr.OcrParser;
-import fr.dossierfacile.process.file.service.ocr.TaxIncomeLeafParser;
-import fr.dossierfacile.process.file.service.ocr.TaxIncomeParser;
-import fr.dossierfacile.process.file.service.StorageFileLoaderService;
-import lombok.AllArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Service;
-import org.springframework.util.CollectionUtils;
-
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import static fr.dossierfacile.common.enums.DocumentSubCategory.CERTIFICATE_VISA;
-
-@Slf4j
-@Service
-@AllArgsConstructor
-public class OcrParserFileProcessor implements Processor {
- private StorageFileLoaderService storageFileLoaderService;
- private final FileRepository fileRepository;
- private final ParsedFileAnalysisRepository parsedFileAnalysisRepository;
- private final TaxIncomeParser taxIncomeParser;
- private final TaxIncomeLeafParser taxIncomeLeafParser;
- private final GuaranteeVisaleParser guaranteeVisaleParser;
-
- /**
- * Gets configured parsers list for the specified type of dffile
- */
- private List getParsers(File file) {
- if (file.getDocument().getDocumentCategory() == DocumentCategory.TAX) {
- return Arrays.asList(taxIncomeParser, taxIncomeLeafParser);
- }
- if (file.getDocument().getDocumentCategory() == DocumentCategory.IDENTIFICATION
- && file.getDocument().getDocumentSubCategory() == CERTIFICATE_VISA)
- return Collections.singletonList(guaranteeVisaleParser);
- return null;
- }
-
- public File process(File dfFile) {
-
- List parsers = getParsers(dfFile);
- if (CollectionUtils.isEmpty(parsers)) {
- log.debug("There is not parser associateed to this kind of document");
- return dfFile;
- }
-
- java.io.File file = storageFileLoaderService.getTemporaryFilePath(dfFile.getStorageFile());
- if (file == null) {
- log.error("File reading Error");
- return dfFile;
- }
- try {
- for (OcrParser parser : parsers) {
- try {
- ParsedFile parsedDocument = parser.parse(file);
- ParsedFileAnalysis parsedFileAnalysis = ParsedFileAnalysis.builder()
- .analysisStatus(ParsedFileAnalysisStatus.COMPLETED)
- .parsedFile(parsedDocument)
- .classification(parsedDocument.getClassification())
- .build();
-
- parsedFileAnalysis.setFile(dfFile);
- parsedFileAnalysisRepository.save(parsedFileAnalysis);
- dfFile.setParsedFileAnalysis(parsedFileAnalysis);
- fileRepository.save(dfFile);
- log.info("Successfully parse file {}", dfFile.getId());
- break;
- } catch (Exception e) {
- log.warn("Unable to parse file {}", dfFile.getId());
- }
- }
- } finally {
- storageFileLoaderService.removeFileIfExist(file);
- }
- return dfFile;
- }
-}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/ImageUtils.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/ImageUtils.java
new file mode 100644
index 000000000..b6c682d33
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/ImageUtils.java
@@ -0,0 +1,21 @@
+package fr.dossierfacile.process.file.util;
+
+import lombok.experimental.UtilityClass;
+import org.springframework.util.DigestUtils;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+@UtilityClass
+public class ImageUtils {
+
+ public static String md5(BufferedImage image) throws IOException {
+ try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
+ ImageIO.write(image, "jpg", baos);
+ byte[] bytes = baos.toByteArray();
+ return DigestUtils.md5DigestAsHex(bytes);
+ }
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/PersonNameComparator.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/PersonNameComparator.java
index 0ba0efff5..4a1555942 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/PersonNameComparator.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/PersonNameComparator.java
@@ -11,7 +11,7 @@ private static String normalizeName(String name) {
if (name == null)
return null;
String normalized = Normalizer.normalize(name, Normalizer.Form.NFD);
- return normalized.replace('-', ' ').replace('.', ' ')
+ return normalized.replace('-', ' ').replace('.', ' ').replace('\'', ' ')
.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "").toUpperCase().trim();
}
public static boolean equalsWithNormalization(String fullName, String fullNameToCompare) {
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/QrCodeFileAnalysisCriteria.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/QrCodeFileAnalysisCriteria.java
index 4640eb0f2..016752280 100644
--- a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/QrCodeFileAnalysisCriteria.java
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/QrCodeFileAnalysisCriteria.java
@@ -34,7 +34,7 @@ public static boolean shouldBeAnalyzed(File file) {
hasSubCategory(document, CDI, CDI_TRIAL, CDD, PUBLIC, CTT, RETIRED, STUDENT, UNEMPLOYED);
case FINANCIAL -> hasSubCategory(document, SALARY, SCHOLARSHIP, SOCIAL_SERVICE, PENSION);
case RESIDENCY -> hasSubCategory(document, GUEST, GUEST_PARENTS);
- case IDENTIFICATION, IDENTIFICATION_LEGAL_PERSON, NULL -> false;
+ case IDENTIFICATION, IDENTIFICATION_LEGAL_PERSON, GUARANTEE_PROVIDER_CERTIFICATE, NULL -> false;
};
}
diff --git a/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/TwoDDocUtil.java b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/TwoDDocUtil.java
new file mode 100644
index 000000000..90962a47e
--- /dev/null
+++ b/dossierfacile-process-file/src/main/java/fr/dossierfacile/process/file/util/TwoDDocUtil.java
@@ -0,0 +1,18 @@
+package fr.dossierfacile.process.file.util;
+
+import lombok.experimental.UtilityClass;
+
+import java.time.LocalDate;
+import java.time.temporal.ChronoUnit;
+
+@UtilityClass
+public class TwoDDocUtil {
+ public static LocalDate getLocalDateFrom2DDocHexDate(String hexDateFrom200) {
+ long dailyCount = Long.parseLong(hexDateFrom200, 16);
+ return LocalDate.of(2000, 1, 1).plusDays(dailyCount);
+ }
+
+ public static String get2DDocHexDateFromLocalDate(LocalDate localDate) {
+ return String.format("%04X", LocalDate.of(2000, 1, 1).until(localDate, ChronoUnit.DAYS));
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/main/resources/parsers/fasttN1ZT.json b/dossierfacile-process-file/src/main/resources/parsers/fasttN1ZT.json
new file mode 100644
index 000000000..91e970490
--- /dev/null
+++ b/dossierfacile-process-file/src/main/resources/parsers/fasttN1ZT.json
@@ -0,0 +1,27 @@
+{
+ "page": {
+ "width": "892"
+ },
+ "classification": {
+ "name": "FASTT_N1_ZT",
+ "background-image-md5": "9180d5834b3af06a0cebea1985a84649"
+ },
+ "zones": [
+ {
+ "name": "visaNumber",
+ "rect": "232,416,256,30"
+ },
+ {
+ "name": "lastName",
+ "rect": "232,478,256,30"
+ },
+ {
+ "name": "firstName",
+ "rect": "232,540,256,30"
+ },
+ {
+ "name": "validityDate",
+ "rect": "41,1086,138,20"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/main/resources/parsers/fasttN2ZT.json b/dossierfacile-process-file/src/main/resources/parsers/fasttN2ZT.json
new file mode 100644
index 000000000..f61687499
--- /dev/null
+++ b/dossierfacile-process-file/src/main/resources/parsers/fasttN2ZT.json
@@ -0,0 +1,35 @@
+{
+ "page": {
+ "width": "892"
+ },
+ "classification": {
+ "name": "FASTT_N2_ZT",
+ "background-image-md5": "d18ff78c030a3dea810fd1dd97f44a0b"
+ },
+ "zones": [
+ {
+ "name": "visaNumber",
+ "rect": "230,391,256,30"
+ },
+ {
+ "name": "lastName",
+ "rect": "230,438,256,30"
+ },
+ {
+ "name": "firstName",
+ "rect": "230,486,256,30"
+ },
+ {
+ "name": "lastName2",
+ "rect": "230,627,256,30"
+ },
+ {
+ "name": "firstName2",
+ "rect": "230,675,256,30"
+ },
+ {
+ "name": "validityDate",
+ "rect": "153,867,138,20"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ApiTesseractTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ApiTesseractTest.java
index a0253c8e4..91fc0416f 100644
--- a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ApiTesseractTest.java
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ApiTesseractTest.java
@@ -12,7 +12,7 @@
@Disabled
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.MOCK)
-public class ApiTesseractTest {
+class ApiTesseractTest {
@Autowired
private ApiTesseract apiTesseract;
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationServiceTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationServiceTest.java
index a37b1eef7..0824df29d 100644
--- a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationServiceTest.java
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/documentrules/GuaranteeProviderRulesValidationServiceTest.java
@@ -38,8 +38,8 @@ Document buildGuaranteeProviderFile() throws JsonProcessingException {
"firstName": "Jean"
}
],
- "signed": true,
- "status": "COMPLETE",
+ "signed": null,
+ "status": null,
"visaNumber": "V11000000000",
"deliveryDate": "01/12/2023",
"validityDate": "02/03/2024",
@@ -55,9 +55,10 @@ Document buildGuaranteeProviderFile() throws JsonProcessingException {
.parsedFileAnalysis(parsedFileAnalysis)
.build();
return Document.builder()
- .tenant(tenant)
+ .tenant(null)
+ .guarantor(Guarantor.builder().tenant(tenant).build())
.documentCategory(DocumentCategory.IDENTIFICATION)
- .documentSubCategory(DocumentSubCategory.CERTIFICATE_VISA)
+ .documentSubCategory(DocumentSubCategory.OTHER_GUARANTEE)
.files(Arrays.asList(dfFile))
.build();
}
@@ -90,13 +91,13 @@ void document_expired() throws Exception {
Assertions.assertThat(report.getAnalysisStatus()).isEqualTo(DocumentAnalysisStatus.DENIED);
Assertions.assertThat(report.getBrokenRules()).hasSize(1);
- Assertions.assertThat(report.getBrokenRules().get(0)).matches(docRule -> docRule.getRule() == DocumentRule.R_GUARANTEE_EXIRED);
+ Assertions.assertThat(report.getBrokenRules().get(0)).matches(docRule -> docRule.getRule() == DocumentRule.R_GUARANTEE_EXPIRED);
}
@Test
void document_wrong_firstname() throws Exception {
Document document = buildGuaranteeProviderFile();
- document.getTenant().setFirstName("Michel");
+ document.getGuarantor().getTenant().setFirstName("Michel");
((GuaranteeProviderFile) document.getFiles().get(0).getParsedFileAnalysis().getParsedFile())
.setValidityDate(LocalDate.now().plusDays(15).format(DateTimeFormatter.ofPattern("dd/MM/yyyy")));
DocumentAnalysisReport report = DocumentAnalysisReport.builder()
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/documentrules/PublicPayslipRulesValidationServiceTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/documentrules/PublicPayslipRulesValidationServiceTest.java
new file mode 100644
index 000000000..ebb60ed8b
--- /dev/null
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/documentrules/PublicPayslipRulesValidationServiceTest.java
@@ -0,0 +1,140 @@
+package fr.dossierfacile.process.file.service.documentrules;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import fr.dossierfacile.common.entity.*;
+import fr.dossierfacile.common.entity.ocr.PublicPayslipFile;
+import fr.dossierfacile.common.enums.DocumentCategory;
+import fr.dossierfacile.common.enums.DocumentSubCategory;
+import fr.dossierfacile.common.enums.ParsedFileAnalysisStatus;
+import fr.dossierfacile.common.enums.ParsedFileClassification;
+import fr.dossierfacile.process.file.util.TwoDDocUtil;
+import org.assertj.core.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+import java.time.YearMonth;
+import java.time.temporal.TemporalAdjusters;
+import java.util.Arrays;
+import java.util.LinkedList;
+
+class PublicPayslipRulesValidationServiceTest {
+
+ private PublicPayslipRulesValidationService publicPayslipRVS = new PublicPayslipRulesValidationService();
+
+ private File buildValidDfFile(LocalDate date) throws JsonProcessingException {
+ BarCodeFileAnalysis barCodeFileAnalysis = BarCodeFileAnalysis.builder()
+ .verifiedData(new ObjectMapper()
+ .readValue("""
+ {
+ "Fin de période": "{startHex}",
+ "Début de période": "{endHex}",
+ "Salaire net imposable": "3000,05",
+ "SIRET de l’employeur": "123456789000001",
+ "Date de début de contrat": "11111111",
+ "Cumul du salaire net imposable": "30000,10",
+ "Ligne 1 de la norme adresse postale du bénéficiaire de la prestation": "MR KALOUF JEAN"
+ }
+ """.replace("{startHex}", TwoDDocUtil.get2DDocHexDateFromLocalDate(date.with(TemporalAdjusters.firstDayOfMonth())))
+ .replace("{endHex}", TwoDDocUtil.get2DDocHexDateFromLocalDate(date.with(TemporalAdjusters.lastDayOfMonth()))),
+ Object.class))
+ .documentType(BarCodeDocumentType.PUBLIC_PAYSLIP)
+ .build();
+
+ PublicPayslipFile parsedFile = PublicPayslipFile.builder()
+ .fullname("MR KALOUF JEAN")
+ .month(YearMonth.from(date))
+ .netTaxableIncome(3000.05)
+ .cumulativeNetTaxableIncome(30000.10)
+ .build();
+
+ ParsedFileAnalysis parsedFileAnalysis = ParsedFileAnalysis.builder().parsedFile(parsedFile)
+ .analysisStatus(ParsedFileAnalysisStatus.COMPLETED)
+ .classification(ParsedFileClassification.PUBLIC_PAYSLIP)
+ .build();
+
+ return File.builder()
+ .fileAnalysis(barCodeFileAnalysis)
+ .parsedFileAnalysis(parsedFileAnalysis)
+ .build();
+ }
+
+
+ Document buildPublicPayslipDocument() throws JsonProcessingException {
+ Tenant tenant = Tenant.builder()
+ .firstName("Jean")
+ .lastName("KALOUF")
+ .build();
+
+ LocalDate currentDate = LocalDate.now();
+ return Document.builder()
+ .tenant(tenant)
+ .monthlySum(3000)
+ .documentCategory(DocumentCategory.FINANCIAL)
+ .documentSubCategory(DocumentSubCategory.SALARY)
+ .files(Arrays.asList(buildValidDfFile(currentDate.minusMonths(1)),
+ buildValidDfFile(currentDate.minusMonths(2)),
+ buildValidDfFile(currentDate.minusMonths(3))))
+ .build();
+ }
+
+ Document buildPublicPayslipDocumentWithWrongMonths() throws JsonProcessingException {
+ Tenant tenant = Tenant.builder()
+ .firstName("Jean")
+ .lastName("KALOUF")
+ .build();
+
+ LocalDate currentDate = LocalDate.now();
+ return Document.builder()
+ .tenant(tenant)
+ .documentCategory(DocumentCategory.FINANCIAL)
+ .documentSubCategory(DocumentSubCategory.SALARY)
+ .files(Arrays.asList(buildValidDfFile(currentDate.minusMonths(3)),
+ buildValidDfFile(currentDate.minusMonths(4)),
+ buildValidDfFile(currentDate.minusMonths(5))))
+ .build();
+ }
+
+ @Test
+ void document_full_test_ok() throws Exception {
+ Document document = buildPublicPayslipDocument();
+
+ DocumentAnalysisReport report = DocumentAnalysisReport.builder()
+ .document(document)
+ .brokenRules(new LinkedList<>())
+ .build();
+ publicPayslipRVS.process(document, report);
+
+ Assertions.assertThat(report.getAnalysisStatus()).isEqualTo(DocumentAnalysisStatus.CHECKED);
+ }
+
+ @Test
+ void document_full_test_wrong_month() throws Exception {
+ Document document = buildPublicPayslipDocumentWithWrongMonths();
+
+ DocumentAnalysisReport report = DocumentAnalysisReport.builder()
+ .document(document)
+ .brokenRules(new LinkedList<>())
+ .build();
+ publicPayslipRVS.process(document, report);
+
+ Assertions.assertThat(report.getAnalysisStatus()).isEqualTo(DocumentAnalysisStatus.DENIED);
+ Assertions.assertThat(report.getBrokenRules()).hasSize(1);
+ Assertions.assertThat(report.getBrokenRules().get(0)).matches(docRule -> docRule.getRule() == DocumentRule.R_PAYSLIP_MONTHS);
+ }
+
+ @Test
+ void document_full_test_wrong_average_amount() throws Exception {
+ Document document = buildPublicPayslipDocument();
+ document.setMonthlySum(2400);
+ DocumentAnalysisReport report = DocumentAnalysisReport.builder()
+ .document(document)
+ .brokenRules(new LinkedList<>())
+ .build();
+ publicPayslipRVS.process(document, report);
+
+ Assertions.assertThat(report.getAnalysisStatus()).isEqualTo(DocumentAnalysisStatus.DENIED);
+ Assertions.assertThat(report.getBrokenRules()).hasSize(1);
+ Assertions.assertThat(report.getBrokenRules().get(0)).matches(docRule -> docRule.getRule() == DocumentRule.R_PAYSLIP_AMOUNT_MISMATCHES);
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttParserTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttParserTest.java
new file mode 100644
index 000000000..5cfbf9775
--- /dev/null
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttParserTest.java
@@ -0,0 +1,20 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+@Disabled
+class GuaranteeFasttParserTest {
+ private final GuaranteeFasttParser parser = new GuaranteeFasttParser();
+
+ @Test
+ void parse() {
+ File file = new File(this.getClass().getResource("/fasttN1ZT.pdf").getFile());
+
+ GuaranteeProviderFile doc = parser.parse(file);
+ System.out.print(doc);
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttV2ParserTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttV2ParserTest.java
new file mode 100644
index 000000000..c5a4c7208
--- /dev/null
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeFasttV2ParserTest.java
@@ -0,0 +1,27 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+@Disabled
+class GuaranteeFasttV2ParserTest {
+ private final GuaranteeFasttV2Parser parser = new GuaranteeFasttV2Parser();
+
+ @Test
+ void parse() {
+ File file = new File(this.getClass().getResource("/fasttN2ZT.pdf").getFile());
+
+ GuaranteeProviderFile doc = parser.parse(file);
+ System.out.print(doc);
+ }
+ @Test
+ void parse_failed() {
+ File file = new File(this.getClass().getResource("/fasttN1ZT.pdf").getFile());
+
+ GuaranteeProviderFile doc = parser.parse(file);
+ System.out.print(doc);
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/GuaranteeVisaleParserTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeVisaleParserTest.java
similarity index 71%
rename from dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/GuaranteeVisaleParserTest.java
rename to dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeVisaleParserTest.java
index db9172b1e..a57a247d5 100644
--- a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/GuaranteeVisaleParserTest.java
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/GuaranteeVisaleParserTest.java
@@ -1,16 +1,12 @@
-package fr.dossierfacile.process.file.service.ocr;
+package fr.dossierfacile.process.file.service.parsers;
import fr.dossierfacile.common.entity.ocr.GuaranteeProviderFile;
-import fr.dossierfacile.common.entity.ocr.TaxIncomeMainFile;
-import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import java.io.File;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-public class GuaranteeVisaleParserTest {
+class GuaranteeVisaleParserTest {
private final GuaranteeVisaleParser visaleParser = new GuaranteeVisaleParser();
@Disabled
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/PublicPayslipFileParserTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/PublicPayslipFileParserTest.java
new file mode 100644
index 000000000..6e97653cf
--- /dev/null
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/PublicPayslipFileParserTest.java
@@ -0,0 +1,24 @@
+package fr.dossierfacile.process.file.service.parsers;
+
+import fr.dossierfacile.common.entity.ocr.PublicPayslipFile;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+
+@Disabled
+class PublicPayslipFileParserTest {
+ private final PublicPayslipParser parser = new PublicPayslipParser();
+
+ @Test
+ void parse() {
+ File file = new File(this.getClass().getResource("/documents/fake_payslip.pdf").getFile());
+
+ PublicPayslipFile doc = parser.parse(file);
+
+ System.out.println(doc.getMonth());
+ System.out.println(doc.getNetTaxableIncome());
+ System.out.println(doc.getCumulativeNetTaxableIncome());
+ System.out.println(doc.getFullname());
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeLeafParserTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeLeafParserTest.java
similarity index 92%
rename from dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeLeafParserTest.java
rename to dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeLeafParserTest.java
index b5f7dad3f..aaaba7fbb 100644
--- a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeLeafParserTest.java
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeLeafParserTest.java
@@ -1,11 +1,11 @@
-package fr.dossierfacile.process.file.service.ocr;
+package fr.dossierfacile.process.file.service.parsers;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import java.util.regex.Matcher;
-public class TaxIncomeLeafParserTest {
+class TaxIncomeLeafParserTest {
private final TaxIncomeLeafParser taxIncomeLeafParser = new TaxIncomeLeafParser();
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeParserTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeParserTest.java
similarity index 88%
rename from dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeParserTest.java
rename to dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeParserTest.java
index e9aeeb33b..91215a429 100644
--- a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/ocr/TaxIncomeParserTest.java
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/service/parsers/TaxIncomeParserTest.java
@@ -1,4 +1,4 @@
-package fr.dossierfacile.process.file.service.ocr;
+package fr.dossierfacile.process.file.service.parsers;
import fr.dossierfacile.common.entity.ocr.TaxIncomeMainFile;
import org.junit.jupiter.api.Disabled;
@@ -7,7 +7,7 @@
import java.io.File;
@Disabled
-public class TaxIncomeParserTest {
+class TaxIncomeParserTest {
private final TaxIncomeParser taxIncomeParser = new TaxIncomeParser(new TaxIncomeLeafParser());
@Test
@@ -18,6 +18,5 @@ void parse() {
TaxIncomeMainFile doc = taxIncomeParser.parse(file);
System.out.print(doc);
-
}
}
\ No newline at end of file
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/util/PersonNameComparatorTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/util/PersonNameComparatorTest.java
index e40efb7e6..164988fce 100644
--- a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/util/PersonNameComparatorTest.java
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/util/PersonNameComparatorTest.java
@@ -44,6 +44,14 @@ void compare_name_should_be_ok_basc() {
Assertions.assertEquals(true, PersonNameComparator.bearlyEqualsTo(fullName, givenLastName, givenFirstName));
}
+ @Test
+ void compare_name_should_be_ok_apostrophe() {
+ String givenFirstName = "Jean";
+ String givenLastName = "M BOL";
+ String fullName = "M'Bol Jean";
+ Assertions.assertEquals(true, PersonNameComparator.bearlyEqualsTo(fullName, givenLastName, givenFirstName));
+ }
+
@Test
void compare_name_should_be_nok_reverse() {
String givenFirstName = "Marche";
diff --git a/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/util/TwoDDocUtilTest.java b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/util/TwoDDocUtilTest.java
new file mode 100644
index 000000000..04ed5b68e
--- /dev/null
+++ b/dossierfacile-process-file/src/test/java/fr/dossierfacile/process/file/util/TwoDDocUtilTest.java
@@ -0,0 +1,18 @@
+package fr.dossierfacile.process.file.util;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.time.LocalDate;
+
+class TwoDDocUtilTest {
+ @Test
+ void check_date() {
+ Assertions.assertEquals(LocalDate.of(2023, 12, 1), TwoDDocUtil.getLocalDateFrom2DDocHexDate("221F"));
+ }
+
+ @Test
+ void check_hex() {
+ Assertions.assertEquals("221F", TwoDDocUtil.get2DDocHexDateFromLocalDate(LocalDate.of(2023, 12, 1)));
+ }
+}
\ No newline at end of file
diff --git a/dossierfacile-task-scheduler/pom.xml b/dossierfacile-task-scheduler/pom.xml
index c2ffe7186..40543ea8c 100644
--- a/dossierfacile-task-scheduler/pom.xml
+++ b/dossierfacile-task-scheduler/pom.xml
@@ -15,7 +15,7 @@
${packaging.type}
- 19
+ 21
1.18.30
diff --git a/system.properties b/system.properties
index a7f513c02..0399015ce 100644
--- a/system.properties
+++ b/system.properties
@@ -1,2 +1,2 @@
-java.runtime.version=19
-maven.version=3.6.2
+java.runtime.version=21
+maven.version=3.9.5