From 8ed04aae8986017531ba9242f6a74bb2ec4df603 Mon Sep 17 00:00:00 2001 From: Imer ouijdane Date: Fri, 11 Sep 2026 14:55:41 +0200 Subject: [PATCH] Premier commit --- .editorconfig | 248 ++ .gitignore | 20 + SIFinance.Api/DbKeepAliveService.cs | 54 + SIFinance.Api/EntraAuthEndpoints.cs | 153 + SIFinance.Api/EntraSyncService.cs | 189 + SIFinance.Api/JwtOptions.cs | 11 + SIFinance.Api/JwtTokenService.cs | 52 + SIFinance.Api/Program.cs | 106 + SIFinance.Api/Properties/launchSettings.json | 23 + SIFinance.Api/SIFinance.Api.csproj | 29 + SIFinance.Api/SIFinance.Api.http | 6 + SIFinance.Api/SharePointEndpoints.cs | 101 + SIFinance.Api/SharePointGraphClientFactory.cs | 22 + SIFinance.Api/SharePointOptions.cs | 16 + SIFinance.Api/SharePointUploadService.cs | 80 + SIFinance.Api/appsettings.Development.json | 22 + SIFinance.Api/appsettings.json | 18 + SIFinance.Modules.Achats/Dtos/BudgetDtos.cs | 21 + SIFinance.Modules.Achats/Dtos/ContratDtos.cs | 57 + .../Dtos/DashboardEndpoints.cs | 127 + .../Dtos/DemandeAchatCreateDto.cs | 41 + .../Dtos/DemandeAchatDto.cs | 35 + .../Dtos/ReferenceDtos.cs | 17 + .../Endpoints/AchatsEndpoints.cs | 709 ++++ .../Endpoints/BudgetsEndpoints.cs | 110 + .../Endpoints/ContratsEndpoints.cs | 389 ++ .../SIFinance.Modules.Achats.csproj | 17 + SIFinance.Modules.Finance/Dtos/AvoirDtos.cs | 14 + SIFinance.Modules.Finance/Dtos/FactureDtos.cs | 53 + .../Dtos/PaiementDtos.cs | 22 + .../Endpoints/AvoirsEndpoints.cs | 78 + .../Endpoints/FacturesEndpoints.cs | 322 ++ .../Endpoints/FinanceDefaults.cs | 10 + .../Endpoints/PaiementsEndpoints.cs | 140 + .../SIFinance.Modules.Finance.csproj | 21 + .../Dtos/ContratPrestataireDtos.cs | 10 + .../Dtos/OrganismeDtos.cs | 11 + .../ContratsPrestatairesEndpoints.cs | 82 + .../Endpoints/OrganismesEndpoints.cs | 93 + .../SIFinance.Modules.SousTraitants.csproj | 17 + SIFinance.Shared/AzureAdOptions.cs | 17 + SIFinance.Shared/CurrentUserService.cs | 38 + SIFinance.Shared/Entities/Acompte.cs | 33 + .../Entities/AffectationsAvoir.cs | 25 + SIFinance.Shared/Entities/Audit.cs | 25 + SIFinance.Shared/Entities/Avoir.cs | 37 + SIFinance.Shared/Entities/BudgetService.cs | 32 + .../Entities/BudgetServiceRevision.cs | 25 + SIFinance.Shared/Entities/Campus.cs | 9 + SIFinance.Shared/Entities/ContratEcheance.cs | 24 + .../Entities/ContratFournisseur.cs | 55 + .../Entities/ContratFournisseurHistorique.cs | 14 + .../Entities/ContratPrestataire.cs | 32 + SIFinance.Shared/Entities/DemandesAchat.cs | 65 + SIFinance.Shared/Entities/Facture.cs | 77 + SIFinance.Shared/Entities/FichiersXml.cs | 25 + SIFinance.Shared/Entities/Fournisseur.cs | 37 + SIFinance.Shared/Entities/HistoriqueAction.cs | 27 + SIFinance.Shared/Entities/ModesPaiement.cs | 21 + SIFinance.Shared/Entities/Organisme.cs | 25 + SIFinance.Shared/Entities/Paiement.cs | 39 + SIFinance.Shared/Entities/Parametre.cs | 19 + SIFinance.Shared/Entities/PiecesJointe.cs | 35 + SIFinance.Shared/Entities/Rapprochement.cs | 31 + .../Entities/SIFinanceDbContext.cs | 1194 ++++++ SIFinance.Shared/Entities/Service.cs | 19 + SIFinance.Shared/Entities/TypesDepense.cs | 19 + SIFinance.Shared/Entities/Utilisateur.cs | 70 + SIFinance.Shared/Entities/UtilisateurRole.cs | 12 + SIFinance.Shared/EntraGraphTokenProvider.cs | 48 + SIFinance.Shared/FacturXEmissionService.cs | 145 + ...8_AddContratsAndPaiementStatut.Designer.cs | 1716 ++++++++ ...0727094858_AddContratsAndPaiementStatut.cs | 148 + ...8_AddCategorieFournisseurLibre.Designer.cs | 1721 ++++++++ ...0803115528_AddCategorieFournisseurLibre.cs | 31 + ...ousTraitantsOrganismesContrats.Designer.cs | 1899 +++++++++ ...4840_AddSousTraitantsOrganismesContrats.cs | 104 + ...3_AddResponsableToUtilisateurs.Designer.cs | 1914 +++++++++ ...0806131533_AddResponsableToUtilisateurs.cs | 55 + ...82905_AddCommentaireToFactures.Designer.cs | 1919 +++++++++ ...20260811082905_AddCommentaireToFactures.cs | 31 + ...1084738_AddContratIdToFactures.Designer.cs | 1935 +++++++++ .../20260811084738_AddContratIdToFactures.cs | 56 + ...260814130216_AddBudgetsService.Designer.cs | 2026 +++++++++ .../20260814130216_AddBudgetsService.cs | 84 + ...58_SplitBudgetRevisionsHistory.Designer.cs | 2065 ++++++++++ ...60814131858_SplitBudgetRevisionsHistory.cs | 141 + .../Migrations/AddMaterialWorkflow.sql | 18 + .../SIFinanceDbContextModelSnapshot.cs | 2062 ++++++++++ SIFinance.Shared/NotificationEmailService.cs | 50 + SIFinance.Shared/SIFinance.Shared.csproj | 28 + SIFinance.slnx | 7 + frontend/.gitignore | 24 + frontend/README.md | 75 + frontend/eslint.config.js | 22 + frontend/index.html | 13 + frontend/package-lock.json | 3611 +++++++++++++++++ frontend/package.json | 36 + frontend/public/favicon.svg | 1 + frontend/public/icons.svg | 24 + frontend/src/App.tsx | 101 + frontend/src/Layout.tsx | 269 ++ frontend/src/SousTraitantsLayout.tsx | 165 + frontend/src/auth.tsx | 71 + frontend/src/authToken.ts | 51 + frontend/src/data.ts | 512 +++ frontend/src/main.tsx | 36 + frontend/src/pages/Archives.tsx | 145 + frontend/src/pages/Contratsfournisseurs.tsx | 853 ++++ frontend/src/pages/Dashboard.tsx | 393 ++ frontend/src/pages/DemandeForm.tsx | 721 ++++ frontend/src/pages/Demandes.tsx | 807 ++++ frontend/src/pages/Fournisseurs.tsx | 502 +++ frontend/src/pages/Login.tsx | 58 + frontend/src/pages/Paiements.tsx | 515 +++ frontend/src/pages/Pilotage.tsx | 625 +++ frontend/src/pages/Portal.tsx | 68 + frontend/src/pages/facturesAvoirs.tsx | 735 ++++ .../src/pages/soustraitants/Arbitrage.tsx | 183 + .../src/pages/soustraitants/ArchivesST.tsx | 143 + .../pages/soustraitants/CahierDesCharges.tsx | 247 ++ .../soustraitants/ContratsPrestataires.tsx | 229 ++ .../soustraitants/ControleConformite.tsx | 162 + .../src/pages/soustraitants/DashboardST.tsx | 185 + .../src/pages/soustraitants/FacturesST.tsx | 215 + .../src/pages/soustraitants/PaiementsST.tsx | 192 + .../src/pages/soustraitants/PilotageST.tsx | 155 + .../src/pages/soustraitants/Prestations.tsx | 378 ++ frontend/src/theme.ts | 78 + frontend/src/ui.tsx | 134 + frontend/tsconfig.app.json | 26 + frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 23 + frontend/vite.config.ts | 7 + 134 files changed, 36627 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitignore create mode 100644 SIFinance.Api/DbKeepAliveService.cs create mode 100644 SIFinance.Api/EntraAuthEndpoints.cs create mode 100644 SIFinance.Api/EntraSyncService.cs create mode 100644 SIFinance.Api/JwtOptions.cs create mode 100644 SIFinance.Api/JwtTokenService.cs create mode 100644 SIFinance.Api/Program.cs create mode 100644 SIFinance.Api/Properties/launchSettings.json create mode 100644 SIFinance.Api/SIFinance.Api.csproj create mode 100644 SIFinance.Api/SIFinance.Api.http create mode 100644 SIFinance.Api/SharePointEndpoints.cs create mode 100644 SIFinance.Api/SharePointGraphClientFactory.cs create mode 100644 SIFinance.Api/SharePointOptions.cs create mode 100644 SIFinance.Api/SharePointUploadService.cs create mode 100644 SIFinance.Api/appsettings.Development.json create mode 100644 SIFinance.Api/appsettings.json create mode 100644 SIFinance.Modules.Achats/Dtos/BudgetDtos.cs create mode 100644 SIFinance.Modules.Achats/Dtos/ContratDtos.cs create mode 100644 SIFinance.Modules.Achats/Dtos/DashboardEndpoints.cs create mode 100644 SIFinance.Modules.Achats/Dtos/DemandeAchatCreateDto.cs create mode 100644 SIFinance.Modules.Achats/Dtos/DemandeAchatDto.cs create mode 100644 SIFinance.Modules.Achats/Dtos/ReferenceDtos.cs create mode 100644 SIFinance.Modules.Achats/Endpoints/AchatsEndpoints.cs create mode 100644 SIFinance.Modules.Achats/Endpoints/BudgetsEndpoints.cs create mode 100644 SIFinance.Modules.Achats/Endpoints/ContratsEndpoints.cs create mode 100644 SIFinance.Modules.Achats/SIFinance.Modules.Achats.csproj create mode 100644 SIFinance.Modules.Finance/Dtos/AvoirDtos.cs create mode 100644 SIFinance.Modules.Finance/Dtos/FactureDtos.cs create mode 100644 SIFinance.Modules.Finance/Dtos/PaiementDtos.cs create mode 100644 SIFinance.Modules.Finance/Endpoints/AvoirsEndpoints.cs create mode 100644 SIFinance.Modules.Finance/Endpoints/FacturesEndpoints.cs create mode 100644 SIFinance.Modules.Finance/Endpoints/FinanceDefaults.cs create mode 100644 SIFinance.Modules.Finance/Endpoints/PaiementsEndpoints.cs create mode 100644 SIFinance.Modules.Finance/SIFinance.Modules.Finance.csproj create mode 100644 SIFinance.Modules.SousTraitants/Dtos/ContratPrestataireDtos.cs create mode 100644 SIFinance.Modules.SousTraitants/Dtos/OrganismeDtos.cs create mode 100644 SIFinance.Modules.SousTraitants/Endpoints/ContratsPrestatairesEndpoints.cs create mode 100644 SIFinance.Modules.SousTraitants/Endpoints/OrganismesEndpoints.cs create mode 100644 SIFinance.Modules.SousTraitants/SIFinance.Modules.SousTraitants.csproj create mode 100644 SIFinance.Shared/AzureAdOptions.cs create mode 100644 SIFinance.Shared/CurrentUserService.cs create mode 100644 SIFinance.Shared/Entities/Acompte.cs create mode 100644 SIFinance.Shared/Entities/AffectationsAvoir.cs create mode 100644 SIFinance.Shared/Entities/Audit.cs create mode 100644 SIFinance.Shared/Entities/Avoir.cs create mode 100644 SIFinance.Shared/Entities/BudgetService.cs create mode 100644 SIFinance.Shared/Entities/BudgetServiceRevision.cs create mode 100644 SIFinance.Shared/Entities/Campus.cs create mode 100644 SIFinance.Shared/Entities/ContratEcheance.cs create mode 100644 SIFinance.Shared/Entities/ContratFournisseur.cs create mode 100644 SIFinance.Shared/Entities/ContratFournisseurHistorique.cs create mode 100644 SIFinance.Shared/Entities/ContratPrestataire.cs create mode 100644 SIFinance.Shared/Entities/DemandesAchat.cs create mode 100644 SIFinance.Shared/Entities/Facture.cs create mode 100644 SIFinance.Shared/Entities/FichiersXml.cs create mode 100644 SIFinance.Shared/Entities/Fournisseur.cs create mode 100644 SIFinance.Shared/Entities/HistoriqueAction.cs create mode 100644 SIFinance.Shared/Entities/ModesPaiement.cs create mode 100644 SIFinance.Shared/Entities/Organisme.cs create mode 100644 SIFinance.Shared/Entities/Paiement.cs create mode 100644 SIFinance.Shared/Entities/Parametre.cs create mode 100644 SIFinance.Shared/Entities/PiecesJointe.cs create mode 100644 SIFinance.Shared/Entities/Rapprochement.cs create mode 100644 SIFinance.Shared/Entities/SIFinanceDbContext.cs create mode 100644 SIFinance.Shared/Entities/Service.cs create mode 100644 SIFinance.Shared/Entities/TypesDepense.cs create mode 100644 SIFinance.Shared/Entities/Utilisateur.cs create mode 100644 SIFinance.Shared/Entities/UtilisateurRole.cs create mode 100644 SIFinance.Shared/EntraGraphTokenProvider.cs create mode 100644 SIFinance.Shared/FacturXEmissionService.cs create mode 100644 SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.cs create mode 100644 SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.cs create mode 100644 SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.cs create mode 100644 SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.cs create mode 100644 SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.cs create mode 100644 SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.cs create mode 100644 SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.cs create mode 100644 SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.Designer.cs create mode 100644 SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.cs create mode 100644 SIFinance.Shared/Migrations/AddMaterialWorkflow.sql create mode 100644 SIFinance.Shared/Migrations/SIFinanceDbContextModelSnapshot.cs create mode 100644 SIFinance.Shared/NotificationEmailService.cs create mode 100644 SIFinance.Shared/SIFinance.Shared.csproj create mode 100644 SIFinance.slnx create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/public/icons.svg create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/Layout.tsx create mode 100644 frontend/src/SousTraitantsLayout.tsx create mode 100644 frontend/src/auth.tsx create mode 100644 frontend/src/authToken.ts create mode 100644 frontend/src/data.ts create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/pages/Archives.tsx create mode 100644 frontend/src/pages/Contratsfournisseurs.tsx create mode 100644 frontend/src/pages/Dashboard.tsx create mode 100644 frontend/src/pages/DemandeForm.tsx create mode 100644 frontend/src/pages/Demandes.tsx create mode 100644 frontend/src/pages/Fournisseurs.tsx create mode 100644 frontend/src/pages/Login.tsx create mode 100644 frontend/src/pages/Paiements.tsx create mode 100644 frontend/src/pages/Pilotage.tsx create mode 100644 frontend/src/pages/Portal.tsx create mode 100644 frontend/src/pages/facturesAvoirs.tsx create mode 100644 frontend/src/pages/soustraitants/Arbitrage.tsx create mode 100644 frontend/src/pages/soustraitants/ArchivesST.tsx create mode 100644 frontend/src/pages/soustraitants/CahierDesCharges.tsx create mode 100644 frontend/src/pages/soustraitants/ContratsPrestataires.tsx create mode 100644 frontend/src/pages/soustraitants/ControleConformite.tsx create mode 100644 frontend/src/pages/soustraitants/DashboardST.tsx create mode 100644 frontend/src/pages/soustraitants/FacturesST.tsx create mode 100644 frontend/src/pages/soustraitants/PaiementsST.tsx create mode 100644 frontend/src/pages/soustraitants/PilotageST.tsx create mode 100644 frontend/src/pages/soustraitants/Prestations.tsx create mode 100644 frontend/src/theme.ts create mode 100644 frontend/src/ui.tsx create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..312c9e5 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,248 @@ +# Supprimer la ligne ci-dessous si vous voulez hériter les paramètres .editorconfig des répertoires supérieurs +root = true + +# Fichiers C# +[*.cs] + +#### Options EditorConfig principales #### + +# Indentation et espacement +indent_size = 4 +indent_style = space +tab_width = 4 + +# Préférences de nouvelle ligne +end_of_line = crlf +insert_final_newline = false + +#### Actions du code .NET #### + +# Membres du type +dotnet_hide_advanced_members = false +dotnet_member_insertion_location = with_other_members_of_the_same_kind +dotnet_property_generation_behavior = prefer_throwing_properties + +# Recherche de symbole +dotnet_search_reference_assemblies = true + +#### Conventions de codage .NET #### + +# Organiser les instructions Using +dotnet_separate_import_directive_groups = false +dotnet_sort_system_directives_first = false +file_header_template = unset + +# Préférences de this. et Me. +dotnet_style_qualification_for_event = false +dotnet_style_qualification_for_field = false +dotnet_style_qualification_for_method = false +dotnet_style_qualification_for_property = false + +# Préférences des mots clés de langage par rapport aux types BCL +dotnet_style_predefined_type_for_locals_parameters_members = true +dotnet_style_predefined_type_for_member_access = true + +# Préférences de parenthèses +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity +dotnet_style_parentheses_in_other_operators = never_if_unnecessary +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity + +# Préférences de modificateur +dotnet_style_require_accessibility_modifiers = for_non_interface_members + +# Préférences de niveau expression +dotnet_prefer_system_hash_code = true +dotnet_style_coalesce_expression = true +dotnet_style_collection_initializer = true +dotnet_style_explicit_tuple_names = true +dotnet_style_namespace_match_folder = true +dotnet_style_null_propagation = true +dotnet_style_object_initializer = true +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_prefer_auto_properties = true +dotnet_style_prefer_collection_expression = when_types_loosely_match +dotnet_style_prefer_compound_assignment = true +dotnet_style_prefer_conditional_expression_over_assignment = true +dotnet_style_prefer_conditional_expression_over_return = true +dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed +dotnet_style_prefer_inferred_anonymous_type_member_names = true +dotnet_style_prefer_inferred_tuple_names = true +dotnet_style_prefer_is_null_check_over_reference_equality_method = true +dotnet_style_prefer_non_hidden_explicit_cast_in_source = true +dotnet_style_prefer_simplified_boolean_expressions = true +dotnet_style_prefer_simplified_interpolation = true + +# Préférences de champ +dotnet_style_readonly_field = true + +# Préférences de paramètre +dotnet_code_quality_unused_parameters = all + +# Préférences de suppression +dotnet_remove_unnecessary_suppression_exclusions = none + +# Préférences de nouvelle ligne +dotnet_style_allow_multiple_blank_lines_experimental = true +dotnet_style_allow_statement_immediately_after_block_experimental = true + +#### Conventions de codage C# #### + +# Préférences de var +csharp_style_var_elsewhere = false +csharp_style_var_for_built_in_types = false +csharp_style_var_when_type_is_apparent = false + +# Membres expression-bodied +csharp_style_expression_bodied_accessors = true +csharp_style_expression_bodied_constructors = false +csharp_style_expression_bodied_indexers = true +csharp_style_expression_bodied_lambdas = true +csharp_style_expression_bodied_local_functions = false +csharp_style_expression_bodied_methods = false +csharp_style_expression_bodied_operators = false +csharp_style_expression_bodied_properties = true + +# Préférences correspondants au modèle +csharp_style_pattern_matching_over_as_with_null_check = true +csharp_style_pattern_matching_over_is_with_cast_check = true +csharp_style_prefer_extended_property_pattern = true +csharp_style_prefer_not_pattern = true +csharp_style_prefer_pattern_matching = true +csharp_style_prefer_switch_expression = true + +# Préférences de vérification de valeur Null +csharp_style_conditional_delegate_call = true + +# Préférences de modificateur +csharp_prefer_static_anonymous_function = true +csharp_prefer_static_local_function = true +csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async +csharp_style_prefer_readonly_struct = true +csharp_style_prefer_readonly_struct_member = true + +# Préférences de bloc de code +csharp_prefer_braces = true +csharp_prefer_simple_using_statement = true +csharp_prefer_system_threading_lock = true +csharp_style_namespace_declarations = block_scoped +csharp_style_prefer_method_group_conversion = true +csharp_style_prefer_primary_constructors = true +csharp_style_prefer_simple_property_accessors = true +csharp_style_prefer_top_level_statements = true + +# Préférences de niveau expression +csharp_prefer_simple_default_expression = true +csharp_style_deconstructed_variable_declaration = true +csharp_style_implicit_object_creation_when_type_is_apparent = true +csharp_style_inlined_variable_declaration = true +csharp_style_prefer_implicitly_typed_lambda_expression = true +csharp_style_prefer_index_operator = true +csharp_style_prefer_local_over_anonymous_function = true +csharp_style_prefer_null_check_over_type_check = true +csharp_style_prefer_range_operator = true +csharp_style_prefer_tuple_swap = true +csharp_style_prefer_unbound_generic_type_in_nameof = true +csharp_style_prefer_utf8_string_literals = true +csharp_style_throw_expression = true +csharp_style_unused_value_assignment_preference = discard_variable +csharp_style_unused_value_expression_statement_preference = discard_variable + +# Préférences pour la directive 'using' +csharp_using_directive_placement = outside_namespace + +# Préférences de nouvelle ligne +csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true +csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true +csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true +csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true +csharp_style_allow_embedded_statements_on_same_line_experimental = true + +#### Règles de formatage C# #### + +# Préférences de nouvelle ligne +csharp_new_line_before_catch = true +csharp_new_line_before_else = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all +csharp_new_line_between_query_expression_clauses = true + +# Préférences de mise en retrait +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true + +# Préférences d'espace +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Préférences d'enveloppement +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true + +#### Styles de nommage #### + +# Règles de nommage + +dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion +dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface +dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i + +dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.types_should_be_pascal_case.symbols = types +dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case + +dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members +dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case + +# Spécifications de symboles + +dotnet_naming_symbols.interface.applicable_kinds = interface +dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.interface.required_modifiers = + +dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum +dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.types.required_modifiers = + +dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method +dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected +dotnet_naming_symbols.non_field_members.required_modifiers = + +# Styles de nommage + +dotnet_naming_style.pascal_case.required_prefix = +dotnet_naming_style.pascal_case.required_suffix = +dotnet_naming_style.pascal_case.word_separator = +dotnet_naming_style.pascal_case.capitalization = pascal_case + +dotnet_naming_style.begins_with_i.required_prefix = I +dotnet_naming_style.begins_with_i.required_suffix = +dotnet_naming_style.begins_with_i.word_separator = +dotnet_naming_style.begins_with_i.capitalization = pascal_case diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..25ff2aa --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +@" +# Visual Studio +.vs/ +*.user +*.suo + +# Build outputs +bin/ +obj/ +**/bin/ +**/obj/ + +# Node (si frontend JS/TS) +frontend/node_modules/ +frontend/dist/ +frontend/build/ + +# Autres +*.log +"@ | Out-File -Encoding utf8 .gitignore \ No newline at end of file diff --git a/SIFinance.Api/DbKeepAliveService.cs b/SIFinance.Api/DbKeepAliveService.cs new file mode 100644 index 0000000..44a7be5 --- /dev/null +++ b/SIFinance.Api/DbKeepAliveService.cs @@ -0,0 +1,54 @@ +using Microsoft.EntityFrameworkCore; +using SIFinance.Shared.Entities; + +namespace SIFinance.Api; + +// Envoie une requête minimale à SQL Server à intervalle régulier pour garder une +// connexion du pool active. Sans ça, une connexion inutilisée trop longtemps entre +// deux sessions de travail peut être coupée silencieusement par un équipement réseau +// (pare-feu/routeur) entre la machine de dev et le serveur SQL distant — la requête +// suivante doit alors attendre l'expiration de la connexion morte avant d'en ouvrir +// une nouvelle, d'où un premier chargement lent (plusieurs secondes) après une pause. +public class DbKeepAliveService(IServiceScopeFactory scopeFactory, ILogger logger) : BackgroundService +{ + private static readonly TimeSpan Interval = TimeSpan.FromMinutes(2); + + // Le Dashboard (et d'autres pages) lancent plusieurs requêtes en parallèle au + // chargement (demandes/factures/avoirs) — chacune a besoin de sa propre connexion + // du pool. Ne garder qu'UNE connexion chaude ne profite qu'à la première requête + // servie ; les autres ouvrent quand même une connexion à froid en parallèle, avec + // le même délai. On chauffe donc plusieurs connexions à la fois. + private const int WarmConnections = 4; + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + // Premier ping immédiat au démarrage : sans ça, PeriodicTimer attend un + // intervalle complet avant son premier tick, donc les toutes premières + // requêtes réelles de l'appli (juste après un dotnet run) n'en profitent + // jamais et paient quand même le coût d'ouverture de connexion à froid. + await PingMany(stoppingToken); + + using var timer = new PeriodicTimer(Interval); + while (await timer.WaitForNextTickAsync(stoppingToken)) + { + await PingMany(stoppingToken); + } + } + + private Task PingMany(CancellationToken ct) => + Task.WhenAll(Enumerable.Range(0, WarmConnections).Select(_ => Ping(ct))); + + private async Task Ping(CancellationToken ct) + { + try + { + using var scope = scopeFactory.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + await db.Database.ExecuteSqlRawAsync("SELECT 1", ct); + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + logger.LogWarning(ex, "Échec du ping de maintien de connexion vers SQL Server."); + } + } +} diff --git a/SIFinance.Api/EntraAuthEndpoints.cs b/SIFinance.Api/EntraAuthEndpoints.cs new file mode 100644 index 0000000..7a8218e --- /dev/null +++ b/SIFinance.Api/EntraAuthEndpoints.cs @@ -0,0 +1,153 @@ +using System.Collections.Concurrent; +using System.Net.Http.Headers; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Api; + +// Le backend pilote lui-même le flux OAuth2 "authorization code + PKCE" avec Entra ID +// (plutôt que MSAL.js côté frontend) : /api/auth/microsoft redirige vers Microsoft, +// /api/auth/callback échange le code, résout l'utilisateur (déjà provisionné par +// EntraSyncService) et émet le jeton "maison" (JwtTokenService) consommé par le +// frontend pour tous les appels API suivants. +public static class EntraAuthEndpoints +{ + // État PKCE en mémoire (code_verifier par "state"), expire après 10 min — un seul + // process API tourne en dev ; à revoir (cache distribué) si on scale horizontalement. + private static readonly ConcurrentDictionary PkceStore = new(); + + public static IEndpointRouteBuilder MapEntraAuthEndpoints(this IEndpointRouteBuilder app) + { + app.MapGet("/api/auth/microsoft", (IOptions azureOptions) => + { + var opts = azureOptions.Value; + var state = Convert.ToHexString(RandomNumberGenerator.GetBytes(16)); + var codeVerifier = Base64UrlEncode(RandomNumberGenerator.GetBytes(32)); + var codeChallenge = Base64UrlEncode(SHA256.HashData(Encoding.UTF8.GetBytes(codeVerifier))); + + CleanupExpired(); + PkceStore[state] = (codeVerifier, DateTime.UtcNow.AddMinutes(10)); + + var url = QueryHelpers.AddQueryString( + $"https://login.microsoftonline.com/{opts.TenantId}/oauth2/v2.0/authorize", + new Dictionary + { + ["client_id"] = opts.ClientId, + ["response_type"] = "code", + ["redirect_uri"] = opts.RedirectUri, + ["response_mode"] = "query", + ["scope"] = "openid profile email User.Read", + ["state"] = state, + ["prompt"] = "select_account", + ["code_challenge"] = codeChallenge, + ["code_challenge_method"] = "S256", + }); + + return Results.Redirect(url); + }); + + app.MapGet("/api/auth/callback", async ( + string? code, string? state, string? error, + IOptions azureOptions, + IHttpClientFactory httpClientFactory, + SIFinanceDbContext db, + JwtTokenService jwtService, + IConfiguration config, + ILogger logger, + CancellationToken ct) => + { + var frontendUrl = (config["FrontendUrl"] ?? "http://localhost:5173").TrimEnd('/'); + + if (!string.IsNullOrEmpty(error)) + return Results.Redirect($"{frontendUrl}/login?error={Uri.EscapeDataString(error)}"); + + if (string.IsNullOrEmpty(code) || string.IsNullOrEmpty(state) + || !PkceStore.TryRemove(state, out var entry) || entry.Expiry < DateTime.UtcNow) + return Results.Redirect($"{frontendUrl}/login?error=invalid_state"); + + var opts = azureOptions.Value; + var http = httpClientFactory.CreateClient(); + + var tokenResponse = await http.PostAsync( + $"https://login.microsoftonline.com/{opts.TenantId}/oauth2/v2.0/token", + new FormUrlEncodedContent(new Dictionary + { + ["client_id"] = opts.ClientId, + ["client_secret"] = opts.ClientSecret, + ["code"] = code, + ["redirect_uri"] = opts.RedirectUri, + ["grant_type"] = "authorization_code", + ["code_verifier"] = entry.CodeVerifier, + }), ct); + + if (!tokenResponse.IsSuccessStatusCode) + { + var body = await tokenResponse.Content.ReadAsStringAsync(ct); + logger.LogWarning("Échange du code OAuth2 échoué ({Status}) : {Body}", tokenResponse.StatusCode, body); + return Results.Redirect($"{frontendUrl}/login?error=token_exchange_failed"); + } + + var tokenJson = JsonDocument.Parse(await tokenResponse.Content.ReadAsStringAsync(ct)).RootElement; + var accessToken = tokenJson.GetProperty("access_token").GetString(); + + var meRequest = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); + meRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); + var meResponse = await http.SendAsync(meRequest, ct); + if (!meResponse.IsSuccessStatusCode) + { + var body = await meResponse.Content.ReadAsStringAsync(ct); + logger.LogWarning("Appel Graph /me échoué ({Status}) : {Body}", meResponse.StatusCode, body); + return Results.Redirect($"{frontendUrl}/login?error=graph_me_failed"); + } + + var me = JsonDocument.Parse(await meResponse.Content.ReadAsStringAsync(ct)).RootElement; + var email = GetStr(me, "mail") ?? GetStr(me, "userPrincipalName"); + if (string.IsNullOrWhiteSpace(email)) + return Results.Redirect($"{frontendUrl}/login?error=no_email"); + + var user = await db.Utilisateurs + .FirstOrDefaultAsync(u => u.Email == email && u.Actif, ct); + // Pas de création à la volée ici : l'utilisateur doit être membre du groupe + // Entra ID synchronisé par EntraSyncService pour pouvoir se connecter. + if (user is null) + { + logger.LogWarning("Connexion refusée — aucun utilisateur actif trouvé pour l'email {Email}.", email); + return Results.Redirect($"{frontendUrl}/login?error=user_not_found"); + } + + user.DateDerniereConnexion = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + + var roles = await db.UtilisateurRoles + .AsNoTracking() + .Where(r => r.AzureOid == user.AzureOid) + .Select(r => r.Role) + .ToListAsync(ct); + + var estResponsable = await db.Utilisateurs.AsNoTracking().AnyAsync(u => u.ResponsableId == user.Id, ct); + + var jwt = jwtService.GenerateToken(user, roles, estResponsable); + return Results.Redirect($"{frontendUrl}/auth/callback?token={Uri.EscapeDataString(jwt)}"); + }); + + return app; + } + + private static string Base64UrlEncode(byte[] bytes) => + Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', '-').Replace('/', '_'); + + private static string? GetStr(JsonElement e, string name) => + e.TryGetProperty(name, out var p) && p.ValueKind == JsonValueKind.String ? p.GetString() : null; + + private static void CleanupExpired() + { + foreach (var key in PkceStore.Where(kv => kv.Value.Expiry < DateTime.UtcNow).Select(kv => kv.Key).ToList()) + PkceStore.TryRemove(key, out _); + } +} diff --git a/SIFinance.Api/EntraSyncService.cs b/SIFinance.Api/EntraSyncService.cs new file mode 100644 index 0000000..88c59fc --- /dev/null +++ b/SIFinance.Api/EntraSyncService.cs @@ -0,0 +1,189 @@ +using System.Net.Http.Headers; +using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Api; + +// Synchronise périodiquement les membres du groupe Entra ID (AzureAd:GroupId) dans +// core.utilisateurs : création des nouveaux membres (Profil par défaut "DEMANDEUR", +// à ajuster manuellement en base pour les profils Finance), mise à jour des noms/ +// emails, désactivation de ceux qui ont quitté le groupe. Remplace la résolution +// "just-in-time" à la connexion — un utilisateur doit être membre du groupe pour +// pouvoir se connecter (voir EntraAuthEndpoints). +public class EntraSyncService( + IServiceScopeFactory scopeFactory, + IHttpClientFactory httpClientFactory, + EntraGraphTokenProvider tokenProvider, + IOptions options, + ILogger logger) : BackgroundService +{ + private static readonly TimeSpan Interval = TimeSpan.FromMinutes(30); + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + await SyncOnce(stoppingToken); + using var timer = new PeriodicTimer(Interval); + while (await timer.WaitForNextTickAsync(stoppingToken)) + { + await SyncOnce(stoppingToken); + } + } + + private async Task SyncOnce(CancellationToken ct) + { + var opts = options.Value; + if (string.IsNullOrWhiteSpace(opts.GroupId) || string.IsNullOrWhiteSpace(opts.ClientSecret)) + { + logger.LogWarning("Synchro Entra ID ignorée — configuration AzureAd incomplète (GroupId/ClientSecret manquants)."); + return; + } + + try + { + var token = await tokenProvider.GetTokenAsync(ct); + var http = httpClientFactory.CreateClient(); + + var membres = new List(); + string? url = $"https://graph.microsoft.com/v1.0/groups/{opts.GroupId}/members" + + "?$select=id,givenName,surname,mail,userPrincipalName,accountEnabled&$top=999"; + + while (url is not null) + { + var request = new HttpRequestMessage(HttpMethod.Get, url); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + var response = await http.SendAsync(request, ct); + response.EnsureSuccessStatusCode(); + + using var stream = await response.Content.ReadAsStreamAsync(ct); + var json = await JsonDocument.ParseAsync(stream, cancellationToken: ct); + membres.AddRange(json.RootElement.GetProperty("value").EnumerateArray().Select(e => e.Clone())); + url = json.RootElement.TryGetProperty("@odata.nextLink", out var next) ? next.GetString() : null; + } + + var valides = membres.Where(EstMembreValide).ToList(); + var emailsActifs = valides.Select(m => GetStr(m, "mail")!.Trim().ToLowerInvariant()).ToHashSet(); + + using var scope = scopeFactory.CreateScope(); + var db = scope.ServiceProvider.GetRequiredService(); + var existants = await db.Utilisateurs.ToDictionaryAsync(u => u.Email.Trim().ToLowerInvariant(), ct); + + var crees = 0; + var mis = 0; + foreach (var m in valides) + { + var email = GetStr(m, "mail")!.Trim().ToLowerInvariant(); + var oid = GetStr(m, "id") ?? ""; + var prenom = GetStr(m, "givenName") ?? ""; + var nom = GetStr(m, "surname") ?? ""; + + if (existants.TryGetValue(email, out var existant)) + { + existant.AzureOid = oid; + if (!string.IsNullOrWhiteSpace(nom)) existant.Nom = nom; + if (!string.IsNullOrWhiteSpace(prenom)) existant.Prenom = prenom; + existant.Actif = true; + mis++; + } + else + { + db.Utilisateurs.Add(new Utilisateur + { + AzureOid = oid, + Nom = nom, + Prenom = prenom, + Email = email, + Profil = "DEMANDEUR", + Actif = true, + DateCreation = DateTime.UtcNow, + }); + crees++; + } + } + + var desactives = 0; + foreach (var (email, existant) in existants) + { + if (existant.Actif && !emailsActifs.Contains(email)) + { + existant.Actif = false; + desactives++; + } + } + + // SaveChanges ici pour que les nouveaux utilisateurs aient un Id avant la + // résolution des liens responsable→collaborateur juste après (impossible de + // référencer un Utilisateur pas encore enregistré en base). + await db.SaveChangesAsync(ct); + + var responsablesResolus = await ResoudreResponsablesAsync(db, valides, token, http, ct); + + logger.LogInformation( + "Synchro Entra ID terminée : {Total} membres valides, {Crees} créés, {Mis} mis à jour, {Desactives} désactivés, {Responsables} liens responsable résolus.", + valides.Count, crees, mis, desactives, responsablesResolus); + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + logger.LogWarning(ex, "Échec de la synchro Entra ID."); + } + } + + // Interroge le manager Entra ID de chaque membre (GET /users/{id}/manager) et + // résout son Id interne via l'email — le manager doit lui-même déjà exister comme + // Utilisateur (généralement le cas, les managers étant aussi membres du groupe + // synchronisé). Un appel par membre plutôt qu'un batch Graph : plus simple/robuste, + // le coût (~une centaine d'appels toutes les 30 min, en tâche de fond) est négligeable. + private async Task ResoudreResponsablesAsync(SIFinanceDbContext db, List valides, string token, HttpClient http, CancellationToken ct) + { + var utilisateurs = await db.Utilisateurs.ToDictionaryAsync(u => u.Email.Trim().ToLowerInvariant(), ct); + var resolus = 0; + + foreach (var m in valides) + { + var oid = GetStr(m, "id"); + var email = GetStr(m, "mail")?.Trim().ToLowerInvariant(); + if (oid is null || email is null || !utilisateurs.TryGetValue(email, out var collaborateur)) continue; + + try + { + var request = new HttpRequestMessage(HttpMethod.Get, $"https://graph.microsoft.com/v1.0/users/{oid}/manager?$select=id,mail,userPrincipalName"); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); + var response = await http.SendAsync(request, ct); + if (!response.IsSuccessStatusCode) continue; // pas de manager (404) ou erreur ponctuelle — on laisse ResponsableId tel quel + + using var managerStream = await response.Content.ReadAsStreamAsync(ct); + using var managerDoc = await JsonDocument.ParseAsync(managerStream, cancellationToken: ct); + var managerEmail = (GetStr(managerDoc.RootElement, "mail") ?? GetStr(managerDoc.RootElement, "userPrincipalName"))?.Trim().ToLowerInvariant(); + if (managerEmail is null || !utilisateurs.TryGetValue(managerEmail, out var responsable)) continue; + if (responsable.Id == collaborateur.Id) continue; // garde-fou, ne devrait pas arriver + + if (collaborateur.ResponsableId != responsable.Id) + { + collaborateur.ResponsableId = responsable.Id; + resolus++; + } + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + logger.LogWarning(ex, "Échec de résolution du manager pour {Email}.", email); + } + } + + await db.SaveChangesAsync(ct); + return resolus; + } + + private static bool EstMembreValide(JsonElement m) + { + var mail = GetStr(m, "mail"); + if (string.IsNullOrWhiteSpace(mail)) return false; + // accountEnabled absent (utilisateur non hydraté) est traité comme actif ; + // seule une valeur explicite "false" exclut le membre. + return !m.TryGetProperty("accountEnabled", out var p) || p.ValueKind != JsonValueKind.False; + } + + private static string? GetStr(JsonElement e, string name) => + e.TryGetProperty(name, out var p) && p.ValueKind == JsonValueKind.String ? p.GetString() : null; +} diff --git a/SIFinance.Api/JwtOptions.cs b/SIFinance.Api/JwtOptions.cs new file mode 100644 index 0000000..5687ffb --- /dev/null +++ b/SIFinance.Api/JwtOptions.cs @@ -0,0 +1,11 @@ +namespace SIFinance.Api; + +// Section "Jwt" de appsettings.Development.json — clé de signature des jetons émis +// par SIFinance.Api après connexion Entra ID (voir JwtTokenService / EntraAuthEndpoints). +public class JwtOptions +{ + public const string SectionName = "Jwt"; + + public string Secret { get; set; } = ""; + public int ExpiresHours { get; set; } = 8; +} diff --git a/SIFinance.Api/JwtTokenService.cs b/SIFinance.Api/JwtTokenService.cs new file mode 100644 index 0000000..f4c0af7 --- /dev/null +++ b/SIFinance.Api/JwtTokenService.cs @@ -0,0 +1,52 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Tokens; +using SIFinance.Shared.Entities; + +namespace SIFinance.Api; + +// Émet le jeton "maison" utilisé pour authentifier les appels à l'API après la +// connexion Entra ID (voir EntraAuthEndpoints.MapGet("/api/auth/callback")). Le +// claim "id" porte directement l'Id de la ligne core.utilisateurs — CurrentUserService +// n'a donc plus besoin d'accéder à la base pour résoudre l'utilisateur courant. +public class JwtTokenService(IOptions options) +{ + public string GenerateToken(Utilisateur user, IEnumerable? roles = null, bool estResponsable = false) + { + var opts = options.Value; + if (string.IsNullOrWhiteSpace(opts.Secret)) + throw new InvalidOperationException("Configuration Jwt:Secret manquante — voir appsettings.Development.json."); + + var claims = new List + { + new("id", user.Id.ToString()), + new(ClaimTypes.Email, user.Email), + new("nom", user.Nom), + new("prenom", user.Prenom), + new("profil", user.Profil), + // true seulement si au moins un utilisateur a ce compte comme responsable_id — + // pilote l'affichage "Responsable" côté frontend (Layout.tsx). + new("estResponsable", estResponsable ? "true" : "false"), + }; + // Sert à scoper les vues (ex. Pilotage) au service du collaborateur connecté — + // absent si l'utilisateur n'est rattaché à aucun service. + if (user.ServiceId.HasValue) + claims.Add(new Claim("serviceId", user.ServiceId.Value.ToString())); + // Un utilisateur peut cumuler plusieurs rôles métier (ex. DEMANDEUR + FINANCE) — + // "profil" reste la colonne historique à valeur unique, "roles" (claim répétée) + // porte la liste réelle depuis core.utilisateur_roles, utilisée pour le bascule + // d'espace de travail côté frontend. + foreach (var role in roles ?? []) + claims.Add(new Claim("roles", role)); + + var key = new SymmetricSecurityKey(Convert.FromBase64String(opts.Secret)); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + var token = new JwtSecurityToken( + claims: claims, + expires: DateTime.UtcNow.AddHours(opts.ExpiresHours <= 0 ? 8 : opts.ExpiresHours), + signingCredentials: creds); + + return new JwtSecurityTokenHandler().WriteToken(token); + } +} diff --git a/SIFinance.Api/Program.cs b/SIFinance.Api/Program.cs new file mode 100644 index 0000000..cdf7b9d --- /dev/null +++ b/SIFinance.Api/Program.cs @@ -0,0 +1,106 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; +using Microsoft.IdentityModel.Tokens; +using SIFinance.Api; +using SIFinance.Modules.Achats.Endpoints; +using SIFinance.Modules.Finance.Endpoints; +using SIFinance.Modules.SousTraitants.Endpoints; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +var builder = WebApplication.CreateBuilder(args); + +builder.Services.AddOpenApi(); + +builder.Services.AddDbContext(options => + options.UseSqlServer(builder.Configuration.GetConnectionString("Default"))); + +builder.Services.AddHostedService(); + +builder.Services.AddHttpClient(); + +builder.Services.Configure(builder.Configuration.GetSection(AzureAdOptions.SectionName)); +builder.Services.Configure(builder.Configuration.GetSection(JwtOptions.SectionName)); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddHostedService(); + +// Valide le jeton "maison" émis par SIFinance.Api après l'échange OAuth2 avec Entra ID +// (voir EntraAuthEndpoints) — pas de validation directe de jeton Entra ID : le backend +// pilote lui-même le flux de connexion, comme l'appli NDF existante (JWT maison + +// synchro périodique du groupe Entra ID via EntraSyncService), plutôt que MSAL.js + +// Microsoft.Identity.Web côté frontend. +var jwtSecret = builder.Configuration["Jwt:Secret"] + ?? throw new InvalidOperationException("Configuration Jwt:Secret manquante — voir appsettings.Development.json."); +builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = false, + ValidateAudience = false, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(Convert.FromBase64String(jwtSecret)), + }; + }); +builder.Services.AddAuthorization(); +builder.Services.AddHttpContextAccessor(); +builder.Services.AddScoped(); + +builder.Services.Configure(builder.Configuration.GetSection(SharePointOptions.SectionName)); + +// Pas de client Graph enregistré directement en DI : tant que TenantId/ClientId/ +// ClientSecret ne sont pas renseignés (ou invalides), ClientSecretCredential lève une +// ArgumentException dès sa construction — avant même le premier appel Graph. Si le +// client était un singleton construit par le conteneur DI, cette exception surviendrait +// pendant la résolution de la requête, hors de portée d'un try/catch dans l'endpoint, +// et remonterait comme une erreur 500 brute. SharePointGraphClientFactory construit le +// client à la demande, à l'intérieur du bloc try/catch de l'endpoint. +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + +const string FrontendCorsPolicy = "Frontend"; +builder.Services.AddCors(options => +{ + options.AddPolicy(FrontendCorsPolicy, policy => + policy.WithOrigins( + "http://localhost:5173", "http://localhost:5174", + "http://127.0.0.1:5173", "http://127.0.0.1:5174") + .AllowAnyHeader() + .AllowAnyMethod() + // Le navigateur ne mémorise le résultat d'un preflight OPTIONS que sur la durée + // indiquée ici (par défaut ~5s) : on l'étend pour éviter un aller-retour OPTIONS + // avant chaque POST/PUT/PATCH répété (création/édition) pendant la session. + .SetPreflightMaxAge(TimeSpan.FromMinutes(30))); +}); + +var app = builder.Build(); + +if (app.Environment.IsDevelopment()) +{ + app.MapOpenApi(); +} + +if (!app.Environment.IsDevelopment()) +{ + app.UseHttpsRedirection(); +} +app.UseCors(FrontendCorsPolicy); +app.UseAuthentication(); +app.UseAuthorization(); + +app.MapAchatsEndpoints(); +app.MapContratsEndpoints(); +app.MapBudgetsEndpoints(); +app.MapFacturesEndpoints(); +app.MapAvoirsEndpoints(); +app.MapPaiementsEndpoints(); +app.MapDashboardEndpoints(); +app.MapSharePointEndpoints(); +app.MapEntraAuthEndpoints(); +app.MapOrganismesEndpoints(); +app.MapContratsPrestatairesEndpoints(); + +app.Run(); \ No newline at end of file diff --git a/SIFinance.Api/Properties/launchSettings.json b/SIFinance.Api/Properties/launchSettings.json new file mode 100644 index 0000000..488930e --- /dev/null +++ b/SIFinance.Api/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "http://0.0.0.0:5272", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": false, + "applicationUrl": "https://0.0.0.0:7286;http://0.0.0.0:5272", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/SIFinance.Api/SIFinance.Api.csproj b/SIFinance.Api/SIFinance.Api.csproj new file mode 100644 index 0000000..9bf9e7e --- /dev/null +++ b/SIFinance.Api/SIFinance.Api.csproj @@ -0,0 +1,29 @@ + + + + net10.0 + enable + enable + direct + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + diff --git a/SIFinance.Api/SIFinance.Api.http b/SIFinance.Api/SIFinance.Api.http new file mode 100644 index 0000000..a124d86 --- /dev/null +++ b/SIFinance.Api/SIFinance.Api.http @@ -0,0 +1,6 @@ +@SIFinance.Api_HostAddress = http://localhost:5272 + +GET {{SIFinance.Api_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/SIFinance.Api/SharePointEndpoints.cs b/SIFinance.Api/SharePointEndpoints.cs new file mode 100644 index 0000000..16c9740 --- /dev/null +++ b/SIFinance.Api/SharePointEndpoints.cs @@ -0,0 +1,101 @@ +using Azure.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Graph; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Api; + +public record SharePointSiteDto(string Id, string Name, string? WebUrl); + +public record SharePointUploadDto(string WebUrl, string ItemId, long Size, string Nom); + +// Endpoints de découverte SharePoint + upload de documents dans les dossiers modules +// (Achats / Sous-Traitants / Contrat Etudiant) du drive configuré (SharePoint:DriveId). +public static class SharePointEndpoints +{ + // Whitelist stricte plutôt que d'accepter n'importe quel nom de dossier envoyé par le + // client — évite qu'un chemin arbitraire soit construit dans l'URL Graph. + private static readonly HashSet DossiersAutorises = + new(StringComparer.OrdinalIgnoreCase) { "Achats", "Sous-Traitants", "Contrat Etudiant" }; + + public static IEndpointRouteBuilder MapSharePointEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/sharepoint").WithTags("SharePoint").RequireAuthorization(); + + group.MapGet("/sites", async (SharePointGraphClientFactory graphFactory, CancellationToken ct) => + { + try + { + var graph = graphFactory.Create(); + var result = await graph.Sites.GetAsync(cfg => cfg.QueryParameters.Search = "*", ct); + var sites = (result?.Value ?? []) + .Select(s => new SharePointSiteDto(s.Id ?? "", s.Name ?? s.DisplayName ?? "(sans nom)", s.WebUrl)) + .ToList(); + return Results.Ok(sites); + } + catch (Exception ex) + { + // Cas fréquents : secret pas encore renseigné, permission Sites.Read.All + // absente, ou admin consent pas encore accordé (403). + return Results.BadRequest(new + { + error = "Impossible de récupérer la liste des sites SharePoint — vérifie les identifiants et les permissions (Sites.Read.All ou Sites.ReadWrite.All, avec admin consent).", + detail = ex.Message, + }); + } + }); + + group.MapPost("/upload", async ( + HttpRequest request, SharePointUploadService uploadService, + SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (!request.HasFormContentType) + return Results.BadRequest(new { error = "Contenu multipart attendu." }); + + var form = await request.ReadFormAsync(ct); + var file = form.Files.GetFile("file"); + var dossier = form["dossier"].ToString(); + var service = form["service"].ToString(); + + if (file is null || file.Length == 0) + return Results.BadRequest(new { error = "Fichier manquant." }); + if (!DossiersAutorises.Contains(dossier)) + return Results.BadRequest(new { error = "Dossier de destination invalide." }); + + // Le sous-dossier "collaborateur" est résolu depuis l'utilisateur authentifié, + // jamais depuis une valeur envoyée par le client — sinon n'importe qui pourrait + // faire apparaître son upload dans le dossier d'un collègue. + var userId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var user = await db.Utilisateurs.AsNoTracking().FirstOrDefaultAsync(u => u.Id == userId, ct); + var collaborateur = user is null ? "Autre" : $"{user.Prenom} {user.Nom}"; + + var segments = new List { dossier }; + if (!string.IsNullOrWhiteSpace(service)) segments.Add(SanitizeSegment(service)); + segments.Add(SanitizeSegment(collaborateur)); + var cheminDossier = string.Join('/', segments); + + try + { + await using var stream = file.OpenReadStream(); + var result = await uploadService.UploadAsync(cheminDossier, file.FileName, stream, file.ContentType, ct); + return Results.Ok(new SharePointUploadDto(result.WebUrl, result.ItemId, result.Size, file.FileName)); + } + catch (Exception ex) + { + return Results.BadRequest(new { error = "Échec de l'upload vers SharePoint.", detail = ex.Message }); + } + }); + + return app; + } + + private static string SanitizeSegment(string value) + { + var trimmed = value.Trim(); + if (trimmed.Length == 0) return "Autre"; + var invalides = new[] { '"', '*', ':', '<', '>', '?', '/', '\\', '|' }; + var nettoye = new string(trimmed.Select(c => invalides.Contains(c) ? '-' : c).ToArray()); + return nettoye.Length == 0 ? "Autre" : nettoye; + } +} diff --git a/SIFinance.Api/SharePointGraphClientFactory.cs b/SIFinance.Api/SharePointGraphClientFactory.cs new file mode 100644 index 0000000..00f9f9f --- /dev/null +++ b/SIFinance.Api/SharePointGraphClientFactory.cs @@ -0,0 +1,22 @@ +using Azure.Identity; +using Microsoft.Extensions.Options; +using Microsoft.Graph; + +namespace SIFinance.Api; + +// Construit le GraphServiceClient à la demande plutôt qu'au démarrage : tant que les +// identifiants SharePoint (TenantId/ClientId/ClientSecret) ne sont pas renseignés, +// ClientSecretCredential lève une exception à la construction — on veut que ça arrive +// à l'intérieur du try/catch de l'endpoint qui l'appelle, pas pendant la résolution DI. +public class SharePointGraphClientFactory(IOptions options) +{ + public GraphServiceClient Create() + { + var opts = options.Value; + if (string.IsNullOrWhiteSpace(opts.TenantId) || string.IsNullOrWhiteSpace(opts.ClientId) || string.IsNullOrWhiteSpace(opts.ClientSecret)) + throw new InvalidOperationException("Configuration SharePoint incomplète (TenantId/ClientId/ClientSecret) — voir SIFinance.Api/appsettings.Development.json."); + + var credential = new ClientSecretCredential(opts.TenantId, opts.ClientId, opts.ClientSecret); + return new GraphServiceClient(credential, ["https://graph.microsoft.com/.default"]); + } +} diff --git a/SIFinance.Api/SharePointOptions.cs b/SIFinance.Api/SharePointOptions.cs new file mode 100644 index 0000000..467e403 --- /dev/null +++ b/SIFinance.Api/SharePointOptions.cs @@ -0,0 +1,16 @@ +namespace SIFinance.Api; + +// Section "SharePoint" de appsettings.Development.json — identifiants de l'app +// registration Azure AD (flux client credentials, app-only, pas de connexion +// utilisateur). SiteId/DriveId sont renseignés une fois trouvés via +// GET /api/sharepoint/sites (et le futur endpoint pour les drives). +public class SharePointOptions +{ + public const string SectionName = "SharePoint"; + + public string TenantId { get; set; } = ""; + public string ClientId { get; set; } = ""; + public string ClientSecret { get; set; } = ""; + public string SiteId { get; set; } = ""; + public string DriveId { get; set; } = ""; +} diff --git a/SIFinance.Api/SharePointUploadService.cs b/SIFinance.Api/SharePointUploadService.cs new file mode 100644 index 0000000..c70d240 --- /dev/null +++ b/SIFinance.Api/SharePointUploadService.cs @@ -0,0 +1,80 @@ +using System.Net.Http.Headers; +using System.Net.Http.Json; +using System.Text.Json; +using Microsoft.Extensions.Options; +using SIFinance.Shared; + +namespace SIFinance.Api; + +public record SharePointUploadResult(string WebUrl, string ItemId, long Size); + +// Dépose un fichier dans un sous-dossier du drive SharePoint configuré (SharePoint:DriveId) +// — un dossier par module (Achats / Sous-Traitants / Contrat Etudiant), créés lors du +// provisioning du site. Réutilise le jeton Graph "app-only" déjà mis en cache par +// EntraGraphTokenProvider plutôt que ClientSecretCredential/GraphServiceClient : plus +// simple pour un simple PUT de contenu, et le jeton couvre déjà Sites.ReadWrite.All. +public class SharePointUploadService( + IHttpClientFactory httpClientFactory, + EntraGraphTokenProvider tokenProvider, + IOptions options) +{ + // Upload "simple" (PUT direct) : suffisant jusqu'à 4 Mo, ce qui couvre largement les + // justificatifs (devis, factures, bons de commande scannés). Au-delà, Graph exige une + // session d'upload par blocs — pas implémenté ici, hors scope du premier jet. + // cheminDossier peut contenir plusieurs segments (ex: "Achats/Informatique/Jean Dupont") — + // Graph crée automatiquement les dossiers intermédiaires manquants lors d'un upload + // vers un chemin qui n'existe pas encore, pas besoin de les créer explicitement. + public async Task UploadAsync(string cheminDossier, string fileName, Stream content, string? contentType, CancellationToken ct) + { + var opts = options.Value; + if (string.IsNullOrWhiteSpace(opts.DriveId)) + throw new InvalidOperationException("Configuration SharePoint incomplète (DriveId) — voir appsettings.Development.json."); + + var token = await tokenProvider.GetTokenAsync(ct); + var http = httpClientFactory.CreateClient(); + http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + + var safeName = Uri.EscapeDataString(fileName); + var url = $"https://graph.microsoft.com/v1.0/drives/{opts.DriveId}/root:/{cheminDossier}/{safeName}:/content?@microsoft.graph.conflictBehavior=rename"; + + using var streamContent = new StreamContent(content); + streamContent.Headers.ContentType = new MediaTypeHeaderValue(string.IsNullOrWhiteSpace(contentType) ? "application/octet-stream" : contentType); + + var response = await http.PutAsync(url, streamContent, ct); + var body = await response.Content.ReadAsStringAsync(ct); + if (!response.IsSuccessStatusCode) + throw new InvalidOperationException($"Échec de l'upload SharePoint ({response.StatusCode}) : {body}"); + + var json = JsonDocument.Parse(body).RootElement; + var itemWebUrl = json.GetProperty("webUrl").GetString()!; + var itemId = json.GetProperty("id").GetString()!; + var size = json.TryGetProperty("size", out var sizeProp) ? sizeProp.GetInt64() : content.Length; + + // L'URL brute de l'item n'est accessible qu'aux membres du groupe SharePoint + // sous-jacent — un lien de partage "organisation" permet à n'importe quel + // utilisateur du tenant, connecté avec son compte Microsoft, de l'ouvrir sans + // avoir besoin d'être membre du groupe. On le préfère donc à l'URL brute quand + // Graph accepte de le créer (si ça échoue, on retombe sur l'URL brute plutôt + // que de faire échouer tout l'upload). + var lienOrganisation = await CreerLienOrganisationAsync(opts.DriveId, itemId, http, ct); + return new SharePointUploadResult(lienOrganisation ?? itemWebUrl, itemId, size); + } + + private static async Task CreerLienOrganisationAsync(string driveId, string itemId, HttpClient http, CancellationToken ct) + { + try + { + var response = await http.PostAsJsonAsync( + $"https://graph.microsoft.com/v1.0/drives/{driveId}/items/{itemId}/createLink", + new { type = "view", scope = "organization" }, ct); + if (!response.IsSuccessStatusCode) return null; + + var json = JsonDocument.Parse(await response.Content.ReadAsStringAsync(ct)).RootElement; + return json.GetProperty("link").GetProperty("webUrl").GetString(); + } + catch + { + return null; + } + } +} diff --git a/SIFinance.Api/appsettings.Development.json b/SIFinance.Api/appsettings.Development.json new file mode 100644 index 0000000..568724b --- /dev/null +++ b/SIFinance.Api/appsettings.Development.json @@ -0,0 +1,22 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AzureAd": { + "ClientSecret": "xA.8Q~fRieIg-HZEunz__xLws0a4E.hdJU88sb8s", + "GroupId": "c1ea877c-6bca-4f47-bfad-f223640813a0" + }, + "Jwt": { + "Secret": "83/Ug9ginhRDJJYUWy2YWlAxu5egbzgewuYaV8elGPxlxXwzWI5GokMD7I9QvhbmA7tz9+IlPOkqy7/uInwUcg==" + }, + "SharePoint": { + "TenantId": "9840a2a0-6ae1-4688-b03d-d2ec291be0f9", + "ClientId": "277b070b-83f7-4c15-8787-7acb51c2f567", + "ClientSecret": "xA.8Q~fRieIg-HZEunz__xLws0a4E.hdJU88sb8s", + "SiteId": "ensup.sharepoint.com,9096beec-6c67-45a9-bba1-693f767c00d4,dc598513-c302-44ce-b25e-6b369c0ba602", + "DriveId": "b!7L6WkGdsqUW7oWk_dnwA1BOFWdwCw85Esl5rNpwLpgIIr8dZ90UYTqtD1bA3wKY7" + } +} diff --git a/SIFinance.Api/appsettings.json b/SIFinance.Api/appsettings.json new file mode 100644 index 0000000..5cd8885 --- /dev/null +++ b/SIFinance.Api/appsettings.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "ConnectionStrings": { + "Default": "Server=192.168.0.3,1433;Database=SIFinance;Trusted_Connection=True;TrustServerCertificate=True;Connection Lifetime=60;Min Pool Size=4" + }, + "AzureAd": { + "TenantId": "9840a2a0-6ae1-4688-b03d-d2ec291be0f9", + "ClientId": "277b070b-83f7-4c15-8787-7acb51c2f567", + "RedirectUri": "http://localhost:5272/api/auth/callback" + }, + "FrontendUrl": "http://localhost:5173" +} \ No newline at end of file diff --git a/SIFinance.Modules.Achats/Dtos/BudgetDtos.cs b/SIFinance.Modules.Achats/Dtos/BudgetDtos.cs new file mode 100644 index 0000000..aa49633 --- /dev/null +++ b/SIFinance.Modules.Achats/Dtos/BudgetDtos.cs @@ -0,0 +1,21 @@ +namespace SIFinance.Modules.Achats.Dtos; + +public record BudgetRevisionDto(int Id, decimal MontantRevise, string? Commentaire, string RevisePar, DateTime DateRevision); + +public record BudgetServiceDto( + int Id, + int ServiceId, + string Service, + int ExerciceAnnee, + decimal MontantInitial, + decimal? MontantRevise, + List Revisions); + +// L'exercice n'est pas calendaire : ExerciceAnnee=2026 désigne la période du 01/09/2026 +// au 31/08/2027 (cf. BudgetService.cs). Un budget initial est unique par service et par +// exercice — la création échoue s'il en existe déjà un (contrainte d'unicité en base). +public record CreateBudgetServiceDto(int ServiceId, int ExerciceAnnee, decimal MontantInitial); + +// Chaque révision est ajoutée à l'historique, jamais écrasée — MontantRevise doit +// représenter le nouveau plafond total, pas un delta à ajouter. +public record CreateBudgetRevisionDto(decimal MontantRevise, string? Commentaire); diff --git a/SIFinance.Modules.Achats/Dtos/ContratDtos.cs b/SIFinance.Modules.Achats/Dtos/ContratDtos.cs new file mode 100644 index 0000000..7f8b642 --- /dev/null +++ b/SIFinance.Modules.Achats/Dtos/ContratDtos.cs @@ -0,0 +1,57 @@ +namespace SIFinance.Modules.Achats.Dtos; + +public record ContratEcheanceDto(int Id, DateOnly Date, decimal Montant, string Statut, string? JustificatifNom); + +public record ContratDocumentDto(int Id, DateOnly Date, string Tag, string Nom); + +// Rapprochement facture ↔ contrat : permet de voir, sur la fiche contrat, quelles factures +// ont déjà été saisies contre lui (sans lien automatique aux échéances — le rapprochement +// avec une échéance précise reste une vérification humaine). +public record ContratFactureLieeDto(int Id, string Numero, decimal MontantTtc, DateOnly DateFacture, string Statut); + +public record ContratDto( + int Id, + int FournisseurId, + string Fournisseur, + int? ServiceId, + string? Service, + int? CampusId, + string? Campus, + string? Reference, + string Objet, + DateOnly? DateSignature, + DateOnly DateDebut, + DateOnly DateFin, + DateOnly? DateResiliation, + string? MotifResiliation, + decimal MontantTotal, + string Periodicite, + string Statut, + List Echeances, + List Documents, + List FacturesLiees); + +public record CreateContratEcheanceDto(DateOnly Date, decimal Montant); + +public record CreateContratDto( + int FournisseurId, + int? ServiceId, + int? CampusId, + string Objet, + string? Reference, + DateOnly? DateSignature, + DateOnly DateDebut, + DateOnly DateFin, + decimal MontantTotal, + string Periodicite, + List Echeances); + +public record ChangerStatutContratDto(string Statut, DateOnly? DateResiliation, string? MotifResiliation, string? Commentaire); + +public record ContratHistoriqueDto(int Id, string? AncienStatut, string NouveauStatut, string? Commentaire, string Utilisateur, DateTime DateAction); + +public record UpdateEcheanceDto(DateOnly Date, decimal Montant, string Statut, string? JustificatifNom); + +public record AddDocumentDto(string Tag, string Nom, DateOnly? Date); + +public record UpdateDocumentDto(string Tag, DateOnly Date); diff --git a/SIFinance.Modules.Achats/Dtos/DashboardEndpoints.cs b/SIFinance.Modules.Achats/Dtos/DashboardEndpoints.cs new file mode 100644 index 0000000..8f40c49 --- /dev/null +++ b/SIFinance.Modules.Achats/Dtos/DashboardEndpoints.cs @@ -0,0 +1,127 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.Achats.Endpoints; + +// Endpoint agrégé du tableau de bord : tous les compteurs et sommes sont calculés +// par SQL Server (COUNT/SUM), et seules les quelques lignes réellement affichées +// sont transférées. Le temps de réponse ne dépend donc plus du volume en base. +public static class DashboardEndpoints +{ + private static readonly string[] StatutsEnAttente = ["SOUMISE", "EN_VALIDATION", "COMPLEMENT_REQUIS"]; + private static readonly string[] StatutsEngages = ["VALIDEE", "FACTUREE"]; + + public static IEndpointRouteBuilder MapDashboardEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/dashboard").WithTags("Dashboard").RequireAuthorization(); + + group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) => + { + var today = DateTime.UtcNow.Date; + // DateEcheance est un DateOnly côté entité : les comparaisons doivent se + // faire avec un DateOnly pour être traduites en SQL. + var todayDate = DateOnly.FromDateTime(today); + + // Bornes du trimestre courant et du précédent, calculées côté C# pour que + // la comparaison reste une simple comparaison de dates traduisible en SQL. + var trimCourant = (today.Month - 1) / 3; + var debutTrimestre = new DateTime(today.Year, trimCourant * 3 + 1, 1); + var debutTrimestrePrecedent = debutTrimestre.AddMonths(-3); + + var demandes = db.DemandesAchats.AsNoTracking(); + var factures = db.Factures.AsNoTracking(); + + // --- KPI demandes --- + var enAttente = demandes.Where(d => StatutsEnAttente.Contains(d.Statut)); + var nbAValider = await enAttente.CountAsync(ct); + var montantAValider = await enAttente.SumAsync(d => (decimal?)d.MontantTtc, ct) ?? 0m; + + var engagees = demandes.Where(d => StatutsEngages.Contains(d.Statut)); + var engageCourant = await engagees + .Where(d => d.DateCreation >= debutTrimestre) + .SumAsync(d => (decimal?)d.MontantTtc, ct) ?? 0m; + var engagePrecedent = await engagees + .Where(d => d.DateCreation >= debutTrimestrePrecedent && d.DateCreation < debutTrimestre) + .SumAsync(d => (decimal?)d.MontantTtc, ct) ?? 0m; + + // --- KPI factures --- + var aPayer = factures.Where(f => f.Statut == "validee"); + var nbAPayer = await aPayer.CountAsync(ct); + var montantAPayer = await aPayer.SumAsync(f => (decimal?)f.MontantTtc, ct) ?? 0m; + + var enCours = factures.Where(f => f.Statut != "payee"); + var nbEchues = await enCours.CountAsync(f => f.DateEcheance < todayDate, ct); + var nbProches = await enCours.CountAsync(f => f.DateEcheance >= todayDate && f.DateEcheance <= todayDate.AddDays(7), ct); + + // --- Listes affichées (volumes bornés par Take) --- + var demandesRecentes = await demandes + .OrderByDescending(d => d.DateCreation) + .Take(4) + .Select(d => new DashboardDemandeDto(d.Id, d.Reference, d.Objet, d.MontantTtc, d.Statut, d.TypeDepenseId)) + .ToListAsync(ct); + + var demandesAValiderApercu = await enAttente + .OrderByDescending(d => d.DateCreation) + .Take(1) + .Select(d => new DashboardDemandeDto(d.Id, d.Reference, d.Objet, d.MontantTtc, d.Statut, d.TypeDepenseId)) + .ToListAsync(ct); + + var echeances = await enCours + .OrderBy(f => f.DateEcheance) + .Take(5) + .Select(f => new DashboardEcheanceDto(f.Id, f.NumeroFacture, f.Fournisseur.RaisonSociale, f.MontantTtc, f.DateEcheance)) + .ToListAsync(ct); + + // Alertes : uniquement les premières lignes réellement affichées. + var facturesEchues = await enCours + .Where(f => f.DateEcheance < todayDate) + .OrderBy(f => f.DateEcheance) + .Take(5) + .Select(f => new DashboardEcheanceDto(f.Id, f.NumeroFacture, f.Fournisseur.RaisonSociale, f.MontantTtc, f.DateEcheance)) + .ToListAsync(ct); + + var avoirsDisponibles = await db.Avoirs + .AsNoTracking() + .Where(a => a.SoldeDisponible > 0) + .OrderByDescending(a => a.DateCreation) + .Take(5) + .Select(a => new DashboardAvoirDto(a.Id, a.NumeroAvoir, a.Fournisseur.RaisonSociale, a.SoldeDisponible)) + .ToListAsync(ct); + + return Results.Ok(new DashboardDto( + engageCourant, engagePrecedent, + nbAValider, montantAValider, + nbAPayer, montantAPayer, + nbProches, nbEchues, + demandesRecentes, + demandesAValiderApercu.FirstOrDefault(), + facturesEchues, + avoirsDisponibles, + echeances)); + }); + + return app; + } +} + +public record DashboardDemandeDto(int Id, string Reference, string Objet, decimal MontantTtc, string Statut, int TypeDepenseId); +public record DashboardEcheanceDto(int Id, string Numero, string Fournisseur, decimal MontantTtc, DateOnly DateEcheance); +public record DashboardAvoirDto(int Id, string Numero, string Fournisseur, decimal SoldeDisponible); + +public record DashboardDto( + decimal EngageCourant, + decimal EngagePrecedent, + int NbAValider, + decimal MontantAValider, + int NbAPayer, + decimal MontantAPayer, + int NbEcheancesProches, + int NbFacturesEchues, + IReadOnlyList DemandesRecentes, + DashboardDemandeDto? PremiereAValider, + IReadOnlyList FacturesEchues, + IReadOnlyList AvoirsDisponibles, + IReadOnlyList Echeances); \ No newline at end of file diff --git a/SIFinance.Modules.Achats/Dtos/DemandeAchatCreateDto.cs b/SIFinance.Modules.Achats/Dtos/DemandeAchatCreateDto.cs new file mode 100644 index 0000000..4513a9e --- /dev/null +++ b/SIFinance.Modules.Achats/Dtos/DemandeAchatCreateDto.cs @@ -0,0 +1,41 @@ +namespace SIFinance.Modules.Achats.Dtos; + +public record CreateJustificatifDto( + string Libelle, string Nom, + string? SharepointUrl, string? SharepointItemId, long? TailleOctets); + +public record DemandeAchatCreateDto( + int ServiceId, + string Objet, + string? Description, + int? FournisseurId, + string? FournisseurLibre, + string? CategorieFournisseurLibre, + int TypeDepenseId, + decimal MontantHt, + decimal TauxTva, + DateOnly DatePrevisionnelle, + int ModePaiementId, + List? Justificatifs); + +public record UpdateDemandeStatutDto(string Statut); + +// Le workflow matériel utilise des actions, plutôt qu'un statut libre envoyé par le +// navigateur : l'API conserve ainsi le contrôle des transitions et des rôles autorisés. +public record WorkflowDemandeDto( + string Action, + string? Commentaire, + string? DocumentNom = null, + string? DocumentSharepointUrl = null, + string? DocumentSharepointItemId = null, + long? DocumentTailleOctets = null); + +public record AjouterFactureDemandeDto( + string Numero, + decimal Montant, + DateOnly DateFacture, + DateOnly DateEcheance, + string DocumentNom, + string DocumentSharepointUrl, + string DocumentSharepointItemId, + long DocumentTailleOctets); diff --git a/SIFinance.Modules.Achats/Dtos/DemandeAchatDto.cs b/SIFinance.Modules.Achats/Dtos/DemandeAchatDto.cs new file mode 100644 index 0000000..b180d91 --- /dev/null +++ b/SIFinance.Modules.Achats/Dtos/DemandeAchatDto.cs @@ -0,0 +1,35 @@ +namespace SIFinance.Modules.Achats.Dtos; + +public record DemandeJustificatifDto(int Id, string Libelle, string Nom, DateTime? DateUpload, string? SharepointUrl); +public record DemandeWorkflowHistoriqueDto(string Action, string? Commentaire, string? AncienStatut, string? NouveauStatut, string Auteur, DateTime? DateAction); + +public record DemandeAchatDto( + int Id, + string Reference, + int ServiceId, + string Objet, + string? Description, + int? FournisseurId, + string? FournisseurLibre, + string? CategorieFournisseurLibre, + int TypeDepenseId, + decimal MontantHt, + decimal TauxTva, + decimal MontantTva, + decimal MontantTtc, + DateOnly DatePrevisionnelle, + int ModePaiementId, + string Statut, + int DemandeurId, + string DemandeurNom, + int? ValideurId, + string? ValideurNom, + decimal SeuilApplicable, + DateTime? DateCreation, + DateTime? DateSoumission, + DateTime? DateDerniereMaj, + List Justificatifs, + bool PeutValider, + bool FactureAjoutee = false, + string? FactureUrl = null, + string? FactureNom = null); diff --git a/SIFinance.Modules.Achats/Dtos/ReferenceDtos.cs b/SIFinance.Modules.Achats/Dtos/ReferenceDtos.cs new file mode 100644 index 0000000..ecd61ab --- /dev/null +++ b/SIFinance.Modules.Achats/Dtos/ReferenceDtos.cs @@ -0,0 +1,17 @@ +namespace SIFinance.Modules.Achats.Dtos; + +public record ServiceDto(int Id, string Libelle); + +public record TypeDepenseDto(int Id, string Libelle); + +public record ModePaiementDto(int Id, string Libelle, bool NecessiteXml); + +public record FournisseurDto(int Id, string RaisonSociale, string? Siret, string Statut); + +public record FournisseurDetailDto( + int Id, string RaisonSociale, string? Siret, string? Email, string? Telephone, + string? Adresse, string Statut, DateTime? DateCreation); + +public record FournisseurSaveDto( + string RaisonSociale, string? Siret, string? Email, string? Telephone, + string? Adresse, string Statut); diff --git a/SIFinance.Modules.Achats/Endpoints/AchatsEndpoints.cs b/SIFinance.Modules.Achats/Endpoints/AchatsEndpoints.cs new file mode 100644 index 0000000..b8d748d --- /dev/null +++ b/SIFinance.Modules.Achats/Endpoints/AchatsEndpoints.cs @@ -0,0 +1,709 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using SIFinance.Shared; +using SIFinance.Modules.Achats.Dtos; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.Achats.Endpoints; + +public static class AchatsEndpoints +{ + public static IEndpointRouteBuilder MapAchatsEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api").WithTags("Achats").RequireAuthorization(); + + group.MapGet("/services", async (SIFinanceDbContext db, CancellationToken ct) => + await db.Services + .AsNoTracking() + .Where(s => s.Actif) + .OrderBy(s => s.Libelle) + .Select(s => new ServiceDto(s.Id, s.Libelle)) + .ToListAsync(ct)); + + group.MapGet("/campus", async (SIFinanceDbContext db, CancellationToken ct) => + await db.Campus.AsNoTracking().OrderBy(c => c.Libelle) + .Select(c => new { c.Id, c.Libelle }).ToListAsync(ct)); + + group.MapGet("/types-depense", async (SIFinanceDbContext db, CancellationToken ct) => + await db.TypesDepenses + .AsNoTracking() + .Where(t => t.Actif) + .OrderBy(t => t.Libelle) + .Select(t => new TypeDepenseDto(t.Id, t.Libelle)) + .ToListAsync(ct)); + + group.MapGet("/modes-paiement", async (SIFinanceDbContext db, CancellationToken ct) => + await db.ModesPaiements + .AsNoTracking() + .Where(m => m.Actif) + .OrderBy(m => m.Libelle) + .Select(m => new ModePaiementDto(m.Id, m.Libelle, m.NecessiteXml)) + .ToListAsync(ct)); + + group.MapGet("/fournisseurs", async (SIFinanceDbContext db, CancellationToken ct) => + await db.Fournisseurs + .AsNoTracking() + .Where(f => f.Statut == "ACTIF") + .OrderBy(f => f.RaisonSociale) + .Select(f => new FournisseurDto(f.Id, f.RaisonSociale, f.Siret, f.Statut)) + .ToListAsync(ct)); + + group.MapGet("/fournisseurs/all", async (SIFinanceDbContext db, CancellationToken ct) => + await db.Fournisseurs + .AsNoTracking() + .OrderBy(f => f.RaisonSociale) + .Select(f => new FournisseurDetailDto(f.Id, f.RaisonSociale, f.Siret, f.Email, f.Telephone, f.Adresse, f.Statut, f.DateCreation)) + .ToListAsync(ct)); + + group.MapPost("/fournisseurs", async (FournisseurSaveDto input, SIFinanceDbContext db, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.RaisonSociale)) + return Results.BadRequest(new { error = "La raison sociale est requise." }); + + var fournisseur = new Fournisseur + { + RaisonSociale = input.RaisonSociale.Trim(), + Siret = string.IsNullOrWhiteSpace(input.Siret) ? null : input.Siret.Trim(), + Email = string.IsNullOrWhiteSpace(input.Email) ? null : input.Email.Trim(), + Telephone = string.IsNullOrWhiteSpace(input.Telephone) ? null : input.Telephone.Trim(), + Adresse = string.IsNullOrWhiteSpace(input.Adresse) ? null : input.Adresse.Trim(), + Statut = input.Statut, + DateCreation = DateTime.UtcNow, + }; + + db.Fournisseurs.Add(fournisseur); + await db.SaveChangesAsync(ct); + + return Results.Created($"/api/fournisseurs/{fournisseur.Id}", + new FournisseurDetailDto(fournisseur.Id, fournisseur.RaisonSociale, fournisseur.Siret, fournisseur.Email, fournisseur.Telephone, fournisseur.Adresse, fournisseur.Statut, fournisseur.DateCreation)); + }); + + group.MapPut("/fournisseurs/{id:int}", async (int id, FournisseurSaveDto input, SIFinanceDbContext db, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.RaisonSociale)) + return Results.BadRequest(new { error = "La raison sociale est requise." }); + + var fournisseur = await db.Fournisseurs.FirstOrDefaultAsync(f => f.Id == id, ct); + if (fournisseur is null) return Results.NotFound(); + + fournisseur.RaisonSociale = input.RaisonSociale.Trim(); + fournisseur.Siret = string.IsNullOrWhiteSpace(input.Siret) ? null : input.Siret.Trim(); + fournisseur.Email = string.IsNullOrWhiteSpace(input.Email) ? null : input.Email.Trim(); + fournisseur.Telephone = string.IsNullOrWhiteSpace(input.Telephone) ? null : input.Telephone.Trim(); + fournisseur.Adresse = string.IsNullOrWhiteSpace(input.Adresse) ? null : input.Adresse.Trim(); + fournisseur.Statut = input.Statut; + + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapDelete("/fournisseurs/{id:int}", async (int id, SIFinanceDbContext db, CancellationToken ct) => + { + var fournisseur = await db.Fournisseurs.FirstOrDefaultAsync(f => f.Id == id, ct); + if (fournisseur is null) return Results.NotFound(); + + db.Fournisseurs.Remove(fournisseur); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException) + { + return Results.BadRequest(new { error = "Impossible de supprimer ce fournisseur : des demandes, factures, avoirs ou contrats y sont rattachés." }); + } + + return Results.NoContent(); + }); + + group.MapGet("/demandes", async (string? scope, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var isFinance = await currentUser.HasRoleAsync("FINANCE", db, ct); + + var query = db.DemandesAchats.AsNoTracking().Include(d => d.Demandeur).Include(d => d.Valideur).AsQueryable(); + + if (scope == "mine") + { + // Forcé explicitement (ex. page Archives, ou un utilisateur Finance+Responsable + // basculé en espace Collaborateur) : strictement ses propres demandes, rien d'autre. + query = query.Where(d => d.DemandeurId == currentUserId); + } + else if (!isFinance) + { + // Par défaut, sans le rôle FINANCE : ses propres demandes + celles de ses + // collaborateurs directs (il en est le responsable) — sans ça, un responsable + // qui n'a pas aussi le rôle FINANCE ne verrait jamais les demandes à valider. + query = query.Where(d => d.DemandeurId == currentUserId || d.Demandeur.ResponsableId == currentUserId); + } + // FINANCE sans scope=mine : visibilité totale (lecture seule — ne valide plus + // rien, cf. PATCH /demandes/{id}/statut). + + var demandes = await query + .OrderByDescending(d => d.DateCreation) + .ToListAsync(ct); + + var demandeIds = demandes.Select(d => d.Id).ToHashSet(); + + var justificatifs = await db.Set() + .AsNoTracking() + .Where(p => p.EntiteType == "DEMANDE") + .ToListAsync(ct); + + var parDemande = justificatifs + .Where(j => demandeIds.Contains(j.EntiteId)) + .ToLookup(j => j.EntiteId); + + var factureParDemande = await (from f in db.Factures.AsNoTracking() + join p in db.PiecesJointes.AsNoTracking() on f.Id equals p.EntiteId + where f.DemandeAchatId.HasValue && demandeIds.Contains(f.DemandeAchatId.Value) + && p.EntiteType == "FACTURE" + orderby p.DateUpload descending + select new { DemandeId = f.DemandeAchatId!.Value, p.SharepointUrl, p.NomFichier }) + .GroupBy(x => x.DemandeId) + .ToDictionaryAsync(g => g.Key, g => g.First(), ct); + + return demandes.Select(d => factureParDemande.TryGetValue(d.Id, out var facture) + ? ToDto(d, parDemande[d.Id], currentUserId, true, facture.SharepointUrl, facture.NomFichier) + : ToDto(d, parDemande[d.Id], currentUserId)); + }); + + group.MapGet("/demandes/{id:int}", async (int id, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var demande = await db.DemandesAchats.AsNoTracking().Include(d => d.Demandeur).Include(d => d.Valideur).FirstOrDefaultAsync(d => d.Id == id, ct); + if (demande is null) return Results.NotFound(); + + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var isFinance = await currentUser.HasRoleAsync("FINANCE", db, ct); + + if (!isFinance && demande.DemandeurId != currentUserId) + return Results.Forbid(); + + var justificatifs = await db.Set().AsNoTracking().Where(p => p.EntiteType == "DEMANDE" && p.EntiteId == id).ToListAsync(ct); + var facture = await (from f in db.Factures.AsNoTracking() + join p in db.PiecesJointes.AsNoTracking() on f.Id equals p.EntiteId + where f.DemandeAchatId == id && p.EntiteType == "FACTURE" + orderby p.DateUpload descending + select new { p.SharepointUrl, p.NomFichier }).FirstOrDefaultAsync(ct); + return Results.Ok(facture is null + ? ToDto(demande, justificatifs, currentUserId) + : ToDto(demande, justificatifs, currentUserId, true, facture.SharepointUrl, facture.NomFichier)); + }); + + // Journal réel des transitions : il permet à Finance comme au demandeur de + // suivre toutes les étapes déjà effectuées, avec leur auteur et commentaire. + group.MapGet("/demandes/{id:int}/historique", async (int id, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var demande = await db.DemandesAchats.AsNoTracking().FirstOrDefaultAsync(d => d.Id == id, ct); + if (demande is null) return Results.NotFound(); + + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + if (!await currentUser.HasRoleAsync("FINANCE", db, ct) && demande.DemandeurId != currentUserId) + return Results.Forbid(); + + return Results.Ok(await db.HistoriqueActions.AsNoTracking() + .Include(h => h.Utilisateur) + .Where(h => h.EntiteType == "DEMANDE" && h.EntiteId == id) + .OrderBy(h => h.DateAction) + .Select(h => new DemandeWorkflowHistoriqueDto(h.Action, h.Commentaire, h.AncienStatut, h.NouveauStatut, + $"{h.Utilisateur.Prenom} {h.Utilisateur.Nom}".Trim(), h.DateAction)) + .ToListAsync(ct)); + }); + + // Le demandeur peut déclarer la facture réellement reçue, quel que soit le + // type d'achat. La facture et son PDF sont créés dans la même transaction. + group.MapPost("/demandes/{id:int}/facture", async (int id, AjouterFactureDemandeDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var demande = await db.DemandesAchats.FirstOrDefaultAsync(d => d.Id == id, ct); + if (demande is null) return Results.NotFound(); + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + if (demande.DemandeurId != currentUserId) return Results.Forbid(); + if (demande.FournisseurId is null) return Results.BadRequest(new { error = "Un fournisseur référencé est requis pour ajouter une facture." }); + if (string.IsNullOrWhiteSpace(input.Numero) || input.Montant <= 0 || string.IsNullOrWhiteSpace(input.DocumentNom) + || string.IsNullOrWhiteSpace(input.DocumentSharepointUrl) || string.IsNullOrWhiteSpace(input.DocumentSharepointItemId) || input.DocumentTailleOctets <= 0) + return Results.BadRequest(new { error = "Les informations et le document de facture sont requis." }); + + const decimal tauxTva = 20m; + var montantHt = Math.Round(input.Montant / (1 + tauxTva / 100m), 2); + var facture = new Facture + { + SourceType = "MANUEL", DemandeAchatId = demande.Id, FournisseurId = demande.FournisseurId.Value, + NumeroFacture = input.Numero.Trim(), DateFacture = input.DateFacture, DateEcheance = input.DateEcheance, + MontantHt = montantHt, TauxTva = tauxTva, MontantTva = input.Montant - montantHt, MontantTtc = input.Montant, + ServiceId = demande.ServiceId, ModePaiementId = demande.ModePaiementId, Statut = "attente", + Commentaire = $"Ajoutée par le demandeur depuis {demande.Reference}.", SaisieParId = currentUserId, DateCreation = DateTime.UtcNow, + Acomptes = [new Acompte { Montant = input.Montant, DatePrevue = input.DateEcheance, Statut = "PREVU", SaisieParId = currentUserId, DateCreation = DateTime.UtcNow }], + }; + db.Factures.Add(facture); + await db.SaveChangesAsync(ct); + db.PiecesJointes.Add(new PiecesJointe + { + EntiteType = "FACTURE", EntiteId = facture.Id, TypePiece = "Facture fournisseur", NomFichier = input.DocumentNom.Trim(), + SharepointUrl = input.DocumentSharepointUrl.Trim(), SharepointItemId = input.DocumentSharepointItemId.Trim(), + TailleOctets = input.DocumentTailleOctets, UploaderId = currentUserId, DateUpload = DateTime.UtcNow, + }); + await db.SaveChangesAsync(ct); + return Results.Created($"/api/factures/{facture.Id}", new { facture.Id }); + }); + + group.MapPost("/demandes", async (DemandeAchatCreateDto input, SIFinanceDbContext db, CurrentUserService currentUser, NotificationEmailService notification, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.Objet)) + return Results.BadRequest(new { error = "L'objet est requis." }); + if (input.MontantHt <= 0) + return Results.BadRequest(new { error = "Le montant HT doit être supérieur à 0." }); + if (string.IsNullOrWhiteSpace(input.FournisseurLibre) && input.FournisseurId is null) + return Results.BadRequest(new { error = "Un fournisseur (référencé ou en saisie libre) est requis." }); + if (!string.IsNullOrWhiteSpace(input.FournisseurLibre) && string.IsNullOrWhiteSpace(input.CategorieFournisseurLibre)) + return Results.BadRequest(new { error = "La catégorie du fournisseur est requise en saisie libre." }); + + var montantTva = Math.Round(input.MontantHt * (input.TauxTva / 100m), 2); + var montantTtc = Math.Round(input.MontantHt + montantTva, 2); + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var fournisseurId = input.FournisseurId; + + // La saisie libre devient un véritable fournisseur référencé. On évite les + // doublons en réutilisant une raison sociale existante (insensible à la + // casse) et on conserve la catégorie saisie dans la demande pour le suivi. + if (!string.IsNullOrWhiteSpace(input.FournisseurLibre)) + { + var nomFournisseur = input.FournisseurLibre.Trim(); + var nomNormalise = nomFournisseur.ToUpper(); + var fournisseurExistant = await db.Fournisseurs + .FirstOrDefaultAsync(f => f.RaisonSociale.ToUpper() == nomNormalise, ct); + if (fournisseurExistant is null) + { + fournisseurExistant = new Fournisseur + { + RaisonSociale = nomFournisseur, + Statut = "ACTIF", + DateCreation = DateTime.UtcNow, + }; + db.Fournisseurs.Add(fournisseurExistant); + await db.SaveChangesAsync(ct); + } + fournisseurId = fournisseurExistant.Id; + } + + var demande = new DemandesAchat + { + Reference = await GenererReferenceAsync(db, ct), + ServiceId = input.ServiceId, + Objet = input.Objet.Trim(), + Description = string.IsNullOrWhiteSpace(input.Description) ? null : input.Description.Trim(), + FournisseurId = fournisseurId, + FournisseurLibre = null, + CategorieFournisseurLibre = string.IsNullOrWhiteSpace(input.CategorieFournisseurLibre) ? null : input.CategorieFournisseurLibre.Trim(), + TypeDepenseId = input.TypeDepenseId, + MontantHt = input.MontantHt, + TauxTva = input.TauxTva, + MontantTva = montantTva, + MontantTtc = montantTtc, + DatePrevisionnelle = input.DatePrevisionnelle, + ModePaiementId = input.ModePaiementId, + Statut = "SOUMISE", + DemandeurId = currentUserId, + SeuilApplicable = CalculerSeuil(input.MontantHt), + DateCreation = DateTime.UtcNow, + DateSoumission = DateTime.UtcNow, + }; + + db.DemandesAchats.Add(demande); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException ex) + { + return Results.BadRequest(new + { + error = "Impossible d'enregistrer la demande — vérifiez le service, le fournisseur, le type de dépense et le mode de paiement.", + detail = ex.InnerException?.Message ?? ex.Message, + }); + } + + var justificatifs = new List(); + if (input.Justificatifs is { Count: > 0 }) + { + foreach (var j in input.Justificatifs) + { + if (string.IsNullOrWhiteSpace(j.Nom)) continue; + justificatifs.Add(new PiecesJointe + { + EntiteType = "DEMANDE", + EntiteId = demande.Id, + TypePiece = string.IsNullOrWhiteSpace(j.Libelle) ? "Justificatif" : j.Libelle.Trim(), + NomFichier = j.Nom.Trim(), + SharepointUrl = j.SharepointUrl ?? "", + SharepointItemId = j.SharepointItemId ?? "", + TailleOctets = j.TailleOctets ?? 0, + UploaderId = currentUserId, + DateUpload = DateTime.UtcNow, + }); + } + if (justificatifs.Count > 0) + { + db.Set().AddRange(justificatifs); + await db.SaveChangesAsync(ct); + } + } + + // Notifie le responsable direct du demandeur par email — best-effort : un + // échec d'envoi (mailbox introuvable, Graph indisponible...) ne doit jamais + // faire échouer la création de la demande, déjà enregistrée à ce stade. + var demandeurAvecResponsable = await db.Utilisateurs.AsNoTracking() + .Include(u => u.Responsable) + .FirstOrDefaultAsync(u => u.Id == currentUserId, ct); + if (demandeurAvecResponsable?.Responsable is { Actif: true } responsable && !string.IsNullOrWhiteSpace(responsable.Email)) + { + var sujet = $"Nouvelle demande d'achat à valider — {demande.Reference}"; + var corps = $"

Bonjour {responsable.Prenom},

" + + $"

{demandeurAvecResponsable.Prenom} {demandeurAvecResponsable.Nom} a soumis une nouvelle demande d'achat :

" + + "
    " + + $"
  • Référence : {demande.Reference}
  • " + + $"
  • Objet : {demande.Objet}
  • " + + $"
  • Montant TTC : {demande.MontantTtc:N2} €
  • " + + "
" + + "

Merci de la consulter dans SI Finance pour la valider ou la refuser.

"; + await notification.EnvoyerAsync(demandeurAvecResponsable.Email, responsable.Email, sujet, corps, ct); + } + + // Le demandeur est toujours l'utilisateur courant ici : pas besoin de charger + // la navigation Demandeur, ToDto affichera "Moi" via la comparaison d'id. + return Results.Created($"/api/demandes/{demande.Id}", ToDto(demande, justificatifs, currentUserId)); + }); + + group.MapPut("/demandes/{id:int}", async (int id, DemandeAchatCreateDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var demande = await db.DemandesAchats.FirstOrDefaultAsync(d => d.Id == id, ct); + if (demande is null) return Results.NotFound(); + + // Seul l'auteur de la demande peut la modifier, et seulement tant qu'elle n'a + // pas encore été tranchée — les mêmes statuts que ceux où un responsable peut + // encore la valider/refuser (cf. PATCH /demandes/{id}/statut). + if (demande.DemandeurId != currentUserId) + return Results.Forbid(); + if (demande.Statut is not ("SOUMISE" or "EN_VALIDATION" or "COMPLEMENT_REQUIS")) + return Results.BadRequest(new { error = "Cette demande ne peut plus être modifiée." }); + + if (string.IsNullOrWhiteSpace(input.Objet)) + return Results.BadRequest(new { error = "L'objet est requis." }); + if (input.MontantHt <= 0) + return Results.BadRequest(new { error = "Le montant HT doit être supérieur à 0." }); + if (string.IsNullOrWhiteSpace(input.FournisseurLibre) && input.FournisseurId is null) + return Results.BadRequest(new { error = "Un fournisseur (référencé ou en saisie libre) est requis." }); + if (!string.IsNullOrWhiteSpace(input.FournisseurLibre) && string.IsNullOrWhiteSpace(input.CategorieFournisseurLibre)) + return Results.BadRequest(new { error = "La catégorie du fournisseur est requise en saisie libre." }); + + var montantTva = Math.Round(input.MontantHt * (input.TauxTva / 100m), 2); + var montantTtc = Math.Round(input.MontantHt + montantTva, 2); + + demande.ServiceId = input.ServiceId; + demande.Objet = input.Objet.Trim(); + demande.Description = string.IsNullOrWhiteSpace(input.Description) ? null : input.Description.Trim(); + demande.FournisseurId = input.FournisseurId; + demande.FournisseurLibre = string.IsNullOrWhiteSpace(input.FournisseurLibre) ? null : input.FournisseurLibre.Trim(); + demande.CategorieFournisseurLibre = string.IsNullOrWhiteSpace(input.CategorieFournisseurLibre) ? null : input.CategorieFournisseurLibre.Trim(); + demande.TypeDepenseId = input.TypeDepenseId; + demande.MontantHt = input.MontantHt; + demande.TauxTva = input.TauxTva; + demande.MontantTva = montantTva; + demande.MontantTtc = montantTtc; + demande.DatePrevisionnelle = input.DatePrevisionnelle; + demande.ModePaiementId = input.ModePaiementId; + demande.SeuilApplicable = CalculerSeuil(input.MontantHt); + // Une modification remet la demande au début du circuit de validation — un + // responsable ayant déjà commencé à l'examiner (EN_VALIDATION) ou demandé un + // complément doit la revoir depuis le début, le contenu ayant changé. + demande.Statut = "SOUMISE"; + demande.ValideurId = null; + demande.DateDerniereMaj = DateTime.UtcNow; + + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException ex) + { + return Results.BadRequest(new + { + error = "Impossible d'enregistrer la modification — vérifiez le service, le fournisseur, le type de dépense et le mode de paiement.", + detail = ex.InnerException?.Message ?? ex.Message, + }); + } + + // Le frontend ne pré-remplit jamais les justificatifs déjà existants dans le + // formulaire d'édition (affichés à part, en lecture seule) — tout ce qui arrive + // ici dans input.Justificatifs est donc forcément une pièce ajoutée pendant + // cette modification, à insérer sans toucher aux justificatifs déjà en base. + if (input.Justificatifs is { Count: > 0 }) + { + var nouveaux = input.Justificatifs + .Where(j => !string.IsNullOrWhiteSpace(j.Nom)) + .Select(j => new PiecesJointe + { + EntiteType = "DEMANDE", + EntiteId = id, + TypePiece = string.IsNullOrWhiteSpace(j.Libelle) ? "Justificatif" : j.Libelle.Trim(), + NomFichier = j.Nom.Trim(), + SharepointUrl = j.SharepointUrl ?? "", + SharepointItemId = j.SharepointItemId ?? "", + TailleOctets = j.TailleOctets ?? 0, + UploaderId = currentUserId, + DateUpload = DateTime.UtcNow, + }) + .ToList(); + if (nouveaux.Count > 0) + { + db.Set().AddRange(nouveaux); + await db.SaveChangesAsync(ct); + } + } + + var justificatifsActuels = await db.Set().AsNoTracking().Where(p => p.EntiteType == "DEMANDE" && p.EntiteId == id).ToListAsync(ct); + return Results.Ok(ToDto(demande, justificatifsActuels, currentUserId)); + }); + + group.MapPatch("/demandes/{id:int}/annuler", async (int id, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var demande = await db.DemandesAchats.FirstOrDefaultAsync(d => d.Id == id, ct); + if (demande is null) return Results.NotFound(); + + if (demande.DemandeurId != currentUserId) + return Results.Forbid(); + if (demande.Statut is not ("SOUMISE" or "EN_VALIDATION" or "COMPLEMENT_REQUIS")) + return Results.BadRequest(new { error = "Cette demande ne peut plus être annulée." }); + + demande.Statut = "ANNULEE"; + demande.DateDerniereMaj = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapPatch("/demandes/{id:int}/statut", async (int id, UpdateDemandeStatutDto input, SIFinanceDbContext db, CurrentUserService currentUser, NotificationEmailService notification, CancellationToken ct) => + { + if (input.Statut is not ("VALIDEE" or "REFUSEE")) + return Results.BadRequest(new { error = "Statut invalide — seuls VALIDEE et REFUSEE sont autorisés depuis cet endpoint." }); + + var demande = await db.DemandesAchats.Include(d => d.Demandeur).Include(d => d.Valideur).FirstOrDefaultAsync(d => d.Id == id, ct); + if (demande is null) return Results.NotFound(); + + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + // Ce n'est plus le rôle FINANCE qui donne le droit de valider, mais le fait + // d'être le responsable direct (manager Entra ID) du demandeur — Finance garde + // seulement un accès en lecture à toutes les demandes (cf. GET /demandes). + if (demande.Demandeur?.ResponsableId != currentUserId) + return Results.Forbid(); + + if (demande.Statut is not ("SOUMISE" or "EN_VALIDATION" or "COMPLEMENT_REQUIS")) + return Results.BadRequest(new { error = "Cette demande n'est plus en attente de validation." }); + + // Compatibilité avec le bouton historique « Valider » : une validation + // manager ouvre désormais le nouveau circuit au lieu de clôturer la demande. + demande.Statut = input.Statut == "VALIDEE" ? "VALIDEE_MANAGER" : input.Statut; + demande.ValideurId = currentUserId; + demande.DateDerniereMaj = DateTime.UtcNow; + + await db.SaveChangesAsync(ct); + + // Notifie le demandeur de la décision — best-effort, comme pour la notification + // de création : un échec d'envoi ne doit pas faire échouer la décision déjà + // enregistrée à ce stade. + if (demande.Demandeur is { Actif: true } demandeur && !string.IsNullOrWhiteSpace(demandeur.Email)) + { + var responsable = await db.Utilisateurs.AsNoTracking().FirstOrDefaultAsync(u => u.Id == currentUserId, ct); + if (responsable is not null && !string.IsNullOrWhiteSpace(responsable.Email)) + { + var valide = input.Statut == "VALIDEE"; + var sujet = $"Demande d'achat {(valide ? "validée" : "refusée")} — {demande.Reference}"; + var corps = $"

Bonjour {demandeur.Prenom},

" + + $"

Votre demande d'achat {demande.Reference} — {demande.Objet} a été " + + (valide + ? "validée" + : "refusée") + + $" par {responsable.Prenom} {responsable.Nom}.

" + + "

Consulte le détail dans SI Finance.

"; + await notification.EnvoyerAsync(responsable.Email, demandeur.Email, sujet, corps, ct); + } + } + + var justificatifs = await db.Set().AsNoTracking().Where(p => p.EntiteType == "DEMANDE" && p.EntiteId == id).ToListAsync(ct); + return Results.Ok(ToDto(demande, justificatifs, currentUserId)); + }); + + // Circuit dédié aux demandes de matériel. Les actions sont volontairement + // contrôlées côté serveur : un utilisateur ne peut pas forcer un statut en + // contournant l'écran, et chaque passage est inscrit dans l'historique. + group.MapPatch("/demandes/{id:int}/workflow", async (int id, WorkflowDemandeDto input, SIFinanceDbContext db, CurrentUserService currentUser, NotificationEmailService notification, CancellationToken ct) => + { + var demande = await db.DemandesAchats + .Include(d => d.Demandeur) + .Include(d => d.Valideur) + .Include(d => d.TypeDepense) + .FirstOrDefaultAsync(d => d.Id == id, ct); + if (demande is null) return Results.NotFound(); + + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var action = input.Action.Trim().ToUpperInvariant(); + var estDemandeur = demande.DemandeurId == currentUserId; + var estManager = demande.Demandeur.ResponsableId == currentUserId; + var estFinance = await currentUser.HasRoleAsync("FINANCE", db, ct); + var estDemandeMateriel = demande.TypeDepense.EstMateriel; + + var ancienStatut = demande.Statut; + string? nouveauStatut = action switch + { + "VALIDER_MANAGER" when estManager && demande.Statut is "SOUMISE" or "EN_VALIDATION" or "COMPLEMENT_REQUIS" + => "VALIDEE_MANAGER", + // Après l'accord du manager, seul le collaborateur à l'origine de la + // demande la transmet au circuit matériel. Finance la consulte alors + // en lecture seule et ne peut pas avancer cette étape. + "TRANSMETTRE_MATERIEL" when estDemandeur && demande.Statut == "VALIDEE_MANAGER" && estDemandeMateriel + => "EN_VALIDATION_MATERIEL", + "VALIDER_MATERIEL" when (estDemandeur || estFinance) && demande.Statut == "EN_VALIDATION_MATERIEL" + => "VALIDEE_MATERIEL", + "TRANSMETTRE_FINANCE" when (estDemandeur || estFinance) && demande.Statut is "VALIDEE_MATERIEL" or "VALIDEE_MANAGER" + => "EN_VALIDATION_FINANCE", + "APPROUVER_FINANCE" when estFinance && demande.Statut == "EN_VALIDATION_FINANCE" + => "APPROUVEE", + "EMETTRE_COMMANDE" when estDemandeur && demande.Statut == "APPROUVEE" + => "COMMANDE_EMISE", + "CONFIRMER_COMMANDE" when estDemandeur && demande.Statut == "COMMANDE_EMISE" + => "COMMANDE_CONFIRMEE", + "MARQUER_EN_LIVRAISON" when estDemandeur && demande.Statut == "COMMANDE_CONFIRMEE" + => "EN_ATTENTE_LIVRAISON", + "DEMARRER_CONTROLE_RECEPTION" when estDemandeur && demande.Statut == "EN_ATTENTE_LIVRAISON" + => "EN_CONTROLE_RECEPTION", + "CONFIRMER_RECEPTION_CONFORME" when estDemandeur && demande.Statut == "EN_CONTROLE_RECEPTION" + => "RECEPTION_CONFORME", + "SIGNALER_NON_CONFORMITE" when estDemandeur && demande.Statut == "EN_CONTROLE_RECEPTION" + => "VERIFIEE_NON_CONFORME", + "VALIDER_RECEPTION" when estDemandeur && demande.Statut == "RECEPTION_CONFORME" + => "RECEPTION_VALIDEE", + "ENVOYER_RAPPROCHEMENT" when estDemandeur && demande.Statut == "RECEPTION_VALIDEE" + => "FACTURE_EN_RAPPROCHEMENT", + "CLOTURER" when estDemandeur && demande.Statut == "FACTURE_EN_RAPPROCHEMENT" + => "CLOTURE", + "DEMANDER_COMPLEMENT" when PeutTraiterEtape(demande.Statut, estManager, estFinance) + => "COMPLEMENT_REQUIS", + "REJETER" when PeutTraiterEtape(demande.Statut, estManager, estFinance) + => "REFUSEE", + _ => null, + }; + + if (nouveauStatut is null) + return Results.BadRequest(new { error = "Action non autorisée pour votre rôle ou pour le statut actuel de la demande." }); + if ((action is "REJETER" or "DEMANDER_COMPLEMENT") && string.IsNullOrWhiteSpace(input.Commentaire)) + return Results.BadRequest(new { error = "Un motif est requis pour un refus ou une demande de complément." }); + if (action == "VALIDER_MATERIEL" && (string.IsNullOrWhiteSpace(input.DocumentNom) + || string.IsNullOrWhiteSpace(input.DocumentSharepointUrl) + || string.IsNullOrWhiteSpace(input.DocumentSharepointItemId) + || input.DocumentTailleOctets is null)) + return Results.BadRequest(new { error = "Un bon de commande ou document matériel est requis pour valider le matériel." }); + + demande.Statut = nouveauStatut; + demande.ValideurId = currentUserId; + demande.DateDerniereMaj = DateTime.UtcNow; + if (action == "VALIDER_MATERIEL") + { + db.PiecesJointes.Add(new PiecesJointe + { + EntiteType = "DEMANDE", + EntiteId = demande.Id, + TypePiece = "Bon de commande matériel", + NomFichier = input.DocumentNom!.Trim(), + SharepointUrl = input.DocumentSharepointUrl!.Trim(), + SharepointItemId = input.DocumentSharepointItemId!.Trim(), + TailleOctets = input.DocumentTailleOctets!.Value, + UploaderId = currentUserId, + DateUpload = DateTime.UtcNow, + }); + } + db.HistoriqueActions.Add(new HistoriqueAction + { + EntiteType = "DEMANDE", + EntiteId = demande.Id, + UtilisateurId = currentUserId, + Action = action, + Commentaire = string.IsNullOrWhiteSpace(input.Commentaire) ? null : input.Commentaire.Trim(), + AncienStatut = ancienStatut, + NouveauStatut = nouveauStatut, + DateAction = DateTime.UtcNow, + }); + await db.SaveChangesAsync(ct); + + // Finance reste informée de l'avancement opérationnel sans avoir à porter + // les rôles de matériel/réception. L'envoi est best-effort : la transition, + // déjà historisée, ne doit jamais échouer à cause d'un email indisponible. + var auteur = await db.Utilisateurs.AsNoTracking().FirstOrDefaultAsync(u => u.Id == currentUserId, ct); + var finances = await (from u in db.Utilisateurs.AsNoTracking() + join r in db.UtilisateurRoles.AsNoTracking() on u.AzureOid equals r.AzureOid + where u.Actif && r.Role == "FINANCE" && !string.IsNullOrEmpty(u.Email) + select u.Email).Distinct().ToListAsync(ct); + if (auteur is not null && !string.IsNullOrEmpty(auteur.Email)) + { + foreach (var emailFinance in finances.Where(email => !string.Equals(email, auteur.Email, StringComparison.OrdinalIgnoreCase))) + { + var sujet = $"Mise à jour de commande — {demande.Reference}"; + var corps = $"

La demande {demande.Reference} — {demande.Objet} est passée au statut {nouveauStatut}.

" + + $"

Action déclarée par {auteur.Prenom} {auteur.Nom}.

" + + (string.IsNullOrWhiteSpace(input.Commentaire) ? "" : $"

Commentaire : {input.Commentaire.Trim()}

"); + await notification.EnvoyerAsync(auteur.Email, emailFinance, sujet, corps, ct); + } + } + + var justificatifs = await db.Set().AsNoTracking().Where(p => p.EntiteType == "DEMANDE" && p.EntiteId == id).ToListAsync(ct); + return Results.Ok(ToDto(demande, justificatifs, currentUserId)); + }); + + return app; + } + + private static bool PeutTraiterEtape(string statut, bool estManager, bool estFinance) => statut switch + { + "SOUMISE" or "EN_VALIDATION" or "COMPLEMENT_REQUIS" => estManager, + "VALIDEE_MANAGER" or "EN_VALIDATION_MATERIEL" or "VALIDEE_MATERIEL" => estFinance, + "EN_VALIDATION_FINANCE" => estFinance, + _ => false, + }; + + private static DemandeAchatDto ToDto(DemandesAchat d, IEnumerable justificatifs, int currentUserId, bool factureAjoutee = false, string? factureUrl = null, string? factureNom = null) => new( + d.Id, d.Reference, d.ServiceId, d.Objet, d.Description, d.FournisseurId, d.FournisseurLibre, + d.CategorieFournisseurLibre, + d.TypeDepenseId, d.MontantHt, d.TauxTva, d.MontantTva, d.MontantTtc, d.DatePrevisionnelle, + d.ModePaiementId, d.Statut, d.DemandeurId, + $"{d.Demandeur?.Prenom} {d.Demandeur?.Nom}".Trim(), + d.ValideurId, + d.ValideurId is null ? null : $"{d.Valideur?.Prenom} {d.Valideur?.Nom}".Trim(), + d.SeuilApplicable, d.DateCreation, + d.DateSoumission, d.DateDerniereMaj, + justificatifs.Select(j => new DemandeJustificatifDto(j.Id, j.TypePiece, j.NomFichier, j.DateUpload, string.IsNullOrEmpty(j.SharepointUrl) ? null : j.SharepointUrl)).ToList(), + d.Demandeur?.ResponsableId == currentUserId, + factureAjoutee, factureUrl, factureNom); + + private static decimal CalculerSeuil(decimal montant) => montant switch + { + < 1000 => 1000, + < 5000 => 5000, + _ => 20000, + }; + + private static async Task GenererReferenceAsync(SIFinanceDbContext db, CancellationToken ct) + { + var prefix = $"DA-{DateTime.UtcNow.Year}-"; + var last = await db.DemandesAchats + .Where(d => d.Reference.StartsWith(prefix)) + .OrderByDescending(d => d.Reference) + .Select(d => d.Reference) + .FirstOrDefaultAsync(ct); + + var nextSeq = 1; + if (last is not null && int.TryParse(last.AsSpan(prefix.Length), out var lastSeq)) + nextSeq = lastSeq + 1; + + return $"{prefix}{nextSeq:D4}"; + } +} diff --git a/SIFinance.Modules.Achats/Endpoints/BudgetsEndpoints.cs b/SIFinance.Modules.Achats/Endpoints/BudgetsEndpoints.cs new file mode 100644 index 0000000..2a853bf --- /dev/null +++ b/SIFinance.Modules.Achats/Endpoints/BudgetsEndpoints.cs @@ -0,0 +1,110 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using SIFinance.Modules.Achats.Dtos; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.Achats.Endpoints; + +public static class BudgetsEndpoints +{ + public static IEndpointRouteBuilder MapBudgetsEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/budgets").WithTags("Budgets").RequireAuthorization(); + + // exercice optionnel : sans filtre, remonte tous les exercices connus (utile pour + // un historique) — Pilotage passera l'exercice courant pour ne charger que l'utile. + group.MapGet("", async (int? exercice, SIFinanceDbContext db, CancellationToken ct) => + { + var query = db.BudgetsServices + .AsNoTracking() + .Include(b => b.Service) + .Include(b => b.Revisions).ThenInclude(r => r.ReviseParUtilisateur) + .AsQueryable(); + if (exercice.HasValue) + query = query.Where(b => b.ExerciceAnnee == exercice.Value); + + var budgets = await query.OrderBy(b => b.Service.Libelle).ToListAsync(ct); + return budgets.Select(ToDto); + }); + + group.MapPost("", async (CreateBudgetServiceDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (!await currentUser.HasRoleAsync("FINANCE", db, ct)) + return Results.Forbid(); + if (input.MontantInitial <= 0) + return Results.BadRequest(new { error = "Le montant initial doit être supérieur à 0." }); + + var existeDeja = await db.BudgetsServices + .AnyAsync(b => b.ServiceId == input.ServiceId && b.ExerciceAnnee == input.ExerciceAnnee, ct); + if (existeDeja) + return Results.BadRequest(new { error = "Un budget existe déjà pour ce service sur cet exercice." }); + + var budget = new BudgetService + { + ServiceId = input.ServiceId, + ExerciceAnnee = input.ExerciceAnnee, + MontantInitial = input.MontantInitial, + SaisieParId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateCreation = DateTime.UtcNow, + }; + db.BudgetsServices.Add(budget); + await db.SaveChangesAsync(ct); + + await db.Entry(budget).Reference(b => b.Service).LoadAsync(ct); + return Results.Created($"/api/budgets/{budget.Id}", ToDto(budget)); + }); + + // Réservé à FINANCE : seul le responsable Finance qui gère le budget peut + // l'augmenter en cours d'exercice. Chaque appel ajoute une ligne à l'historique + // (budgets_service_revisions) plutôt que d'écraser une valeur unique. + group.MapPost("/{id:int}/revisions", async (int id, CreateBudgetRevisionDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (!await currentUser.HasRoleAsync("FINANCE", db, ct)) + return Results.Forbid(); + + var budget = await db.BudgetsServices + .Include(b => b.Service) + .Include(b => b.Revisions).ThenInclude(r => r.ReviseParUtilisateur) + .FirstOrDefaultAsync(b => b.Id == id, ct); + if (budget is null) return Results.NotFound(); + if (input.MontantRevise <= 0) + return Results.BadRequest(new { error = "Le montant révisé doit être supérieur à 0." }); + + var revision = new BudgetServiceRevision + { + BudgetServiceId = id, + MontantRevise = input.MontantRevise, + Commentaire = string.IsNullOrWhiteSpace(input.Commentaire) ? null : input.Commentaire.Trim(), + RevisePar = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateRevision = DateTime.UtcNow, + }; + db.BudgetServiceRevisions.Add(revision); + budget.DateDerniereMaj = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + + await db.Entry(revision).Reference(r => r.ReviseParUtilisateur).LoadAsync(ct); + budget.Revisions.Add(revision); + return Results.Created($"/api/budgets/{id}/revisions/{revision.Id}", ToDto(budget)); + }); + + return app; + } + + private static BudgetServiceDto ToDto(BudgetService b) + { + var revisions = b.Revisions + .OrderByDescending(r => r.DateRevision) + .Select(r => new BudgetRevisionDto( + r.Id, r.MontantRevise, r.Commentaire, + $"{r.ReviseParUtilisateur.Prenom} {r.ReviseParUtilisateur.Nom}".Trim(), + r.DateRevision)) + .ToList(); + return new BudgetServiceDto( + b.Id, b.ServiceId, b.Service.Libelle, b.ExerciceAnnee, + b.MontantInitial, revisions.Count > 0 ? revisions[0].MontantRevise : null, + revisions); + } +} diff --git a/SIFinance.Modules.Achats/Endpoints/ContratsEndpoints.cs b/SIFinance.Modules.Achats/Endpoints/ContratsEndpoints.cs new file mode 100644 index 0000000..d20bff4 --- /dev/null +++ b/SIFinance.Modules.Achats/Endpoints/ContratsEndpoints.cs @@ -0,0 +1,389 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using SIFinance.Shared; +using SIFinance.Modules.Achats.Dtos; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.Achats.Endpoints; + +public record ContratAlerteDto(int ContratId, string Fournisseur, string Objet, string Type, string Severite, string Message, DateTime DateCalcul); + +public static class ContratsEndpoints +{ + private const int SeuilRenouvellementJours = 60; + + public static IEndpointRouteBuilder MapContratsEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/contrats").WithTags("Contrats").RequireAuthorization(); + + // Les contrats sont gérés par les responsables de service. Finance conserve + // une visibilité transverse en lecture seule, sans droit de modification. + group.AddEndpointFilter(async (context, next) => + { + var db = context.HttpContext.RequestServices.GetRequiredService(); + var currentUser = context.HttpContext.RequestServices.GetRequiredService(); + var isFinance = await currentUser.HasRoleAsync("FINANCE", db, context.HttpContext.RequestAborted); + var userId = await currentUser.GetOrCreateCurrentUserIdAsync(context.HttpContext.RequestAborted); + var hasResponsableRole = await currentUser.HasRoleAsync("RESPONSABLE", db, context.HttpContext.RequestAborted); + var aDesCollaborateurs = await db.Utilisateurs.AsNoTracking() + .AnyAsync(u => u.Actif && u.ResponsableId == userId, context.HttpContext.RequestAborted); + var estResponsable = aDesCollaborateurs || (hasResponsableRole && await db.Utilisateurs.AsNoTracking() + .AnyAsync(u => u.Actif && u.Id == userId && u.ServiceId != null, context.HttpContext.RequestAborted)); + + if (!isFinance && !estResponsable) return Results.Forbid(); + // Un profil Finance seul consulte uniquement. Un responsable qui possède aussi + // ce rôle conserve la gestion de ses propres contrats de service. + if (isFinance && !estResponsable && !HttpMethods.IsGet(context.HttpContext.Request.Method)) return Results.Forbid(); + if (!HttpMethods.IsGet(context.HttpContext.Request.Method) + && context.HttpContext.Request.RouteValues.TryGetValue("id", out var routeId) + && int.TryParse(routeId?.ToString(), out var contratId)) + { + var serviceId = await db.ContratsFournisseurs.AsNoTracking() + .Where(c => c.Id == contratId).Select(c => c.ServiceId).FirstOrDefaultAsync(context.HttpContext.RequestAborted); + var peutGerer = serviceId.HasValue && await db.Utilisateurs.AsNoTracking() + .AnyAsync(u => u.Actif && u.ServiceId == serviceId && (u.ResponsableId == userId || (hasResponsableRole && u.Id == userId)), context.HttpContext.RequestAborted); + if (!peutGerer) return Results.Forbid(); + } + return await next(context); + }); + + group.MapGet("", async (SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var isFinance = await currentUser.HasRoleAsync("FINANCE", db, ct); + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var query = db.ContratsFournisseurs.AsNoTracking().AsQueryable(); + if (!isFinance) + { + var servicesGerés = db.Utilisateurs.Where(u => u.ResponsableId == currentUserId).Select(u => u.ServiceId); + query = query.Where(c => c.ServiceId.HasValue && servicesGerés.Contains(c.ServiceId)); + } + if (!isFinance && await currentUser.HasRoleAsync("RESPONSABLE", db, ct)) + { + var monService = db.Utilisateurs.Where(u => u.Id == currentUserId && u.Actif).Select(u => u.ServiceId); + query = db.ContratsFournisseurs.AsNoTracking() + .Where(c => c.ServiceId.HasValue && monService.Contains(c.ServiceId)); + } + var contrats = await query + // Plusieurs Include de collections dans une seule requête produisent un + // produit cartésien (lignes dupliquées) : on découpe en requêtes séparées. + .AsSplitQuery() + .Include(c => c.Fournisseur) + .Include(c => c.Service) + .Include(c => c.Campus) + .Include(c => c.Echeances).ThenInclude(e => e.Justificatif) + .Include(c => c.Factures) + .OrderByDescending(c => c.DateCreation) + .ToListAsync(ct); + + // Filtrer par EntiteType suffit (cf. même correctif sur /api/demandes) : + // un filtre .Contains() sur une liste d'ids en mémoire forcerait EF à générer + // un paramètre SQL par contrat, ce qui grossit avec la table et empêche + // SQL Server de réutiliser un plan de requête en cache. + var documents = await db.Set() + .AsNoTracking() + .Where(p => p.EntiteType == "CONTRAT") + .ToListAsync(ct); + + // Lookup groupé par contrat : accès O(1) au lieu d'un Where qui re-parcourt + // toute la liste des documents pour chaque contrat. + var documentsParContrat = documents.ToLookup(d => d.EntiteId); + + return contrats.Select(c => ToDto(c, documentsParContrat[c.Id])); + }); + + // Alertes calculées à la volée (pas de table dédiée, pas de job planifié) : + // même filtrage par service que MapGet("") ci-dessus, réappliqué ici plutôt + // que factorisé, pour ne pas modifier la signature de la requête existante. + group.MapGet("/alertes", async (SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var isFinance = await currentUser.HasRoleAsync("FINANCE", db, ct); + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var query = db.ContratsFournisseurs.AsNoTracking().AsQueryable(); + if (!isFinance) + { + var servicesGerés = db.Utilisateurs.Where(u => u.ResponsableId == currentUserId).Select(u => u.ServiceId); + query = query.Where(c => c.ServiceId.HasValue && servicesGerés.Contains(c.ServiceId)); + } + if (!isFinance && await currentUser.HasRoleAsync("RESPONSABLE", db, ct)) + { + var monService = db.Utilisateurs.Where(u => u.Id == currentUserId && u.Actif).Select(u => u.ServiceId); + query = db.ContratsFournisseurs.AsNoTracking() + .Where(c => c.ServiceId.HasValue && monService.Contains(c.ServiceId)); + } + + var contrats = await query + .Where(c => c.Statut != "RESILIE" && c.Statut != "EXPIRE" && c.Statut != "ARCHIVE") + .Include(c => c.Fournisseur) + .Include(c => c.Echeances) + .ToListAsync(ct); + + var today = DateOnly.FromDateTime(DateTime.UtcNow); + var alertes = new List(); + var maintenant = DateTime.UtcNow; + + foreach (var c in contrats) + { + var joursAvantFin = c.DateFin.DayNumber - today.DayNumber; + if (joursAvantFin <= SeuilRenouvellementJours) + { + var expire = joursAvantFin < 0; + alertes.Add(new ContratAlerteDto( + c.Id, c.Fournisseur.RaisonSociale, c.Objet, + expire ? "CONTRAT_EXPIRE" : "CONTRAT_A_RENOUVELER", + expire ? "danger" : joursAvantFin <= 30 ? "warning" : "info", + expire + ? $"Expiré depuis le {c.DateFin:dd/MM/yyyy}" + : $"Échéance dans {joursAvantFin} j ({c.DateFin:dd/MM/yyyy})", + maintenant)); + } + + var echeancesEnRetard = c.Echeances.Where(e => e.Statut == "A_VENIR" && e.DateEcheance < today).ToList(); + if (echeancesEnRetard.Count > 0) + { + var montant = echeancesEnRetard.Sum(e => e.Montant); + alertes.Add(new ContratAlerteDto( + c.Id, c.Fournisseur.RaisonSociale, c.Objet, + "ECHEANCE_CONTRAT_RETARD", "danger", + $"{echeancesEnRetard.Count} échéance(s) en retard · {montant:N0} €", + maintenant)); + } + } + + return Results.Ok(alertes.OrderBy(a => a.Severite == "danger" ? 0 : a.Severite == "warning" ? 1 : 2)); + }); + + group.MapPost("", async (CreateContratDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.Objet)) + return Results.BadRequest(new { error = "L'objet du contrat est requis." }); + if (input.ServiceId is null || input.CampusId is null) + return Results.BadRequest(new { error = "Le service responsable et le campus sont requis." }); + if (input.Echeances.Count == 0) + return Results.BadRequest(new { error = "Renseigner au moins une échéance." }); + + var saisieParId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var hasResponsableRole = await currentUser.HasRoleAsync("RESPONSABLE", db, ct); + var estResponsableDuService = await db.Utilisateurs.AsNoTracking() + .AnyAsync(u => u.Actif && u.ServiceId == input.ServiceId && + (u.ResponsableId == saisieParId || (hasResponsableRole && u.Id == saisieParId)), ct); + if (!estResponsableDuService) return Results.Forbid(); + + var contrat = new ContratFournisseur + { + FournisseurId = input.FournisseurId, + ServiceId = input.ServiceId, + CampusId = input.CampusId, + Objet = input.Objet.Trim(), + Reference = string.IsNullOrWhiteSpace(input.Reference) ? null : input.Reference.Trim(), + DateSignature = input.DateSignature, + DateDebut = input.DateDebut, + DateFin = input.DateFin, + MontantTotal = input.MontantTotal, + Periodicite = input.Periodicite, + Statut = "ACTIF", + SaisieParId = saisieParId, + DateCreation = DateTime.UtcNow, + Echeances = input.Echeances.Select(e => new ContratEcheance + { + DateEcheance = e.Date, + Montant = e.Montant, + Statut = "A_VENIR", + DateCreation = DateTime.UtcNow, + }).ToList(), + }; + + db.ContratsFournisseurs.Add(contrat); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException ex) + { + return Results.BadRequest(new { error = "Impossible d'enregistrer le contrat — vérifiez le fournisseur.", detail = ex.InnerException?.Message ?? ex.Message }); + } + + db.ContratsFournisseursHistorique.Add(new ContratFournisseurHistorique + { + ContratId = contrat.Id, + NouveauStatut = "ACTIF", + Commentaire = "Contrat cr\u00e9\u00e9", + UtilisateurId = contrat.SaisieParId, + DateAction = DateTime.UtcNow, + }); + await db.SaveChangesAsync(ct); + await db.Entry(contrat).Reference(c => c.Fournisseur).LoadAsync(ct); + return Results.Created($"/api/contrats/{contrat.Id}", ToDto(contrat, [])); + }); + + group.MapGet("/{id:int}/historique", async (int id, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var contrat = await db.ContratsFournisseurs.AsNoTracking().FirstOrDefaultAsync(c => c.Id == id, ct); + if (contrat is null) return Results.NotFound(); + var isFinance = await currentUser.HasRoleAsync("FINANCE", db, ct); + if (!isFinance) + { + var userId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var hasResponsableRole = await currentUser.HasRoleAsync("RESPONSABLE", db, ct); + var estResponsableDuService = await db.Utilisateurs.AsNoTracking() + .AnyAsync(u => u.Actif && u.ServiceId == contrat.ServiceId && + (u.ResponsableId == userId || (hasResponsableRole && u.Id == userId)), ct); + if (!estResponsableDuService) return Results.Forbid(); + } + var historique = await db.ContratsFournisseursHistorique.AsNoTracking() + .Where(h => h.ContratId == id).Include(h => h.Utilisateur) + .OrderBy(h => h.DateAction).Select(h => new ContratHistoriqueDto(h.Id, h.AncienStatut, h.NouveauStatut, h.Commentaire, + h.Utilisateur.Prenom + " " + h.Utilisateur.Nom, h.DateAction)).ToListAsync(ct); + return Results.Ok(historique); + }); + + group.MapPatch("/{id:int}/statut", async (int id, ChangerStatutContratDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var contrat = await db.ContratsFournisseurs.FirstOrDefaultAsync(c => c.Id == id, ct); + if (contrat is null) return Results.NotFound(); + var userId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var hasResponsableRole = await currentUser.HasRoleAsync("RESPONSABLE", db, ct); + var estResponsableDuService = await db.Utilisateurs.AsNoTracking() + .AnyAsync(u => u.Actif && u.ServiceId == contrat.ServiceId && + (u.ResponsableId == userId || (hasResponsableRole && u.Id == userId)), ct); + if (!estResponsableDuService) return Results.Forbid(); + var statut = input.Statut.Trim().ToUpperInvariant(); + if (statut is not ("BROUILLON" or "ACTIF" or "A_RENOUVELER" or "SUSPENDU" or "RESILIE" or "EXPIRE" or "ARCHIVE")) + return Results.BadRequest(new { error = "Statut de contrat invalide." }); + if (statut == "RESILIE" && (input.DateResiliation is null || string.IsNullOrWhiteSpace(input.MotifResiliation))) + return Results.BadRequest(new { error = "La date et le motif de résiliation sont requis." }); + var ancienStatut = contrat.Statut; + contrat.Statut = statut; + contrat.DateResiliation = statut == "RESILIE" ? input.DateResiliation : null; + contrat.MotifResiliation = statut == "RESILIE" ? input.MotifResiliation!.Trim() : null; + contrat.DateDerniereMaj = DateTime.UtcNow; + db.ContratsFournisseursHistorique.Add(new ContratFournisseurHistorique + { + ContratId = contrat.Id, + AncienStatut = ancienStatut, + NouveauStatut = statut, + Commentaire = string.IsNullOrWhiteSpace(input.Commentaire) ? null : input.Commentaire.Trim(), + UtilisateurId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateAction = DateTime.UtcNow, + }); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapPost("/{id:int}/echeances", async (int id, CreateContratEcheanceDto input, SIFinanceDbContext db, CancellationToken ct) => + { + var contrat = await db.ContratsFournisseurs.FindAsync([id], ct); + if (contrat is null) return Results.NotFound(); + + var echeance = new ContratEcheance { ContratId = id, DateEcheance = input.Date, Montant = input.Montant, Statut = "A_VENIR", DateCreation = DateTime.UtcNow }; + db.ContratEcheances.Add(echeance); + await db.SaveChangesAsync(ct); + return Results.Created($"/api/contrats/{id}/echeances/{echeance.Id}", new ContratEcheanceDto(echeance.Id, echeance.DateEcheance, echeance.Montant, echeance.Statut, null)); + }); + + group.MapPut("/{id:int}/echeances/{echeanceId:int}", async (int id, int echeanceId, UpdateEcheanceDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var echeance = await db.ContratEcheances.Include(e => e.Justificatif) + .FirstOrDefaultAsync(e => e.Id == echeanceId && e.ContratId == id, ct); + if (echeance is null) return Results.NotFound(); + + echeance.DateEcheance = input.Date; + echeance.Montant = input.Montant; + echeance.Statut = input.Statut; + + if (!string.IsNullOrWhiteSpace(input.JustificatifNom)) + { + if (echeance.Justificatif is not null) + { + echeance.Justificatif.NomFichier = input.JustificatifNom; + } + else + { + // Pas encore de stockage SharePoint branché : on garde une trace du nom de + // fichier, l'URL/ID SharePoint seront renseignés quand l'upload sera en place. + var justificatif = new PiecesJointe + { + EntiteType = "CONTRAT_ECHEANCE", + EntiteId = echeance.Id, + TypePiece = "Justificatif", + NomFichier = input.JustificatifNom, + SharepointUrl = "", + SharepointItemId = "", + TailleOctets = 0, + UploaderId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateUpload = DateTime.UtcNow, + }; + db.Set().Add(justificatif); + echeance.Justificatif = justificatif; + } + } + + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapDelete("/{id:int}/echeances/{echeanceId:int}", async (int id, int echeanceId, SIFinanceDbContext db, CancellationToken ct) => + { + var echeance = await db.ContratEcheances.FirstOrDefaultAsync(e => e.Id == echeanceId && e.ContratId == id, ct); + if (echeance is null) return Results.NotFound(); + db.ContratEcheances.Remove(echeance); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapPost("/{id:int}/documents", async (int id, AddDocumentDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var contrat = await db.ContratsFournisseurs.FindAsync([id], ct); + if (contrat is null) return Results.NotFound(); + + var document = new PiecesJointe + { + EntiteType = "CONTRAT", + EntiteId = id, + TypePiece = input.Tag, + NomFichier = input.Nom, + SharepointUrl = "", + SharepointItemId = "", + TailleOctets = 0, + UploaderId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateUpload = input.Date.HasValue ? input.Date.Value.ToDateTime(TimeOnly.MinValue) : DateTime.UtcNow, + }; + db.Set().Add(document); + await db.SaveChangesAsync(ct); + return Results.Created($"/api/contrats/{id}/documents/{document.Id}", ToDocumentDto(document)); + }); + + group.MapPut("/{id:int}/documents/{documentId:int}", async (int id, int documentId, UpdateDocumentDto input, SIFinanceDbContext db, CancellationToken ct) => + { + var document = await db.Set().FirstOrDefaultAsync(d => d.Id == documentId && d.EntiteId == id && d.EntiteType == "CONTRAT", ct); + if (document is null) return Results.NotFound(); + document.TypePiece = input.Tag; + document.DateUpload = input.Date.ToDateTime(TimeOnly.MinValue); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapDelete("/{id:int}/documents/{documentId:int}", async (int id, int documentId, SIFinanceDbContext db, CancellationToken ct) => + { + var document = await db.Set().FirstOrDefaultAsync(d => d.Id == documentId && d.EntiteId == id && d.EntiteType == "CONTRAT", ct); + if (document is null) return Results.NotFound(); + db.Set().Remove(document); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + return app; + } + + private static ContratDocumentDto ToDocumentDto(PiecesJointe p) => + new(p.Id, DateOnly.FromDateTime(p.DateUpload ?? DateTime.UtcNow), p.TypePiece, p.NomFichier); + + private static ContratDto ToDto(ContratFournisseur c, IEnumerable documents) => new( + c.Id, c.FournisseurId, c.Fournisseur.RaisonSociale, c.ServiceId, c.Service?.Libelle, c.CampusId, c.Campus?.Libelle, c.Reference, c.Objet, + c.DateSignature, c.DateDebut, c.DateFin, c.DateResiliation, c.MotifResiliation, c.MontantTotal, c.Periodicite, c.Statut, + c.Echeances.Select(e => new ContratEcheanceDto(e.Id, e.DateEcheance, e.Montant, e.Statut, e.Justificatif?.NomFichier)).ToList(), + documents.Select(ToDocumentDto).ToList(), + c.Factures.Select(f => new ContratFactureLieeDto(f.Id, f.NumeroFacture, f.MontantTtc, f.DateFacture, f.Statut)).ToList()); +} \ No newline at end of file diff --git a/SIFinance.Modules.Achats/SIFinance.Modules.Achats.csproj b/SIFinance.Modules.Achats/SIFinance.Modules.Achats.csproj new file mode 100644 index 0000000..11d0c99 --- /dev/null +++ b/SIFinance.Modules.Achats/SIFinance.Modules.Achats.csproj @@ -0,0 +1,17 @@ + + + + + + + + + + + + net10.0 + enable + enable + + + diff --git a/SIFinance.Modules.Finance/Dtos/AvoirDtos.cs b/SIFinance.Modules.Finance/Dtos/AvoirDtos.cs new file mode 100644 index 0000000..4553161 --- /dev/null +++ b/SIFinance.Modules.Finance/Dtos/AvoirDtos.cs @@ -0,0 +1,14 @@ +namespace SIFinance.Modules.Finance.Dtos; + +public record AvoirDto( + int Id, + string Numero, + int FournisseurId, + string Fournisseur, + decimal Montant, + decimal SoldeDisponible, + DateOnly Date, + int? FactureLieeId, + string? FactureLieeNumero); + +public record CreateAvoirDto(string Numero, int FournisseurId, decimal Montant, DateOnly Date); diff --git a/SIFinance.Modules.Finance/Dtos/FactureDtos.cs b/SIFinance.Modules.Finance/Dtos/FactureDtos.cs new file mode 100644 index 0000000..70f78ef --- /dev/null +++ b/SIFinance.Modules.Finance/Dtos/FactureDtos.cs @@ -0,0 +1,53 @@ +namespace SIFinance.Modules.Finance.Dtos; + +public record FactureEcheanceDto(int Id, DateOnly Date, decimal Montant, string Statut); +public record FactureJustificatifDto(string Nom, string Url); + +public record FactureDto( + int Id, + string Numero, + int FournisseurId, + string Fournisseur, + decimal MontantHt, + decimal TauxTva, + decimal MontantTva, + decimal MontantTtc, + DateOnly DateFacture, + DateOnly DateEcheance, + string Statut, + int? DemandeAchatId, + int? AvoirLieId, + string? AvoirLieNumero, + decimal? AvoirLieMontant, + string? Commentaire, + bool EstDoublonSignale, + int? ContratId, + string? ContratObjet, + List Echeances, + List? Justificatifs = null); + +// Le formulaire ne saisit qu'un montant TTC global (pas de ventilation HT/TVA à la création) — +// on applique un taux de TVA par défaut de 20% et on recalcule HT/TVA à partir du TTC saisi. +public record CreateFactureDto(string Numero, int FournisseurId, decimal Montant, DateOnly DateFacture, DateOnly DateEcheance, int? DemandeAchatId, string? Commentaire, int? ContratId); + +public record UpdateFactureCommentaireDto(string? Commentaire); + +public record UpdateFactureStatutDto(string Statut); + +public record CreateEcheanceDto(DateOnly Date, decimal Montant); + +public record UpdateEcheanceDto(DateOnly Date, decimal Montant); + +// Résultat de l'analyse d'un PDF Factur-X/ZUGFeRD uploadé (réception de facture +// électronique) — Conforme=false si le PDF n'a pas de XML EN16931 embarqué, auquel cas +// les autres champs sont null et la saisie reste manuelle. +public record FacturXAnalyseDto( + bool Conforme, + string? Numero, + string? FournisseurNom, + decimal? MontantHt, + decimal? MontantTva, + decimal? MontantTtc, + DateOnly? DateFacture, + DateOnly? DateEcheance, + string? Erreur); diff --git a/SIFinance.Modules.Finance/Dtos/PaiementDtos.cs b/SIFinance.Modules.Finance/Dtos/PaiementDtos.cs new file mode 100644 index 0000000..9020164 --- /dev/null +++ b/SIFinance.Modules.Finance/Dtos/PaiementDtos.cs @@ -0,0 +1,22 @@ +namespace SIFinance.Modules.Finance.Dtos; + +// Un "paiement" côté UI peut regrouper plusieurs factures d'un même fournisseur. +// La table fin.paiements est 1 ligne par facture : on regroupe par Reference pour l'affichage. +public record PaiementGroupeDto( + string Reference, + int FournisseurId, + string Fournisseur, + List FactureIds, + List FactureNumeros, + decimal MontantTotal, + int ModePaiementId, + string ModePaiement, + DateOnly DatePaiement, + string Statut, + bool Rapproche); + +public record FactureDisponibleDto(int Id, int FournisseurId, string Fournisseur, string Numero, decimal Montant, DateOnly DateFacture); + +public record CreatePaiementDto(List FactureIds, int ModePaiementId, DateOnly DatePaiement, string Reference, string Statut, bool Rapproche); + +public record UpdatePaiementDto(int ModePaiementId, DateOnly DatePaiement, string Reference, string Statut, bool Rapproche); diff --git a/SIFinance.Modules.Finance/Endpoints/AvoirsEndpoints.cs b/SIFinance.Modules.Finance/Endpoints/AvoirsEndpoints.cs new file mode 100644 index 0000000..8a1c7f1 --- /dev/null +++ b/SIFinance.Modules.Finance/Endpoints/AvoirsEndpoints.cs @@ -0,0 +1,78 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using SIFinance.Modules.Finance.Dtos; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.Finance.Endpoints; + +public static class AvoirsEndpoints +{ + public static IEndpointRouteBuilder MapAvoirsEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/avoirs").WithTags("Avoirs").RequireAuthorization(); + + group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) => + // Projection directe en DTO : EF ne récupère que les colonnes utiles et + // ne remonte qu'une seule affectation, au lieu de charger toute la collection. + await db.Avoirs + .AsNoTracking() + .OrderByDescending(a => a.DateCreation) + .Select(a => new AvoirDto( + a.Id, + a.NumeroAvoir, + a.FournisseurId, + a.Fournisseur.RaisonSociale, + a.MontantInitial, + a.SoldeDisponible, + a.DateAvoir, + a.AffectationsAvoirs.Select(af => (int?)af.FactureId).FirstOrDefault(), + a.AffectationsAvoirs.Select(af => af.Facture.NumeroFacture).FirstOrDefault())) + .ToListAsync(ct)); + + group.MapPost("", async (CreateAvoirDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.Numero)) + return Results.BadRequest(new { error = "Le numéro d'avoir est requis." }); + if (input.Montant <= 0) + return Results.BadRequest(new { error = "Le montant doit être supérieur à 0." }); + + var avoir = new Avoir + { + FournisseurId = input.FournisseurId, + NumeroAvoir = input.Numero.Trim(), + DateAvoir = input.Date, + MontantInitial = input.Montant, + SoldeDisponible = input.Montant, + SaisieParId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateCreation = DateTime.UtcNow, + }; + + db.Avoirs.Add(avoir); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException ex) + { + return Results.BadRequest(new { error = "Impossible d'enregistrer l'avoir — vérifiez le fournisseur.", detail = ex.InnerException?.Message ?? ex.Message }); + } + + await db.Entry(avoir).Reference(a => a.Fournisseur).LoadAsync(ct); + return Results.Created($"/api/avoirs/{avoir.Id}", ToDto(avoir)); + }); + + return app; + } + + private static AvoirDto ToDto(Avoir a) + { + var affectation = a.AffectationsAvoirs.FirstOrDefault(); + return new AvoirDto( + a.Id, a.NumeroAvoir, a.FournisseurId, a.Fournisseur.RaisonSociale, + a.MontantInitial, a.SoldeDisponible, a.DateAvoir, + affectation?.FactureId, affectation?.Facture.NumeroFacture); + } +} diff --git a/SIFinance.Modules.Finance/Endpoints/FacturesEndpoints.cs b/SIFinance.Modules.Finance/Endpoints/FacturesEndpoints.cs new file mode 100644 index 0000000..89d3e2c --- /dev/null +++ b/SIFinance.Modules.Finance/Endpoints/FacturesEndpoints.cs @@ -0,0 +1,322 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using s2industries.ZUGFeRD; +using SIFinance.Modules.Finance.Dtos; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.Finance.Endpoints; + +public static class FacturesEndpoints +{ + public static IEndpointRouteBuilder MapFacturesEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/factures").WithTags("Factures").RequireAuthorization(); + + // Réception de factures électroniques (Factur-X/ZUGFeRD, échéance réglementaire du + // 1er septembre) : le PDF fourni par le fournisseur embarque un XML EN16931 — on le + // détecte et on en extrait les données pour pré-remplir/vérifier le formulaire de + // saisie, plutôt que de forcer une ressaisie manuelle sujette aux erreurs. + group.MapPost("/analyser-facturx", async (HttpRequest request, CancellationToken ct) => + { + if (!request.HasFormContentType) + return Results.BadRequest(new { error = "Contenu multipart attendu." }); + + var form = await request.ReadFormAsync(ct); + var file = form.Files.GetFile("file"); + if (file is null || file.Length == 0) + return Results.BadRequest(new { error = "Fichier manquant." }); + + try + { + await using var stream = file.OpenReadStream(); + var desc = InvoiceDescriptor.Load(stream); + var echeance = desc.GetTradePaymentTerms().FirstOrDefault()?.DueDate; + + return Results.Ok(new FacturXAnalyseDto( + Conforme: true, + Numero: desc.InvoiceNo, + FournisseurNom: desc.Seller?.Name, + MontantHt: desc.TaxBasisAmount ?? desc.LineTotalAmount, + MontantTva: desc.TaxTotalAmount, + MontantTtc: desc.GrandTotalAmount, + DateFacture: desc.InvoiceDate.HasValue ? DateOnly.FromDateTime(desc.InvoiceDate.Value) : null, + DateEcheance: echeance.HasValue ? DateOnly.FromDateTime(echeance.Value) : null, + Erreur: null)); + } + catch (Exception) + { + // Cas normal pour un PDF "papier scanné" sans XML embarqué — pas une erreur + // serveur, juste une facture non électronique à saisir manuellement. + return Results.Ok(new FacturXAnalyseDto( + Conforme: false, Numero: null, FournisseurNom: null, + MontantHt: null, MontantTva: null, MontantTtc: null, + DateFacture: null, DateEcheance: null, + Erreur: "Ce PDF ne contient pas de facture électronique Factur-X/ZUGFeRD reconnue — saisie manuelle requise.")); + } + }); + + group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) => + // Projection directe en DTO : SQL Server ne renvoie que les colonnes utilisées, + // et une seule affectation d'avoir est remontée (OUTER APPLY TOP 1) au lieu de + // charger toute la collection avec l'entité Avoir complète. + { + var factures = await db.Factures + .AsNoTracking() + .OrderByDescending(f => f.DateCreation) + .Select(f => new FactureDto( + f.Id, f.NumeroFacture, f.FournisseurId, f.Fournisseur.RaisonSociale, + f.MontantHt, f.TauxTva, f.MontantTva, f.MontantTtc, + f.DateFacture, f.DateEcheance, f.Statut, f.DemandeAchatId, + f.AffectationsAvoirs.Select(a => (int?)a.AvoirId).FirstOrDefault(), + f.AffectationsAvoirs.Select(a => a.Avoir.NumeroAvoir).FirstOrDefault(), + f.AffectationsAvoirs.Select(a => (decimal?)a.MontantAffecte).FirstOrDefault(), + f.Commentaire, + f.EstDoublonSignale ?? false, + f.ContratId, + f.Contrat != null ? f.Contrat.Objet : null, + // ToEcheanceDto n'est pas utilisable ici : EF ne sait pas traduire + // un appel de méthode C# en SQL, le constructeur est donc écrit inline. + f.Acomptes.Select(a => new FactureEcheanceDto(a.Id, a.DatePrevue, a.Montant, a.Statut)).ToList())) + .ToListAsync(ct); + + var factureIds = factures.Select(f => f.Id).ToArray(); + var justificatifs = await db.PiecesJointes.AsNoTracking() + .Where(p => p.EntiteType == "FACTURE" && factureIds.Contains(p.EntiteId)) + .OrderBy(p => p.DateUpload) + .Select(p => new { p.EntiteId, p.NomFichier, p.SharepointUrl }) + .ToListAsync(ct); + var parFacture = justificatifs.ToLookup(p => p.EntiteId); + + return factures.Select(f => f with + { + Justificatifs = parFacture[f.Id] + .Where(p => !string.IsNullOrWhiteSpace(p.SharepointUrl)) + .Select(p => new FactureJustificatifDto(p.NomFichier, p.SharepointUrl)) + .ToList(), + }); + }); + + group.MapPost("", async (CreateFactureDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.Numero)) + return Results.BadRequest(new { error = "Le numéro de facture est requis." }); + if (input.Montant <= 0) + return Results.BadRequest(new { error = "Le montant doit être supérieur à 0." }); + + if (input.ContratId.HasValue) + { + var contratExiste = await db.ContratsFournisseurs + .AnyAsync(c => c.Id == input.ContratId.Value && c.FournisseurId == input.FournisseurId, ct); + if (!contratExiste) + return Results.BadRequest(new { error = "Le contrat sélectionné n'appartient pas à ce fournisseur." }); + } + + var montantTtc = input.Montant; + var montantHt = Math.Round(montantTtc / (1 + FinanceDefaults.DefaultTauxTva / 100m), 2); + var montantTva = Math.Round(montantTtc - montantHt, 2); + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + + // Doublon potentiel : même fournisseur + même montant TTC dans une fenêtre de 30 + // jours autour de la date de facture, indépendamment du numéro (souvent mal + // ressaisi) — signalement non bloquant, l'utilisateur peut le lever ensuite. + var fenetreDebut = input.DateFacture.AddDays(-30); + var fenetreFin = input.DateFacture.AddDays(30); + var doublonPotentiel = await db.Factures.AnyAsync(f => + f.FournisseurId == input.FournisseurId && + f.MontantTtc == montantTtc && + f.DateFacture >= fenetreDebut && f.DateFacture <= fenetreFin, ct); + + var facture = new Facture + { + SourceType = "MANUEL", + DemandeAchatId = input.DemandeAchatId, + ContratId = input.ContratId, + FournisseurId = input.FournisseurId, + NumeroFacture = input.Numero.Trim(), + DateFacture = input.DateFacture, + DateEcheance = input.DateEcheance, + MontantHt = montantHt, + TauxTva = FinanceDefaults.DefaultTauxTva, + MontantTva = montantTva, + MontantTtc = montantTtc, + ServiceId = FinanceDefaults.DefaultServiceId, + ModePaiementId = FinanceDefaults.DefaultModePaiementId, + Statut = "attente", + Commentaire = string.IsNullOrWhiteSpace(input.Commentaire) ? null : input.Commentaire.Trim(), + EstDoublonSignale = doublonPotentiel, + SaisieParId = currentUserId, + DateCreation = DateTime.UtcNow, + Acomptes = [new Acompte + { + Montant = montantTtc, + DatePrevue = input.DateEcheance, + Statut = "PREVU", + SaisieParId = currentUserId, + DateCreation = DateTime.UtcNow, + }], + }; + + db.Factures.Add(facture); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException ex) + { + return Results.BadRequest(new { error = "Impossible d'enregistrer la facture — vérifiez le fournisseur.", detail = ex.InnerException?.Message ?? ex.Message }); + } + + await db.Entry(facture).Reference(f => f.Fournisseur).LoadAsync(ct); + if (facture.ContratId.HasValue) + await db.Entry(facture).Reference(f => f.Contrat).LoadAsync(ct); + return Results.Created($"/api/factures/{facture.Id}", ToDto(facture)); + }); + + group.MapPatch("/{id:int}/statut", async (int id, UpdateFactureStatutDto input, SIFinanceDbContext db, CancellationToken ct) => + { + var facture = await db.Factures.FindAsync([id], ct); + if (facture is null) return Results.NotFound(); + facture.Statut = input.Statut; + facture.DateDerniereMaj = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapPatch("/{id:int}/commentaire", async (int id, UpdateFactureCommentaireDto input, SIFinanceDbContext db, CancellationToken ct) => + { + var facture = await db.Factures.FindAsync([id], ct); + if (facture is null) return Results.NotFound(); + facture.Commentaire = string.IsNullOrWhiteSpace(input.Commentaire) ? null : input.Commentaire.Trim(); + facture.DateDerniereMaj = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + // Permet à l'utilisateur de lever un signalement de doublon potentiel une fois + // vérifié qu'il s'agit bien de deux factures distinctes (cf. demande métier : + // avertissement désactivable, pas un blocage). + group.MapPatch("/{id:int}/doublon", async (int id, SIFinanceDbContext db, CancellationToken ct) => + { + var facture = await db.Factures.FindAsync([id], ct); + if (facture is null) return Results.NotFound(); + facture.EstDoublonSignale = false; + facture.DateDerniereMaj = DateTime.UtcNow; + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapDelete("/{id:int}", async (int id, SIFinanceDbContext db, CancellationToken ct) => + { + var facture = await db.Factures + .Include(f => f.Acomptes) + .Include(f => f.AffectationsAvoirs).ThenInclude(a => a.Avoir) + .FirstOrDefaultAsync(f => f.Id == id, ct); + if (facture is null) return Results.NotFound(); + + foreach (var affectation in facture.AffectationsAvoirs) + { + affectation.Avoir.SoldeDisponible += affectation.MontantAffecte; + } + db.AffectationsAvoirs.RemoveRange(facture.AffectationsAvoirs); + db.Acomptes.RemoveRange(facture.Acomptes); + db.Factures.Remove(facture); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapPost("/{id:int}/echeances", async (int id, CreateEcheanceDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var facture = await db.Factures.FindAsync([id], ct); + if (facture is null) return Results.NotFound(); + var echeance = new Acompte + { + FactureId = id, + Montant = input.Montant, + DatePrevue = input.Date, + Statut = "PREVU", + SaisieParId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateCreation = DateTime.UtcNow, + }; + db.Acomptes.Add(echeance); + await db.SaveChangesAsync(ct); + return Results.Created($"/api/factures/{id}/echeances/{echeance.Id}", ToEcheanceDto(echeance)); + }); + + group.MapPut("/{id:int}/echeances/{echeanceId:int}", async (int id, int echeanceId, UpdateEcheanceDto input, SIFinanceDbContext db, CancellationToken ct) => + { + var echeance = await db.Acomptes.FirstOrDefaultAsync(a => a.Id == echeanceId && a.FactureId == id, ct); + if (echeance is null) return Results.NotFound(); + echeance.DatePrevue = input.Date; + echeance.Montant = input.Montant; + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapDelete("/{id:int}/echeances/{echeanceId:int}", async (int id, int echeanceId, SIFinanceDbContext db, CancellationToken ct) => + { + var echeance = await db.Acomptes.FirstOrDefaultAsync(a => a.Id == echeanceId && a.FactureId == id, ct); + if (echeance is null) return Results.NotFound(); + db.Acomptes.Remove(echeance); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapPost("/{id:int}/avoir/{avoirId:int}", async (int id, int avoirId, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + var facture = await db.Factures.FindAsync([id], ct); + var avoir = await db.Avoirs.FindAsync([avoirId], ct); + if (facture is null || avoir is null) return Results.NotFound(); + if (avoir.FournisseurId != facture.FournisseurId) + return Results.BadRequest(new { error = "L'avoir doit être du même fournisseur que la facture." }); + if (avoir.SoldeDisponible <= 0) + return Results.BadRequest(new { error = "Cet avoir n'a plus de solde disponible." }); + + var montantAffecte = Math.Min(avoir.SoldeDisponible, facture.MontantTtc); + db.AffectationsAvoirs.Add(new AffectationsAvoir + { + AvoirId = avoirId, + FactureId = id, + MontantAffecte = montantAffecte, + AffecteParId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateAffectation = DateTime.UtcNow, + }); + avoir.SoldeDisponible -= montantAffecte; + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapDelete("/{id:int}/avoir/{avoirId:int}", async (int id, int avoirId, SIFinanceDbContext db, CancellationToken ct) => + { + var affectation = await db.AffectationsAvoirs.Include(a => a.Avoir) + .FirstOrDefaultAsync(a => a.FactureId == id && a.AvoirId == avoirId, ct); + if (affectation is null) return Results.NotFound(); + affectation.Avoir.SoldeDisponible += affectation.MontantAffecte; + db.AffectationsAvoirs.Remove(affectation); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + return app; + } + + private static FactureEcheanceDto ToEcheanceDto(Acompte a) => + new(a.Id, a.DatePrevue, a.Montant, a.Statut); + + // Conservé pour le MapPost, qui construit le DTO à partir de l'entité fraîchement + // créée (sans affectation d'avoir à ce stade). + private static FactureDto ToDto(Facture f) + { + var affectation = f.AffectationsAvoirs.FirstOrDefault(); + return new FactureDto( + f.Id, f.NumeroFacture, f.FournisseurId, f.Fournisseur.RaisonSociale, + f.MontantHt, f.TauxTva, f.MontantTva, f.MontantTtc, + f.DateFacture, f.DateEcheance, f.Statut, f.DemandeAchatId, + affectation?.AvoirId, affectation?.Avoir.NumeroAvoir, affectation?.MontantAffecte, + f.Commentaire, f.EstDoublonSignale ?? false, + f.ContratId, f.Contrat?.Objet, + f.Acomptes.Select(ToEcheanceDto).ToList()); + } +} diff --git a/SIFinance.Modules.Finance/Endpoints/FinanceDefaults.cs b/SIFinance.Modules.Finance/Endpoints/FinanceDefaults.cs new file mode 100644 index 0000000..970ed93 --- /dev/null +++ b/SIFinance.Modules.Finance/Endpoints/FinanceDefaults.cs @@ -0,0 +1,10 @@ +namespace SIFinance.Modules.Finance.Endpoints; + +internal static class FinanceDefaults +{ + // Le formulaire de saisie facture ne demande pas encore de service/mode de paiement — + // valeurs par défaut en attendant l'ajout de ces champs côté UI. + public const int DefaultServiceId = 1; + public const int DefaultModePaiementId = 1; + public const decimal DefaultTauxTva = 20m; +} diff --git a/SIFinance.Modules.Finance/Endpoints/PaiementsEndpoints.cs b/SIFinance.Modules.Finance/Endpoints/PaiementsEndpoints.cs new file mode 100644 index 0000000..bfbf881 --- /dev/null +++ b/SIFinance.Modules.Finance/Endpoints/PaiementsEndpoints.cs @@ -0,0 +1,140 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using SIFinance.Modules.Finance.Dtos; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.Finance.Endpoints; + +public static class PaiementsEndpoints +{ + public static IEndpointRouteBuilder MapPaiementsEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/paiements").WithTags("Paiements").RequireAuthorization(); + + group.MapGet("/factures-disponibles", async (SIFinanceDbContext db, CancellationToken ct) => + { + var facturesPayeesIds = db.Paiements.Select(p => p.FactureId); + return await db.Factures + .AsNoTracking() + .Include(f => f.Fournisseur) + .Where(f => f.Statut == "validee" && !facturesPayeesIds.Contains(f.Id)) + .OrderBy(f => f.DateFacture) + .Select(f => new FactureDisponibleDto(f.Id, f.FournisseurId, f.Fournisseur.RaisonSociale, f.NumeroFacture, f.MontantTtc, f.DateFacture)) + .ToListAsync(ct); + }); + + group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) => + { + var paiements = await db.Paiements + .AsNoTracking() + .Include(p => p.Facture).ThenInclude(f => f.Fournisseur) + .Include(p => p.ModeReglement) + .ToListAsync(ct); + + return paiements + .GroupBy(p => p.ReferencePaiement ?? $"PAI-{p.Id}") + .Select(g => + { + var first = g.First(); + return new PaiementGroupeDto( + g.Key, first.Facture.FournisseurId, first.Facture.Fournisseur.RaisonSociale, + g.Select(p => p.FactureId).ToList(), + g.Select(p => p.Facture.NumeroFacture).ToList(), + g.Sum(p => p.MontantPaye), + first.ModeReglementId, first.ModeReglement.Libelle, + first.DatePaiement, first.Statut, first.Rapproche); + }) + .OrderByDescending(p => p.DatePaiement); + }); + + group.MapPost("", async (CreatePaiementDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (input.FactureIds.Count == 0) + return Results.BadRequest(new { error = "Sélectionner au moins une facture." }); + if (string.IsNullOrWhiteSpace(input.Reference)) + return Results.BadRequest(new { error = "La référence de paiement est requise." }); + + var factures = await db.Factures.Where(f => input.FactureIds.Contains(f.Id)).ToListAsync(ct); + if (factures.Count != input.FactureIds.Count) + return Results.BadRequest(new { error = "Une ou plusieurs factures sont introuvables." }); + if (factures.Select(f => f.FournisseurId).Distinct().Count() > 1) + return Results.BadRequest(new { error = "Un paiement regroupe uniquement des factures d'un même fournisseur." }); + + var dejaCouvertes = await db.Paiements.AnyAsync(p => input.FactureIds.Contains(p.FactureId), ct); + if (dejaCouvertes) + return Results.BadRequest(new { error = "Une ou plusieurs factures sont déjà couvertes par un paiement." }); + + var referenceExistante = await db.Paiements.AnyAsync(p => p.ReferencePaiement == input.Reference, ct); + if (referenceExistante) + return Results.BadRequest(new { error = "Cette référence de paiement existe déjà." }); + + var currentUserId = await currentUser.GetOrCreateCurrentUserIdAsync(ct); + var lignes = factures.Select(f => new Paiement + { + FactureId = f.Id, + MontantPaye = f.MontantTtc, + DatePaiement = input.DatePaiement, + ModeReglementId = input.ModePaiementId, + ReferencePaiement = input.Reference, + EnregistreParId = currentUserId, + DateSaisie = DateTime.UtcNow, + Statut = input.Statut, + Rapproche = input.Rapproche, + }).ToList(); + + db.Paiements.AddRange(lignes); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException ex) + { + return Results.BadRequest(new { error = "Impossible d'enregistrer le paiement.", detail = ex.InnerException?.Message ?? ex.Message }); + } + + return Results.Created($"/api/paiements/{Uri.EscapeDataString(input.Reference)}", (object?)null); + }); + + group.MapPatch("/{reference}", async (string reference, UpdatePaiementDto input, SIFinanceDbContext db, CancellationToken ct) => + { + var lignes = await db.Paiements.Where(p => p.ReferencePaiement == reference).ToListAsync(ct); + if (lignes.Count == 0) return Results.NotFound(); + + foreach (var p in lignes) + { + p.ModeReglementId = input.ModePaiementId; + p.DatePaiement = input.DatePaiement; + p.ReferencePaiement = input.Reference; + p.Statut = input.Statut; + p.Rapproche = input.Rapproche; + } + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapPatch("/{reference}/rapproche", async (string reference, SIFinanceDbContext db, CancellationToken ct) => + { + var lignes = await db.Paiements.Where(p => p.ReferencePaiement == reference).ToListAsync(ct); + if (lignes.Count == 0) return Results.NotFound(); + + var nouvelEtat = !lignes[0].Rapproche; + foreach (var p in lignes) p.Rapproche = nouvelEtat; + await db.SaveChangesAsync(ct); + return Results.Ok(new { rapproche = nouvelEtat }); + }); + + group.MapDelete("/{reference}", async (string reference, SIFinanceDbContext db, CancellationToken ct) => + { + var lignes = await db.Paiements.Where(p => p.ReferencePaiement == reference).ToListAsync(ct); + if (lignes.Count == 0) return Results.NotFound(); + db.Paiements.RemoveRange(lignes); + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + return app; + } +} diff --git a/SIFinance.Modules.Finance/SIFinance.Modules.Finance.csproj b/SIFinance.Modules.Finance/SIFinance.Modules.Finance.csproj new file mode 100644 index 0000000..2ad1571 --- /dev/null +++ b/SIFinance.Modules.Finance/SIFinance.Modules.Finance.csproj @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + net10.0 + enable + enable + + + diff --git a/SIFinance.Modules.SousTraitants/Dtos/ContratPrestataireDtos.cs b/SIFinance.Modules.SousTraitants/Dtos/ContratPrestataireDtos.cs new file mode 100644 index 0000000..41e9313 --- /dev/null +++ b/SIFinance.Modules.SousTraitants/Dtos/ContratPrestataireDtos.cs @@ -0,0 +1,10 @@ +namespace SIFinance.Modules.SousTraitants.Dtos; + +public record ContratPrestataireDto( + int Id, int OrganismeId, string Organisme, string Reference, string Objet, + DateOnly? DateSignature, DateOnly DateDebut, DateOnly DateFin, + decimal MontantTotal, string Statut); + +public record ContratPrestataireCreateDto( + int OrganismeId, string Objet, DateOnly? DateSignature, + DateOnly DateDebut, DateOnly DateFin, decimal MontantTotal); diff --git a/SIFinance.Modules.SousTraitants/Dtos/OrganismeDtos.cs b/SIFinance.Modules.SousTraitants/Dtos/OrganismeDtos.cs new file mode 100644 index 0000000..f865ef8 --- /dev/null +++ b/SIFinance.Modules.SousTraitants/Dtos/OrganismeDtos.cs @@ -0,0 +1,11 @@ +namespace SIFinance.Modules.SousTraitants.Dtos; + +public record OrganismeDto(int Id, string RaisonSociale, string? Siret, string Statut); + +public record OrganismeDetailDto( + int Id, string RaisonSociale, string? Siret, string? Email, string? Telephone, + string? Adresse, string Statut, DateTime? DateCreation); + +public record OrganismeSaveDto( + string RaisonSociale, string? Siret, string? Email, string? Telephone, + string? Adresse, string Statut); diff --git a/SIFinance.Modules.SousTraitants/Endpoints/ContratsPrestatairesEndpoints.cs b/SIFinance.Modules.SousTraitants/Endpoints/ContratsPrestatairesEndpoints.cs new file mode 100644 index 0000000..44c5cf8 --- /dev/null +++ b/SIFinance.Modules.SousTraitants/Endpoints/ContratsPrestatairesEndpoints.cs @@ -0,0 +1,82 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using SIFinance.Modules.SousTraitants.Dtos; +using SIFinance.Shared; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.SousTraitants.Endpoints; + +public static class ContratsPrestatairesEndpoints +{ + public static IEndpointRouteBuilder MapContratsPrestatairesEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/soustraitants/contrats").WithTags("SousTraitants-Contrats").RequireAuthorization(); + + group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) => + await db.ContratsPrestataires + .AsNoTracking() + .Include(c => c.Organisme) + .OrderByDescending(c => c.DateCreation) + .Select(c => ToDto(c)) + .ToListAsync(ct)); + + group.MapPost("", async (ContratPrestataireCreateDto input, SIFinanceDbContext db, CurrentUserService currentUser, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.Objet)) + return Results.BadRequest(new { error = "L'objet du contrat est requis." }); + if (input.DateFin < input.DateDebut) + return Results.BadRequest(new { error = "La date de fin doit être postérieure à la date de début." }); + + var contrat = new ContratPrestataire + { + OrganismeId = input.OrganismeId, + Reference = await GenererReferenceAsync(db, ct), + Objet = input.Objet.Trim(), + DateSignature = input.DateSignature, + DateDebut = input.DateDebut, + DateFin = input.DateFin, + MontantTotal = input.MontantTotal, + Statut = "ACTIF", + SaisieParId = await currentUser.GetOrCreateCurrentUserIdAsync(ct), + DateCreation = DateTime.UtcNow, + }; + + db.ContratsPrestataires.Add(contrat); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException ex) + { + return Results.BadRequest(new { error = "Impossible d'enregistrer le contrat — vérifiez l'organisme.", detail = ex.InnerException?.Message ?? ex.Message }); + } + + await db.Entry(contrat).Reference(c => c.Organisme).LoadAsync(ct); + return Results.Created($"/api/soustraitants/contrats/{contrat.Id}", ToDto(contrat)); + }); + + return app; + } + + private static async Task GenererReferenceAsync(SIFinanceDbContext db, CancellationToken ct) + { + var prefix = $"ST-CONT-{DateTime.UtcNow.Year}-"; + var last = await db.ContratsPrestataires + .Where(c => c.Reference.StartsWith(prefix)) + .OrderByDescending(c => c.Reference) + .Select(c => c.Reference) + .FirstOrDefaultAsync(ct); + + var nextSeq = 1; + if (last is not null && int.TryParse(last.AsSpan(prefix.Length), out var lastSeq)) + nextSeq = lastSeq + 1; + + return $"{prefix}{nextSeq:D3}"; + } + + private static ContratPrestataireDto ToDto(ContratPrestataire c) => new( + c.Id, c.OrganismeId, c.Organisme.RaisonSociale, c.Reference, c.Objet, + c.DateSignature, c.DateDebut, c.DateFin, c.MontantTotal, c.Statut); +} diff --git a/SIFinance.Modules.SousTraitants/Endpoints/OrganismesEndpoints.cs b/SIFinance.Modules.SousTraitants/Endpoints/OrganismesEndpoints.cs new file mode 100644 index 0000000..55bfa56 --- /dev/null +++ b/SIFinance.Modules.SousTraitants/Endpoints/OrganismesEndpoints.cs @@ -0,0 +1,93 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; +using Microsoft.EntityFrameworkCore; +using SIFinance.Modules.SousTraitants.Dtos; +using SIFinance.Shared.Entities; + +namespace SIFinance.Modules.SousTraitants.Endpoints; + +public static class OrganismesEndpoints +{ + public static IEndpointRouteBuilder MapOrganismesEndpoints(this IEndpointRouteBuilder app) + { + var group = app.MapGroup("/api/soustraitants/organismes").WithTags("SousTraitants-Organismes").RequireAuthorization(); + + group.MapGet("", async (SIFinanceDbContext db, CancellationToken ct) => + await db.Organismes + .AsNoTracking() + .Where(o => o.Statut == "ACTIF") + .OrderBy(o => o.RaisonSociale) + .Select(o => new OrganismeDto(o.Id, o.RaisonSociale, o.Siret, o.Statut)) + .ToListAsync(ct)); + + group.MapGet("/all", async (SIFinanceDbContext db, CancellationToken ct) => + await db.Organismes + .AsNoTracking() + .OrderBy(o => o.RaisonSociale) + .Select(o => new OrganismeDetailDto(o.Id, o.RaisonSociale, o.Siret, o.Email, o.Telephone, o.Adresse, o.Statut, o.DateCreation)) + .ToListAsync(ct)); + + group.MapPost("", async (OrganismeSaveDto input, SIFinanceDbContext db, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.RaisonSociale)) + return Results.BadRequest(new { error = "La raison sociale est requise." }); + + var organisme = new Organisme + { + RaisonSociale = input.RaisonSociale.Trim(), + Siret = string.IsNullOrWhiteSpace(input.Siret) ? null : input.Siret.Trim(), + Email = string.IsNullOrWhiteSpace(input.Email) ? null : input.Email.Trim(), + Telephone = string.IsNullOrWhiteSpace(input.Telephone) ? null : input.Telephone.Trim(), + Adresse = string.IsNullOrWhiteSpace(input.Adresse) ? null : input.Adresse.Trim(), + Statut = input.Statut, + DateCreation = DateTime.UtcNow, + }; + + db.Organismes.Add(organisme); + await db.SaveChangesAsync(ct); + + return Results.Created($"/api/soustraitants/organismes/{organisme.Id}", + new OrganismeDetailDto(organisme.Id, organisme.RaisonSociale, organisme.Siret, organisme.Email, organisme.Telephone, organisme.Adresse, organisme.Statut, organisme.DateCreation)); + }); + + group.MapPut("/{id:int}", async (int id, OrganismeSaveDto input, SIFinanceDbContext db, CancellationToken ct) => + { + if (string.IsNullOrWhiteSpace(input.RaisonSociale)) + return Results.BadRequest(new { error = "La raison sociale est requise." }); + + var organisme = await db.Organismes.FirstOrDefaultAsync(o => o.Id == id, ct); + if (organisme is null) return Results.NotFound(); + + organisme.RaisonSociale = input.RaisonSociale.Trim(); + organisme.Siret = string.IsNullOrWhiteSpace(input.Siret) ? null : input.Siret.Trim(); + organisme.Email = string.IsNullOrWhiteSpace(input.Email) ? null : input.Email.Trim(); + organisme.Telephone = string.IsNullOrWhiteSpace(input.Telephone) ? null : input.Telephone.Trim(); + organisme.Adresse = string.IsNullOrWhiteSpace(input.Adresse) ? null : input.Adresse.Trim(); + organisme.Statut = input.Statut; + + await db.SaveChangesAsync(ct); + return Results.NoContent(); + }); + + group.MapDelete("/{id:int}", async (int id, SIFinanceDbContext db, CancellationToken ct) => + { + var organisme = await db.Organismes.FirstOrDefaultAsync(o => o.Id == id, ct); + if (organisme is null) return Results.NotFound(); + + db.Organismes.Remove(organisme); + try + { + await db.SaveChangesAsync(ct); + } + catch (DbUpdateException) + { + return Results.BadRequest(new { error = "Impossible de supprimer cet organisme : des contrats y sont rattachés." }); + } + + return Results.NoContent(); + }); + + return app; + } +} diff --git a/SIFinance.Modules.SousTraitants/SIFinance.Modules.SousTraitants.csproj b/SIFinance.Modules.SousTraitants/SIFinance.Modules.SousTraitants.csproj new file mode 100644 index 0000000..d33ca87 --- /dev/null +++ b/SIFinance.Modules.SousTraitants/SIFinance.Modules.SousTraitants.csproj @@ -0,0 +1,17 @@ + + + + + + + + + + + + net10.0 + enable + enable + + + diff --git a/SIFinance.Shared/AzureAdOptions.cs b/SIFinance.Shared/AzureAdOptions.cs new file mode 100644 index 0000000..d182965 --- /dev/null +++ b/SIFinance.Shared/AzureAdOptions.cs @@ -0,0 +1,17 @@ +namespace SIFinance.Shared; + +// Section "AzureAd" de appsettings*.json — App registration Azure AD "SI Finance", +// utilisée à la fois pour le flux OAuth2 (authorization code + PKCE, connexion +// utilisateur) dans EntraAuthEndpoints et pour l'appel Graph "app-only" (client +// credentials) dans EntraSyncService/EntraGraphTokenProvider. ClientSecret et GroupId +// sont renseignés dans appsettings.Development.json (secrets, non commités). +public class AzureAdOptions +{ + public const string SectionName = "AzureAd"; + + public string TenantId { get; set; } = ""; + public string ClientId { get; set; } = ""; + public string ClientSecret { get; set; } = ""; + public string RedirectUri { get; set; } = ""; + public string GroupId { get; set; } = ""; +} diff --git a/SIFinance.Shared/CurrentUserService.cs b/SIFinance.Shared/CurrentUserService.cs new file mode 100644 index 0000000..9e334d1 --- /dev/null +++ b/SIFinance.Shared/CurrentUserService.cs @@ -0,0 +1,38 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using SIFinance.Shared.Entities; + +namespace SIFinance.Shared; + +public class CurrentUserService(IHttpContextAccessor httpContextAccessor) +{ + public Task GetOrCreateCurrentUserIdAsync(CancellationToken ct = default) + { + var principal = httpContextAccessor.HttpContext?.User + ?? throw new InvalidOperationException("Aucun contexte HTTP disponible pour résoudre l'utilisateur courant."); + + var idClaim = principal.FindFirst("id")?.Value + ?? throw new UnauthorizedAccessException("Le jeton ne contient pas de claim 'id' — utilisateur non identifiable."); + + return Task.FromResult(int.Parse(idClaim)); + } + + // Vérifie si l'utilisateur courant possède le rôle donné (ex. "FINANCE"). + // Passe par une jointure utilisateurs <-> utilisateur_roles via azure_oid, car + // le JWT ne porte que l'Id interne, pas l'azure_oid. + public async Task HasRoleAsync(string role, SIFinanceDbContext db, CancellationToken ct = default) + { + var userId = await GetOrCreateCurrentUserIdAsync(ct); + + // Les habilitations fonctionnelles viennent exclusivement de + // core.utilisateur_roles. Un profil Entra ou le statut Responsable ne donne + // jamais à lui seul l'accès aux écrans ou actions Finance. + return await db.UtilisateurRoles + .AsNoTracking() + .Join(db.Utilisateurs.AsNoTracking(), + ur => ur.AzureOid, + u => u.AzureOid, + (ur, u) => new { u.Id, ur.Role }) + .AnyAsync(x => x.Id == userId && x.Role == role, ct); + } +} diff --git a/SIFinance.Shared/Entities/Acompte.cs b/SIFinance.Shared/Entities/Acompte.cs new file mode 100644 index 0000000..ef7e1c7 --- /dev/null +++ b/SIFinance.Shared/Entities/Acompte.cs @@ -0,0 +1,33 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Acompte +{ + public int Id { get; set; } + + public int FactureId { get; set; } + + public decimal Montant { get; set; } + + public DateOnly DatePrevue { get; set; } + + public DateOnly? DateVerse { get; set; } + + public string Statut { get; set; } = null!; + + public int? JustificatifId { get; set; } + + public string? Commentaire { get; set; } + + public int SaisieParId { get; set; } + + public DateTime? DateCreation { get; set; } + + public virtual Facture Facture { get; set; } = null!; + + public virtual PiecesJointe? Justificatif { get; set; } + + public virtual Utilisateur SaisiePar { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/AffectationsAvoir.cs b/SIFinance.Shared/Entities/AffectationsAvoir.cs new file mode 100644 index 0000000..584ff7c --- /dev/null +++ b/SIFinance.Shared/Entities/AffectationsAvoir.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class AffectationsAvoir +{ + public int Id { get; set; } + + public int AvoirId { get; set; } + + public int FactureId { get; set; } + + public decimal MontantAffecte { get; set; } + + public int AffecteParId { get; set; } + + public DateTime? DateAffectation { get; set; } + + public virtual Utilisateur AffectePar { get; set; } = null!; + + public virtual Avoir Avoir { get; set; } = null!; + + public virtual Facture Facture { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/Audit.cs b/SIFinance.Shared/Entities/Audit.cs new file mode 100644 index 0000000..f56ecf3 --- /dev/null +++ b/SIFinance.Shared/Entities/Audit.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Audit +{ + public long Id { get; set; } + + public string EntiteType { get; set; } = null!; + + public int EntiteId { get; set; } + + public int UtilisateurId { get; set; } + + public string Action { get; set; } = null!; + + public string? DetailJson { get; set; } + + public string? IpAdresse { get; set; } + + public DateTime DateAction { get; set; } + + public virtual Utilisateur Utilisateur { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/Avoir.cs b/SIFinance.Shared/Entities/Avoir.cs new file mode 100644 index 0000000..6ad42bc --- /dev/null +++ b/SIFinance.Shared/Entities/Avoir.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Avoir +{ + public int Id { get; set; } + + public int FournisseurId { get; set; } + + public string NumeroAvoir { get; set; } = null!; + + public DateOnly DateAvoir { get; set; } + + public decimal MontantInitial { get; set; } + + public decimal SoldeDisponible { get; set; } + + public int? FactureId { get; set; } + + public int? JustificatifId { get; set; } + + public int SaisieParId { get; set; } + + public DateTime? DateCreation { get; set; } + + public virtual ICollection AffectationsAvoirs { get; set; } = new List(); + + public virtual Facture? Facture { get; set; } + + public virtual Fournisseur Fournisseur { get; set; } = null!; + + public virtual PiecesJointe? Justificatif { get; set; } + + public virtual Utilisateur SaisiePar { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/BudgetService.cs b/SIFinance.Shared/Entities/BudgetService.cs new file mode 100644 index 0000000..f397696 --- /dev/null +++ b/SIFinance.Shared/Entities/BudgetService.cs @@ -0,0 +1,32 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +// Budget annuel d'un service pour le module Achats. Exercice non calendaire (1er +// septembre au 31 août) — ExerciceAnnee porte l'année de démarrage (ex. 2026 = exercice +// du 01/09/2026 au 31/08/2027). Le montant "actif" du budget est MontantInitial tant +// qu'aucune révision n'existe, sinon la révision la plus récente (cf. Revisions) — +// chaque révision est conservée pour l'historique, jamais écrasée. +public partial class BudgetService +{ + public int Id { get; set; } + + public int ServiceId { get; set; } + + public int ExerciceAnnee { get; set; } + + public decimal MontantInitial { get; set; } + + public int SaisieParId { get; set; } + + public DateTime? DateCreation { get; set; } + + public DateTime? DateDerniereMaj { get; set; } + + public virtual Service Service { get; set; } = null!; + + public virtual Utilisateur SaisiePar { get; set; } = null!; + + public virtual ICollection Revisions { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/BudgetServiceRevision.cs b/SIFinance.Shared/Entities/BudgetServiceRevision.cs new file mode 100644 index 0000000..a81f652 --- /dev/null +++ b/SIFinance.Shared/Entities/BudgetServiceRevision.cs @@ -0,0 +1,25 @@ +using System; + +namespace SIFinance.Shared.Entities; + +// Historique des révisions d'un budget de service (augmentation en cours d'exercice, +// décidée par un responsable Finance) — une ligne par révision, jamais écrasée, pour +// garder la traçabilité complète de qui a changé le budget et pourquoi. +public partial class BudgetServiceRevision +{ + public int Id { get; set; } + + public int BudgetServiceId { get; set; } + + public decimal MontantRevise { get; set; } + + public string? Commentaire { get; set; } + + public int RevisePar { get; set; } + + public DateTime DateRevision { get; set; } + + public virtual BudgetService BudgetService { get; set; } = null!; + + public virtual Utilisateur ReviseParUtilisateur { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/Campus.cs b/SIFinance.Shared/Entities/Campus.cs new file mode 100644 index 0000000..d002a96 --- /dev/null +++ b/SIFinance.Shared/Entities/Campus.cs @@ -0,0 +1,9 @@ +namespace SIFinance.Shared.Entities; + +public partial class Campus +{ + public int Id { get; set; } + public string Libelle { get; set; } = null!; + public bool Actif { get; set; } + public virtual ICollection ContratsFournisseurs { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/ContratEcheance.cs b/SIFinance.Shared/Entities/ContratEcheance.cs new file mode 100644 index 0000000..44fc4ee --- /dev/null +++ b/SIFinance.Shared/Entities/ContratEcheance.cs @@ -0,0 +1,24 @@ +using System; + +namespace SIFinance.Shared.Entities; + +public partial class ContratEcheance +{ + public int Id { get; set; } + + public int ContratId { get; set; } + + public DateOnly DateEcheance { get; set; } + + public decimal Montant { get; set; } + + public string Statut { get; set; } = null!; + + public int? JustificatifId { get; set; } + + public DateTime? DateCreation { get; set; } + + public virtual ContratFournisseur Contrat { get; set; } = null!; + + public virtual PiecesJointe? Justificatif { get; set; } +} diff --git a/SIFinance.Shared/Entities/ContratFournisseur.cs b/SIFinance.Shared/Entities/ContratFournisseur.cs new file mode 100644 index 0000000..e670f7f --- /dev/null +++ b/SIFinance.Shared/Entities/ContratFournisseur.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class ContratFournisseur +{ + public int Id { get; set; } + + public int FournisseurId { get; set; } + + public int? ServiceId { get; set; } + + public int? CampusId { get; set; } + + public string? Reference { get; set; } + + public string Objet { get; set; } = null!; + + public DateOnly? DateSignature { get; set; } + + public DateOnly DateDebut { get; set; } + + public DateOnly DateFin { get; set; } + + public DateOnly? DateResiliation { get; set; } + + public string? MotifResiliation { get; set; } + + public decimal MontantTotal { get; set; } + + public string Periodicite { get; set; } = null!; + + public string Statut { get; set; } = null!; + + public int SaisieParId { get; set; } + + public DateTime? DateCreation { get; set; } + + public DateTime? DateDerniereMaj { get; set; } + + public virtual Fournisseur Fournisseur { get; set; } = null!; + + public virtual Service? Service { get; set; } + + public virtual Campus? Campus { get; set; } + + public virtual Utilisateur SaisiePar { get; set; } = null!; + + public virtual ICollection Echeances { get; set; } = new List(); + + public virtual ICollection Factures { get; set; } = new List(); + + public virtual ICollection Historique { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/ContratFournisseurHistorique.cs b/SIFinance.Shared/Entities/ContratFournisseurHistorique.cs new file mode 100644 index 0000000..54351c4 --- /dev/null +++ b/SIFinance.Shared/Entities/ContratFournisseurHistorique.cs @@ -0,0 +1,14 @@ +namespace SIFinance.Shared.Entities; + +public partial class ContratFournisseurHistorique +{ + public int Id { get; set; } + public int ContratId { get; set; } + public string? AncienStatut { get; set; } + public string NouveauStatut { get; set; } = null!; + public string? Commentaire { get; set; } + public int UtilisateurId { get; set; } + public DateTime DateAction { get; set; } + public virtual ContratFournisseur Contrat { get; set; } = null!; + public virtual Utilisateur Utilisateur { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/ContratPrestataire.cs b/SIFinance.Shared/Entities/ContratPrestataire.cs new file mode 100644 index 0000000..bd648fc --- /dev/null +++ b/SIFinance.Shared/Entities/ContratPrestataire.cs @@ -0,0 +1,32 @@ +using System; + +namespace SIFinance.Shared.Entities; + +public partial class ContratPrestataire +{ + public int Id { get; set; } + + public int OrganismeId { get; set; } + + public string Reference { get; set; } = null!; + + public string Objet { get; set; } = null!; + + public DateOnly? DateSignature { get; set; } + + public DateOnly DateDebut { get; set; } + + public DateOnly DateFin { get; set; } + + public decimal MontantTotal { get; set; } + + public string Statut { get; set; } = null!; + + public int SaisieParId { get; set; } + + public DateTime? DateCreation { get; set; } + + public virtual Organisme Organisme { get; set; } = null!; + + public virtual Utilisateur SaisiePar { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/DemandesAchat.cs b/SIFinance.Shared/Entities/DemandesAchat.cs new file mode 100644 index 0000000..5246fda --- /dev/null +++ b/SIFinance.Shared/Entities/DemandesAchat.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class DemandesAchat +{ + public int Id { get; set; } + + public string Reference { get; set; } = null!; + + public int ServiceId { get; set; } + + public string Objet { get; set; } = null!; + + public string? Description { get; set; } + + public int? FournisseurId { get; set; } + + public string? FournisseurLibre { get; set; } + + public string? CategorieFournisseurLibre { get; set; } + + public int TypeDepenseId { get; set; } + + public decimal MontantHt { get; set; } + + public decimal TauxTva { get; set; } + + public decimal MontantTva { get; set; } + + public decimal MontantTtc { get; set; } + + public DateOnly DatePrevisionnelle { get; set; } + + public int ModePaiementId { get; set; } + + public string Statut { get; set; } = null!; + + public int DemandeurId { get; set; } + + public int? ValideurId { get; set; } + + public decimal SeuilApplicable { get; set; } + + public DateTime? DateCreation { get; set; } + + public DateTime? DateSoumission { get; set; } + + public DateTime? DateDerniereMaj { get; set; } + + public virtual Utilisateur Demandeur { get; set; } = null!; + + public virtual ICollection Factures { get; set; } = new List(); + + public virtual Fournisseur? Fournisseur { get; set; } + + public virtual ModesPaiement ModePaiement { get; set; } = null!; + + public virtual Service Service { get; set; } = null!; + + public virtual TypesDepense TypeDepense { get; set; } = null!; + + public virtual Utilisateur? Valideur { get; set; } +} diff --git a/SIFinance.Shared/Entities/Facture.cs b/SIFinance.Shared/Entities/Facture.cs new file mode 100644 index 0000000..fa586f9 --- /dev/null +++ b/SIFinance.Shared/Entities/Facture.cs @@ -0,0 +1,77 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Facture +{ + public int Id { get; set; } + + public string SourceType { get; set; } = null!; + + public int? DemandeAchatId { get; set; } + + public int? ContratId { get; set; } + + public int FournisseurId { get; set; } + + public string NumeroFacture { get; set; } = null!; + + public DateOnly DateFacture { get; set; } + + public DateOnly DateEcheance { get; set; } + + public decimal MontantHt { get; set; } + + public decimal TauxTva { get; set; } + + public decimal MontantTva { get; set; } + + public decimal MontantTtc { get; set; } + + public int? CategorieDepenseId { get; set; } + + public int ServiceId { get; set; } + + public int ModePaiementId { get; set; } + + public string Statut { get; set; } = null!; + + public bool? EstDoublonSignale { get; set; } + + public string? Commentaire { get; set; } + + public int SaisieParId { get; set; } + + public int? ValideBapParId { get; set; } + + public DateTime? DateValidationBap { get; set; } + + public DateTime? DateCreation { get; set; } + + public DateTime? DateDerniereMaj { get; set; } + + public virtual ICollection Acomptes { get; set; } = new List(); + + public virtual ICollection AffectationsAvoirs { get; set; } = new List(); + + public virtual ICollection Avoirs { get; set; } = new List(); + + public virtual TypesDepense? CategorieDepense { get; set; } + + public virtual DemandesAchat? DemandeAchat { get; set; } + + public virtual ContratFournisseur? Contrat { get; set; } + + public virtual Fournisseur Fournisseur { get; set; } = null!; + + public virtual ModesPaiement ModePaiement { get; set; } = null!; + + public virtual ICollection Paiements { get; set; } = new List(); + + public virtual Utilisateur SaisiePar { get; set; } = null!; + + public virtual Service Service { get; set; } = null!; + + public virtual Utilisateur? ValideBapPar { get; set; } +} diff --git a/SIFinance.Shared/Entities/FichiersXml.cs b/SIFinance.Shared/Entities/FichiersXml.cs new file mode 100644 index 0000000..8163437 --- /dev/null +++ b/SIFinance.Shared/Entities/FichiersXml.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class FichiersXml +{ + public int Id { get; set; } + + public string NomFichier { get; set; } = null!; + + public string SharepointUrl { get; set; } = null!; + + public DateTime? DateGeneration { get; set; } + + public int GenereParId { get; set; } + + public int NombreVirements { get; set; } + + public decimal MontantTotal { get; set; } + + public virtual Utilisateur GenerePar { get; set; } = null!; + + public virtual ICollection Paiements { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/Fournisseur.cs b/SIFinance.Shared/Entities/Fournisseur.cs new file mode 100644 index 0000000..de9c49b --- /dev/null +++ b/SIFinance.Shared/Entities/Fournisseur.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Fournisseur +{ + public int Id { get; set; } + + public string RaisonSociale { get; set; } = null!; + + public string? Siret { get; set; } + + public string? Email { get; set; } + + public string? Telephone { get; set; } + + public string? Adresse { get; set; } + + public byte[]? IbanChiffre { get; set; } + + public string? Bic { get; set; } + + public string? TitulaireCompte { get; set; } + + public string Statut { get; set; } = null!; + + public DateTime? DateCreation { get; set; } + + public virtual ICollection Avoirs { get; set; } = new List(); + + public virtual ICollection ContratsFournisseurs { get; set; } = new List(); + + public virtual ICollection DemandesAchats { get; set; } = new List(); + + public virtual ICollection Factures { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/HistoriqueAction.cs b/SIFinance.Shared/Entities/HistoriqueAction.cs new file mode 100644 index 0000000..6bc6191 --- /dev/null +++ b/SIFinance.Shared/Entities/HistoriqueAction.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class HistoriqueAction +{ + public int Id { get; set; } + + public string EntiteType { get; set; } = null!; + + public int EntiteId { get; set; } + + public int UtilisateurId { get; set; } + + public string Action { get; set; } = null!; + + public string? Commentaire { get; set; } + + public string? AncienStatut { get; set; } + + public string? NouveauStatut { get; set; } + + public DateTime? DateAction { get; set; } + + public virtual Utilisateur Utilisateur { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/ModesPaiement.cs b/SIFinance.Shared/Entities/ModesPaiement.cs new file mode 100644 index 0000000..00bbeb3 --- /dev/null +++ b/SIFinance.Shared/Entities/ModesPaiement.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class ModesPaiement +{ + public int Id { get; set; } + + public string Libelle { get; set; } = null!; + + public bool NecessiteXml { get; set; } + + public bool Actif { get; set; } + + public virtual ICollection DemandesAchats { get; set; } = new List(); + + public virtual ICollection Factures { get; set; } = new List(); + + public virtual ICollection Paiements { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/Organisme.cs b/SIFinance.Shared/Entities/Organisme.cs new file mode 100644 index 0000000..ecd99da --- /dev/null +++ b/SIFinance.Shared/Entities/Organisme.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Organisme +{ + public int Id { get; set; } + + public string RaisonSociale { get; set; } = null!; + + public string? Siret { get; set; } + + public string? Email { get; set; } + + public string? Telephone { get; set; } + + public string? Adresse { get; set; } + + public string Statut { get; set; } = null!; + + public DateTime? DateCreation { get; set; } + + public virtual ICollection ContratsPrestataires { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/Paiement.cs b/SIFinance.Shared/Entities/Paiement.cs new file mode 100644 index 0000000..dd801ff --- /dev/null +++ b/SIFinance.Shared/Entities/Paiement.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Paiement +{ + public int Id { get; set; } + + public int FactureId { get; set; } + + public decimal MontantPaye { get; set; } + + public DateOnly DatePaiement { get; set; } + + public int ModeReglementId { get; set; } + + public string? ReferencePaiement { get; set; } + + public int? FichierXmlId { get; set; } + + public int EnregistreParId { get; set; } + + public DateTime? DateSaisie { get; set; } + + public string Statut { get; set; } = null!; + + public bool Rapproche { get; set; } + + public virtual Utilisateur EnregistrePar { get; set; } = null!; + + public virtual Facture Facture { get; set; } = null!; + + public virtual FichiersXml? FichierXml { get; set; } + + public virtual ModesPaiement ModeReglement { get; set; } = null!; + + public virtual ICollection Rapprochements { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/Parametre.cs b/SIFinance.Shared/Entities/Parametre.cs new file mode 100644 index 0000000..a597865 --- /dev/null +++ b/SIFinance.Shared/Entities/Parametre.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Parametre +{ + public string Cle { get; set; } = null!; + + public string Valeur { get; set; } = null!; + + public string? Description { get; set; } + + public int? ModifiePar { get; set; } + + public DateTime? DateModif { get; set; } + + public virtual Utilisateur? ModifieParNavigation { get; set; } +} diff --git a/SIFinance.Shared/Entities/PiecesJointe.cs b/SIFinance.Shared/Entities/PiecesJointe.cs new file mode 100644 index 0000000..b2647d9 --- /dev/null +++ b/SIFinance.Shared/Entities/PiecesJointe.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class PiecesJointe +{ + public int Id { get; set; } + + public string EntiteType { get; set; } = null!; + + public int EntiteId { get; set; } + + public string TypePiece { get; set; } = null!; + + public string NomFichier { get; set; } = null!; + + public string SharepointUrl { get; set; } = null!; + + public string SharepointItemId { get; set; } = null!; + + public long TailleOctets { get; set; } + + public int UploaderId { get; set; } + + public DateTime? DateUpload { get; set; } + + public virtual ICollection Acomptes { get; set; } = new List(); + + public virtual ICollection Avoirs { get; set; } = new List(); + + public virtual ICollection ContratEcheances { get; set; } = new List(); + + public virtual Utilisateur Uploader { get; set; } = null!; +} diff --git a/SIFinance.Shared/Entities/Rapprochement.cs b/SIFinance.Shared/Entities/Rapprochement.cs new file mode 100644 index 0000000..008d1e3 --- /dev/null +++ b/SIFinance.Shared/Entities/Rapprochement.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Rapprochement +{ + public int Id { get; set; } + + public int? PaiementId { get; set; } + + public string? ReferenceBancaire { get; set; } + + public DateOnly DateOperationBanq { get; set; } + + public string? LibelleBanque { get; set; } + + public decimal MontantBanque { get; set; } + + public string Statut { get; set; } = null!; + + public string? Commentaire { get; set; } + + public int? TraiteParId { get; set; } + + public DateTime? DateTraitement { get; set; } + + public virtual Paiement? Paiement { get; set; } + + public virtual Utilisateur? TraitePar { get; set; } +} diff --git a/SIFinance.Shared/Entities/SIFinanceDbContext.cs b/SIFinance.Shared/Entities/SIFinanceDbContext.cs new file mode 100644 index 0000000..3eef911 --- /dev/null +++ b/SIFinance.Shared/Entities/SIFinanceDbContext.cs @@ -0,0 +1,1194 @@ +using System; +using System.Collections.Generic; +using Microsoft.EntityFrameworkCore; + +namespace SIFinance.Shared.Entities; + +public partial class SIFinanceDbContext : DbContext +{ + public SIFinanceDbContext() + { + } + + public SIFinanceDbContext(DbContextOptions options) + : base(options) + { + } + + public virtual DbSet Acomptes { get; set; } + + public virtual DbSet UtilisateurRoles { get; set; } + + public virtual DbSet AffectationsAvoirs { get; set; } + + public virtual DbSet Audits { get; set; } + + public virtual DbSet Avoirs { get; set; } + + public virtual DbSet BudgetsServices { get; set; } + + public virtual DbSet BudgetServiceRevisions { get; set; } + + public virtual DbSet Campus { get; set; } + public virtual DbSet ContratsFournisseursHistorique { get; set; } + + public virtual DbSet ContratsFournisseurs { get; set; } + + public virtual DbSet ContratEcheances { get; set; } + + public virtual DbSet DemandesAchats { get; set; } + + public virtual DbSet Factures { get; set; } + + public virtual DbSet FichiersXmls { get; set; } + + public virtual DbSet Fournisseurs { get; set; } + + public virtual DbSet HistoriqueActions { get; set; } + + public virtual DbSet ModesPaiements { get; set; } + + public virtual DbSet Paiements { get; set; } + + public virtual DbSet Parametres { get; set; } + + public virtual DbSet PiecesJointes { get; set; } + + public virtual DbSet Rapprochements { get; set; } + + public virtual DbSet Services { get; set; } + + public virtual DbSet TypesDepenses { get; set; } + + public virtual DbSet Utilisateurs { get; set; } + + public virtual DbSet Organismes { get; set; } + + public virtual DbSet ContratsPrestataires { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + // Ne s'applique qu'aux outils design-time (ex: dotnet ef migrations) qui instancient + // le DbContext sans passer par la DI. En production/dev, les options viennent de + // Program.cs (AddDbContext) et ne doivent pas être écrasées ici. + if (!optionsBuilder.IsConfigured) + { + optionsBuilder.UseSqlServer("Server=192.168.0.3,1433;Database=SIFinance;Trusted_Connection=True;TrustServerCertificate=True;Connection Lifetime=60;Min Pool Size=4"); + } + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__acomptes__3213E83F0A5FDF52"); + + entity.ToTable("acomptes", "fin"); + + entity.HasIndex(e => e.FactureId, "IX_aco_facture"); + + entity.HasIndex(e => e.JustificatifId, "IX_aco_justif"); + + entity.HasIndex(e => e.SaisieParId, "IX_aco_saisie_par"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Commentaire) + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnName("commentaire"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.DatePrevue).HasColumnName("date_prevue"); + entity.Property(e => e.DateVerse).HasColumnName("date_verse"); + entity.Property(e => e.FactureId).HasColumnName("facture_id"); + entity.Property(e => e.JustificatifId).HasColumnName("justificatif_id"); + entity.Property(e => e.Montant) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + entity.Property(e => e.SaisieParId).HasColumnName("saisie_par_id"); + entity.Property(e => e.Statut) + .HasMaxLength(20) + .IsUnicode(false) + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + entity.HasOne(d => d.Facture).WithMany(p => p.Acomptes) + .HasForeignKey(d => d.FactureId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + entity.HasOne(d => d.Justificatif).WithMany(p => p.Acomptes) + .HasForeignKey(d => d.JustificatifId) + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + entity.HasOne(d => d.SaisiePar).WithMany(p => p.Acomptes) + .HasForeignKey(d => d.SaisieParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__affectat__3213E83FEA440C49"); + + entity.ToTable("affectations_avoir", "fin"); + + entity.HasIndex(e => e.AffecteParId, "IX_aff_affecte_par"); + + entity.HasIndex(e => e.AvoirId, "IX_aff_avoir"); + + entity.HasIndex(e => e.FactureId, "IX_aff_facture"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.AffecteParId).HasColumnName("affecte_par_id"); + entity.Property(e => e.AvoirId).HasColumnName("avoir_id"); + entity.Property(e => e.DateAffectation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_affectation"); + entity.Property(e => e.FactureId).HasColumnName("facture_id"); + entity.Property(e => e.MontantAffecte) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + entity.HasOne(d => d.AffectePar).WithMany(p => p.AffectationsAvoirs) + .HasForeignKey(d => d.AffecteParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__affectati__affec__09A971A2"); + + entity.HasOne(d => d.Avoir).WithMany(p => p.AffectationsAvoirs) + .HasForeignKey(d => d.AvoirId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__affectati__avoir__07C12930"); + + entity.HasOne(d => d.Facture).WithMany(p => p.AffectationsAvoirs) + .HasForeignKey(d => d.FactureId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__affectati__factu__08B54D69"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__audit__3213E83F12911979"); + + entity.ToTable("audit", "core"); + + entity.HasIndex(e => new { e.EntiteType, e.EntiteId }, "IX_audit_entite"); + + entity.HasIndex(e => e.UtilisateurId, "IX_audit_utilisateur"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Action) + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnName("action"); + entity.Property(e => e.DateAction) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_action"); + entity.Property(e => e.DetailJson) + .IsUnicode(false) + .HasColumnName("detail_json"); + entity.Property(e => e.EntiteId).HasColumnName("entite_id"); + entity.Property(e => e.EntiteType) + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnName("entite_type"); + entity.Property(e => e.IpAdresse) + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnName("ip_adresse"); + entity.Property(e => e.UtilisateurId).HasColumnName("utilisateur_id"); + + entity.HasOne(d => d.Utilisateur).WithMany(p => p.Audits) + .HasForeignKey(d => d.UtilisateurId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__audit__utilisate__71D1E811"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__avoirs__3213E83FA6ADF4CA"); + + entity.ToTable("avoirs", "fin"); + + entity.HasIndex(e => e.FactureId, "IX_avo_facture"); + + entity.HasIndex(e => e.FournisseurId, "IX_avo_fournisseur"); + + entity.HasIndex(e => e.JustificatifId, "IX_avo_justif"); + + entity.HasIndex(e => e.SaisieParId, "IX_avo_saisie_par"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.DateAvoir).HasColumnName("date_avoir"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.FactureId).HasColumnName("facture_id"); + entity.Property(e => e.FournisseurId).HasColumnName("fournisseur_id"); + entity.Property(e => e.JustificatifId).HasColumnName("justificatif_id"); + entity.Property(e => e.MontantInitial) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + entity.Property(e => e.NumeroAvoir) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("numero_avoir"); + entity.Property(e => e.SaisieParId).HasColumnName("saisie_par_id"); + entity.Property(e => e.SoldeDisponible) + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + entity.HasOne(d => d.Facture).WithMany(p => p.Avoirs) + .HasForeignKey(d => d.FactureId) + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + entity.HasOne(d => d.Fournisseur).WithMany(p => p.Avoirs) + .HasForeignKey(d => d.FournisseurId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + entity.HasOne(d => d.Justificatif).WithMany(p => p.Avoirs) + .HasForeignKey(d => d.JustificatifId) + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + entity.HasOne(d => d.SaisiePar).WithMany(p => p.Avoirs) + .HasForeignKey(d => d.SaisieParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__contrats_fournisseurs"); + + entity.ToTable("contrats_fournisseurs", "achats"); + + entity.HasIndex(e => e.FournisseurId, "IX_ctf_fournisseur"); + + entity.HasIndex(e => e.SaisieParId, "IX_ctf_saisie_par"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.FournisseurId).HasColumnName("fournisseur_id"); + entity.Property(e => e.ServiceId).HasColumnName("service_id"); + entity.Property(e => e.CampusId).HasColumnName("campus_id"); + entity.Property(e => e.Reference) + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnName("reference"); + entity.Property(e => e.Objet) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("objet"); + entity.Property(e => e.DateSignature).HasColumnName("date_signature"); + entity.Property(e => e.DateDebut).HasColumnName("date_debut"); + entity.Property(e => e.DateFin).HasColumnName("date_fin"); + entity.Property(e => e.DateResiliation).HasColumnName("date_resiliation"); + entity.Property(e => e.MotifResiliation).HasMaxLength(500).IsUnicode(false).HasColumnName("motif_resiliation"); + entity.Property(e => e.MontantTotal) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + entity.Property(e => e.Periodicite) + .HasMaxLength(20) + .IsUnicode(false) + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + entity.Property(e => e.Statut) + .HasMaxLength(20) + .IsUnicode(false) + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + entity.Property(e => e.SaisieParId).HasColumnName("saisie_par_id"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.DateDerniereMaj) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj"); + + entity.HasOne(d => d.Fournisseur).WithMany(p => p.ContratsFournisseurs) + .HasForeignKey(d => d.FournisseurId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + entity.HasOne(d => d.SaisiePar).WithMany(p => p.ContratsFournisseurs) + .HasForeignKey(d => d.SaisieParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + entity.HasOne(d => d.Service).WithMany() + .HasForeignKey(d => d.ServiceId) + .HasConstraintName("FK_contrats_fournisseurs_service"); + entity.HasOne(d => d.Campus).WithMany(p => p.ContratsFournisseurs) + .HasForeignKey(d => d.CampusId) + .HasConstraintName("FK_contrats_fournisseurs_campus"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK_campus"); + entity.ToTable("campus", "ref"); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Libelle).HasMaxLength(100).IsUnicode(false).HasColumnName("libelle"); + entity.Property(e => e.Actif).HasColumnName("actif"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id); + entity.ToTable("contrats_fournisseurs_historique", "achats"); + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.ContratId).HasColumnName("contrat_id"); + entity.Property(e => e.AncienStatut).HasMaxLength(20).IsUnicode(false).HasColumnName("ancien_statut"); + entity.Property(e => e.NouveauStatut).HasMaxLength(20).IsUnicode(false).HasColumnName("nouveau_statut"); + entity.Property(e => e.Commentaire).HasMaxLength(1000).HasColumnName("commentaire"); + entity.Property(e => e.UtilisateurId).HasColumnName("utilisateur_id"); + entity.Property(e => e.DateAction).HasColumnType("datetime").HasColumnName("date_action"); + entity.HasOne(e => e.Contrat).WithMany(c => c.Historique).HasForeignKey(e => e.ContratId); + entity.HasOne(e => e.Utilisateur).WithMany().HasForeignKey(e => e.UtilisateurId); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__contrats_echeances"); + + entity.ToTable("contrats_echeances", "achats"); + + entity.HasIndex(e => e.ContratId, "IX_cte_contrat"); + + entity.HasIndex(e => e.JustificatifId, "IX_cte_justif"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.ContratId).HasColumnName("contrat_id"); + entity.Property(e => e.DateEcheance).HasColumnName("date_echeance"); + entity.Property(e => e.Montant) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + entity.Property(e => e.Statut) + .HasMaxLength(20) + .IsUnicode(false) + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + entity.Property(e => e.JustificatifId).HasColumnName("justificatif_id"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + + entity.HasOne(d => d.Contrat).WithMany(p => p.Echeances) + .HasForeignKey(d => d.ContratId) + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK__contrats_echeances__contrat"); + + entity.HasOne(d => d.Justificatif).WithMany(p => p.ContratEcheances) + .HasForeignKey(d => d.JustificatifId) + .HasConstraintName("FK__contrats_echeances__justificatif"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__demandes__3213E83FB1E63F22"); + + entity.ToTable("demandes_achat", "achats"); + + entity.HasIndex(e => e.DemandeurId, "IX_da_demandeur"); + + entity.HasIndex(e => e.FournisseurId, "IX_da_fournisseur"); + + entity.HasIndex(e => e.ModePaiementId, "IX_da_mode_paiement"); + + entity.HasIndex(e => e.ServiceId, "IX_da_service"); + + entity.HasIndex(e => e.Statut, "IX_da_statut"); + + entity.HasIndex(e => e.TypeDepenseId, "IX_da_type_depense"); + + entity.HasIndex(e => e.ValideurId, "IX_da_valideur"); + + entity.HasIndex(e => e.Reference, "UQ__demandes__FD90DA99FE701252").IsUnique(); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.DateDerniereMaj) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj"); + entity.Property(e => e.DatePrevisionnelle).HasColumnName("date_previsionnelle"); + entity.Property(e => e.DateSoumission) + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + entity.Property(e => e.DemandeurId).HasColumnName("demandeur_id"); + entity.Property(e => e.Description) + .IsUnicode(false) + .HasColumnName("description"); + entity.Property(e => e.FournisseurId).HasColumnName("fournisseur_id"); + entity.Property(e => e.FournisseurLibre) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("fournisseur_libre"); + entity.Property(e => e.CategorieFournisseurLibre) + .HasMaxLength(100) + .HasColumnName("categorie_fournisseur_libre"); + entity.Property(e => e.ModePaiementId).HasColumnName("mode_paiement_id"); + entity.Property(e => e.MontantHt) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + entity.Property(e => e.MontantTtc) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + entity.Property(e => e.MontantTva) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + entity.Property(e => e.Objet) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("objet"); + entity.Property(e => e.Reference) + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnName("reference"); + entity.Property(e => e.ServiceId).HasColumnName("service_id"); + entity.Property(e => e.SeuilApplicable) + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + entity.Property(e => e.Statut) + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnName("statut"); + entity.Property(e => e.TauxTva) + .HasDefaultValue(20m) + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + entity.Property(e => e.TypeDepenseId).HasColumnName("type_depense_id"); + entity.Property(e => e.ValideurId).HasColumnName("valideur_id"); + + entity.HasOne(d => d.Demandeur).WithMany(p => p.DemandesAchatDemandeurs) + .HasForeignKey(d => d.DemandeurId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + entity.HasOne(d => d.Fournisseur).WithMany(p => p.DemandesAchats) + .HasForeignKey(d => d.FournisseurId) + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + entity.HasOne(d => d.ModePaiement).WithMany(p => p.DemandesAchats) + .HasForeignKey(d => d.ModePaiementId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__demandes___mode___778AC167"); + + entity.HasOne(d => d.Service).WithMany(p => p.DemandesAchats) + .HasForeignKey(d => d.ServiceId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + entity.HasOne(d => d.TypeDepense).WithMany(p => p.DemandesAchats) + .HasForeignKey(d => d.TypeDepenseId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__demandes___type___76969D2E"); + + entity.HasOne(d => d.Valideur).WithMany(p => p.DemandesAchatValideurs) + .HasForeignKey(d => d.ValideurId) + .HasConstraintName("FK__demandes___valid__797309D9"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("budgets_service", "achats"); + + entity.HasIndex(e => new { e.ServiceId, e.ExerciceAnnee }, "IX_budgets_service_unique").IsUnique(); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.ServiceId).HasColumnName("service_id"); + entity.Property(e => e.ExerciceAnnee).HasColumnName("exercice_annee"); + entity.Property(e => e.MontantInitial) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + entity.Property(e => e.SaisieParId).HasColumnName("saisie_par_id"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.DateDerniereMaj) + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj"); + + entity.HasOne(d => d.Service).WithMany() + .HasForeignKey(d => d.ServiceId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_budgets_service_service"); + + entity.HasOne(d => d.SaisiePar).WithMany() + .HasForeignKey(d => d.SaisieParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_budgets_service_saisie_par"); + }); + + modelBuilder.Entity(entity => + { + entity.ToTable("budgets_service_revisions", "achats"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.BudgetServiceId).HasColumnName("budget_service_id"); + entity.Property(e => e.MontantRevise) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_revise"); + entity.Property(e => e.Commentaire) + .HasMaxLength(1000) + .HasColumnName("commentaire"); + entity.Property(e => e.RevisePar).HasColumnName("revise_par"); + entity.Property(e => e.DateRevision) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_revision"); + + entity.HasOne(d => d.BudgetService).WithMany(p => p.Revisions) + .HasForeignKey(d => d.BudgetServiceId) + .OnDelete(DeleteBehavior.Cascade) + .HasConstraintName("FK_budgets_service_revisions_budget"); + + entity.HasOne(d => d.ReviseParUtilisateur).WithMany() + .HasForeignKey(d => d.RevisePar) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK_budgets_service_revisions_revise_par"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__factures__3213E83F68386F7E"); + + entity.ToTable("factures", "fin"); + + entity.HasIndex(e => e.CategorieDepenseId, "IX_fac_categorie"); + + entity.HasIndex(e => e.DemandeAchatId, "IX_fac_demande"); + + entity.HasIndex(e => e.FournisseurId, "IX_fac_fournisseur"); + + entity.HasIndex(e => e.ModePaiementId, "IX_fac_mode_paiement"); + + entity.HasIndex(e => e.SaisieParId, "IX_fac_saisie_par"); + + entity.HasIndex(e => e.ServiceId, "IX_fac_service"); + + entity.HasIndex(e => e.Statut, "IX_fac_statut"); + + entity.HasIndex(e => e.ValideBapParId, "IX_fac_valide_bap"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.CategorieDepenseId).HasColumnName("categorie_depense_id"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.DateDerniereMaj) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj"); + entity.Property(e => e.DateEcheance).HasColumnName("date_echeance"); + entity.Property(e => e.DateFacture).HasColumnName("date_facture"); + entity.Property(e => e.DateValidationBap) + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + entity.Property(e => e.DemandeAchatId).HasColumnName("demande_achat_id"); + entity.Property(e => e.ContratId).HasColumnName("contrat_id"); + entity.Property(e => e.EstDoublonSignale) + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + entity.Property(e => e.Commentaire) + .HasMaxLength(1000) + .HasColumnName("commentaire"); + entity.Property(e => e.FournisseurId).HasColumnName("fournisseur_id"); + entity.Property(e => e.ModePaiementId).HasColumnName("mode_paiement_id"); + entity.Property(e => e.MontantHt) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + entity.Property(e => e.MontantTtc) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + entity.Property(e => e.MontantTva) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + entity.Property(e => e.NumeroFacture) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("numero_facture"); + entity.Property(e => e.SaisieParId).HasColumnName("saisie_par_id"); + entity.Property(e => e.ServiceId).HasColumnName("service_id"); + entity.Property(e => e.SourceType) + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnName("source_type"); + entity.Property(e => e.Statut) + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnName("statut"); + entity.Property(e => e.TauxTva) + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + entity.Property(e => e.ValideBapParId).HasColumnName("valide_bap_par_id"); + + entity.HasOne(d => d.CategorieDepense).WithMany(p => p.Factures) + .HasForeignKey(d => d.CategorieDepenseId) + .HasConstraintName("FK__factures__catego__7C4F7684"); + + entity.HasOne(d => d.DemandeAchat).WithMany(p => p.Factures) + .HasForeignKey(d => d.DemandeAchatId) + .HasConstraintName("FK__factures__demand__7A672E12"); + + entity.HasOne(d => d.Contrat).WithMany(p => p.Factures) + .HasForeignKey(d => d.ContratId) + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_factures_contrat"); + + entity.HasOne(d => d.Fournisseur).WithMany(p => p.Factures) + .HasForeignKey(d => d.FournisseurId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + entity.HasOne(d => d.ModePaiement).WithMany(p => p.Factures) + .HasForeignKey(d => d.ModePaiementId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + entity.HasOne(d => d.SaisiePar).WithMany(p => p.FactureSaisiePars) + .HasForeignKey(d => d.SaisieParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + entity.HasOne(d => d.Service).WithMany(p => p.Factures) + .HasForeignKey(d => d.ServiceId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__factures__servic__7D439ABD"); + + entity.HasOne(d => d.ValideBapPar).WithMany(p => p.FactureValideBapPars) + .HasForeignKey(d => d.ValideBapParId) + .HasConstraintName("FK__factures__valide__00200768"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__fichiers__3213E83FEE8C3E2D"); + + entity.ToTable("fichiers_xml", "fin"); + + entity.HasIndex(e => e.GenereParId, "IX_xml_genere_par"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.DateGeneration) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_generation"); + entity.Property(e => e.GenereParId).HasColumnName("genere_par_id"); + entity.Property(e => e.MontantTotal) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + entity.Property(e => e.NomFichier) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("nom_fichier"); + entity.Property(e => e.NombreVirements).HasColumnName("nombre_virements"); + entity.Property(e => e.SharepointUrl) + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnName("sharepoint_url"); + + entity.HasOne(d => d.GenerePar).WithMany(p => p.FichiersXmls) + .HasForeignKey(d => d.GenereParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__fourniss__3213E83F16CB24B1"); + + entity.ToTable("fournisseurs", "ref"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Adresse) + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnName("adresse"); + entity.Property(e => e.Bic) + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnName("bic"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.Email) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("email"); + entity.Property(e => e.IbanChiffre) + .HasMaxLength(256) + .HasColumnName("iban_chiffre"); + entity.Property(e => e.RaisonSociale) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("raison_sociale"); + entity.Property(e => e.Siret) + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnName("siret"); + entity.Property(e => e.Statut) + .HasMaxLength(10) + .IsUnicode(false) + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + entity.Property(e => e.Telephone) + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnName("telephone"); + entity.Property(e => e.TitulaireCompte) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("titulaire_compte"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__historiq__3213E83FB787CC6E"); + + entity.ToTable("historique_actions", "core"); + + entity.HasIndex(e => new { e.EntiteType, e.EntiteId }, "IX_histo_entite"); + + entity.HasIndex(e => e.UtilisateurId, "IX_histo_utilisateur"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Action) + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnName("action"); + entity.Property(e => e.AncienStatut) + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnName("ancien_statut"); + entity.Property(e => e.Commentaire) + .IsUnicode(false) + .HasColumnName("commentaire"); + entity.Property(e => e.DateAction) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_action"); + entity.Property(e => e.EntiteId).HasColumnName("entite_id"); + entity.Property(e => e.EntiteType) + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnName("entite_type"); + entity.Property(e => e.NouveauStatut) + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnName("nouveau_statut"); + entity.Property(e => e.UtilisateurId).HasColumnName("utilisateur_id"); + + entity.HasOne(d => d.Utilisateur).WithMany(p => p.HistoriqueActions) + .HasForeignKey(d => d.UtilisateurId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__historiqu__utili__73BA3083"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__modes_pa__3213E83F2F37EC08"); + + entity.ToTable("modes_paiement", "ref"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Actif) + .HasDefaultValue(true) + .HasColumnName("actif"); + entity.Property(e => e.Libelle) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("libelle"); + entity.Property(e => e.NecessiteXml).HasColumnName("necessite_xml"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__paiement__3213E83F4F594948"); + + entity.ToTable("paiements", "fin"); + + entity.HasIndex(e => e.EnregistreParId, "IX_pai_enregistre_par"); + + entity.HasIndex(e => e.FactureId, "IX_pai_facture"); + + entity.HasIndex(e => e.ModeReglementId, "IX_pai_mode"); + + entity.HasIndex(e => e.FichierXmlId, "IX_pai_xml"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.DatePaiement).HasColumnName("date_paiement"); + entity.Property(e => e.DateSaisie) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_saisie"); + entity.Property(e => e.EnregistreParId).HasColumnName("enregistre_par_id"); + entity.Property(e => e.FactureId).HasColumnName("facture_id"); + entity.Property(e => e.FichierXmlId).HasColumnName("fichier_xml_id"); + entity.Property(e => e.ModeReglementId).HasColumnName("mode_reglement_id"); + entity.Property(e => e.MontantPaye) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + entity.Property(e => e.ReferencePaiement) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("reference_paiement"); + entity.Property(e => e.Statut) + .HasMaxLength(20) + .IsUnicode(false) + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + entity.Property(e => e.Rapproche) + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + entity.HasOne(d => d.EnregistrePar).WithMany(p => p.Paiements) + .HasForeignKey(d => d.EnregistreParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + entity.HasOne(d => d.Facture).WithMany(p => p.Paiements) + .HasForeignKey(d => d.FactureId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + entity.HasOne(d => d.FichierXml).WithMany(p => p.Paiements) + .HasForeignKey(d => d.FichierXmlId) + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + entity.HasOne(d => d.ModeReglement).WithMany(p => p.Paiements) + .HasForeignKey(d => d.ModeReglementId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Cle).HasName("PK__parametr__D837B8B8460C2DCC"); + + entity.ToTable("parametres", "core"); + + entity.HasIndex(e => e.ModifiePar, "IX_parametres_modifie_par"); + + entity.Property(e => e.Cle) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("cle"); + entity.Property(e => e.DateModif) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_modif"); + entity.Property(e => e.Description) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("description"); + entity.Property(e => e.ModifiePar).HasColumnName("modifie_par"); + entity.Property(e => e.Valeur) + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnName("valeur"); + + entity.HasOne(d => d.ModifieParNavigation).WithMany(p => p.Parametres) + .HasForeignKey(d => d.ModifiePar) + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__pieces_j__3213E83F9643E6F1"); + + entity.ToTable("pieces_jointes", "core"); + + entity.HasIndex(e => new { e.EntiteType, e.EntiteId }, "IX_pj_entite"); + + entity.HasIndex(e => e.UploaderId, "IX_pj_uploader"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.DateUpload) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_upload"); + entity.Property(e => e.EntiteId).HasColumnName("entite_id"); + entity.Property(e => e.EntiteType) + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnName("entite_type"); + entity.Property(e => e.NomFichier) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("nom_fichier"); + entity.Property(e => e.SharepointItemId) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("sharepoint_item_id"); + entity.Property(e => e.SharepointUrl) + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnName("sharepoint_url"); + entity.Property(e => e.TailleOctets).HasColumnName("taille_octets"); + entity.Property(e => e.TypePiece) + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnName("type_piece"); + entity.Property(e => e.UploaderId).HasColumnName("uploader_id"); + + entity.HasOne(d => d.Uploader).WithMany(p => p.PiecesJointes) + .HasForeignKey(d => d.UploaderId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__rapproch__3213E83F92CAA2D1"); + + entity.ToTable("rapprochement", "fin"); + + entity.HasIndex(e => e.PaiementId, "IX_rap_paiement"); + + entity.HasIndex(e => e.TraiteParId, "IX_rap_traite_par"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Commentaire) + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnName("commentaire"); + entity.Property(e => e.DateOperationBanq).HasColumnName("date_operation_banq"); + entity.Property(e => e.DateTraitement) + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + entity.Property(e => e.LibelleBanque) + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnName("libelle_banque"); + entity.Property(e => e.MontantBanque) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + entity.Property(e => e.PaiementId).HasColumnName("paiement_id"); + entity.Property(e => e.ReferenceBancaire) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("reference_bancaire"); + entity.Property(e => e.Statut) + .HasMaxLength(30) + .IsUnicode(false) + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + entity.Property(e => e.TraiteParId).HasColumnName("traite_par_id"); + + entity.HasOne(d => d.Paiement).WithMany(p => p.Rapprochements) + .HasForeignKey(d => d.PaiementId) + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + entity.HasOne(d => d.TraitePar).WithMany(p => p.Rapprochements) + .HasForeignKey(d => d.TraiteParId) + .HasConstraintName("FK__rapproche__trait__10566F31"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__services__3213E83FFB9EB51B"); + + entity.ToTable("services", "ref"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Actif) + .HasDefaultValue(true) + .HasColumnName("actif"); + entity.Property(e => e.Libelle) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("libelle"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__types_de__3213E83FC8C79A18"); + + entity.ToTable("types_depense", "ref"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Actif) + .HasDefaultValue(true) + .HasColumnName("actif"); + entity.Property(e => e.Libelle) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("libelle"); + entity.Property(e => e.EstMateriel) + .HasDefaultValue(false) + .HasColumnName("est_materiel"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__utilisat__3213E83F466D0E1A"); + + entity.ToTable("utilisateurs", "core"); + + entity.HasIndex(e => e.ServiceId, "IX_utilisateurs_service"); + + entity.HasIndex(e => e.AzureOid, "UQ__utilisat__2C305FD1B8914BF0").IsUnique(); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.Actif) + .HasDefaultValue(true) + .HasColumnName("actif"); + entity.Property(e => e.AzureOid) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("azure_oid"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + entity.Property(e => e.DateDerniereConnexion) + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + entity.Property(e => e.Email) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("email"); + entity.Property(e => e.Nom) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("nom"); + entity.Property(e => e.Prenom) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("prenom"); + entity.Property(e => e.Profil) + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnName("profil"); + entity.Property(e => e.ServiceId).HasColumnName("service_id"); + entity.Property(e => e.ResponsableId).HasColumnName("responsable_id"); + + entity.HasOne(d => d.Service).WithMany(p => p.Utilisateurs) + .HasForeignKey(d => d.ServiceId) + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + entity.HasOne(d => d.Responsable).WithMany(p => p.Collaborateurs) + .HasForeignKey(d => d.ResponsableId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__utilisateurs__responsable"); + }); + modelBuilder.Entity(entity => + { + entity.HasKey(e => new { e.AzureOid, e.Role }).HasName("PK_utilisateur_roles"); + + entity.ToTable("utilisateur_roles", "core"); + + entity.Property(e => e.AzureOid) + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnName("azure_oid"); + entity.Property(e => e.Role) + .HasMaxLength(50) + .HasColumnName("role"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__organismes"); + + entity.ToTable("organismes", "st"); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.RaisonSociale) + .HasMaxLength(255) + .HasColumnName("raison_sociale"); + entity.Property(e => e.Siret) + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnName("siret"); + entity.Property(e => e.Email) + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnName("email"); + entity.Property(e => e.Telephone) + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnName("telephone"); + entity.Property(e => e.Adresse) + .HasMaxLength(255) + .HasColumnName("adresse"); + entity.Property(e => e.Statut) + .HasMaxLength(20) + .IsUnicode(false) + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + }); + + modelBuilder.Entity(entity => + { + entity.HasKey(e => e.Id).HasName("PK__contrats_prestataires"); + + entity.ToTable("contrats_prestataires", "st"); + + entity.HasIndex(e => e.OrganismeId, "IX_ctp_organisme"); + + entity.HasIndex(e => e.SaisieParId, "IX_ctp_saisie_par"); + + entity.HasIndex(e => e.Reference, "UQ_ctp_reference").IsUnique(); + + entity.Property(e => e.Id).HasColumnName("id"); + entity.Property(e => e.OrganismeId).HasColumnName("organisme_id"); + entity.Property(e => e.Reference) + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnName("reference"); + entity.Property(e => e.Objet) + .HasMaxLength(255) + .HasColumnName("objet"); + entity.Property(e => e.DateSignature).HasColumnName("date_signature"); + entity.Property(e => e.DateDebut).HasColumnName("date_debut"); + entity.Property(e => e.DateFin).HasColumnName("date_fin"); + entity.Property(e => e.MontantTotal) + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + entity.Property(e => e.Statut) + .HasMaxLength(20) + .IsUnicode(false) + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + entity.Property(e => e.SaisieParId).HasColumnName("saisie_par_id"); + entity.Property(e => e.DateCreation) + .HasDefaultValueSql("(getdate())") + .HasColumnType("datetime") + .HasColumnName("date_creation"); + + entity.HasOne(d => d.Organisme).WithMany(p => p.ContratsPrestataires) + .HasForeignKey(d => d.OrganismeId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__contrats_prestataires__organisme"); + + entity.HasOne(d => d.SaisiePar).WithMany(p => p.ContratsPrestataires) + .HasForeignKey(d => d.SaisieParId) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + }); + + OnModelCreatingPartial(modelBuilder); + } + + partial void OnModelCreatingPartial(ModelBuilder modelBuilder); +} diff --git a/SIFinance.Shared/Entities/Service.cs b/SIFinance.Shared/Entities/Service.cs new file mode 100644 index 0000000..d9ed978 --- /dev/null +++ b/SIFinance.Shared/Entities/Service.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Service +{ + public int Id { get; set; } + + public string Libelle { get; set; } = null!; + + public bool Actif { get; set; } + + public virtual ICollection DemandesAchats { get; set; } = new List(); + + public virtual ICollection Factures { get; set; } = new List(); + + public virtual ICollection Utilisateurs { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/TypesDepense.cs b/SIFinance.Shared/Entities/TypesDepense.cs new file mode 100644 index 0000000..ae32752 --- /dev/null +++ b/SIFinance.Shared/Entities/TypesDepense.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class TypesDepense +{ + public int Id { get; set; } + + public string Libelle { get; set; } = null!; + + public bool Actif { get; set; } + + public bool EstMateriel { get; set; } + + public virtual ICollection DemandesAchats { get; set; } = new List(); + + public virtual ICollection Factures { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/Utilisateur.cs b/SIFinance.Shared/Entities/Utilisateur.cs new file mode 100644 index 0000000..0219968 --- /dev/null +++ b/SIFinance.Shared/Entities/Utilisateur.cs @@ -0,0 +1,70 @@ +using System; +using System.Collections.Generic; + +namespace SIFinance.Shared.Entities; + +public partial class Utilisateur +{ + public int Id { get; set; } + + public string AzureOid { get; set; } = null!; + + public string Nom { get; set; } = null!; + + public string Prenom { get; set; } = null!; + + public string Email { get; set; } = null!; + + public string Profil { get; set; } = null!; + + public int? ServiceId { get; set; } + + // Manager direct, synchronisé depuis Entra ID (EntraSyncService) — c'est cette + // personne, et non le rôle FINANCE, qui a le droit de valider/refuser les demandes + // d'achat du collaborateur. + public int? ResponsableId { get; set; } + + public bool Actif { get; set; } + + public DateTime? DateCreation { get; set; } + + public DateTime? DateDerniereConnexion { get; set; } + + public virtual ICollection Acomptes { get; set; } = new List(); + + public virtual ICollection AffectationsAvoirs { get; set; } = new List(); + + public virtual ICollection Audits { get; set; } = new List(); + + public virtual ICollection Avoirs { get; set; } = new List(); + + public virtual ICollection ContratsFournisseurs { get; set; } = new List(); + + public virtual ICollection ContratsPrestataires { get; set; } = new List(); + + public virtual ICollection DemandesAchatDemandeurs { get; set; } = new List(); + + public virtual ICollection DemandesAchatValideurs { get; set; } = new List(); + + public virtual ICollection FactureSaisiePars { get; set; } = new List(); + + public virtual ICollection FactureValideBapPars { get; set; } = new List(); + + public virtual ICollection FichiersXmls { get; set; } = new List(); + + public virtual ICollection HistoriqueActions { get; set; } = new List(); + + public virtual ICollection Paiements { get; set; } = new List(); + + public virtual ICollection Parametres { get; set; } = new List(); + + public virtual ICollection PiecesJointes { get; set; } = new List(); + + public virtual ICollection Rapprochements { get; set; } = new List(); + + public virtual Service? Service { get; set; } + + public virtual Utilisateur? Responsable { get; set; } + + public virtual ICollection Collaborateurs { get; set; } = new List(); +} diff --git a/SIFinance.Shared/Entities/UtilisateurRole.cs b/SIFinance.Shared/Entities/UtilisateurRole.cs new file mode 100644 index 0000000..5213129 --- /dev/null +++ b/SIFinance.Shared/Entities/UtilisateurRole.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace SIFinance.Shared.Entities +{ + public partial class UtilisateurRole + { + public string AzureOid { get; set; } = null!; + public string Role { get; set; } = null!; + } +} diff --git a/SIFinance.Shared/EntraGraphTokenProvider.cs b/SIFinance.Shared/EntraGraphTokenProvider.cs new file mode 100644 index 0000000..68a9e3d --- /dev/null +++ b/SIFinance.Shared/EntraGraphTokenProvider.cs @@ -0,0 +1,48 @@ +using System.Net.Http.Json; +using System.Text.Json.Serialization; +using Microsoft.Extensions.Options; + +namespace SIFinance.Shared; + +// Jeton Graph "app-only" (client_credentials) pour les appels serveur-à-serveur +// (synchro du groupe Entra ID, envoi d'emails de notification, etc.). Mis en cache 55 +// minutes pour éviter un aller-retour Azure AD à chaque appel. Vit dans SIFinance.Shared +// (et non SIFinance.Api) car les modules en ont besoin eux aussi (ex. notification par +// email à la création d'une demande), et référencer SIFinance.Api depuis un module +// créerait un cycle. +public class EntraGraphTokenProvider(IHttpClientFactory httpClientFactory, IOptions options) +{ + private string? _token; + private DateTime _expiresAt; + + public async Task GetTokenAsync(CancellationToken ct) + { + if (_token is not null && DateTime.UtcNow < _expiresAt) return _token; + + var opts = options.Value; + var http = httpClientFactory.CreateClient(); + var response = await http.PostAsync( + $"https://login.microsoftonline.com/{opts.TenantId}/oauth2/v2.0/token", + new FormUrlEncodedContent(new Dictionary + { + ["grant_type"] = "client_credentials", + ["client_id"] = opts.ClientId, + ["client_secret"] = opts.ClientSecret, + ["scope"] = "https://graph.microsoft.com/.default", + }), ct); + + response.EnsureSuccessStatusCode(); + var payload = await response.Content.ReadFromJsonAsync(cancellationToken: ct) + ?? throw new InvalidOperationException("Réponse de jeton Graph vide."); + + _token = payload.AccessToken; + _expiresAt = DateTime.UtcNow.AddMinutes(55); + return _token; + } + + private class TokenResponse + { + [JsonPropertyName("access_token")] + public string AccessToken { get; set; } = ""; + } +} diff --git a/SIFinance.Shared/FacturXEmissionService.cs b/SIFinance.Shared/FacturXEmissionService.cs new file mode 100644 index 0000000..bcde879 --- /dev/null +++ b/SIFinance.Shared/FacturXEmissionService.cs @@ -0,0 +1,145 @@ +using System.Text; +using iText.Kernel.Colors; +using iText.Kernel.Pdf; +using iText.Kernel.Pdf.Filespec; +using iText.Layout; +using iText.Layout.Element; +using iText.Pdfa; +using Microsoft.Extensions.Configuration; +using s2industries.ZUGFeRD; + +namespace SIFinance.Shared; + +public record FacturXLigne(string Designation, decimal Quantite, decimal PrixUnitaireHt); + +public record FacturXPartie(string Nom, string? Rue, string? CodePostal, string? Ville, string PaysCode, string? NumeroTva); + +public record FacturXEmissionRequest( + string NumeroFacture, + DateOnly DateFacture, + FacturXPartie Vendeur, + FacturXPartie Acheteur, + List Lignes, + decimal TauxTva); + +// Moteur réutilisable d'émission de factures électroniques Factur-X/ZUGFeRD (norme +// EN16931) : XML structuré via ZUGFeRD-csharp, assemblage PDF/A-3 + attachement du XML +// via iText7. Vit dans SIFinance.Shared pour être appelable depuis n'importe quel +// module (Achats, Sous-Traitants, futur Contrat Étudiant) une fois qu'un cas d'usage +// concret d'émission est identifié — voir appels dans les endpoints des modules. +// +// Nécessite un profil colorimétrique ICC sRGB valide (obligatoire pour la conformité +// PDF/A-3, "PdfOutputIntent") — fichier binaire non fourni ici (voir +// SIFinance.Shared/Resources/README.md pour comment se le procurer et où le déposer). +public class FacturXEmissionService(IConfiguration configuration) +{ + private string CheminProfilIcc => + configuration["FacturX:ProfilIccSrgb"] ?? Path.Combine(AppContext.BaseDirectory, "Resources", "sRGB.icc"); + + public byte[] Generer(FacturXEmissionRequest request) + { + if (request.Lignes.Count == 0) + throw new ArgumentException("Une facture électronique doit contenir au moins une ligne.", nameof(request)); + + var montantHt = request.Lignes.Sum(l => Math.Round(l.Quantite * l.PrixUnitaireHt, 2)); + var montantTva = Math.Round(montantHt * request.TauxTva / 100m, 2); + var montantTtc = montantHt + montantTva; + + var xmlBytes = GenererXml(request, montantHt, montantTva, montantTtc); + return AssemblerPdfA3(request, xmlBytes, montantHt, montantTva, montantTtc); + } + + private static byte[] GenererXml(FacturXEmissionRequest request, decimal montantHt, decimal montantTva, decimal montantTtc) + { + var desc = InvoiceDescriptor.CreateInvoice(request.NumeroFacture, request.DateFacture.ToDateTime(TimeOnly.MinValue), CurrencyCodes.EUR); + + desc.SetSeller( + request.Vendeur.Nom, request.Vendeur.CodePostal ?? "", request.Vendeur.Ville ?? "", request.Vendeur.Rue ?? "", + ParsePays(request.Vendeur.PaysCode), request.Vendeur.NumeroTva); + desc.SetBuyer( + request.Acheteur.Nom, request.Acheteur.CodePostal ?? "", request.Acheteur.Ville ?? "", request.Acheteur.Rue ?? "", + ParsePays(request.Acheteur.PaysCode), request.Acheteur.NumeroTva); + + foreach (var ligne in request.Lignes) + { + desc.AddTradeLineItem( + name: ligne.Designation, + netUnitPrice: ligne.PrixUnitaireHt, + unitCode: QuantityCodes.C62, + billedQuantity: ligne.Quantite, + taxType: TaxTypes.VAT, + categoryCode: TaxCategoryCodes.S, + taxPercent: request.TauxTva); + } + + desc.AddApplicableTradeTax(montantHt, request.TauxTva, montantTva, TaxTypes.VAT, TaxCategoryCodes.S); + desc.SetTotals( + lineTotalAmount: montantHt, taxBasisAmount: montantHt, taxTotalAmount: montantTva, + grandTotalAmount: montantTtc, duePayableAmount: montantTtc); + + using var xmlStream = new MemoryStream(); + desc.Save(xmlStream, ZUGFeRDVersion.Version23, Profile.Comfort); + return xmlStream.ToArray(); + } + + private byte[] AssemblerPdfA3(FacturXEmissionRequest request, byte[] xmlBytes, decimal montantHt, decimal montantTva, decimal montantTtc) + { + var cheminIcc = CheminProfilIcc; + if (!File.Exists(cheminIcc)) + throw new InvalidOperationException( + $"Profil ICC sRGB introuvable ({cheminIcc}) — requis pour générer un PDF/A-3 conforme. " + + "Voir SIFinance.Shared/Resources/README.md."); + + using var pdfMemStream = new MemoryStream(); + using (var iccStream = File.OpenRead(cheminIcc)) + { + var outputIntent = new PdfOutputIntent("Custom", "", "http://www.color.org", "sRGB IEC61966-2.1", iccStream); + using var writer = new PdfWriter(pdfMemStream); + using var pdfDoc = new PdfADocument(writer, PdfAConformance.PDF_A_3B, outputIntent); + pdfDoc.SetTagged(); + + using (var document = new Document(pdfDoc)) + { + document.Add(new Paragraph($"Facture {request.NumeroFacture}").SetFontSize(18).SimulateBold()); + document.Add(new Paragraph($"Date : {request.DateFacture:dd/MM/yyyy}")); + document.Add(new Paragraph(" ")); + + document.Add(new Paragraph("Émetteur").SimulateBold()); + document.Add(new Paragraph($"{request.Vendeur.Nom}\n{request.Vendeur.Rue}\n{request.Vendeur.CodePostal} {request.Vendeur.Ville}")); + document.Add(new Paragraph(" ")); + + document.Add(new Paragraph("Destinataire").SimulateBold()); + document.Add(new Paragraph($"{request.Acheteur.Nom}\n{request.Acheteur.Rue}\n{request.Acheteur.CodePostal} {request.Acheteur.Ville}")); + document.Add(new Paragraph(" ")); + + var table = new Table(4).UseAllAvailableWidth(); + table.AddHeaderCell("Désignation"); + table.AddHeaderCell("Qté"); + table.AddHeaderCell("PU HT"); + table.AddHeaderCell("Total HT"); + foreach (var ligne in request.Lignes) + { + table.AddCell(ligne.Designation); + table.AddCell(ligne.Quantite.ToString("N2")); + table.AddCell($"{ligne.PrixUnitaireHt:N2} €"); + table.AddCell($"{(ligne.Quantite * ligne.PrixUnitaireHt):N2} €"); + } + document.Add(table); + + document.Add(new Paragraph(" ")); + document.Add(new Paragraph($"Total HT : {montantHt:N2} €")); + document.Add(new Paragraph($"TVA ({request.TauxTva:N0}%) : {montantTva:N2} €")); + document.Add(new Paragraph($"Total TTC : {montantTtc:N2} €").SimulateBold()); + } + + var fileSpec = PdfFileSpec.CreateEmbeddedFileSpec( + pdfDoc, xmlBytes, "Facture électronique Factur-X", "factur-x.xml", PdfName.ApplicationXml, null, PdfName.Data); + pdfDoc.AddFileAttachment("factur-x.xml", fileSpec); + } + + return pdfMemStream.ToArray(); + } + + private static s2industries.ZUGFeRD.CountryCodes ParsePays(string code) => + Enum.TryParse(code, true, out var parsed) ? parsed : s2industries.ZUGFeRD.CountryCodes.FR; +} diff --git a/SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.Designer.cs b/SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.Designer.cs new file mode 100644 index 0000000..a8b7f4f --- /dev/null +++ b/SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.Designer.cs @@ -0,0 +1,1716 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260727094858_AddContratsAndPaiementStatut")] + partial class AddContratsAndPaiementStatut + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.cs b/SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.cs new file mode 100644 index 0000000..c952570 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260727094858_AddContratsAndPaiementStatut.cs @@ -0,0 +1,148 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class AddContratsAndPaiementStatut : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "statut", + schema: "fin", + table: "paiements", + type: "varchar(20)", + unicode: false, + maxLength: 20, + nullable: false, + defaultValue: "PROGRAMME"); + + migrationBuilder.AddColumn( + name: "rapproche", + schema: "fin", + table: "paiements", + type: "bit", + nullable: false, + defaultValue: false); + + migrationBuilder.CreateTable( + name: "contrats_fournisseurs", + schema: "achats", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + fournisseur_id = table.Column(type: "int", nullable: false), + reference = table.Column(type: "varchar(50)", unicode: false, maxLength: 50, nullable: true), + objet = table.Column(type: "varchar(255)", unicode: false, maxLength: 255, nullable: false), + date_signature = table.Column(type: "date", nullable: true), + date_debut = table.Column(type: "date", nullable: false), + date_fin = table.Column(type: "date", nullable: false), + montant_total = table.Column(type: "decimal(18,2)", nullable: false), + periodicite = table.Column(type: "varchar(20)", unicode: false, maxLength: 20, nullable: false, defaultValue: "mensuelle"), + statut = table.Column(type: "varchar(20)", unicode: false, maxLength: 20, nullable: false, defaultValue: "ACTIF"), + saisie_par_id = table.Column(type: "int", nullable: false), + date_creation = table.Column(type: "datetime", nullable: true, defaultValueSql: "(getdate())"), + date_derniere_maj = table.Column(type: "datetime", nullable: true, defaultValueSql: "(getdate())") + }, + constraints: table => + { + table.PrimaryKey("PK__contrats_fournisseurs", x => x.id); + table.ForeignKey( + name: "FK__contrats_fournisseurs__fournisseur", + column: x => x.fournisseur_id, + principalSchema: "ref", + principalTable: "fournisseurs", + principalColumn: "id"); + table.ForeignKey( + name: "FK__contrats_fournisseurs__saisie_par", + column: x => x.saisie_par_id, + principalSchema: "core", + principalTable: "utilisateurs", + principalColumn: "id"); + }); + + migrationBuilder.CreateTable( + name: "contrats_echeances", + schema: "achats", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + contrat_id = table.Column(type: "int", nullable: false), + date_echeance = table.Column(type: "date", nullable: false), + montant = table.Column(type: "decimal(18,2)", nullable: false), + statut = table.Column(type: "varchar(20)", unicode: false, maxLength: 20, nullable: false, defaultValue: "A_VENIR"), + justificatif_id = table.Column(type: "int", nullable: true), + date_creation = table.Column(type: "datetime", nullable: true, defaultValueSql: "(getdate())") + }, + constraints: table => + { + table.PrimaryKey("PK__contrats_echeances", x => x.id); + table.ForeignKey( + name: "FK__contrats_echeances__contrat", + column: x => x.contrat_id, + principalSchema: "achats", + principalTable: "contrats_fournisseurs", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK__contrats_echeances__justificatif", + column: x => x.justificatif_id, + principalSchema: "core", + principalTable: "pieces_jointes", + principalColumn: "id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_ctf_fournisseur", + schema: "achats", + table: "contrats_fournisseurs", + column: "fournisseur_id"); + + migrationBuilder.CreateIndex( + name: "IX_ctf_saisie_par", + schema: "achats", + table: "contrats_fournisseurs", + column: "saisie_par_id"); + + migrationBuilder.CreateIndex( + name: "IX_cte_contrat", + schema: "achats", + table: "contrats_echeances", + column: "contrat_id"); + + migrationBuilder.CreateIndex( + name: "IX_cte_justif", + schema: "achats", + table: "contrats_echeances", + column: "justificatif_id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "contrats_echeances", + schema: "achats"); + + migrationBuilder.DropTable( + name: "contrats_fournisseurs", + schema: "achats"); + + migrationBuilder.DropColumn( + name: "rapproche", + schema: "fin", + table: "paiements"); + + migrationBuilder.DropColumn( + name: "statut", + schema: "fin", + table: "paiements"); + } + } +} diff --git a/SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.Designer.cs b/SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.Designer.cs new file mode 100644 index 0000000..d1538a6 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.Designer.cs @@ -0,0 +1,1721 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260803115528_AddCategorieFournisseurLibre")] + partial class AddCategorieFournisseurLibre + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.cs b/SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.cs new file mode 100644 index 0000000..f93c60b --- /dev/null +++ b/SIFinance.Shared/Migrations/20260803115528_AddCategorieFournisseurLibre.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class AddCategorieFournisseurLibre : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "categorie_fournisseur_libre", + schema: "achats", + table: "demandes_achat", + type: "nvarchar(100)", + maxLength: 100, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "categorie_fournisseur_libre", + schema: "achats", + table: "demandes_achat"); + } + } +} diff --git a/SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.Designer.cs b/SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.Designer.cs new file mode 100644 index 0000000..e166514 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.Designer.cs @@ -0,0 +1,1899 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260806084840_AddSousTraitantsOrganismesContrats")] + partial class AddSousTraitantsOrganismesContrats + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("objet"); + + b.Property("OrganismeId") + .HasColumnType("int") + .HasColumnName("organisme_id"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_prestataires"); + + b.HasIndex(new[] { "OrganismeId" }, "IX_ctp_organisme"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctp_saisie_par"); + + b.HasIndex(new[] { "Reference" }, "UQ_ctp_reference") + .IsUnique(); + + b.ToTable("contrats_prestataires", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("adresse"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("telephone"); + + b.HasKey("Id") + .HasName("PK__organismes"); + + b.ToTable("organismes", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.UtilisateurRole", b => + { + b.Property("AzureOid") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("Role") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("role"); + + b.HasKey("AzureOid", "Role") + .HasName("PK_utilisateur_roles"); + + b.ToTable("utilisateur_roles", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.HasOne("SIFinance.Shared.Entities.Organisme", "Organisme") + .WithMany("ContratsPrestataires") + .HasForeignKey("OrganismeId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__organisme"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsPrestataires") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + + b.Navigation("Organisme"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Navigation("ContratsPrestataires"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("ContratsPrestataires"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.cs b/SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.cs new file mode 100644 index 0000000..02603a7 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260806084840_AddSousTraitantsOrganismesContrats.cs @@ -0,0 +1,104 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class AddSousTraitantsOrganismesContrats : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "st"); + + migrationBuilder.CreateTable( + name: "organismes", + schema: "st", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + raison_sociale = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + siret = table.Column(type: "varchar(20)", unicode: false, maxLength: 20, nullable: true), + email = table.Column(type: "varchar(255)", unicode: false, maxLength: 255, nullable: true), + telephone = table.Column(type: "varchar(30)", unicode: false, maxLength: 30, nullable: true), + adresse = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: true), + statut = table.Column(type: "varchar(20)", unicode: false, maxLength: 20, nullable: false, defaultValue: "ACTIF"), + date_creation = table.Column(type: "datetime", nullable: true, defaultValueSql: "(getdate())") + }, + constraints: table => + { + table.PrimaryKey("PK__organismes", x => x.id); + }); + + migrationBuilder.CreateTable( + name: "contrats_prestataires", + schema: "st", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + organisme_id = table.Column(type: "int", nullable: false), + reference = table.Column(type: "varchar(50)", unicode: false, maxLength: 50, nullable: false), + objet = table.Column(type: "nvarchar(255)", maxLength: 255, nullable: false), + date_signature = table.Column(type: "date", nullable: true), + date_debut = table.Column(type: "date", nullable: false), + date_fin = table.Column(type: "date", nullable: false), + montant_total = table.Column(type: "decimal(18,2)", nullable: false), + statut = table.Column(type: "varchar(20)", unicode: false, maxLength: 20, nullable: false, defaultValue: "ACTIF"), + saisie_par_id = table.Column(type: "int", nullable: false), + date_creation = table.Column(type: "datetime", nullable: true, defaultValueSql: "(getdate())") + }, + constraints: table => + { + table.PrimaryKey("PK__contrats_prestataires", x => x.id); + table.ForeignKey( + name: "FK__contrats_prestataires__organisme", + column: x => x.organisme_id, + principalSchema: "st", + principalTable: "organismes", + principalColumn: "id"); + table.ForeignKey( + name: "FK__contrats_prestataires__saisie_par", + column: x => x.saisie_par_id, + principalSchema: "core", + principalTable: "utilisateurs", + principalColumn: "id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_ctp_organisme", + schema: "st", + table: "contrats_prestataires", + column: "organisme_id"); + + migrationBuilder.CreateIndex( + name: "IX_ctp_saisie_par", + schema: "st", + table: "contrats_prestataires", + column: "saisie_par_id"); + + migrationBuilder.CreateIndex( + name: "UQ_ctp_reference", + schema: "st", + table: "contrats_prestataires", + column: "reference", + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "contrats_prestataires", + schema: "st"); + + migrationBuilder.DropTable( + name: "organismes", + schema: "st"); + } + } +} diff --git a/SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.Designer.cs b/SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.Designer.cs new file mode 100644 index 0000000..517ec4f --- /dev/null +++ b/SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.Designer.cs @@ -0,0 +1,1914 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260806131533_AddResponsableToUtilisateurs")] + partial class AddResponsableToUtilisateurs + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("objet"); + + b.Property("OrganismeId") + .HasColumnType("int") + .HasColumnName("organisme_id"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_prestataires"); + + b.HasIndex(new[] { "OrganismeId" }, "IX_ctp_organisme"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctp_saisie_par"); + + b.HasIndex(new[] { "Reference" }, "UQ_ctp_reference") + .IsUnique(); + + b.ToTable("contrats_prestataires", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("adresse"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("telephone"); + + b.HasKey("Id") + .HasName("PK__organismes"); + + b.ToTable("organismes", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ResponsableId") + .HasColumnType("int") + .HasColumnName("responsable_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex("ResponsableId"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.UtilisateurRole", b => + { + b.Property("AzureOid") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("Role") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("role"); + + b.HasKey("AzureOid", "Role") + .HasName("PK_utilisateur_roles"); + + b.ToTable("utilisateur_roles", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.HasOne("SIFinance.Shared.Entities.Organisme", "Organisme") + .WithMany("ContratsPrestataires") + .HasForeignKey("OrganismeId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__organisme"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsPrestataires") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + + b.Navigation("Organisme"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Responsable") + .WithMany("Collaborateurs") + .HasForeignKey("ResponsableId") + .HasConstraintName("FK__utilisateurs__responsable"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Responsable"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Navigation("ContratsPrestataires"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("Collaborateurs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("ContratsPrestataires"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.cs b/SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.cs new file mode 100644 index 0000000..fc4dce4 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260806131533_AddResponsableToUtilisateurs.cs @@ -0,0 +1,55 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class AddResponsableToUtilisateurs : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "responsable_id", + schema: "core", + table: "utilisateurs", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_utilisateurs_responsable_id", + schema: "core", + table: "utilisateurs", + column: "responsable_id"); + + migrationBuilder.AddForeignKey( + name: "FK__utilisateurs__responsable", + schema: "core", + table: "utilisateurs", + column: "responsable_id", + principalSchema: "core", + principalTable: "utilisateurs", + principalColumn: "id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK__utilisateurs__responsable", + schema: "core", + table: "utilisateurs"); + + migrationBuilder.DropIndex( + name: "IX_utilisateurs_responsable_id", + schema: "core", + table: "utilisateurs"); + + migrationBuilder.DropColumn( + name: "responsable_id", + schema: "core", + table: "utilisateurs"); + } + } +} diff --git a/SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.Designer.cs b/SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.Designer.cs new file mode 100644 index 0000000..496d828 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.Designer.cs @@ -0,0 +1,1919 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260811082905_AddCommentaireToFactures")] + partial class AddCommentaireToFactures + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("objet"); + + b.Property("OrganismeId") + .HasColumnType("int") + .HasColumnName("organisme_id"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_prestataires"); + + b.HasIndex(new[] { "OrganismeId" }, "IX_ctp_organisme"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctp_saisie_par"); + + b.HasIndex(new[] { "Reference" }, "UQ_ctp_reference") + .IsUnique(); + + b.ToTable("contrats_prestataires", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("Commentaire") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("adresse"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("telephone"); + + b.HasKey("Id") + .HasName("PK__organismes"); + + b.ToTable("organismes", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ResponsableId") + .HasColumnType("int") + .HasColumnName("responsable_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex("ResponsableId"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.UtilisateurRole", b => + { + b.Property("AzureOid") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("Role") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("role"); + + b.HasKey("AzureOid", "Role") + .HasName("PK_utilisateur_roles"); + + b.ToTable("utilisateur_roles", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.HasOne("SIFinance.Shared.Entities.Organisme", "Organisme") + .WithMany("ContratsPrestataires") + .HasForeignKey("OrganismeId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__organisme"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsPrestataires") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + + b.Navigation("Organisme"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Responsable") + .WithMany("Collaborateurs") + .HasForeignKey("ResponsableId") + .HasConstraintName("FK__utilisateurs__responsable"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Responsable"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Navigation("ContratsPrestataires"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("Collaborateurs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("ContratsPrestataires"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.cs b/SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.cs new file mode 100644 index 0000000..927b1f2 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260811082905_AddCommentaireToFactures.cs @@ -0,0 +1,31 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class AddCommentaireToFactures : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "commentaire", + schema: "fin", + table: "factures", + type: "nvarchar(1000)", + maxLength: 1000, + nullable: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropColumn( + name: "commentaire", + schema: "fin", + table: "factures"); + } + } +} diff --git a/SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.Designer.cs b/SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.Designer.cs new file mode 100644 index 0000000..9859459 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.Designer.cs @@ -0,0 +1,1935 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260811084738_AddContratIdToFactures")] + partial class AddContratIdToFactures + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("objet"); + + b.Property("OrganismeId") + .HasColumnType("int") + .HasColumnName("organisme_id"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_prestataires"); + + b.HasIndex(new[] { "OrganismeId" }, "IX_ctp_organisme"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctp_saisie_par"); + + b.HasIndex(new[] { "Reference" }, "UQ_ctp_reference") + .IsUnique(); + + b.ToTable("contrats_prestataires", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("Commentaire") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire"); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex("ContratId"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("adresse"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("telephone"); + + b.HasKey("Id") + .HasName("PK__organismes"); + + b.ToTable("organismes", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ResponsableId") + .HasColumnType("int") + .HasColumnName("responsable_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex("ResponsableId"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.UtilisateurRole", b => + { + b.Property("AzureOid") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("Role") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("role"); + + b.HasKey("AzureOid", "Role") + .HasName("PK_utilisateur_roles"); + + b.ToTable("utilisateur_roles", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.HasOne("SIFinance.Shared.Entities.Organisme", "Organisme") + .WithMany("ContratsPrestataires") + .HasForeignKey("OrganismeId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__organisme"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsPrestataires") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + + b.Navigation("Organisme"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Factures") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_factures_contrat"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("Contrat"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Responsable") + .WithMany("Collaborateurs") + .HasForeignKey("ResponsableId") + .HasConstraintName("FK__utilisateurs__responsable"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Responsable"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Navigation("ContratsPrestataires"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("Collaborateurs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("ContratsPrestataires"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.cs b/SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.cs new file mode 100644 index 0000000..956a94f --- /dev/null +++ b/SIFinance.Shared/Migrations/20260811084738_AddContratIdToFactures.cs @@ -0,0 +1,56 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class AddContratIdToFactures : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "contrat_id", + schema: "fin", + table: "factures", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_factures_contrat_id", + schema: "fin", + table: "factures", + column: "contrat_id"); + + migrationBuilder.AddForeignKey( + name: "FK_factures_contrat", + schema: "fin", + table: "factures", + column: "contrat_id", + principalSchema: "achats", + principalTable: "contrats_fournisseurs", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_factures_contrat", + schema: "fin", + table: "factures"); + + migrationBuilder.DropIndex( + name: "IX_factures_contrat_id", + schema: "fin", + table: "factures"); + + migrationBuilder.DropColumn( + name: "contrat_id", + schema: "fin", + table: "factures"); + } + } +} diff --git a/SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.Designer.cs b/SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.Designer.cs new file mode 100644 index 0000000..bf227db --- /dev/null +++ b/SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.Designer.cs @@ -0,0 +1,2026 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260814130216_AddBudgetsService")] + partial class AddBudgetsService + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CommentaireRevision") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire_revision"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj"); + + b.Property("DateRevision") + .HasColumnType("datetime") + .HasColumnName("date_revision"); + + b.Property("ExerciceAnnee") + .HasColumnType("int") + .HasColumnName("exercice_annee"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("MontantRevise") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_revise"); + + b.Property("RevisePar") + .HasColumnType("int") + .HasColumnName("revise_par"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id"); + + b.HasIndex("RevisePar"); + + b.HasIndex("SaisieParId"); + + b.HasIndex(new[] { "ServiceId", "ExerciceAnnee" }, "IX_budgets_service_unique") + .IsUnique(); + + b.ToTable("budgets_service", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("objet"); + + b.Property("OrganismeId") + .HasColumnType("int") + .HasColumnName("organisme_id"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_prestataires"); + + b.HasIndex(new[] { "OrganismeId" }, "IX_ctp_organisme"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctp_saisie_par"); + + b.HasIndex(new[] { "Reference" }, "UQ_ctp_reference") + .IsUnique(); + + b.ToTable("contrats_prestataires", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("Commentaire") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire"); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex("ContratId"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("adresse"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("telephone"); + + b.HasKey("Id") + .HasName("PK__organismes"); + + b.ToTable("organismes", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ResponsableId") + .HasColumnType("int") + .HasColumnName("responsable_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex("ResponsableId"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.UtilisateurRole", b => + { + b.Property("AzureOid") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("Role") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("role"); + + b.HasKey("AzureOid", "Role") + .HasName("PK_utilisateur_roles"); + + b.ToTable("utilisateur_roles", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ReviseParUtilisateur") + .WithMany() + .HasForeignKey("RevisePar") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_budgets_service_revise_par"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany() + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK_budgets_service_saisie_par"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany() + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK_budgets_service_service"); + + b.Navigation("ReviseParUtilisateur"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.HasOne("SIFinance.Shared.Entities.Organisme", "Organisme") + .WithMany("ContratsPrestataires") + .HasForeignKey("OrganismeId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__organisme"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsPrestataires") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + + b.Navigation("Organisme"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Factures") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_factures_contrat"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("Contrat"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Responsable") + .WithMany("Collaborateurs") + .HasForeignKey("ResponsableId") + .HasConstraintName("FK__utilisateurs__responsable"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Responsable"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Navigation("ContratsPrestataires"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("Collaborateurs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("ContratsPrestataires"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.cs b/SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.cs new file mode 100644 index 0000000..88acd34 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260814130216_AddBudgetsService.cs @@ -0,0 +1,84 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class AddBudgetsService : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "budgets_service", + schema: "achats", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + service_id = table.Column(type: "int", nullable: false), + exercice_annee = table.Column(type: "int", nullable: false), + montant_initial = table.Column(type: "decimal(18,2)", nullable: false), + montant_revise = table.Column(type: "decimal(18,2)", nullable: true), + date_revision = table.Column(type: "datetime", nullable: true), + revise_par = table.Column(type: "int", nullable: true), + commentaire_revision = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), + saisie_par_id = table.Column(type: "int", nullable: false), + date_creation = table.Column(type: "datetime", nullable: true, defaultValueSql: "(getdate())"), + date_derniere_maj = table.Column(type: "datetime", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_budgets_service", x => x.id); + table.ForeignKey( + name: "FK_budgets_service_revise_par", + column: x => x.revise_par, + principalSchema: "core", + principalTable: "utilisateurs", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + table.ForeignKey( + name: "FK_budgets_service_saisie_par", + column: x => x.saisie_par_id, + principalSchema: "core", + principalTable: "utilisateurs", + principalColumn: "id"); + table.ForeignKey( + name: "FK_budgets_service_service", + column: x => x.service_id, + principalSchema: "ref", + principalTable: "services", + principalColumn: "id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_budgets_service_revise_par", + schema: "achats", + table: "budgets_service", + column: "revise_par"); + + migrationBuilder.CreateIndex( + name: "IX_budgets_service_saisie_par_id", + schema: "achats", + table: "budgets_service", + column: "saisie_par_id"); + + migrationBuilder.CreateIndex( + name: "IX_budgets_service_unique", + schema: "achats", + table: "budgets_service", + columns: new[] { "service_id", "exercice_annee" }, + unique: true); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "budgets_service", + schema: "achats"); + } + } +} diff --git a/SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.Designer.cs b/SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.Designer.cs new file mode 100644 index 0000000..842f871 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.Designer.cs @@ -0,0 +1,2065 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + [Migration("20260814131858_SplitBudgetRevisionsHistory")] + partial class SplitBudgetRevisionsHistory + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj"); + + b.Property("ExerciceAnnee") + .HasColumnType("int") + .HasColumnName("exercice_annee"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id"); + + b.HasIndex("SaisieParId"); + + b.HasIndex(new[] { "ServiceId", "ExerciceAnnee" }, "IX_budgets_service_unique") + .IsUnique(); + + b.ToTable("budgets_service", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetServiceRevision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BudgetServiceId") + .HasColumnType("int") + .HasColumnName("budget_service_id"); + + b.Property("Commentaire") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire"); + + b.Property("DateRevision") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_revision") + .HasDefaultValueSql("(getdate())"); + + b.Property("MontantRevise") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_revise"); + + b.Property("RevisePar") + .HasColumnType("int") + .HasColumnName("revise_par"); + + b.HasKey("Id"); + + b.HasIndex("BudgetServiceId"); + + b.HasIndex("RevisePar"); + + b.ToTable("budgets_service_revisions", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("objet"); + + b.Property("OrganismeId") + .HasColumnType("int") + .HasColumnName("organisme_id"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_prestataires"); + + b.HasIndex(new[] { "OrganismeId" }, "IX_ctp_organisme"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctp_saisie_par"); + + b.HasIndex(new[] { "Reference" }, "UQ_ctp_reference") + .IsUnique(); + + b.ToTable("contrats_prestataires", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("Commentaire") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire"); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex("ContratId"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("adresse"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("telephone"); + + b.HasKey("Id") + .HasName("PK__organismes"); + + b.ToTable("organismes", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ResponsableId") + .HasColumnType("int") + .HasColumnName("responsable_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex("ResponsableId"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.UtilisateurRole", b => + { + b.Property("AzureOid") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("Role") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("role"); + + b.HasKey("AzureOid", "Role") + .HasName("PK_utilisateur_roles"); + + b.ToTable("utilisateur_roles", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany() + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK_budgets_service_saisie_par"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany() + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK_budgets_service_service"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetServiceRevision", b => + { + b.HasOne("SIFinance.Shared.Entities.BudgetService", "BudgetService") + .WithMany("Revisions") + .HasForeignKey("BudgetServiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_budgets_service_revisions_budget"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ReviseParUtilisateur") + .WithMany() + .HasForeignKey("RevisePar") + .IsRequired() + .HasConstraintName("FK_budgets_service_revisions_revise_par"); + + b.Navigation("BudgetService"); + + b.Navigation("ReviseParUtilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.HasOne("SIFinance.Shared.Entities.Organisme", "Organisme") + .WithMany("ContratsPrestataires") + .HasForeignKey("OrganismeId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__organisme"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsPrestataires") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + + b.Navigation("Organisme"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Factures") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_factures_contrat"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("Contrat"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Responsable") + .WithMany("Collaborateurs") + .HasForeignKey("ResponsableId") + .HasConstraintName("FK__utilisateurs__responsable"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Responsable"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.Navigation("Revisions"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Navigation("ContratsPrestataires"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("Collaborateurs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("ContratsPrestataires"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.cs b/SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.cs new file mode 100644 index 0000000..555a768 --- /dev/null +++ b/SIFinance.Shared/Migrations/20260814131858_SplitBudgetRevisionsHistory.cs @@ -0,0 +1,141 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + /// + public partial class SplitBudgetRevisionsHistory : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "FK_budgets_service_revise_par", + schema: "achats", + table: "budgets_service"); + + migrationBuilder.DropIndex( + name: "IX_budgets_service_revise_par", + schema: "achats", + table: "budgets_service"); + + migrationBuilder.DropColumn( + name: "commentaire_revision", + schema: "achats", + table: "budgets_service"); + + migrationBuilder.DropColumn( + name: "date_revision", + schema: "achats", + table: "budgets_service"); + + migrationBuilder.DropColumn( + name: "montant_revise", + schema: "achats", + table: "budgets_service"); + + migrationBuilder.DropColumn( + name: "revise_par", + schema: "achats", + table: "budgets_service"); + + migrationBuilder.CreateTable( + name: "budgets_service_revisions", + schema: "achats", + columns: table => new + { + id = table.Column(type: "int", nullable: false) + .Annotation("SqlServer:Identity", "1, 1"), + budget_service_id = table.Column(type: "int", nullable: false), + montant_revise = table.Column(type: "decimal(18,2)", nullable: false), + commentaire = table.Column(type: "nvarchar(1000)", maxLength: 1000, nullable: true), + revise_par = table.Column(type: "int", nullable: false), + date_revision = table.Column(type: "datetime", nullable: false, defaultValueSql: "(getdate())") + }, + constraints: table => + { + table.PrimaryKey("PK_budgets_service_revisions", x => x.id); + table.ForeignKey( + name: "FK_budgets_service_revisions_budget", + column: x => x.budget_service_id, + principalSchema: "achats", + principalTable: "budgets_service", + principalColumn: "id", + onDelete: ReferentialAction.Cascade); + table.ForeignKey( + name: "FK_budgets_service_revisions_revise_par", + column: x => x.revise_par, + principalSchema: "core", + principalTable: "utilisateurs", + principalColumn: "id"); + }); + + migrationBuilder.CreateIndex( + name: "IX_budgets_service_revisions_budget_service_id", + schema: "achats", + table: "budgets_service_revisions", + column: "budget_service_id"); + + migrationBuilder.CreateIndex( + name: "IX_budgets_service_revisions_revise_par", + schema: "achats", + table: "budgets_service_revisions", + column: "revise_par"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "budgets_service_revisions", + schema: "achats"); + + migrationBuilder.AddColumn( + name: "commentaire_revision", + schema: "achats", + table: "budgets_service", + type: "nvarchar(1000)", + maxLength: 1000, + nullable: true); + + migrationBuilder.AddColumn( + name: "date_revision", + schema: "achats", + table: "budgets_service", + type: "datetime", + nullable: true); + + migrationBuilder.AddColumn( + name: "montant_revise", + schema: "achats", + table: "budgets_service", + type: "decimal(18,2)", + nullable: true); + + migrationBuilder.AddColumn( + name: "revise_par", + schema: "achats", + table: "budgets_service", + type: "int", + nullable: true); + + migrationBuilder.CreateIndex( + name: "IX_budgets_service_revise_par", + schema: "achats", + table: "budgets_service", + column: "revise_par"); + + migrationBuilder.AddForeignKey( + name: "FK_budgets_service_revise_par", + schema: "achats", + table: "budgets_service", + column: "revise_par", + principalSchema: "core", + principalTable: "utilisateurs", + principalColumn: "id", + onDelete: ReferentialAction.SetNull); + } + } +} diff --git a/SIFinance.Shared/Migrations/AddMaterialWorkflow.sql b/SIFinance.Shared/Migrations/AddMaterialWorkflow.sql new file mode 100644 index 0000000..8546fd8 --- /dev/null +++ b/SIFinance.Shared/Migrations/AddMaterialWorkflow.sql @@ -0,0 +1,18 @@ +-- À exécuter une seule fois sur la base SIFinance. +-- Le flag permet de rattacher n'importe quelle catégorie au circuit matériel, +-- sans dépendre de son libellé. +ALTER TABLE ref.types_depense +ADD est_materiel bit NOT NULL + CONSTRAINT DF_types_depense_est_materiel DEFAULT (0); +GO + +-- Les nouveaux statuts du workflow dépassent parfois la taille historique de 30. +ALTER TABLE achats.demandes_achat +ALTER COLUMN statut varchar(50) NOT NULL; +GO + +-- Catégories à traiter comme du matériel : adapte cette liste à tes règles métier. +UPDATE ref.types_depense +SET est_materiel = 1 +WHERE libelle IN ('Fournitures', 'Informatique', 'Mobilier'); +GO diff --git a/SIFinance.Shared/Migrations/SIFinanceDbContextModelSnapshot.cs b/SIFinance.Shared/Migrations/SIFinanceDbContextModelSnapshot.cs new file mode 100644 index 0000000..f0c737f --- /dev/null +++ b/SIFinance.Shared/Migrations/SIFinanceDbContextModelSnapshot.cs @@ -0,0 +1,2062 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using SIFinance.Shared.Entities; + +#nullable disable + +namespace SIFinance.Shared.Migrations +{ + [DbContext(typeof(SIFinanceDbContext))] + partial class SIFinanceDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "10.0.9") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevue") + .HasColumnType("date") + .HasColumnName("date_prevue"); + + b.Property("DateVerse") + .HasColumnType("date") + .HasColumnName("date_verse"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PREVU") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__acomptes__3213E83F0A5FDF52"); + + b.HasIndex(new[] { "FactureId" }, "IX_aco_facture"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_aco_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_aco_saisie_par"); + + b.ToTable("acomptes", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("AffecteParId") + .HasColumnType("int") + .HasColumnName("affecte_par_id"); + + b.Property("AvoirId") + .HasColumnType("int") + .HasColumnName("avoir_id"); + + b.Property("DateAffectation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_affectation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("MontantAffecte") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_affecte"); + + b.HasKey("Id") + .HasName("PK__affectat__3213E83FEA440C49"); + + b.HasIndex(new[] { "AffecteParId" }, "IX_aff_affecte_par"); + + b.HasIndex(new[] { "AvoirId" }, "IX_aff_avoir"); + + b.HasIndex(new[] { "FactureId" }, "IX_aff_facture"); + + b.ToTable("affectations_avoir", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("DetailJson") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("detail_json"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("entite_type"); + + b.Property("IpAdresse") + .HasMaxLength(45) + .IsUnicode(false) + .HasColumnType("varchar(45)") + .HasColumnName("ip_adresse"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__audit__3213E83F12911979"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_audit_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_audit_utilisateur"); + + b.ToTable("audit", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateAvoir") + .HasColumnType("date") + .HasColumnName("date_avoir"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("NumeroAvoir") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_avoir"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("SoldeDisponible") + .HasColumnType("decimal(18, 2)") + .HasColumnName("solde_disponible"); + + b.HasKey("Id") + .HasName("PK__avoirs__3213E83FA6ADF4CA"); + + b.HasIndex(new[] { "FactureId" }, "IX_avo_facture"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_avo_fournisseur"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_avo_justif"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_avo_saisie_par"); + + b.ToTable("avoirs", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj"); + + b.Property("ExerciceAnnee") + .HasColumnType("int") + .HasColumnName("exercice_annee"); + + b.Property("MontantInitial") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_initial"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id"); + + b.HasIndex("SaisieParId"); + + b.HasIndex(new[] { "ServiceId", "ExerciceAnnee" }, "IX_budgets_service_unique") + .IsUnique(); + + b.ToTable("budgets_service", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetServiceRevision", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("BudgetServiceId") + .HasColumnType("int") + .HasColumnName("budget_service_id"); + + b.Property("Commentaire") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire"); + + b.Property("DateRevision") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_revision") + .HasDefaultValueSql("(getdate())"); + + b.Property("MontantRevise") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_revise"); + + b.Property("RevisePar") + .HasColumnType("int") + .HasColumnName("revise_par"); + + b.HasKey("Id"); + + b.HasIndex("BudgetServiceId"); + + b.HasIndex("RevisePar"); + + b.ToTable("budgets_service_revisions", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("JustificatifId") + .HasColumnType("int") + .HasColumnName("justificatif_id"); + + b.Property("Montant") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("A_VENIR") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_echeances"); + + b.HasIndex(new[] { "ContratId" }, "IX_cte_contrat"); + + b.HasIndex(new[] { "JustificatifId" }, "IX_cte_justif"); + + b.ToTable("contrats_echeances", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Periodicite") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("mensuelle") + .HasColumnName("periodicite"); + + b.Property("Reference") + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_fournisseurs"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_ctf_fournisseur"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctf_saisie_par"); + + b.ToTable("contrats_fournisseurs", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDebut") + .HasColumnType("date") + .HasColumnName("date_debut"); + + b.Property("DateFin") + .HasColumnType("date") + .HasColumnName("date_fin"); + + b.Property("DateSignature") + .HasColumnType("date") + .HasColumnName("date_signature"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("objet"); + + b.Property("OrganismeId") + .HasColumnType("int") + .HasColumnName("organisme_id"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("reference"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__contrats_prestataires"); + + b.HasIndex(new[] { "OrganismeId" }, "IX_ctp_organisme"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_ctp_saisie_par"); + + b.HasIndex(new[] { "Reference" }, "UQ_ctp_reference") + .IsUnique(); + + b.ToTable("contrats_prestataires", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieFournisseurLibre") + .HasMaxLength(100) + .HasColumnType("nvarchar(100)") + .HasColumnName("categorie_fournisseur_libre"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DatePrevisionnelle") + .HasColumnType("date") + .HasColumnName("date_previsionnelle"); + + b.Property("DateSoumission") + .HasColumnType("datetime") + .HasColumnName("date_soumission"); + + b.Property("DemandeurId") + .HasColumnType("int") + .HasColumnName("demandeur_id"); + + b.Property("Description") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("description"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("FournisseurLibre") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("fournisseur_libre"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("Objet") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("objet"); + + b.Property("Reference") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("reference"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SeuilApplicable") + .HasColumnType("decimal(18, 2)") + .HasColumnName("seuil_applicable"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .ValueGeneratedOnAdd() + .HasColumnType("decimal(5, 2)") + .HasDefaultValue(20m) + .HasColumnName("taux_tva"); + + b.Property("TypeDepenseId") + .HasColumnType("int") + .HasColumnName("type_depense_id"); + + b.Property("ValideurId") + .HasColumnType("int") + .HasColumnName("valideur_id"); + + b.HasKey("Id") + .HasName("PK__demandes__3213E83FB1E63F22"); + + b.HasIndex(new[] { "DemandeurId" }, "IX_da_demandeur"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_da_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_da_mode_paiement"); + + b.HasIndex(new[] { "ServiceId" }, "IX_da_service"); + + b.HasIndex(new[] { "Statut" }, "IX_da_statut"); + + b.HasIndex(new[] { "TypeDepenseId" }, "IX_da_type_depense"); + + b.HasIndex(new[] { "ValideurId" }, "IX_da_valideur"); + + b.HasIndex(new[] { "Reference" }, "UQ__demandes__FD90DA99FE701252") + .IsUnique(); + + b.ToTable("demandes_achat", "achats"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("CategorieDepenseId") + .HasColumnType("int") + .HasColumnName("categorie_depense_id"); + + b.Property("Commentaire") + .HasMaxLength(1000) + .HasColumnType("nvarchar(1000)") + .HasColumnName("commentaire"); + + b.Property("ContratId") + .HasColumnType("int") + .HasColumnName("contrat_id"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereMaj") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_derniere_maj") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateEcheance") + .HasColumnType("date") + .HasColumnName("date_echeance"); + + b.Property("DateFacture") + .HasColumnType("date") + .HasColumnName("date_facture"); + + b.Property("DateValidationBap") + .HasColumnType("datetime") + .HasColumnName("date_validation_bap"); + + b.Property("DemandeAchatId") + .HasColumnType("int") + .HasColumnName("demande_achat_id"); + + b.Property("EstDoublonSignale") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("est_doublon_signale"); + + b.Property("FournisseurId") + .HasColumnType("int") + .HasColumnName("fournisseur_id"); + + b.Property("ModePaiementId") + .HasColumnType("int") + .HasColumnName("mode_paiement_id"); + + b.Property("MontantHt") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ht"); + + b.Property("MontantTtc") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_ttc"); + + b.Property("MontantTva") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_tva"); + + b.Property("NumeroFacture") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("numero_facture"); + + b.Property("SaisieParId") + .HasColumnType("int") + .HasColumnName("saisie_par_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.Property("SourceType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("source_type"); + + b.Property("Statut") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("statut"); + + b.Property("TauxTva") + .HasColumnType("decimal(5, 2)") + .HasColumnName("taux_tva"); + + b.Property("ValideBapParId") + .HasColumnType("int") + .HasColumnName("valide_bap_par_id"); + + b.HasKey("Id") + .HasName("PK__factures__3213E83F68386F7E"); + + b.HasIndex("ContratId"); + + b.HasIndex(new[] { "CategorieDepenseId" }, "IX_fac_categorie"); + + b.HasIndex(new[] { "DemandeAchatId" }, "IX_fac_demande"); + + b.HasIndex(new[] { "FournisseurId" }, "IX_fac_fournisseur"); + + b.HasIndex(new[] { "ModePaiementId" }, "IX_fac_mode_paiement"); + + b.HasIndex(new[] { "SaisieParId" }, "IX_fac_saisie_par"); + + b.HasIndex(new[] { "ServiceId" }, "IX_fac_service"); + + b.HasIndex(new[] { "Statut" }, "IX_fac_statut"); + + b.HasIndex(new[] { "ValideBapParId" }, "IX_fac_valide_bap"); + + b.ToTable("factures", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateGeneration") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_generation") + .HasDefaultValueSql("(getdate())"); + + b.Property("GenereParId") + .HasColumnType("int") + .HasColumnName("genere_par_id"); + + b.Property("MontantTotal") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_total"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("NombreVirements") + .HasColumnType("int") + .HasColumnName("nombre_virements"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.HasKey("Id") + .HasName("PK__fichiers__3213E83FEE8C3E2D"); + + b.HasIndex(new[] { "GenereParId" }, "IX_xml_genere_par"); + + b.ToTable("fichiers_xml", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("adresse"); + + b.Property("Bic") + .HasMaxLength(11) + .IsUnicode(false) + .HasColumnType("varchar(11)") + .HasColumnName("bic"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("IbanChiffre") + .HasMaxLength(256) + .HasColumnType("varbinary(256)") + .HasColumnName("iban_chiffre"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(14) + .IsUnicode(false) + .HasColumnType("varchar(14)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(10) + .IsUnicode(false) + .HasColumnType("varchar(10)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("telephone"); + + b.Property("TitulaireCompte") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("titulaire_compte"); + + b.HasKey("Id") + .HasName("PK__fourniss__3213E83F16CB24B1"); + + b.ToTable("fournisseurs", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Action") + .IsRequired() + .HasMaxLength(50) + .IsUnicode(false) + .HasColumnType("varchar(50)") + .HasColumnName("action"); + + b.Property("AncienStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("ancien_statut"); + + b.Property("Commentaire") + .IsUnicode(false) + .HasColumnType("varchar(max)") + .HasColumnName("commentaire"); + + b.Property("DateAction") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_action") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NouveauStatut") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("nouveau_statut"); + + b.Property("UtilisateurId") + .HasColumnType("int") + .HasColumnName("utilisateur_id"); + + b.HasKey("Id") + .HasName("PK__historiq__3213E83FB787CC6E"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_histo_entite"); + + b.HasIndex(new[] { "UtilisateurId" }, "IX_histo_utilisateur"); + + b.ToTable("historique_actions", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.Property("NecessiteXml") + .HasColumnType("bit") + .HasColumnName("necessite_xml"); + + b.HasKey("Id") + .HasName("PK__modes_pa__3213E83F2F37EC08"); + + b.ToTable("modes_paiement", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Adresse") + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("adresse"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("Email") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("RaisonSociale") + .IsRequired() + .HasMaxLength(255) + .HasColumnType("nvarchar(255)") + .HasColumnName("raison_sociale"); + + b.Property("Siret") + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("siret"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("ACTIF") + .HasColumnName("statut"); + + b.Property("Telephone") + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("telephone"); + + b.HasKey("Id") + .HasName("PK__organismes"); + + b.ToTable("organismes", "st"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DatePaiement") + .HasColumnType("date") + .HasColumnName("date_paiement"); + + b.Property("DateSaisie") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_saisie") + .HasDefaultValueSql("(getdate())"); + + b.Property("EnregistreParId") + .HasColumnType("int") + .HasColumnName("enregistre_par_id"); + + b.Property("FactureId") + .HasColumnType("int") + .HasColumnName("facture_id"); + + b.Property("FichierXmlId") + .HasColumnType("int") + .HasColumnName("fichier_xml_id"); + + b.Property("ModeReglementId") + .HasColumnType("int") + .HasColumnName("mode_reglement_id"); + + b.Property("MontantPaye") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_paye"); + + b.Property("Rapproche") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(false) + .HasColumnName("rapproche"); + + b.Property("ReferencePaiement") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_paiement"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasDefaultValue("PROGRAMME") + .HasColumnName("statut"); + + b.HasKey("Id") + .HasName("PK__paiement__3213E83F4F594948"); + + b.HasIndex(new[] { "EnregistreParId" }, "IX_pai_enregistre_par"); + + b.HasIndex(new[] { "FactureId" }, "IX_pai_facture"); + + b.HasIndex(new[] { "ModeReglementId" }, "IX_pai_mode"); + + b.HasIndex(new[] { "FichierXmlId" }, "IX_pai_xml"); + + b.ToTable("paiements", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.Property("Cle") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("cle"); + + b.Property("DateModif") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_modif") + .HasDefaultValueSql("(getdate())"); + + b.Property("Description") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("description"); + + b.Property("ModifiePar") + .HasColumnType("int") + .HasColumnName("modifie_par"); + + b.Property("Valeur") + .IsRequired() + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("valeur"); + + b.HasKey("Cle") + .HasName("PK__parametr__D837B8B8460C2DCC"); + + b.HasIndex(new[] { "ModifiePar" }, "IX_parametres_modifie_par"); + + b.ToTable("parametres", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("DateUpload") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_upload") + .HasDefaultValueSql("(getdate())"); + + b.Property("EntiteId") + .HasColumnType("int") + .HasColumnName("entite_id"); + + b.Property("EntiteType") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("entite_type"); + + b.Property("NomFichier") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("nom_fichier"); + + b.Property("SharepointItemId") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("sharepoint_item_id"); + + b.Property("SharepointUrl") + .IsRequired() + .HasMaxLength(999) + .IsUnicode(false) + .HasColumnType("varchar(999)") + .HasColumnName("sharepoint_url"); + + b.Property("TailleOctets") + .HasColumnType("bigint") + .HasColumnName("taille_octets"); + + b.Property("TypePiece") + .IsRequired() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasColumnName("type_piece"); + + b.Property("UploaderId") + .HasColumnType("int") + .HasColumnName("uploader_id"); + + b.HasKey("Id") + .HasName("PK__pieces_j__3213E83F9643E6F1"); + + b.HasIndex(new[] { "EntiteType", "EntiteId" }, "IX_pj_entite"); + + b.HasIndex(new[] { "UploaderId" }, "IX_pj_uploader"); + + b.ToTable("pieces_jointes", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Commentaire") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("commentaire"); + + b.Property("DateOperationBanq") + .HasColumnType("date") + .HasColumnName("date_operation_banq"); + + b.Property("DateTraitement") + .HasColumnType("datetime") + .HasColumnName("date_traitement"); + + b.Property("LibelleBanque") + .HasMaxLength(500) + .IsUnicode(false) + .HasColumnType("varchar(500)") + .HasColumnName("libelle_banque"); + + b.Property("MontantBanque") + .HasColumnType("decimal(18, 2)") + .HasColumnName("montant_banque"); + + b.Property("PaiementId") + .HasColumnType("int") + .HasColumnName("paiement_id"); + + b.Property("ReferenceBancaire") + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("reference_bancaire"); + + b.Property("Statut") + .IsRequired() + .ValueGeneratedOnAdd() + .HasMaxLength(30) + .IsUnicode(false) + .HasColumnType("varchar(30)") + .HasDefaultValue("A_RAPPROCHER") + .HasColumnName("statut"); + + b.Property("TraiteParId") + .HasColumnType("int") + .HasColumnName("traite_par_id"); + + b.HasKey("Id") + .HasName("PK__rapproch__3213E83F92CAA2D1"); + + b.HasIndex(new[] { "PaiementId" }, "IX_rap_paiement"); + + b.HasIndex(new[] { "TraiteParId" }, "IX_rap_traite_par"); + + b.ToTable("rapprochement", "fin"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__services__3213E83FFB9EB51B"); + + b.ToTable("services", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("Libelle") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("libelle"); + + b.HasKey("Id") + .HasName("PK__types_de__3213E83FC8C79A18"); + + b.ToTable("types_depense", "ref"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("int") + .HasColumnName("id"); + + SqlServerPropertyBuilderExtensions.UseIdentityColumn(b.Property("Id")); + + b.Property("Actif") + .ValueGeneratedOnAdd() + .HasColumnType("bit") + .HasDefaultValue(true) + .HasColumnName("actif"); + + b.Property("AzureOid") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("DateCreation") + .ValueGeneratedOnAdd() + .HasColumnType("datetime") + .HasColumnName("date_creation") + .HasDefaultValueSql("(getdate())"); + + b.Property("DateDerniereConnexion") + .HasColumnType("datetime") + .HasColumnName("date_derniere_connexion"); + + b.Property("Email") + .IsRequired() + .HasMaxLength(255) + .IsUnicode(false) + .HasColumnType("varchar(255)") + .HasColumnName("email"); + + b.Property("Nom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("nom"); + + b.Property("Prenom") + .IsRequired() + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("prenom"); + + b.Property("Profil") + .IsRequired() + .HasMaxLength(20) + .IsUnicode(false) + .HasColumnType("varchar(20)") + .HasColumnName("profil"); + + b.Property("ResponsableId") + .HasColumnType("int") + .HasColumnName("responsable_id"); + + b.Property("ServiceId") + .HasColumnType("int") + .HasColumnName("service_id"); + + b.HasKey("Id") + .HasName("PK__utilisat__3213E83F466D0E1A"); + + b.HasIndex("ResponsableId"); + + b.HasIndex(new[] { "ServiceId" }, "IX_utilisateurs_service"); + + b.HasIndex(new[] { "AzureOid" }, "UQ__utilisat__2C305FD1B8914BF0") + .IsUnique(); + + b.ToTable("utilisateurs", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.UtilisateurRole", b => + { + b.Property("AzureOid") + .HasMaxLength(100) + .IsUnicode(false) + .HasColumnType("varchar(100)") + .HasColumnName("azure_oid"); + + b.Property("Role") + .HasMaxLength(50) + .HasColumnType("nvarchar(50)") + .HasColumnName("role"); + + b.HasKey("AzureOid", "Role") + .HasName("PK_utilisateur_roles"); + + b.ToTable("utilisateur_roles", "core"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Acompte", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Acomptes") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__acomptes__factur__01142BA1"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Acomptes") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__acomptes__justif__02084FDA"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Acomptes") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__acomptes__saisie__02FC7413"); + + b.Navigation("Facture"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.AffectationsAvoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "AffectePar") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AffecteParId") + .IsRequired() + .HasConstraintName("FK__affectati__affec__09A971A2"); + + b.HasOne("SIFinance.Shared.Entities.Avoir", "Avoir") + .WithMany("AffectationsAvoirs") + .HasForeignKey("AvoirId") + .IsRequired() + .HasConstraintName("FK__affectati__avoir__07C12930"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("AffectationsAvoirs") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__affectati__factu__08B54D69"); + + b.Navigation("AffectePar"); + + b.Navigation("Avoir"); + + b.Navigation("Facture"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Audit", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("Audits") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__audit__utilisate__71D1E811"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Avoirs") + .HasForeignKey("FactureId") + .HasConstraintName("FK__avoirs__facture___04E4BC85"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Avoirs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__avoirs__fourniss__03F0984C"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("Avoirs") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__avoirs__justific__05D8E0BE"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("Avoirs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__avoirs__saisie_p__06CD04F7"); + + b.Navigation("Facture"); + + b.Navigation("Fournisseur"); + + b.Navigation("Justificatif"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany() + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK_budgets_service_saisie_par"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany() + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK_budgets_service_service"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetServiceRevision", b => + { + b.HasOne("SIFinance.Shared.Entities.BudgetService", "BudgetService") + .WithMany("Revisions") + .HasForeignKey("BudgetServiceId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK_budgets_service_revisions_budget"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ReviseParUtilisateur") + .WithMany() + .HasForeignKey("RevisePar") + .IsRequired() + .HasConstraintName("FK_budgets_service_revisions_revise_par"); + + b.Navigation("BudgetService"); + + b.Navigation("ReviseParUtilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratEcheance", b => + { + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Echeances") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.Cascade) + .IsRequired() + .HasConstraintName("FK__contrats_echeances__contrat"); + + b.HasOne("SIFinance.Shared.Entities.PiecesJointe", "Justificatif") + .WithMany("ContratEcheances") + .HasForeignKey("JustificatifId") + .HasConstraintName("FK__contrats_echeances__justificatif"); + + b.Navigation("Contrat"); + + b.Navigation("Justificatif"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("ContratsFournisseurs") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__fournisseur"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsFournisseurs") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_fournisseurs__saisie_par"); + + b.Navigation("Fournisseur"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratPrestataire", b => + { + b.HasOne("SIFinance.Shared.Entities.Organisme", "Organisme") + .WithMany("ContratsPrestataires") + .HasForeignKey("OrganismeId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__organisme"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("ContratsPrestataires") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__contrats_prestataires__saisie_par"); + + b.Navigation("Organisme"); + + b.Navigation("SaisiePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Demandeur") + .WithMany("DemandesAchatDemandeurs") + .HasForeignKey("DemandeurId") + .IsRequired() + .HasConstraintName("FK__demandes___deman__787EE5A0"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("DemandesAchats") + .HasForeignKey("FournisseurId") + .HasConstraintName("FK__demandes___fourn__75A278F5"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("DemandesAchats") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__demandes___mode___778AC167"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("DemandesAchats") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__demandes___servi__74AE54BC"); + + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "TypeDepense") + .WithMany("DemandesAchats") + .HasForeignKey("TypeDepenseId") + .IsRequired() + .HasConstraintName("FK__demandes___type___76969D2E"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Valideur") + .WithMany("DemandesAchatValideurs") + .HasForeignKey("ValideurId") + .HasConstraintName("FK__demandes___valid__797309D9"); + + b.Navigation("Demandeur"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("Service"); + + b.Navigation("TypeDepense"); + + b.Navigation("Valideur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.HasOne("SIFinance.Shared.Entities.TypesDepense", "CategorieDepense") + .WithMany("Factures") + .HasForeignKey("CategorieDepenseId") + .HasConstraintName("FK__factures__catego__7C4F7684"); + + b.HasOne("SIFinance.Shared.Entities.ContratFournisseur", "Contrat") + .WithMany("Factures") + .HasForeignKey("ContratId") + .OnDelete(DeleteBehavior.SetNull) + .HasConstraintName("FK_factures_contrat"); + + b.HasOne("SIFinance.Shared.Entities.DemandesAchat", "DemandeAchat") + .WithMany("Factures") + .HasForeignKey("DemandeAchatId") + .HasConstraintName("FK__factures__demand__7A672E12"); + + b.HasOne("SIFinance.Shared.Entities.Fournisseur", "Fournisseur") + .WithMany("Factures") + .HasForeignKey("FournisseurId") + .IsRequired() + .HasConstraintName("FK__factures__fourni__7B5B524B"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModePaiement") + .WithMany("Factures") + .HasForeignKey("ModePaiementId") + .IsRequired() + .HasConstraintName("FK__factures__mode_p__7E37BEF6"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "SaisiePar") + .WithMany("FactureSaisiePars") + .HasForeignKey("SaisieParId") + .IsRequired() + .HasConstraintName("FK__factures__saisie__7F2BE32F"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Factures") + .HasForeignKey("ServiceId") + .IsRequired() + .HasConstraintName("FK__factures__servic__7D439ABD"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ValideBapPar") + .WithMany("FactureValideBapPars") + .HasForeignKey("ValideBapParId") + .HasConstraintName("FK__factures__valide__00200768"); + + b.Navigation("CategorieDepense"); + + b.Navigation("Contrat"); + + b.Navigation("DemandeAchat"); + + b.Navigation("Fournisseur"); + + b.Navigation("ModePaiement"); + + b.Navigation("SaisiePar"); + + b.Navigation("Service"); + + b.Navigation("ValideBapPar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "GenerePar") + .WithMany("FichiersXmls") + .HasForeignKey("GenereParId") + .IsRequired() + .HasConstraintName("FK__fichiers___gener__0A9D95DB"); + + b.Navigation("GenerePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.HistoriqueAction", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Utilisateur") + .WithMany("HistoriqueActions") + .HasForeignKey("UtilisateurId") + .IsRequired() + .HasConstraintName("FK__historiqu__utili__73BA3083"); + + b.Navigation("Utilisateur"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "EnregistrePar") + .WithMany("Paiements") + .HasForeignKey("EnregistreParId") + .IsRequired() + .HasConstraintName("FK__paiements__enreg__0E6E26BF"); + + b.HasOne("SIFinance.Shared.Entities.Facture", "Facture") + .WithMany("Paiements") + .HasForeignKey("FactureId") + .IsRequired() + .HasConstraintName("FK__paiements__factu__0B91BA14"); + + b.HasOne("SIFinance.Shared.Entities.FichiersXml", "FichierXml") + .WithMany("Paiements") + .HasForeignKey("FichierXmlId") + .HasConstraintName("FK__paiements__fichi__0D7A0286"); + + b.HasOne("SIFinance.Shared.Entities.ModesPaiement", "ModeReglement") + .WithMany("Paiements") + .HasForeignKey("ModeReglementId") + .IsRequired() + .HasConstraintName("FK__paiements__mode___0C85DE4D"); + + b.Navigation("EnregistrePar"); + + b.Navigation("Facture"); + + b.Navigation("FichierXml"); + + b.Navigation("ModeReglement"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Parametre", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "ModifieParNavigation") + .WithMany("Parametres") + .HasForeignKey("ModifiePar") + .HasConstraintName("FK__parametre__modif__70DDC3D8"); + + b.Navigation("ModifieParNavigation"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Uploader") + .WithMany("PiecesJointes") + .HasForeignKey("UploaderId") + .IsRequired() + .HasConstraintName("FK__pieces_jo__uploa__72C60C4A"); + + b.Navigation("Uploader"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Rapprochement", b => + { + b.HasOne("SIFinance.Shared.Entities.Paiement", "Paiement") + .WithMany("Rapprochements") + .HasForeignKey("PaiementId") + .HasConstraintName("FK__rapproche__paiem__0F624AF8"); + + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "TraitePar") + .WithMany("Rapprochements") + .HasForeignKey("TraiteParId") + .HasConstraintName("FK__rapproche__trait__10566F31"); + + b.Navigation("Paiement"); + + b.Navigation("TraitePar"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.HasOne("SIFinance.Shared.Entities.Utilisateur", "Responsable") + .WithMany("Collaborateurs") + .HasForeignKey("ResponsableId") + .HasConstraintName("FK__utilisateurs__responsable"); + + b.HasOne("SIFinance.Shared.Entities.Service", "Service") + .WithMany("Utilisateurs") + .HasForeignKey("ServiceId") + .HasConstraintName("FK__utilisate__servi__6FE99F9F"); + + b.Navigation("Responsable"); + + b.Navigation("Service"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Avoir", b => + { + b.Navigation("AffectationsAvoirs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.BudgetService", b => + { + b.Navigation("Revisions"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ContratFournisseur", b => + { + b.Navigation("Echeances"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.DemandesAchat", b => + { + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Facture", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Avoirs"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.FichiersXml", b => + { + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Fournisseur", b => + { + b.Navigation("Avoirs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.ModesPaiement", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Paiements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Organisme", b => + { + b.Navigation("ContratsPrestataires"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Paiement", b => + { + b.Navigation("Rapprochements"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.PiecesJointe", b => + { + b.Navigation("Acomptes"); + + b.Navigation("Avoirs"); + + b.Navigation("ContratEcheances"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Service", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + + b.Navigation("Utilisateurs"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.TypesDepense", b => + { + b.Navigation("DemandesAchats"); + + b.Navigation("Factures"); + }); + + modelBuilder.Entity("SIFinance.Shared.Entities.Utilisateur", b => + { + b.Navigation("Acomptes"); + + b.Navigation("AffectationsAvoirs"); + + b.Navigation("Audits"); + + b.Navigation("Avoirs"); + + b.Navigation("Collaborateurs"); + + b.Navigation("ContratsFournisseurs"); + + b.Navigation("ContratsPrestataires"); + + b.Navigation("DemandesAchatDemandeurs"); + + b.Navigation("DemandesAchatValideurs"); + + b.Navigation("FactureSaisiePars"); + + b.Navigation("FactureValideBapPars"); + + b.Navigation("FichiersXmls"); + + b.Navigation("HistoriqueActions"); + + b.Navigation("Paiements"); + + b.Navigation("Parametres"); + + b.Navigation("PiecesJointes"); + + b.Navigation("Rapprochements"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/SIFinance.Shared/NotificationEmailService.cs b/SIFinance.Shared/NotificationEmailService.cs new file mode 100644 index 0000000..12db355 --- /dev/null +++ b/SIFinance.Shared/NotificationEmailService.cs @@ -0,0 +1,50 @@ +using System.Net.Http.Headers; +using System.Net.Http.Json; +using Microsoft.Extensions.Logging; + +namespace SIFinance.Shared; + +// Envoie des notifications par email via Microsoft Graph (permission application +// Mail.Send, déjà accordée) — envoie "au nom de" une boîte du tenant (ex. le +// demandeur), pas besoin de boîte de service dédiée. Les échecs sont journalisés mais +// n'interrompent jamais le flux appelant (une notification ratée ne doit pas faire +// échouer la création d'une demande). Vit dans SIFinance.Shared car les modules +// (Achats, Sous-Traitants...) doivent pouvoir l'appeler directement. +public class NotificationEmailService( + IHttpClientFactory httpClientFactory, + EntraGraphTokenProvider tokenProvider, + ILogger logger) +{ + public async Task EnvoyerAsync(string expediteurEmail, string destinataireEmail, string sujet, string corpsHtml, CancellationToken ct) + { + try + { + var token = await tokenProvider.GetTokenAsync(ct); + var http = httpClientFactory.CreateClient(); + http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + + var payload = new + { + message = new + { + subject = sujet, + body = new { contentType = "HTML", content = corpsHtml }, + toRecipients = new[] { new { emailAddress = new { address = destinataireEmail } } }, + }, + saveToSentItems = false, + }; + + var response = await http.PostAsJsonAsync( + $"https://graph.microsoft.com/v1.0/users/{Uri.EscapeDataString(expediteurEmail)}/sendMail", payload, ct); + if (!response.IsSuccessStatusCode) + { + var body = await response.Content.ReadAsStringAsync(ct); + logger.LogWarning("Échec d'envoi d'email de notification ({Status}) : {Body}", response.StatusCode, body); + } + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + logger.LogWarning(ex, "Échec d'envoi d'email de notification à {Destinataire}.", destinataireEmail); + } + } +} diff --git a/SIFinance.Shared/SIFinance.Shared.csproj b/SIFinance.Shared/SIFinance.Shared.csproj new file mode 100644 index 0000000..aecb8b8 --- /dev/null +++ b/SIFinance.Shared/SIFinance.Shared.csproj @@ -0,0 +1,28 @@ + + + + net10.0 + enable + enable + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/SIFinance.slnx b/SIFinance.slnx new file mode 100644 index 0000000..a07a079 --- /dev/null +++ b/SIFinance.slnx @@ -0,0 +1,7 @@ + + + + + + + diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000..a547bf3 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 0000000..c300135 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,75 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + + // Remove tseslint.configs.recommended and replace with this + tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + tseslint.configs.stylisticTypeChecked, + + // Other configs... + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) + +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + // Other configs... + // Enable lint rules for React + reactX.configs['recommended-typescript'], + // Enable lint rules for React DOM + reactDom.configs.recommended, + ], + languageOptions: { + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + // other options... + }, + }, +]) + +``` diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..ef614d2 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,22 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + }, + }, +]) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..c1cc3ec --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + SI Finance + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..0430a85 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3611 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^9.2.0", + "@mui/material": "^9.2.0", + "@tanstack/react-query": "^5.101.4", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router-dom": "^7.18.1" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "eslint": "^10.6.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^17.7.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.62.0", + "vite": "^8.1.1" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.3.tgz", + "integrity": "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/core/node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.3.tgz", + "integrity": "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz", + "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mui/core-downloads-tracker": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.2.0.tgz", + "integrity": "sha512-+XMav+ZaXkZKUFUgzjrfMEedfyJKxxviAske2q8N8CWDMeqZdDU2lWMkiUPiB388hGaDqhwvOAwkrsc/pUyp8g==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-9.2.0.tgz", + "integrity": "sha512-VgBd3z7Qc3vd/thcNSMC03nHRh/U4DzMUd+1dRyJTbm/hGo7+N6N4GDuJZDNHa6LZhhwG6Cu1X3DNvrVv8sNag==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^9.2.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.2.0.tgz", + "integrity": "sha512-+YTRSgGKGrrRo2XJZXs7JRA6qHoHWvNtxyqxnrRJTBmIuLOUpxxh7m4G9lF4tWberxGFY+EqkkRPgJCl+fSMJg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/core-downloads-tracker": "^9.2.0", + "@mui/system": "^9.2.0", + "@mui/types": "^9.1.1", + "@mui/utils": "^9.2.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.6", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^9.2.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-9.2.0.tgz", + "integrity": "sha512-w9wpyDxGPGnAACPB2hKhCDmILJIAvQxrfjUbIAEa0AznX1rOjaz5N+yB1uuw8ixnJcpEh/tPbD9oEe19wcWPHw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/utils": "^9.2.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.1.1.tgz", + "integrity": "sha512-neaYKdJfvEG54q8efHLJR7swpHG/gfSv9xGqW5iTSMsubD7yPCPFrhVBt284j1DOF3uZaaDJSHQL7gz6jGF21Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-9.2.0.tgz", + "integrity": "sha512-YvUJwKoGVtbnOm2PyPi5TvX2d1rOA6sqSpEWVs4WmXNIaFTuYmNUaVdU2o1NKUEe31URnD3E8ZVUMcsLQXwcYg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/private-theming": "^9.2.0", + "@mui/styled-engine": "^9.1.1", + "@mui/types": "^9.1.1", + "@mui/utils": "^9.2.0", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-9.1.1.tgz", + "integrity": "sha512-Zjt7u8wNvDg40rPTGoL+TnfkpuSKjwubsNSFRH1KAVZLcaV4I3AFNHIFbvH7p4F3alEibSbdd90xAgn5Rnfndg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.2.0.tgz", + "integrity": "sha512-OsUH5zhlSOM4xmLl53+agug1M1UyWb4zxFxWQCqwKTKUeQPvTENtg3JhrroBD2qpCLKsX5W/DYGERJ4mBUbc8g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/types": "^9.1.1", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.6" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz", + "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.139.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.139.0.tgz", + "integrity": "sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.1.5.tgz", + "integrity": "sha512-lZg8fqIv2v7FF237bwMgzGZEJvGL79/s5knJ/i6FmsGF4XXlzccZ4jb+TrFIxtSSxFtIpdsgrPZeMk1I9AFcyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.1.5.tgz", + "integrity": "sha512-51Bnx9pNiMRKSUNtBfySkNJ9vMU9Hh3I1ozDd6gyPPYzaXCfnptUcEZxXGYFn+ul2dtcMUiqGR1Yai2K10uoTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.1.5.tgz", + "integrity": "sha512-Tm+gbfC0aHu1tBA/JvKQh32S0K6YgCHkiAF4/W6xX0K0RmNuc94VeK419dJoE65R5aRxmo+noZQSWrAMF6yb6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.1.5.tgz", + "integrity": "sha512-JMzDKCCXq93YccG5gz3hvOs1oXRKAf0XYpfOS88e+wZrC8Iugj6j68867vrYZkvpDDpKn/KoKORThmchMpF6TA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.1.5.tgz", + "integrity": "sha512-uML21j2K5TfPGutKxub+M+nLjZIrWjXQ5Grx4lCe/nimTj9B4L63zHpjXLl4y0L3mcm2htEQIb06oCG/szerNw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.1.5.tgz", + "integrity": "sha512-navSiuTMogvnQoZoM/v+l3ZWo50/NTwSHSzheABx/RCnmUPaKwq9qSo4Br2OYRs21+Fz8uFqITZM3H4opOB0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.1.5.tgz", + "integrity": "sha512-lAryqH7IteztmCXQXk0etKj4wBQ7Gx5S6LjKhsgp9zb8I5bsuvU/2llH1hDQcjsFeqIsovMVN339/8pUDDBXxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.1.5.tgz", + "integrity": "sha512-fsK/sNBnxzBlL4O1JNrZakVQxPspqpED5dLtNsZS9oOKmtSpdNIzxH2kkol5HYTWJN47sE20ztMJPxfZ89qGOg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.1.5.tgz", + "integrity": "sha512-gLYb4BIadlfTOYT5gO503n8zQjXflgzpD0FcyKh0Mzx3rqCZKnHoJWV9xe1KXUJ5lx2JfcSHr/mhzS0PC/McAA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.1.5.tgz", + "integrity": "sha512-FjcpEKUyJygHgs1o50VYNvkt5+7Le/VEdYt0AkRpkL33MnyQfwr8l5mXwMmfmTbyMPr5vJLC+8/Gd9gXnwU1QQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.1.5.tgz", + "integrity": "sha512-Me+PfPI2TMeOQk0gYWfLQZtTktrmzbr8cDboqX83XKc7UrgAi55gF+2dUkWdxd19n55Essp2yeca+O9N5rBxHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.1.5.tgz", + "integrity": "sha512-yc5WrLzXks6zCQfn9Oxr8pORKyl/pF+QjHmW/Qx3qu0oyrrNC+y2JLTU1E2rcWYAmzlnqngWXHQjy51VzW70Vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.1.5.tgz", + "integrity": "sha512-VbQGPX2b4r48TAMIM2cjgluIM1HYutm4pcTEJsle7iEP7sB1dFqtPLBVbdLAZCxy1txCcPxf4QFf4v8uvltPqA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.11.1", + "@emnapi/runtime": "1.11.1", + "@napi-rs/wasm-runtime": "^1.1.6" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.1.tgz", + "integrity": "sha512-RSvbQmHzdKzNsLYa/wHrbc3KN4sYLKAdPZxqiM2HATqv/SBk2/ENSHpvXGaLOMcsAyz0poEGqkmmKYG3OWiJEQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.1.5.tgz", + "integrity": "sha512-gHv82k63z4qpV5+Q1y/12KrK0ltWBukVDI8nZcbT7Tt/ZlOIVwppazneq0F93oDxTo3IgAMEDIoQh3E2n6mVsw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.1.5.tgz", + "integrity": "sha512-tTZuDBPw85tEN5PQi1pnEBzDy0Z49HtScLAbD5t6hyeU92A95pRWaSMw1GZZi/RwgSgUIl0xrSlXIT/9QzvYSA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tanstack/query-core": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.4.tgz", + "integrity": "sha512-gNwcvOJcRbLWPOLG/2OBm+zM+Yv+MKsXKEOWC57USuZDEsI71hEErQsiEGx5wX9rzWWkfwM0fVSPoiIFSsxfiw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.101.4", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.4.tgz", + "integrity": "sha512-yRg2pfOCxIs4ZJW3XYYHU/WgtD04FHSnfHlpRT7h7pR77hwkdRG4wxbKe4aq6P0RvXUTBSQpQeadS1SUYUe+KA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.101.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.3.tgz", + "integrity": "sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.64.0.tgz", + "integrity": "sha512-CGvQPBxN3wZLu6Rz2kFUpZeoCm78xUic92ck39KPePkO1NPOwjCqdQnm5Q87tpWw9vcBvW8XLrDXjH9PWYtJ3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/type-utils": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.64.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.64.0.tgz", + "integrity": "sha512-KA0OshtlcCCXmbfqyZkM5pV3/WNraJf7DkJRLpyrmwPtud57H5BDX7C3k0LPSPxpprfRL+cJDGabF10mvNCoCw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.64.0.tgz", + "integrity": "sha512-tk4WpOJ6IEbGrVHaNmM0YRrwAD3exZlIK3iadQNAxh4YKk6jvUQ4ecq18n+v7+meh+cJ3j+D8nbk8sRKhlwLQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.64.0", + "@typescript-eslint/types": "^8.64.0", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.64.0.tgz", + "integrity": "sha512-CXEaFdYXjSTgKhisNkwCcJwTP8Pl+fmRrEQrri4nm3vU743bALrxzLmq7fHG/7e6a5xO0lDYeURpZmBuhHk54w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.64.0.tgz", + "integrity": "sha512-2yo8rRNKuzbVWQp5kslhANqZ2uDAeROQHBRZNPu8JDsHmeFNj/XJJhX/FhNUWmkHHvoNsKa6+tHJiig87EzsQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.64.0.tgz", + "integrity": "sha512-XWG4Fmmv/6SvyS9nH8jWrKs6terwJvE8cyRt1CzYYqzp9OrPhCT4cMc/f7C6RZCwG+qMmiffJS1/qJP8G1URtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.64.0.tgz", + "integrity": "sha512-qjhfuTfLXjA4IOzXvz0rTjT01BqEiIgPoUeMwiEjnaHKJMTNo8rH5pYW1a2L/0Dnux2fPC85AeyJoWaGa8WxTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.64.0.tgz", + "integrity": "sha512-Pztpsn1aCE1oWDvDEfUk31nngvvF7vUB5SwHFEaZIFpvw7WJtqUHHL4plBZDA9HfWJJjL13BdG0YrJInTUvoVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.64.0", + "@typescript-eslint/tsconfig-utils": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/visitor-keys": "8.64.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.64.0.tgz", + "integrity": "sha512-aJUGVB3+U0htrrCjoA8qukw8cm8fNCGAxK/tVoS70k8aeb7DETKeFozRiVFIwEeN9WJLsjaP3ph8I60tY2XZoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.64.0", + "@typescript-eslint/types": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.64.0.tgz", + "integrity": "sha512-mrtuL8Nsn6gi2H4mo5KMTp823M+3Q19Ew/i+Zlikq20tIMm99C3Ez0dCmkWWnxut20esQvTg8aUSEhMcAOXhEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.64.0", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.3.tgz", + "integrity": "sha512-vmFvco5/QuC2f9Oj+wTk0+9XeDFkHxSamwZKYc7MxYwKICfvUvlMhqKI0VuICPltGqh1neqBKDvO4kes1ya8vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.43", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.43.tgz", + "integrity": "sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", + "integrity": "sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.6", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.6.tgz", + "integrity": "sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.10.42", + "caniuse-lite": "^1.0.30001803", + "electron-to-chromium": "^1.5.389", + "node-releases": "^2.0.51", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.393", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.393.tgz", + "integrity": "sha512-kiDJdIUawuEIcp9XoICKp1iTYDEbgguIPq526N1Q7jIQDeQ3CqoMx71025PI/7E48Ddtw2HuWsVjY7afEgNxmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.7.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.7.0.tgz", + "integrity": "sha512-GVTD7s1vdIl6UYvAfriOPeY1Df8LIZjfofLvHwde+erDHGGuHyuM6xoxRxmHiebhYuD2p1vN4wWh0XzPARSGDQ==", + "dev": true, + "license": "MIT", + "peer": true, + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.3.tgz", + "integrity": "sha512-5EMmLCV98Pi4o/f/3DP/v/tNqLHMIc9I8LKClNDWhZ9JTho89/kQcitCXQBMG7sAfVRK0Ie3T2EDOzp1YXYiVA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.7.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.7.0.tgz", + "integrity": "sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.51", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.51.tgz", + "integrity": "sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.20", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.20.tgz", + "integrity": "sha512-lW616l85ucIQL+FocMmL7pQFPqBmwejrCMg+iPxyImlrANNJG9NHq/RkyCZopDhd8C3LA03PHRJDjkbGu8vvug==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.16", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.7" + } + }, + "node_modules/react-is": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", + "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", + "license": "MIT" + }, + "node_modules/react-router": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.18.1.tgz", + "integrity": "sha512-GDLgg3i3uM0aeJO3Fm+TCS+sDQ7gu12T6x0qdTEzcwqEfleci7JwugVNIF3U//0FWKnJT7ptG+20B2jfDqnZAg==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.18.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.18.1.tgz", + "integrity": "sha512-KaZh+X/6UtEp28x51AUYZDMg9NGoz2ja3dNHa+ta/tk40vCzKhQ/RypCWBMLbmDr6//E24Vv5uPsrqXFozdkAg==", + "license": "MIT", + "dependencies": { + "react-router": "7.18.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rolldown": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.1.5.tgz", + "integrity": "sha512-t9z29cJjXf/vxQ8dyhCSpt6H6aSwHTk8cT5I3iy6SMXuFpk5mB6PL6XfC8PCwrPTx93udwKUm9HRteAlTGBLiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.139.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.1.5", + "@rolldown/binding-darwin-arm64": "1.1.5", + "@rolldown/binding-darwin-x64": "1.1.5", + "@rolldown/binding-freebsd-x64": "1.1.5", + "@rolldown/binding-linux-arm-gnueabihf": "1.1.5", + "@rolldown/binding-linux-arm64-gnu": "1.1.5", + "@rolldown/binding-linux-arm64-musl": "1.1.5", + "@rolldown/binding-linux-ppc64-gnu": "1.1.5", + "@rolldown/binding-linux-s390x-gnu": "1.1.5", + "@rolldown/binding-linux-x64-gnu": "1.1.5", + "@rolldown/binding-linux-x64-musl": "1.1.5", + "@rolldown/binding-openharmony-arm64": "1.1.5", + "@rolldown/binding-wasm32-wasi": "1.1.5", + "@rolldown/binding-win32-arm64-msvc": "1.1.5", + "@rolldown/binding-win32-x64-msvc": "1.1.5" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.64.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.64.0.tgz", + "integrity": "sha512-0qg+pDNMnqYzqH9AnNK+39tejHvsShUOUUoRUgtnTGE7QuMZhiFDnozq8nHJVq+Wae6NMLKNWLg5WmkcC/ndyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.64.0", + "@typescript-eslint/parser": "8.64.0", + "@typescript-eslint/typescript-estree": "8.64.0", + "@typescript-eslint/utils": "8.64.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.1.5.tgz", + "integrity": "sha512-7ULLwsCdYx/nRyrpiEwvqb5TFHrMVZyBt+rg/OAXT7rgj/z+DtTDyKFeLAdDkubDVDKD8jOsndmy7m55XcfUsw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.5", + "postcss": "^8.5.17", + "rolldown": "~1.1.5", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.3.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..f64aee9 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,36 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/icons-material": "^9.2.0", + "@mui/material": "^9.2.0", + "@tanstack/react-query": "^5.101.4", + "react": "^19.2.7", + "react-dom": "^19.2.7", + "react-router-dom": "^7.18.1" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.13.2", + "@types/react": "^19.2.17", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.3", + "eslint": "^10.6.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.3", + "globals": "^17.7.0", + "typescript": "~6.0.2", + "typescript-eslint": "^8.62.0", + "vite": "^8.1.1" + } +} diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..6893eb1 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/icons.svg b/frontend/public/icons.svg new file mode 100644 index 0000000..e952219 --- /dev/null +++ b/frontend/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000..e28edb6 --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,101 @@ +// src/App.tsx — connexion → portail → module +import { useEffect } from 'react'; +import { Routes, Route, Navigate, Outlet, useNavigate, useSearchParams } from 'react-router-dom'; +import { Box, Typography } from '@mui/material'; +import { useAuth } from './auth'; +import Layout from './Layout'; +import SousTraitantsLayout from './SousTraitantsLayout'; +import Login from './pages/Login'; +import Portal from './pages/Portal'; +import Dashboard from './pages/Dashboard'; +import Demandes from './pages/Demandes'; +import DemandeForm from './pages/DemandeForm'; +import Contratsfournisseurs from './pages/Contratsfournisseurs'; +import Paiements from './pages/Paiements'; +import Pilotage from './pages/Pilotage'; +import Fournisseurs from './pages/Fournisseurs'; +import FacturesAvoirs from './pages/facturesAvoirs'; +import Archives from './pages/Archives'; +import DashboardST from './pages/soustraitants/DashboardST'; +import CahierDesCharges from './pages/soustraitants/CahierDesCharges'; +import ContratsPrestataires from './pages/soustraitants/ContratsPrestataires'; +import Prestations from './pages/soustraitants/Prestations'; +import FacturesST from './pages/soustraitants/FacturesST'; +import ControleConformite from './pages/soustraitants/ControleConformite'; +import Arbitrage from './pages/soustraitants/Arbitrage'; +import PaiementsST from './pages/soustraitants/PaiementsST'; +import PilotageST from './pages/soustraitants/PilotageST'; +import ArchivesST from './pages/soustraitants/ArchivesST'; + +function RequireAuth() { + const { authed } = useAuth(); + return authed ? : ; +} + +// Point d'atterrissage après /api/auth/callback côté backend, qui redirige ici avec +// le jeton "maison" en paramètre d'URL une fois l'échange OAuth2 terminé. +function AuthCallback() { + const [searchParams] = useSearchParams(); + const { handleCallback } = useAuth(); + const navigate = useNavigate(); + + useEffect(() => { + const token = searchParams.get('token'); + if (token) { + handleCallback(token); + navigate('/portal', { replace: true }); + } else { + navigate('/login', { replace: true }); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return null; +} + +function Placeholder({ titre }: { titre: string }) { + return ( + + {titre} + Écran à venir. + + ); +} + +export default function App() { + return ( + + } /> + } /> + }> + } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + + } /> + + ); +} diff --git a/frontend/src/Layout.tsx b/frontend/src/Layout.tsx new file mode 100644 index 0000000..b555153 --- /dev/null +++ b/frontend/src/Layout.tsx @@ -0,0 +1,269 @@ +// src/Layout.tsx — coquille claire, sidebar rétractable au survol +import { useState, useEffect, createContext, useContext, type ReactNode } from 'react'; +import { Outlet, useNavigate, useLocation } from 'react-router-dom'; +import { + Box, Drawer, List, ListItemButton, ListItemIcon, ListItemText, Typography, Stack, Chip, Avatar, Tooltip, +} from '@mui/material'; +import { useQuery } from '@tanstack/react-query'; +import { achatsApi, financeApi } from './data'; +import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; +import DashboardOutlinedIcon from '@mui/icons-material/DashboardOutlined'; +import ReceiptLongOutlinedIcon from '@mui/icons-material/ReceiptLongOutlined'; +import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined'; +import StorefrontOutlinedIcon from '@mui/icons-material/StorefrontOutlined'; +import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined'; +import BarChartOutlinedIcon from '@mui/icons-material/BarChartOutlined'; +import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined'; +import { tk } from './theme'; +import { Logo } from './ui'; +import { useAuth } from './auth'; + +// --- Contexte du rôle (espace de travail) --- +export type Role = 'collaborateur' | 'finance'; +const WorkspaceCtx = createContext<{ role: Role; setRole: (r: Role) => void }>({ role: 'finance', setRole: () => {} }); +export const useWorkspace = () => useContext(WorkspaceCtx); + +const RAIL = 74; // largeur réduite (icônes) +const FULL = 252; // largeur déployée +type BadgeKey = 'demandes' | 'factures'; +type NavItem = { label: string; path: string; icon: ReactNode; badgeKey?: BadgeKey }; +type NavGroup = { section: string; items: NavItem[] }; + +// Les badges reflètent des compteurs réels (demandes en attente de validation, +// factures en litige ou échues) — calculés dans le composant à partir des données +// mises en cache par React Query, pas des valeurs figées. +const navByRole: Record = { + collaborateur: [ + { section: 'Pilotage', items: [ + { label: 'Tableau de bord', path: '/', icon: }, + { label: 'Pilotage', path: '/pilotage', icon: }, + ] }, + { section: 'Mes achats', items: [ + { label: 'Mes demandes', path: '/demandes', icon: , badgeKey: 'demandes' }, + ] }, + { section: 'Documents', items: [ + { label: 'Archives', path: '/archives', icon: }, + { label: 'Contrats', path: '/contrats', icon: }, + ] }, + ], + finance: [ + { section: 'Pilotage', items: [ + { label: 'Tableau de bord', path: '/', icon: }, + { label: 'Pilotage', path: '/pilotage', icon: }, + ] }, + { section: 'Achats', items: [ + { label: "Demandes d'achat", path: '/demandes', icon: , badgeKey: 'demandes' }, + { label: 'Fournisseurs', path: '/fournisseurs', icon: }, + ] }, + { section: 'Facturation', items: [ + { label: 'Factures & Avoirs', path: '/facturesavoirs', icon: , badgeKey: 'factures' }, + { label: 'Paiements', path: '/paiements', icon: }, + ] }, + + { section: 'Documents', items: [ + + { label: 'Contrats', path: '/contrats', icon: }, + ] }, + ], +}; + +// Une demande est actionnable par son responsable dès sa soumission (SOUMISE), pas +// seulement une fois basculée en EN_VALIDATION — le badge doit refléter ça dès le départ. +const DEMANDES_A_VALIDER = new Set(['SOUMISE', 'EN_VALIDATION']); +const posteByRole: Record = { + collaborateur: 'Collaborateur', + finance: 'Finance', +}; + +const initialesDe = (nom: string) => + nom.split(' ').filter(Boolean).slice(0, 2).map((s) => s[0]?.toUpperCase()).join(''); + +function RoleToggle({ role, setRole }: { role: Role; setRole: (r: Role) => void }) { + return ( + + ESPACE DE TRAVAIL + + {(['collaborateur', 'finance'] as Role[]).map((r) => ( + setRole(r)} + sx={{ + flex: 1, textAlign: 'center', py: 0.75, borderRadius: 1.5, cursor: 'pointer', fontSize: 13, fontWeight: 600, whiteSpace: 'nowrap', + color: role === r ? tk.ink : tk.muted, + bgcolor: role === r ? tk.surface : 'transparent', + boxShadow: role === r ? '0 1px 2px rgba(16,24,40,0.06)' : 'none', + }}> + {r === 'collaborateur' ? 'Collaborateur' : 'Finance'} + + ))} + + + ); +} + +export default function Layout() { + const [open, setOpen] = useState(false); // déployée au survol + const navigate = useNavigate(); + const location = useLocation(); + const isActive = (p: string) => (p === '/' ? location.pathname === '/' : location.pathname.startsWith(p)); + + // Le nom/les initiales viennent du compte Microsoft connecté. L'espace de travail + // (Collaborateur/Finance) est verrouillé sur le Profil de la ligne core.utilisateurs + // (colonne alimentée par la synchro Entra ID) SAUF si l'utilisateur cumule le rôle + // FINANCE (table core.utilisateur_roles) en plus de son profil de base — dans ce cas + // le sélecteur reste actif pour qu'il puisse basculer entre ses deux casquettes (ex. + // quelqu'un qui soumet ses propres demandes ET valide celles des autres). + const { displayName, roles, estResponsable } = useAuth(); + const hasFinanceRole = roles.includes('FINANCE'); + // Seul le rôle explicite FINANCE dans utilisateur_roles ouvre l'espace Finance. + // Un profil Entra ou le statut Responsable conserve l'espace Collaborateur. + const roleLocked: Role | null = hasFinanceRole ? null : 'collaborateur'; + + const [role, setRoleState] = useState(roleLocked ?? 'finance'); + useEffect(() => { + if (roleLocked) setRoleState(roleLocked); + }, [roleLocked]); + const setRole = roleLocked ? () => {} : setRoleState; + + // Les contrats ne sont visibles que pour un responsable de service. Finance y + // accède dans son propre espace, avec des droits de consultation uniquement. + const groups = navByRole[role].map((group) => ({ + ...group, + items: group.items.filter((item) => item.path !== '/contrats' || hasFinanceRole || estResponsable), + })).filter((group) => group.items.length > 0); + const w = open ? FULL : RAIL; + + const nomAffiche = displayName ?? '—'; + const initiales = initialesDe(nomAffiche) || '—'; + + // Mêmes queryKey que dans les pages : React Query dédoublonne les appels et sert le + // cache, donc plus de refetch à chaque navigation. scope=mine n'est forcé que pour un + // utilisateur Finance qui a explicitement basculé en espace Collaborateur (vue étroite + // volontaire) — pour tous les autres (demandeur simple ou responsable sans rôle + // FINANCE), on laisse le backend décider (ses demandes + celles de ses collaborateurs + // s'il en est responsable), sinon un responsable ne verrait jamais ce qu'il doit valider. + const demandesScope = role === 'collaborateur' && hasFinanceRole ? 'mine' : undefined; + const { data: demandes = [] } = useQuery({ queryKey: ['demandes', demandesScope], queryFn: () => achatsApi.getDemandes(demandesScope) }); + const { data: factures = [] } = useQuery({ queryKey: ['factures'], queryFn: financeApi.getFactures }); + + const today = new Date(); + today.setHours(0, 0, 0, 0); + const badges: Record = { + // peutValider : sinon un utilisateur Finance qui n'est responsable de personne + // verrait un badge alors qu'il n'a rien à valider lui-même. + demandes: demandes.filter((d) => d.peutValider && DEMANDES_A_VALIDER.has(d.statut)).length, + factures: factures.filter((f) => f.statut === 'litige' || (f.statut !== 'payee' && new Date(f.dateEcheance) < today)).length, + }; + + return ( + + + {/* Le rail réserve toujours RAIL px ; la barre déployée se superpose (overlay) sans décaler le contenu */} + + + setOpen(true)} + onMouseLeave={() => setOpen(false)} + sx={{ + width: w, flexShrink: 0, whiteSpace: 'nowrap', + '& .MuiDrawer-paper': { + width: w, boxSizing: 'border-box', display: 'flex', flexDirection: 'column', + overflowX: 'hidden', overflowY: 'auto', + bgcolor: tk.surface, + borderRight: `1px solid ${tk.line}`, + transition: 'width .18s ease', + boxShadow: open ? '4px 0 24px rgba(16,24,40,0.08)' : 'none', + }, + }} + > + {/* Logo — le texte n'apparaît qu'en déployé */} + + + {open && ( + + SI Finance + Module Achats + + )} + + + {open && ( + <> + + navigate('/portal')} + sx={{ px: 1, py: 1, borderRadius: 1.5, border: `1px solid ${tk.line}`, cursor: 'pointer', color: tk.muted, '&:hover': { color: tk.ink, bgcolor: tk.canvas } }}> + + Retour au portail + + + {!roleLocked && } + + )} + + + {groups.map((group) => ( + + {open && ( + + {group.section} + + )} + {group.items.map((item) => { + const active = isActive(item.path); + const badgeValue = item.badgeKey ? badges[item.badgeKey] : 0; + const bouton = ( + navigate(item.path)} + sx={{ + justifyContent: open ? 'flex-start' : 'center', px: open ? 1.25 : 1.5, + borderRadius: 1.5, mb: 0.25, + borderLeft: active ? `2px solid ${tk.primary}` : '2px solid transparent', + bgcolor: active ? tk.primarySoft : 'transparent', + '&:hover': { bgcolor: active ? tk.primarySoft : tk.canvas }, + '&.Mui-selected': { bgcolor: tk.primarySoft }, + '&.Mui-selected:hover': { bgcolor: tk.primarySoft }, + }}> + + {item.icon} + + {open && ( + <> + + {badgeValue > 0 && } + + )} + + ); + // En mode réduit : infobulle avec le nom (accessibilité + tactile) + return open ? bouton : ( + + {bouton} + + ); + })} + + ))} + + + {/* Utilisateur */} + + {initiales} + {open && ( + + {nomAffiche} + + {role === 'collaborateur' && estResponsable ? 'Responsable' : posteByRole[role]} + + + )} + + + + + + + + + ); +} diff --git a/frontend/src/SousTraitantsLayout.tsx b/frontend/src/SousTraitantsLayout.tsx new file mode 100644 index 0000000..70d76ab --- /dev/null +++ b/frontend/src/SousTraitantsLayout.tsx @@ -0,0 +1,165 @@ +// src/SousTraitantsLayout.tsx — coquille du Module Sous-Traitants, même structure que Layout.tsx +// (sidebar rétractable au survol) mais nav dédiée et accent vert pour distinguer le module. +import { useState, type ReactNode } from 'react'; +import { Outlet, useNavigate, useLocation } from 'react-router-dom'; +import { + Box, Drawer, List, ListItemButton, ListItemIcon, ListItemText, Typography, Stack, Chip, Avatar, Tooltip, +} from '@mui/material'; +import ChevronLeftIcon from '@mui/icons-material/ChevronLeft'; +import DashboardOutlinedIcon from '@mui/icons-material/DashboardOutlined'; +import DescriptionOutlinedIcon from '@mui/icons-material/DescriptionOutlined'; +import FolderOutlinedIcon from '@mui/icons-material/FolderOutlined'; +import GroupsOutlinedIcon from '@mui/icons-material/GroupsOutlined'; +import ReceiptLongOutlinedIcon from '@mui/icons-material/ReceiptLongOutlined'; +import FactCheckOutlinedIcon from '@mui/icons-material/FactCheckOutlined'; +import GavelOutlinedIcon from '@mui/icons-material/GavelOutlined'; +import PaymentsOutlinedIcon from '@mui/icons-material/PaymentsOutlined'; +import BarChartOutlinedIcon from '@mui/icons-material/BarChartOutlined'; +import ArchiveOutlinedIcon from '@mui/icons-material/ArchiveOutlined'; +import { tk } from './theme'; +import { useAuth } from './auth'; + +const RAIL = 74; +const FULL = 252; + +const initialesDe = (nom: string) => + nom.split(' ').filter(Boolean).slice(0, 2).map((s) => s[0]?.toUpperCase()).join(''); + +type NavItem = { label: string; path: string; icon: ReactNode; badge?: number }; + +const NAV: { section: string; items: NavItem[] }[] = [ + { section: 'Pilotage', items: [ + { label: 'Tableau de bord', path: '/soustraitants', icon: }, + ] }, + { section: 'Prestataires', items: [ + { label: 'Cahier des charges', path: '/soustraitants/cahier-des-charges', icon: }, + { label: 'Contrats', path: '/soustraitants/contrats', icon: }, + { label: 'Prestations', path: '/soustraitants/prestations', icon: , badge: 3 }, + ] }, + { section: 'Facturation', items: [ + { label: 'Factures', path: '/soustraitants/factures', icon: , badge: 1 }, + { label: 'Paiements', path: '/soustraitants/paiements', icon: }, + ] }, + { section: 'Suivi', items: [ + { label: 'Contrôle conformité', path: '/soustraitants/conformite', icon: }, + { label: 'Arbitrage', path: '/soustraitants/arbitrage', icon: , badge: 1 }, + { label: 'Pilotage', path: '/soustraitants/pilotage', icon: }, + ] }, + { section: 'Documents', items: [ + { label: 'Archives', path: '/soustraitants/archives', icon: }, + ] }, +]; + +export default function SousTraitantsLayout() { + const [open, setOpen] = useState(false); + const navigate = useNavigate(); + const location = useLocation(); + const isActive = (p: string) => (p === '/soustraitants' ? location.pathname === '/soustraitants' : location.pathname.startsWith(p)); + const w = open ? FULL : RAIL; + + const { displayName } = useAuth(); + const nomAffiche = displayName ?? 'Camille Laurent'; + const initiales = initialesDe(nomAffiche) || 'CL'; + + return ( + + + + setOpen(true)} + onMouseLeave={() => setOpen(false)} + sx={{ + width: w, flexShrink: 0, whiteSpace: 'nowrap', + '& .MuiDrawer-paper': { + width: w, boxSizing: 'border-box', display: 'flex', flexDirection: 'column', + overflowX: 'hidden', overflowY: 'auto', + bgcolor: tk.surface, + borderRight: `1px solid ${tk.line}`, + transition: 'width .18s ease', + boxShadow: open ? '4px 0 24px rgba(16,24,40,0.08)' : 'none', + }, + }} + > + + ST + {open && ( + + SI Finance + Module Sous-Traitants + + )} + + + {open && ( + + navigate('/portal')} + sx={{ px: 1, py: 1, borderRadius: 1.5, border: `1px solid ${tk.line}`, cursor: 'pointer', color: tk.muted, '&:hover': { color: tk.ink, bgcolor: tk.canvas } }}> + + Retour au portail + + + )} + + + {NAV.map((group) => ( + + {open && ( + + {group.section} + + )} + {group.items.map((item) => { + const active = isActive(item.path); + const bouton = ( + navigate(item.path)} + sx={{ + justifyContent: open ? 'flex-start' : 'center', px: open ? 1.25 : 1.5, + borderRadius: 1.5, mb: 0.25, + borderLeft: active ? `2px solid ${tk.green}` : '2px solid transparent', + bgcolor: active ? tk.greenSoft : 'transparent', + '&:hover': { bgcolor: active ? tk.greenSoft : tk.canvas }, + '&.Mui-selected': { bgcolor: tk.greenSoft }, + '&.Mui-selected:hover': { bgcolor: tk.greenSoft }, + }}> + + {item.icon} + + {open && ( + <> + + {item.badge != null && } + + )} + + ); + return open ? bouton : ( + + {bouton} + + ); + })} + + ))} + + + + {initiales} + {open && ( + + {nomAffiche} + Service Finance · Sous-Traitants + + )} + + + + + + + + ); +} diff --git a/frontend/src/auth.tsx b/frontend/src/auth.tsx new file mode 100644 index 0000000..0277c5b --- /dev/null +++ b/frontend/src/auth.tsx @@ -0,0 +1,71 @@ +// src/auth.tsx — connexion Microsoft / Entra ID pilotée par le backend : login() +// redirige simplement vers SIFinance.Api, qui gère tout le flux OAuth2 (authorization +// code + PKCE) avec Entra ID et renvoie ici avec un jeton "maison" en paramètre d'URL +// (voir la route /auth/callback dans App.tsx, et EntraAuthEndpoints.cs côté backend). +import { createContext, useContext, useEffect, useState, type ReactNode } from 'react'; +import { API_BASE_URL, getToken, setToken, clearToken, decodeToken, type JwtClaims } from './authToken'; + +type AuthCtx = { + authed: boolean; + userId: number | null; + displayName: string | null; + profil: string | null; + roles: string[]; + estResponsable: boolean; + serviceId: number | null; + login: () => void; + logout: () => void; + handleCallback: (token: string) => void; +}; +const Ctx = createContext({ + authed: false, userId: null, displayName: null, profil: null, roles: [], estResponsable: false, serviceId: null, + login: () => {}, logout: () => {}, handleCallback: () => {}, +}); +export const useAuth = () => useContext(Ctx); + +export function AuthProvider({ children }: { children: ReactNode }) { + const [claims, setClaims] = useState(() => { + const token = getToken(); + return token ? decodeToken(token) : null; + }); + + useEffect(() => { + // Jeton expiré (session de 8h dépassée) : on nettoie plutôt que de laisser l'app + // croire l'utilisateur connecté jusqu'au premier appel API qui échouerait en 401. + if (claims && claims.exp * 1000 < Date.now()) { + clearToken(); + setClaims(null); + } + }, [claims]); + + const login = () => { + window.location.href = `${API_BASE_URL}/api/auth/microsoft`; + }; + + const logout = () => { + clearToken(); + setClaims(null); + }; + + const handleCallback = (token: string) => { + setToken(token); + setClaims(decodeToken(token)); + }; + + const roles = claims?.roles == null ? [] : Array.isArray(claims.roles) ? claims.roles : [claims.roles]; + + const value: AuthCtx = { + authed: !!claims, + userId: claims ? Number(claims.id) : null, + displayName: claims ? `${claims.prenom} ${claims.nom}`.trim() : null, + profil: claims?.profil ?? null, + roles, + estResponsable: claims?.estResponsable === 'true', + serviceId: claims?.serviceId != null ? Number(claims.serviceId) : null, + login, + logout, + handleCallback, + }; + + return {children}; +} diff --git a/frontend/src/authToken.ts b/frontend/src/authToken.ts new file mode 100644 index 0000000..9693dad --- /dev/null +++ b/frontend/src/authToken.ts @@ -0,0 +1,51 @@ +// src/authToken.ts — stockage du jeton "maison" émis par SIFinance.Api après la +// connexion Entra ID. Module séparé (plutôt que dans auth.tsx ou data.ts) pour éviter +// une dépendance circulaire : auth.tsx en a besoin pour le contexte React, data.ts en +// a besoin pour attacher le jeton aux appels fetch. +export const API_BASE_URL = 'http://127.0.0.1:5272'; + +const TOKEN_KEY = 'sifinance_token'; + +export function getToken(): string | null { + return sessionStorage.getItem(TOKEN_KEY); +} + +export function setToken(token: string) { + sessionStorage.setItem(TOKEN_KEY, token); +} + +export function clearToken() { + sessionStorage.removeItem(TOKEN_KEY); +} + +export type JwtClaims = { + id: string; + email: string; + nom: string; + prenom: string; + profil: string; + // Un claim JWT répété (un par rôle) est sérialisé par .NET en tableau s'il y en a + // plusieurs, mais en simple chaîne s'il n'y en a qu'un seul — d'où le type union. + roles?: string | string[]; + estResponsable?: string; + serviceId?: string; + exp: number; +}; + +// atob() seul corrompt les caractères non-ASCII (accents des noms/prénoms français) — +// le détour par decodeURIComponent restitue un décodage UTF-8 correct. +export function decodeToken(token: string): JwtClaims | null { + try { + const payload = token.split('.')[1]; + const base64 = payload.replace(/-/g, '+').replace(/_/g, '/'); + const json = decodeURIComponent( + atob(base64) + .split('') + .map((c) => '%' + c.charCodeAt(0).toString(16).padStart(2, '0')) + .join(''), + ); + return JSON.parse(json); + } catch { + return null; + } +} diff --git a/frontend/src/data.ts b/frontend/src/data.ts new file mode 100644 index 0000000..d1d091a --- /dev/null +++ b/frontend/src/data.ts @@ -0,0 +1,512 @@ +// src/data.ts — types du domaine + données mock + couche API +// Les méthodes de référence/demandes de achatsApi appellent désormais le vrai backend +// (SIFinance.Api, module Achats). getEcheances/getAlertes/getDepensesParService restent +// mockées : pas encore d'endpoint côté API pour ces agrégats (aucune page ne les consomme +// pour l'instant — Dashboard.tsx et Pilotage.tsx ont leurs propres données locales). + +import { API_BASE_URL, getToken, clearToken } from './authToken'; + +async function apiFetch(path: string, init?: RequestInit): Promise { + const token = getToken(); + // Content-Type n'est ajouté que s'il y a un corps à envoyer : le poser sur les GET/DELETE + // sans body transforme la requête en requête CORS "non simple" et déclenche un aller-retour + // OPTIONS (preflight) supplémentaire avant chaque appel, y compris les simples lectures. + const res = await fetch(`${API_BASE_URL}${path}`, { + ...init, + headers: { + // FormData (upload de fichier) fixe elle-même son Content-Type multipart avec la + // boundary requise — la poser ici manuellement casserait le parsing côté serveur. + ...(init?.body && !(init.body instanceof FormData) ? { 'Content-Type': 'application/json' } : {}), + ...(token ? { Authorization: `Bearer ${token}` } : {}), + ...(init?.headers ?? {}), + }, + }); + if (res.status === 401) { + // Jeton absent/expiré/invalide : on force une reconnexion propre plutôt que de + // laisser l'app dans un état incohérent (données non chargées sans message clair). + clearToken(); + window.location.href = '/login'; + throw new Error('Session expirée — reconnexion nécessaire.'); + } + if (!res.ok) { + const body = await res.json().catch(() => null); + throw new Error(body?.error ?? `Erreur API (${res.status})`); + } + if (res.status === 204) return undefined as T; + const text = await res.text(); + return (text ? JSON.parse(text) : undefined) as T; +} + +export type StatutDemande = + | 'BROUILLON' | 'SOUMISE' | 'EN_VALIDATION' | 'COMPLEMENT_REQUIS' + | 'VALIDEE' | 'REFUSEE' | 'FACTUREE' | 'ANNULEE' | 'ARCHIVEE' + | 'VALIDEE_MANAGER' | 'EN_VALIDATION_MATERIEL' | 'VALIDEE_MATERIEL' + | 'EN_VALIDATION_FINANCE' | 'APPROUVEE' | 'COMMANDE_EMISE' + | 'COMMANDE_CONFIRMEE' | 'EN_ATTENTE_LIVRAISON' | 'EN_ATTENTE_VERIFICATION_RECEPTION' + | 'VERIFIEE_CONFORME' | 'VERIFIEE_NON_CONFORME' | 'RECEPTION_VALIDEE' + | 'FACTURE_EN_RAPPROCHEMENT' | 'CLOTUREE' + // Libellés du workflow Achats actuel. Les variantes précédentes restent prises en + // charge afin d'afficher correctement les demandes déjà créées. + | 'EN_CONTROLE_RECEPTION' | 'RECEPTION_CONFORME' | 'CLOTURE'; + +export interface Service { id: number; libelle: string; actif: boolean; } +export interface Campus { id: number; libelle: string; } +export interface TypeDepense { id: number; libelle: string; actif: boolean; } +export interface ModePaiement { id: number; libelle: string; necessiteXml: boolean; actif: boolean; } +export interface Fournisseur { id: number; raisonSociale: string; siret?: string; statut: string; } + +export interface FournisseurDetail { + id: number; raisonSociale: string; siret: string | null; email: string | null; + telephone: string | null; adresse: string | null; statut: string; dateCreation: string | null; +} +export interface FournisseurSave { + raisonSociale: string; siret?: string | null; email?: string | null; + telephone?: string | null; adresse?: string | null; statut: string; +} + +// Étape de la timeline d'historique (panneau de détail) +export interface EtapeHistorique { + titre: string; + auteur: string; + date: string; // ISO + fait: boolean; // true = étape passée, false = étape courante + note?: string; +} +export interface DemandeWorkflowHistorique { + action: string; commentaire: string | null; ancienStatut: string | null; nouveauStatut: string | null; + auteur: string; dateAction: string | null; +} +export interface DocumentWorkflow { nom: string; sharepointUrl: string; sharepointItemId: string; tailleOctets: number; } + +export interface DemandeJustificatif { id: number; libelle: string; nom: string; dateUpload: string | null; sharepointUrl?: string | null; } + +export interface DemandeAchat { + id: number; reference: string; serviceId: number; objet: string; description?: string | null; + fournisseurId?: number | null; fournisseurLibre?: string | null; categorieFournisseurLibre?: string | null; typeDepenseId: number; + montantHt: number; tauxTva: number; montantTva: number; montantTtc: number; + datePrevisionnelle: string; modePaiementId: number; statut: StatutDemande; + demandeurId: number; demandeurNom: string; valideurId?: number | null; valideurNom?: string | null; seuilApplicable: number; dateCreation: string; + dateSoumission: string | null; dateDerniereMaj: string | null; + justificatifs: DemandeJustificatif[]; + factureAjoutee?: boolean; factureUrl?: string | null; factureNom?: string | null; + // Calculé côté backend : true seulement si l'utilisateur connecté est le responsable + // direct (manager Entra ID) du demandeur — le rôle FINANCE ne donne plus ce droit. + peutValider: boolean; +} +export interface DemandeAchatCreate { + serviceId: number; objet: string; description?: string; fournisseurId?: number | null; + fournisseurLibre?: string | null; categorieFournisseurLibre?: string | null; typeDepenseId: number; montantHt: number; tauxTva: number; + datePrevisionnelle: string; modePaiementId: number; + justificatifs?: { libelle: string; nom: string; sharepointUrl?: string; sharepointItemId?: string; tailleOctets?: number }[]; +} + +// Construit une timeline dérivée des horodatages réels de la demande (création, soumission, +// décision) — ce n'est pas un journal d'événements détaillé (non stocké en base), seulement +// ce qui est déductible des colonnes dateCreation/dateSoumission/dateDerniereMaj/statut. +export const construireHistorique = (d: DemandeAchat): EtapeHistorique[] => { + const etapes: EtapeHistorique[] = [ + { titre: "Demande créée", auteur: d.demandeurNom, date: d.dateCreation, fait: true }, + ]; + if (d.dateSoumission) { + etapes.push({ titre: "Soumise à validation", auteur: d.demandeurNom, date: d.dateSoumission, fait: true }); + } + const decisionLabel: Partial> = { + VALIDEE: "Validée", REFUSEE: "Refusée", COMPLEMENT_REQUIS: "Complément requis", + FACTUREE: "Facturée", ANNULEE: "Annulée", ARCHIVEE: "Archivée", + VALIDEE_MANAGER: "Validée par le manager", + EN_VALIDATION_MATERIEL: "En validation matériel", + VALIDEE_MATERIEL: "Matériel validé", + EN_VALIDATION_FINANCE: "En validation Finance", + APPROUVEE: "Commande approuvée", + COMMANDE_EMISE: "Commande émise", + COMMANDE_CONFIRMEE: "Commande confirmée", + EN_ATTENTE_LIVRAISON: "En attente de livraison", + EN_ATTENTE_VERIFICATION_RECEPTION: "En contrôle de réception", + EN_CONTROLE_RECEPTION: "En contrôle de réception", + VERIFIEE_CONFORME: "Réception conforme", + RECEPTION_CONFORME: "Réception conforme", + VERIFIEE_NON_CONFORME: "Réception non conforme", + RECEPTION_VALIDEE: "Réception validée", + FACTURE_EN_RAPPROCHEMENT: "Facture en rapprochement", + CLOTURE: "Demande clôturée", CLOTUREE: "Demande clôturée", + }; + if (d.dateDerniereMaj && decisionLabel[d.statut]) { + // ANNULEE est toujours à l'initiative du demandeur lui-même (pas d'un validateur) — + // valideurNom resterait celui d'une éventuelle décision précédente, donc trompeur ici. + const auteur = d.statut === 'ANNULEE' ? d.demandeurNom : (d.valideurNom ?? '—'); + etapes.push({ + titre: decisionLabel[d.statut]!, + auteur, + date: d.dateDerniereMaj, + fait: true, + }); + } else if (d.statut === "SOUMISE" || d.statut === "EN_VALIDATION") { + etapes.push({ titre: "En attente de validation", auteur: "—", date: "", fait: false }); + } + return etapes; +}; + +export interface Echeance { + jour: string; mois: string; fournisseur: string; ref: string; montant: number; + etat: 'echu' | 'proche' | 'avenir'; etatLabel: string; lien?: string; +} +export interface Alerte { couleur: 'warn' | 'info' | 'error' | 'primary'; titre: string; detail: string; lien?: string; } +export interface DepenseService { service: string; montant: number; } + +const delay = (ms = 200) => new Promise((r) => setTimeout(r, ms)); + +// Ces listes ne servent plus qu'aux helpers de libellé ci-dessous (nomService, etc.) — +// achatsApi.getServices()/getFournisseurs()/etc. viennent désormais du vrai backend. +const services: Service[] = [ + { id: 1, libelle: 'Informatique', actif: true }, + { id: 2, libelle: 'Finance', actif: true }, + { id: 3, libelle: 'Ressources Humaines', actif: true }, + { id: 4, libelle: 'Formation', actif: true }, +]; +const typesDepense: TypeDepense[] = [ + { id: 1, libelle: 'Investissement', actif: true }, + { id: 2, libelle: 'Fonctionnement', actif: true }, + { id: 3, libelle: 'Abonnement', actif: true }, + { id: 4, libelle: 'Prestation de service', actif: true }, +]; +const modesPaiement: ModePaiement[] = [ + { id: 1, libelle: 'Virement', necessiteXml: true, actif: true }, + { id: 2, libelle: 'Carte bancaire', necessiteXml: false, actif: true }, + { id: 3, libelle: 'Prélèvement', necessiteXml: false, actif: true }, +]; +const fournisseurs: Fournisseur[] = [ + { id: 1, raisonSociale: 'Dell France', siret: '38272007200025', statut: 'ACTIF' }, + { id: 2, raisonSociale: 'RS Components', statut: 'ACTIF' }, + { id: 3, raisonSociale: 'Sophos', statut: 'ACTIF' }, + { id: 4, raisonSociale: 'Synology', statut: 'ACTIF' }, +]; + +// --- Helpers de libellés --- +export const nomService = (id: number) => services.find((s) => s.id === id)?.libelle ?? '—'; +export const nomTypeDepense = (id: number) => typesDepense.find((t) => t.id === id)?.libelle ?? '—'; +export const nomModePaiement = (id: number) => modesPaiement.find((m) => m.id === id)?.libelle ?? '—'; +export const nomFournisseur = (d: DemandeAchat) => + d.fournisseurLibre ?? fournisseurs.find((f) => f.id === d.fournisseurId)?.raisonSociale ?? '—'; + +// --- Helpers de dates --- +export const formatDateFr = (iso: string) => new Date(iso).toLocaleDateString('fr-FR'); +export const formatDateHeureFr = (iso: string) => + new Date(iso).toLocaleString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' }); + +// ============================================================================ +// SharePoint — upload de documents dans les dossiers modules (endpoint générique, +// réutilisé par Achats / Sous-Traitants / Contrat Etudiant). +// ============================================================================ + +export type DossierSharePoint = 'Achats' | 'Sous-Traitants' | 'Contrat Etudiant'; +export interface SharePointUpload { webUrl: string; itemId: string; size: number; nom: string; } + +export const sharepointApi = { + // service : sous-dossier optionnel (ex. le service du demandeur pour Achats) — le nom + // du collaborateur, lui, est toujours résolu côté serveur depuis l'utilisateur connecté. + async upload(dossier: DossierSharePoint, file: File, service?: string) { + const form = new FormData(); + form.append('file', file); + form.append('dossier', dossier); + if (service) form.append('service', service); + return apiFetch('/api/sharepoint/upload', { method: 'POST', body: form }); + }, +}; + +export const achatsApi = { + async getServices() { return apiFetch('/api/services'); }, + async getCampus() { return apiFetch('/api/campus'); }, + async getBudgets(exercice?: number) { + return apiFetch(`/api/budgets${exercice != null ? `?exercice=${exercice}` : ''}`); + }, + async creerBudget(input: BudgetServiceCreate) { + return apiFetch('/api/budgets', { method: 'POST', body: JSON.stringify(input) }); + }, + // Réservé à FINANCE côté backend — ajoute une révision à l'historique du budget + // (augmentation en cours d'exercice), sans jamais écraser les révisions précédentes. + async reviserBudget(id: number, input: BudgetRevisionCreate) { + return apiFetch(`/api/budgets/${id}/revisions`, { method: 'POST', body: JSON.stringify(input) }); + }, + async getTypesDepense() { return apiFetch('/api/types-depense'); }, + async getModesPaiement() { return apiFetch('/api/modes-paiement'); }, + async getFournisseurs() { return apiFetch('/api/fournisseurs'); }, + // scope='mine' force la restriction aux demandes du demandeur connecté, même pour un + // utilisateur qui a aussi le rôle FINANCE (utile en espace de travail Collaborateur). + async getDemandes(scope?: 'mine') { return apiFetch(`/api/demandes${scope ? `?scope=${scope}` : ''}`); }, + async getDemande(id: number) { return apiFetch(`/api/demandes/${id}`); }, + async getHistoriqueDemande(id: number) { return apiFetch(`/api/demandes/${id}/historique`); }, + async ajouterFactureDemande(id: number, input: { numero: string; montant: number; dateFacture: string; dateEcheance: string; document: DocumentWorkflow }) { + return apiFetch<{ id: number }>(`/api/demandes/${id}/facture`, { method: 'POST', body: JSON.stringify({ + numero: input.numero, montant: input.montant, dateFacture: input.dateFacture, dateEcheance: input.dateEcheance, + documentNom: input.document.nom, documentSharepointUrl: input.document.sharepointUrl, + documentSharepointItemId: input.document.sharepointItemId, documentTailleOctets: input.document.tailleOctets, + }) }); + }, + async modifierDemande(id: number, input: DemandeAchatCreate) { + return apiFetch(`/api/demandes/${id}`, { method: 'PUT', body: JSON.stringify(input) }); + }, + async annulerDemande(id: number) { + return apiFetch(`/api/demandes/${id}/annuler`, { method: 'PATCH' }); + }, + + async getEcheances(): Promise { + await delay(); + return [ + { jour: '18', mois: 'JUIN', fournisseur: 'Cabinet Mazet', ref: 'FAC-2026-0509', montant: 22000, etat: 'echu', etatLabel: 'Échue', lien: '/factures' }, + { jour: '27', mois: 'JUIN', fournisseur: 'OVHcloud', ref: 'FAC-2026-0518', montant: 2280, etat: 'proche', etatLabel: 'Dans 5 j', lien: '/factures' }, + { jour: '15', mois: 'JUIL', fournisseur: 'Dell France', ref: 'FAC-2026-0521', montant: 12400, etat: 'avenir', etatLabel: 'À venir', lien: '/factures' }, + ]; + }, + async getAlertes(): Promise { + await delay(); + return [ + { couleur: 'warn', titre: 'Doublon potentiel — FAC-2026-0512', detail: 'OVHcloud · 2 280 € · identique à une facture existante', lien: '/factures' }, + { couleur: 'info', titre: 'Avoir disponible — Dell France', detail: '1 500 € à affecter avant le prochain paiement', lien: '/factures' }, + { couleur: 'error', titre: 'Échéance dépassée — FAC-2026-0509', detail: 'Cabinet Mazet · 22 000 € · échue le 18/06 · dossier bloqué', lien: '/paiements' }, + { couleur: 'primary', titre: 'Demandes à valider', detail: '1 demande en attente de votre décision', lien: '/demandes' }, + ]; + }, + async getDepensesParService(): Promise { + await delay(); + return [ + { service: 'Informatique', montant: 23400 }, + { service: 'Finance', montant: 22000 }, + { service: 'Marketing', montant: 15600 }, + { service: 'Ressources Humaines', montant: 8900 }, + { service: 'Services Généraux', montant: 740 }, + ]; + }, + + async createDemande(input: DemandeAchatCreate): Promise { + return apiFetch('/api/demandes', { method: 'POST', body: JSON.stringify(input) }); + }, + async changerStatutDemande(id: number, statut: 'VALIDEE' | 'REFUSEE'): Promise { + return apiFetch(`/api/demandes/${id}/statut`, { method: 'PATCH', body: JSON.stringify({ statut }) }); + }, + async avancerWorkflowDemande(id: number, action: string, commentaire?: string, document?: DocumentWorkflow): Promise { + return apiFetch(`/api/demandes/${id}/workflow`, { method: 'PATCH', body: JSON.stringify({ + action, commentaire: commentaire || null, + documentNom: document?.nom ?? null, documentSharepointUrl: document?.sharepointUrl ?? null, + documentSharepointItemId: document?.sharepointItemId ?? null, documentTailleOctets: document?.tailleOctets ?? null, + }) }); + }, +}; + +// ============================================================================ +// Fournisseurs (CRUD complet) — endpoints /api/fournisseurs/*. +// ============================================================================ + +// ============================================================================ +// Sous-Traitants — organismes prestataires + contrats (endpoints /api/soustraitants/*). +// ============================================================================ + +export interface OrganismeST { id: number; raisonSociale: string; siret?: string | null; statut: string; } + +export type StatutContratST = 'ACTIF' | 'EXPIRE' | 'A_RENOUVELER'; + +export interface ContratPrestataire { + id: number; organismeId: number; organisme: string; reference: string; objet: string; + dateSignature: string | null; dateDebut: string; dateFin: string; montantTotal: number; statut: StatutContratST; +} +export interface ContratPrestataireCreate { + organismeId: number; objet: string; dateSignature?: string | null; + dateDebut: string; dateFin: string; montantTotal: number; +} + +export const soustraitantsApi = { + async getOrganismes() { return apiFetch('/api/soustraitants/organismes'); }, + async getContrats() { return apiFetch('/api/soustraitants/contrats'); }, + async createContrat(input: ContratPrestataireCreate) { + return apiFetch('/api/soustraitants/contrats', { method: 'POST', body: JSON.stringify(input) }); + }, +}; + +export const fournisseurApi = { + async getAll() { return apiFetch('/api/fournisseurs/all'); }, + async create(input: FournisseurSave) { + return apiFetch('/api/fournisseurs', { method: 'POST', body: JSON.stringify(input) }); + }, + async update(id: number, input: FournisseurSave) { + return apiFetch(`/api/fournisseurs/${id}`, { method: 'PUT', body: JSON.stringify(input) }); + }, + async remove(id: number) { + return apiFetch(`/api/fournisseurs/${id}`, { method: 'DELETE' }); + }, +}; + +// ============================================================================ +// Finance & Contrats — appellent SIFinance.Modules.Finance / le module Achats +// (endpoints /api/factures, /api/avoirs, /api/paiements, /api/contrats). +// ============================================================================ + +export type StatutFacture = 'attente' | 'validee' | 'litige' | 'payee'; + +export interface FactureEcheance { id: number; date: string; montant: number; statut: string; } +export interface FactureJustificatif { nom: string; url: string; } + +export interface Facture { + id: number; numero: string; fournisseurId: number; fournisseur: string; + montantHt: number; tauxTva: number; montantTva: number; montantTtc: number; + dateFacture: string; dateEcheance: string; statut: StatutFacture; + demandeAchatId: number | null; + avoirLieId: number | null; avoirLieNumero: string | null; avoirLieMontant: number | null; + commentaire: string | null; estDoublonSignale: boolean; + contratId: number | null; contratObjet: string | null; + echeances: FactureEcheance[]; + justificatifs?: FactureJustificatif[]; +} +export interface FactureCreate { + numero: string; fournisseurId: number; montant: number; + dateFacture: string; dateEcheance: string; demandeAchatId?: number | null; commentaire?: string | null; + contratId?: number | null; +} + +export interface Avoir { + id: number; numero: string; fournisseurId: number; fournisseur: string; + montant: number; soldeDisponible: number; date: string; + factureLieeId: number | null; factureLieeNumero: string | null; +} +export interface AvoirCreate { numero: string; fournisseurId: number; montant: number; date: string; } + +export type StatutPaiement = 'programme' | 'execute' | 'rejete'; + +export interface PaiementGroupe { + reference: string; fournisseurId: number; fournisseur: string; + factureIds: number[]; factureNumeros: string[]; montantTotal: number; + modePaiementId: number; modePaiement: string; datePaiement: string; + statut: StatutPaiement; rapproche: boolean; +} +export interface FactureDisponible { id: number; fournisseurId: number; fournisseur: string; numero: string; montant: number; dateFacture: string; } +export interface PaiementCreate { + factureIds: number[]; modePaiementId: number; datePaiement: string; + reference: string; statut: StatutPaiement; rapproche: boolean; +} +export interface PaiementUpdate { modePaiementId: number; datePaiement: string; reference: string; statut: StatutPaiement; rapproche: boolean; } + +export type StatutContrat = 'BROUILLON' | 'ACTIF' | 'A_RENOUVELER' | 'SUSPENDU' | 'RESILIE' | 'EXPIRE' | 'ARCHIVE' | 'TERMINE'; +export type StatutContratEcheance = 'A_VENIR' | 'PAYEE'; + +export interface ContratEcheance { id: number; date: string; montant: number; statut: StatutContratEcheance; justificatifNom: string | null; } +export interface ContratDocument { id: number; date: string; tag: string; nom: string; } +export interface ContratFactureLiee { id: number; numero: string; montantTtc: number; dateFacture: string; statut: string; } +export interface Contrat { + id: number; fournisseurId: number; fournisseur: string; serviceId: number | null; service: string | null; campusId: number | null; campus: string | null; reference: string | null; objet: string; + dateSignature: string | null; dateDebut: string; dateFin: string; dateResiliation: string | null; motifResiliation: string | null; montantTotal: number; + periodicite: string; statut: StatutContrat; echeances: ContratEcheance[]; documents: ContratDocument[]; + facturesLiees: ContratFactureLiee[]; +} +export interface ContratEcheanceCreate { date: string; montant: number; } +export interface ContratCreate { + fournisseurId: number; serviceId: number; campusId: number; objet: string; reference?: string | null; dateSignature?: string | null; + dateDebut: string; dateFin: string; montantTotal: number; periodicite: string; echeances: ContratEcheanceCreate[]; +} +export interface ContratHistorique { id: number; ancienStatut: string | null; nouveauStatut: string; commentaire: string | null; utilisateur: string; dateAction: string; } + +export interface FacturXAnalyse { + conforme: boolean; numero: string | null; fournisseurNom: string | null; + montantHt: number | null; montantTva: number | null; montantTtc: number | null; + dateFacture: string | null; dateEcheance: string | null; erreur: string | null; +} + +// Exercice non calendaire (1er septembre au 31 août) — exerciceAnnee désigne l'année de +// démarrage : 2026 = exercice du 01/09/2026 au 31/08/2027. +export interface BudgetRevision { + id: number; montantRevise: number; commentaire: string | null; revisePar: string; dateRevision: string; +} +export interface BudgetServiceItem { + id: number; serviceId: number; service: string; exerciceAnnee: number; + montantInitial: number; montantRevise: number | null; + revisions: BudgetRevision[]; +} +export interface BudgetServiceCreate { serviceId: number; exerciceAnnee: number; montantInitial: number; } +export interface BudgetRevisionCreate { montantRevise: number; commentaire?: string | null; } + +export const financeApi = { + async getFactures() { return apiFetch('/api/factures'); }, + // Réception de facture électronique (Factur-X/ZUGFeRD) : extrait les données du XML + // embarqué dans le PDF fournisseur pour pré-remplir le formulaire de saisie. + async analyserFacturX(file: File) { + const form = new FormData(); + form.append('file', file); + return apiFetch('/api/factures/analyser-facturx', { method: 'POST', body: form }); + }, + async createFacture(input: FactureCreate) { + return apiFetch('/api/factures', { method: 'POST', body: JSON.stringify(input) }); + }, + async changerStatutFacture(id: number, statut: StatutFacture) { + return apiFetch(`/api/factures/${id}/statut`, { method: 'PATCH', body: JSON.stringify({ statut }) }); + }, + async modifierCommentaireFacture(id: number, commentaire: string | null) { + return apiFetch(`/api/factures/${id}/commentaire`, { method: 'PATCH', body: JSON.stringify({ commentaire }) }); + }, + // Lève un signalement de doublon potentiel après vérification humaine que ce sont bien + // deux factures distinctes (même fournisseur + même montant sur une période proche). + async leverDoublonFacture(id: number) { + return apiFetch(`/api/factures/${id}/doublon`, { method: 'PATCH' }); + }, + async supprimerFacture(id: number) { + return apiFetch(`/api/factures/${id}`, { method: 'DELETE' }); + }, + async ajouterEcheanceFacture(factureId: number, input: { date: string; montant: number }) { + return apiFetch(`/api/factures/${factureId}/echeances`, { method: 'POST', body: JSON.stringify(input) }); + }, + async modifierEcheanceFacture(factureId: number, echeanceId: number, input: { date: string; montant: number }) { + return apiFetch(`/api/factures/${factureId}/echeances/${echeanceId}`, { method: 'PUT', body: JSON.stringify(input) }); + }, + async supprimerEcheanceFacture(factureId: number, echeanceId: number) { + return apiFetch(`/api/factures/${factureId}/echeances/${echeanceId}`, { method: 'DELETE' }); + }, + async lierAvoir(factureId: number, avoirId: number) { + return apiFetch(`/api/factures/${factureId}/avoir/${avoirId}`, { method: 'POST' }); + }, + async delierAvoir(factureId: number, avoirId: number) { + return apiFetch(`/api/factures/${factureId}/avoir/${avoirId}`, { method: 'DELETE' }); + }, + + async getAvoirs() { return apiFetch('/api/avoirs'); }, + async createAvoir(input: AvoirCreate) { + return apiFetch('/api/avoirs', { method: 'POST', body: JSON.stringify(input) }); + }, + + async getPaiements() { return apiFetch('/api/paiements'); }, + async getFacturesDisponibles() { return apiFetch('/api/paiements/factures-disponibles'); }, + async createPaiement(input: PaiementCreate) { + return apiFetch('/api/paiements', { method: 'POST', body: JSON.stringify(input) }); + }, + async modifierPaiement(reference: string, input: PaiementUpdate) { + return apiFetch(`/api/paiements/${encodeURIComponent(reference)}`, { method: 'PATCH', body: JSON.stringify(input) }); + }, + async toggleRapprochePaiement(reference: string) { + return apiFetch<{ rapproche: boolean }>(`/api/paiements/${encodeURIComponent(reference)}/rapproche`, { method: 'PATCH' }); + }, + async supprimerPaiement(reference: string) { + return apiFetch(`/api/paiements/${encodeURIComponent(reference)}`, { method: 'DELETE' }); + }, + + async getContrats() { return apiFetch('/api/contrats'); }, + async createContrat(input: ContratCreate) { + return apiFetch('/api/contrats', { method: 'POST', body: JSON.stringify(input) }); + }, + async getHistoriqueContrat(id: number) { return apiFetch(`/api/contrats/${id}/historique`); }, + async changerStatutContrat(id: number, input: { statut: StatutContrat; dateResiliation?: string; motifResiliation?: string; commentaire?: string }) { + return apiFetch(`/api/contrats/${id}/statut`, { method: 'PATCH', body: JSON.stringify(input) }); + }, + async ajouterEcheanceContrat(contratId: number, input: ContratEcheanceCreate) { + return apiFetch(`/api/contrats/${contratId}/echeances`, { method: 'POST', body: JSON.stringify(input) }); + }, + async modifierEcheanceContrat(contratId: number, echeanceId: number, input: { date: string; montant: number; statut: StatutContratEcheance; justificatifNom?: string | null }) { + return apiFetch(`/api/contrats/${contratId}/echeances/${echeanceId}`, { method: 'PUT', body: JSON.stringify(input) }); + }, + async supprimerEcheanceContrat(contratId: number, echeanceId: number) { + return apiFetch(`/api/contrats/${contratId}/echeances/${echeanceId}`, { method: 'DELETE' }); + }, + async ajouterDocumentContrat(contratId: number, input: { tag: string; nom: string; date?: string | null }) { + return apiFetch(`/api/contrats/${contratId}/documents`, { method: 'POST', body: JSON.stringify(input) }); + }, + async modifierDocumentContrat(contratId: number, documentId: number, input: { tag: string; date: string }) { + return apiFetch(`/api/contrats/${contratId}/documents/${documentId}`, { method: 'PUT', body: JSON.stringify(input) }); + }, + async supprimerDocumentContrat(contratId: number, documentId: number) { + return apiFetch(`/api/contrats/${contratId}/documents/${documentId}`, { method: 'DELETE' }); + }, +}; diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000..f0e4233 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,36 @@ +// src/main.tsx +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { BrowserRouter } from 'react-router-dom'; +import { ThemeProvider, CssBaseline } from '@mui/material'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { theme } from './theme'; +import { AuthProvider } from './auth'; +import App from './App'; + +// staleTime : les données restent considérées fraîches 30 s, donc pas de refetch +// à chaque navigation. refetchOnWindowFocus désactivé pour éviter un rechargement +// dès qu'on revient sur l'onglet. +const queryClient = new QueryClient({ + defaultOptions: { + queries: { + staleTime: 30_000, + refetchOnWindowFocus: false, + }, + }, +}); + +ReactDOM.createRoot(document.getElementById('root')!).render( + + + + + + + + + + + + , +); diff --git a/frontend/src/pages/Archives.tsx b/frontend/src/pages/Archives.tsx new file mode 100644 index 0000000..c9436fb --- /dev/null +++ b/frontend/src/pages/Archives.tsx @@ -0,0 +1,145 @@ +import { useState, useEffect } from "react"; +import { achatsApi, type DemandeAchat, type StatutDemande } from "../data"; + +/* + Vue personnelle du collaborateur : ses demandes d'achat, avec leurs justificatifs. + scope='mine' est toujours forcé ici (même pour un utilisateur qui a aussi le rôle + FINANCE) — cette page est explicitement "mes archives", pas une vue Finance globale. +*/ + +const fmtDate = (iso: string) => { + if (!iso) return "—"; + const [y, m, d] = iso.split("-"); + return `${d}/${m}/${y}`; +}; +const fmtEur = (n: number) => `${n.toLocaleString("fr-FR")} €`; + +const STATUT_DEMANDE_LABEL: Partial> = { + BROUILLON: "Brouillon", + SOUMISE: "Soumise", + EN_VALIDATION: "À valider", + COMPLEMENT_REQUIS: "Complément", + VALIDEE: "Validée", + REFUSEE: "Refusée", + FACTUREE: "Facturée", + ANNULEE: "Annulée", + ARCHIVEE: "Archivée", +}; + +export default function Archives() { + const [demandes, setDemandes] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + achatsApi.getDemandes('mine') + .then(setDemandes) + .catch((e) => setError(e instanceof Error ? e.message : "Erreur de chargement.")) + .finally(() => setLoading(false)); + }, []); + + return ( +
+ +
+
+

Achats · historique

+

Archives

+

Mes demandes et leurs justificatifs.

+
+ + {error &&

{error}

} + +
+

Mes demandes ({demandes.length})

+
+ + + + + + + + + {demandes.map((d) => { + const premierJustif = d.justificatifs.find((j) => j.sharepointUrl); + return ( + + + + + + + + + ); + })} + {!loading && demandes.length === 0 && ( + + )} + +
RéfObjetDate de créationJustif.Montant TTCStatut
{d.reference}{d.objet}{d.dateCreation ? fmtDate(d.dateCreation.slice(0, 10)) : "—"} + {d.justificatifs.length === 0 ? ( + 📄 0 + ) : premierJustif ? ( + 1 ? "s" : ""}`}> + 📄 {d.justificatifs.length} + + ) : ( + 📄 {d.justificatifs.length} + )} + {fmtEur(d.montantTtc)} + {STATUT_DEMANDE_LABEL[d.statut] ?? d.statut.replaceAll("_", " ")} +
Aucune demande pour l'instant.
+
+
+
+
+ ); +} + +const css = ` +.arc-root{ + --bg:#F5F4EF; --surface:#FFFFFF; --surface-2:#EFEDE6; + --text:#1C1E1B; --text-2:#65655D; --text-muted:#95948A; --border:#DEDCD3; + --accent:#2E4A6B; --accent-light:#E7EDF3; + --success:#3B6E52; --success-light:#E4EEE7; + --warning:#9C6B1E; --warning-light:#F6ECD9; + --danger:#A63D34; --danger-light:#F5E4E1; + --violet:#5B4B8A; --violet-light:#EDE9F6; + --radius:8px; + --mono:'IBM Plex Mono', ui-monospace, monospace; + --sans:'Inter', system-ui, sans-serif; + background:var(--bg); color:var(--text); font-family:var(--sans); font-size:14px; line-height:1.5; +} +.arc-root *{ box-sizing:border-box; } +.arc-wrap{ max-width:1180px; margin:0 auto; } +.arc-header{ margin-bottom:24px; padding-bottom:20px; border-bottom:1px solid var(--border); } +.arc-eyebrow{ font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-muted); margin:0 0 6px; } +.arc-h1{ font-size:24px; font-weight:700; margin:0; letter-spacing:-0.01em; } +.arc-sub{ font-size:13px; color:var(--text-muted); margin:6px 0 0; } +.arc-error{ font-size:13px; color:var(--danger); margin:0 0 16px; } + +.arc-section{ margin-bottom:28px; } +.arc-section-title{ font-size:15px; font-weight:700; margin:0 0 12px; } + +.arc-table-wrap{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); overflow:hidden; } +.arc-table{ width:100%; border-collapse:collapse; font-size:13px; } +.arc-table th{ text-align:left; font-size:11px; text-transform:uppercase; letter-spacing:0.04em; color:var(--text-muted); font-weight:500; padding:10px 16px; border-bottom:1px solid var(--border); } +.arc-table td{ padding:12px 16px; border-bottom:1px solid var(--border); } +.arc-table tbody tr:last-child td{ border-bottom:none; } +.arc-objet-cell{ font-weight:500; } +.arc-mono-cell{ font-family:var(--mono); font-size:12px; color:var(--text-2); } +.arc-amount{ font-family:var(--mono); text-align:right; } +.arc-empty{ text-align:center; color:var(--text-muted); padding:32px !important; } +.arc-muted-inline{ color:var(--text-muted); } +.arc-icon-link{ font-size:12.5px; color:var(--accent); text-decoration:none; white-space:nowrap; } +.arc-icon-link:hover{ text-decoration:underline; } +.arc-icon-link.muted{ color:var(--text-muted); cursor:default; } + +.arc-status-pill{ font-size:12px; font-weight:600; padding:3px 10px; border-radius:20px; white-space:nowrap; background:var(--surface-2); color:var(--text-2); } +.arc-status-pill[data-statut="VALIDEE"], .arc-status-pill[data-statut="FACTUREE"]{ background:var(--success-light); color:var(--success); } +.arc-status-pill[data-statut="SOUMISE"], .arc-status-pill[data-statut="EN_VALIDATION"]{ background:var(--accent-light); color:var(--accent); } +.arc-status-pill[data-statut="COMPLEMENT_REQUIS"]{ background:var(--warning-light); color:var(--warning); } +.arc-status-pill[data-statut="REFUSEE"], .arc-status-pill[data-statut="ANNULEE"]{ background:var(--danger-light); color:var(--danger); } +`; diff --git a/frontend/src/pages/Contratsfournisseurs.tsx b/frontend/src/pages/Contratsfournisseurs.tsx new file mode 100644 index 0000000..071e0e2 --- /dev/null +++ b/frontend/src/pages/Contratsfournisseurs.tsx @@ -0,0 +1,853 @@ +import { useEffect, useMemo, useState } from "react"; +import { useLocation } from "react-router-dom"; +import { + achatsApi, financeApi, + type Campus, type Contrat, type ContratEcheance, type ContratHistorique, type Fournisseur, + type Service, type StatutContrat, type StatutContratEcheance, +} from "../data"; +import { useAuth } from "../auth"; + +/* + Mêmes tokens de couleur / structure que FacturesAvoirs.tsx / Paiements.tsx. + Données réelles via financeApi (module achats — tables achats.contrats_fournisseurs / + achats.contrats_echeances, documents via core.pieces_jointes). + Les justificatifs ne sont pour l'instant qu'un nom de fichier — l'upload SharePoint + réel viendra se brancher dessus plus tard (cf. core.pieces_jointes.sharepoint_url). +*/ + +interface EchRow { id: string; date: string; montant: string; justifName: string | null; } +interface DocRow { id: string; date: string; tag: string; nom: string; } +interface EditBuffer { echeanceId: number; date: string; montant: string; statut: StatutContratEcheance; justif: string | null; } + +const uid = () => Math.random().toString(36).slice(2, 10); + +const fmtDate = (iso: string) => { + if (!iso) return ""; + const [y, m, d] = iso.split("-"); + return `${d}/${m}/${y}`; +}; + +const fmtEur = (n: number) => `${n.toLocaleString("fr-FR")} €`; + +const PAGE_SIZE = 5; + +function getPageList(current: number, total: number): (number | "…")[] { + const delta = 1; + const range: (number | "…")[] = []; + for (let i = Math.max(2, current - delta); i <= Math.min(total - 1, current + delta); i++) range.push(i); + if (current - delta > 2) range.unshift("…"); + if (current + delta < total - 1) range.push("…"); + range.unshift(1); + if (total > 1) range.push(total); + return range; +} + +function Pagination({ page, totalPages, onChange }: { page: number; totalPages: number; onChange: (p: number) => void }) { + if (totalPages <= 1) return null; + const pages = getPageList(page, totalPages); + return ( +
+ + {pages.map((p, i) => + p === "…" ? ( + + … + + ) : ( + + ) + )} + +
+ ); +} + +export default function ContratsFournisseurs() { + const { estResponsable } = useAuth(); + const peutGerer = estResponsable; + const location = useLocation(); + const [contracts, setContracts] = useState([]); + const [fournisseurs, setFournisseurs] = useState([]); + const [services, setServices] = useState([]); + const [campus, setCampus] = useState([]); + const [historiques, setHistoriques] = useState>({}); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + const [openIds, setOpenIds] = useState>(new Set()); + const [showForm, setShowForm] = useState(false); + const [page, setPage] = useState(1); + + // --- form fields --- + const [fournisseurId, setFournisseurId] = useState(""); + const [serviceId, setServiceId] = useState(""); + const [campusId, setCampusId] = useState(""); + const [objet, setObjet] = useState(""); + const [signature, setSignature] = useState(""); + const [reference, setReference] = useState(""); + const [debut, setDebut] = useState(""); + const [fin, setFin] = useState(""); + const [montantTotal, setMontantTotal] = useState(""); + const [periodicite, setPeriodicite] = useState<"mensuelle" | "trimestrielle" | "annuelle" | "unique">("mensuelle"); + const [echRows, setEchRows] = useState([]); + const [docs, setDocs] = useState([]); + const [editBuffer, setEditBuffer] = useState(null); + const [saving, setSaving] = useState(false); + const [statutEnCours, setStatutEnCours] = useState(null); + const [nouveauStatut, setNouveauStatut] = useState("ACTIF"); + const [commentaireStatut, setCommentaireStatut] = useState(""); + const [dateResiliation, setDateResiliation] = useState(""); + const [motifResiliation, setMotifResiliation] = useState(""); + + const refresh = async () => setContracts(await financeApi.getContrats()); + + useEffect(() => { + let cancelled = false; + setLoading(true); + setError(null); + Promise.all([financeApi.getContrats(), achatsApi.getFournisseurs(), achatsApi.getServices(), achatsApi.getCampus()]) + .then(([c, f, s, ca]) => { if (!cancelled) { setContracts(c); setFournisseurs(f); setServices(s); setCampus(ca); if (f.length > 0) setFournisseurId(f[0].id); } }) + .catch((err) => { if (!cancelled) setError(err instanceof Error ? err.message : "Échec du chargement."); }) + .finally(() => { if (!cancelled) setLoading(false); }); + return () => { cancelled = true; }; + }, []); + + // Arrivée depuis une alerte du dashboard : ouvre directement le contrat visé et + // se place sur la page de pagination qui le contient. On attend que `contracts` + // soit chargé (sinon l'index de recherche est vide) et on nettoie le state + // d'historique une fois consommé, pour ne pas rouvrir à chaque navigation suivante. + useEffect(() => { + const openContratId = (location.state as { openContratId?: number } | null)?.openContratId; + if (openContratId == null || contracts.length === 0) return; + + setOpenIds((prev) => new Set(prev).add(openContratId)); + if (!historiques[openContratId]) { + financeApi.getHistoriqueContrat(openContratId) + .then((h) => setHistoriques((before) => ({ ...before, [openContratId]: h }))) + .catch(() => undefined); + } + + const index = contracts.findIndex((c) => c.id === openContratId); + if (index !== -1) setPage(Math.floor(index / PAGE_SIZE) + 1); + + window.history.replaceState({}, ""); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [location.state, contracts]); + + const startEditEch = (e: ContratEcheance) => { + setEditBuffer({ echeanceId: e.id, date: e.date, montant: String(e.montant), statut: e.statut, justif: e.justificatifNom }); + }; + + const cancelEditEch = () => setEditBuffer(null); + + const saveEditEch = async (contratId: number) => { + if (!editBuffer) return; + try { + await financeApi.modifierEcheanceContrat(contratId, editBuffer.echeanceId, { + date: editBuffer.date, + montant: parseFloat(editBuffer.montant.replace(/\s/g, "").replace(",", ".")) || 0, + statut: editBuffer.statut, + justificatifNom: editBuffer.justif, + }); + await refresh(); + } catch (err) { + alert(err instanceof Error ? err.message : "Échec de la mise à jour de l'échéance."); + } + setEditBuffer(null); + }; + + const deleteEch = async (contratId: number, echeanceId: number) => { + if (!window.confirm("Supprimer cette échéance ?")) return; + try { + await financeApi.supprimerEcheanceContrat(contratId, echeanceId); + await refresh(); + } catch (err) { + alert(err instanceof Error ? err.message : "Échec de la suppression."); + } + if (editBuffer?.echeanceId === echeanceId) setEditBuffer(null); + }; + + const toggleOpen = (id: number) => { + setOpenIds((prev) => { + const next = new Set(prev); + if (next.has(id)) next.delete(id); + else { + next.add(id); + if (!historiques[id]) { + financeApi.getHistoriqueContrat(id) + .then((h) => setHistoriques((before) => ({ ...before, [id]: h }))) + .catch(() => undefined); + } + } + return next; + }); + }; + + const addEchRow = () => { + setEchRows((rows) => [...rows, { id: uid(), date: "", montant: "", justifName: null }]); + }; + + const removeEchRow = (id: string) => { + setEchRows((rows) => rows.filter((r) => r.id !== id)); + }; + + const updateEchRow = (id: string, patch: Partial) => { + setEchRows((rows) => rows.map((r) => (r.id === id ? { ...r, ...patch } : r))); + }; + + const attachFacture = (id: string, file: File | undefined) => { + if (!file) return; + updateEchRow(id, { justifName: file.name }); + }; + + const genererEcheancier = () => { + const montant = parseFloat(montantTotal.replace(/\s/g, "").replace(",", ".")); + if (!debut || !fin || !montant) { + alert("Renseigner la date de début, la date de fin et le montant total avant de générer l'échéancier."); + return; + } + const stepMonths = { mensuelle: 1, trimestrielle: 3, annuelle: 12, unique: 999 }[periodicite]; + const start = new Date(debut); + const end = new Date(fin); + const dates: Date[] = []; + + if (periodicite === "unique") { + dates.push(new Date(start)); + } else { + const cursor = new Date(start); + while (cursor <= end) { + dates.push(new Date(cursor)); + cursor.setMonth(cursor.getMonth() + stepMonths); + } + } + + const montantParEcheance = Math.round((montant / dates.length) * 100) / 100; + const rows: EchRow[] = dates.map((d, i) => { + const isLast = i === dates.length - 1; + const amt = isLast ? Math.round((montant - montantParEcheance * (dates.length - 1)) * 100) / 100 : montantParEcheance; + return { id: uid(), date: d.toISOString().split("T")[0], montant: String(amt), justifName: null }; + }); + setEchRows(rows); + }; + + const addDocs = (files: FileList | null) => { + if (!files) return; + const today = new Date().toISOString().split("T")[0]; + const newDocs: DocRow[] = Array.from(files).map((f) => ({ + id: uid(), + date: today, + tag: "Contrat signé", + nom: f.name, + })); + setDocs((prev) => [...prev, ...newDocs]); + }; + + const updateDoc = (id: string, patch: Partial) => { + setDocs((prev) => prev.map((d) => (d.id === id ? { ...d, ...patch } : d))); + }; + + const removeDoc = (id: string) => { + setDocs((prev) => prev.filter((d) => d.id !== id)); + }; + + const sortedDocs = useMemo(() => [...docs].sort((a, b) => a.date.localeCompare(b.date)), [docs]); + + const resetForm = () => { + setServiceId(""); + setCampusId(""); + setObjet(""); + setSignature(""); + setReference(""); + setDebut(""); + setFin(""); + setMontantTotal(""); + setPeriodicite("mensuelle"); + setEchRows([]); + setDocs([]); + }; + + const saveContract = async () => { + if (!fournisseurId || !serviceId || !campusId || !objet || !debut || !fin || echRows.length === 0) { + const manquants = [ + !fournisseurId && "le fournisseur", + !serviceId && "le service responsable", + !campusId && "le campus", + !objet && "l'objet", + !debut && "la date de début", + !fin && "la date de fin", + echRows.length === 0 && "au moins une échéance", + ].filter(Boolean); + alert(`Renseignez ${manquants.join(", ")} avant d'enregistrer.`); + return; + } + setSaving(true); + try { + const contrat = await financeApi.createContrat({ + fournisseurId, + serviceId, + campusId, + objet, + reference: reference || null, + dateSignature: signature || null, + dateDebut: debut, + dateFin: fin, + montantTotal: echRows.reduce((sum, r) => sum + (parseFloat(r.montant) || 0), 0), + periodicite, + echeances: echRows.map((r) => ({ date: r.date, montant: parseFloat(r.montant) || 0 })), + }); + + // Deuxième passe : justificatifs des échéances + documents généraux (les endpoints de + // création ne prennent que date/montant, les pièces jointes se rattachent après coup). + await Promise.all( + echRows.map((r, i) => + r.justifName ? financeApi.modifierEcheanceContrat(contrat.id, contrat.echeances[i].id, { + date: r.date, montant: parseFloat(r.montant) || 0, statut: "A_VENIR", justificatifNom: r.justifName, + }) : Promise.resolve() + ) + ); + await Promise.all(docs.map((d) => financeApi.ajouterDocumentContrat(contrat.id, { tag: d.tag, nom: d.nom, date: d.date }))); + + await refresh(); + resetForm(); + setShowForm(false); + setPage(1); + } catch (err) { + alert(err instanceof Error ? err.message : "Échec de l'enregistrement du contrat."); + } finally { + setSaving(false); + } + }; + + const enregistrerStatut = async (contratId: number) => { + if (nouveauStatut === "RESILIE" && (!dateResiliation || !motifResiliation.trim())) { + alert("La date et le motif de résiliation sont obligatoires."); + return; + } + try { + await financeApi.changerStatutContrat(contratId, { + statut: nouveauStatut, + dateResiliation: nouveauStatut === "RESILIE" ? dateResiliation : undefined, + motifResiliation: nouveauStatut === "RESILIE" ? motifResiliation : undefined, + commentaire: commentaireStatut || undefined, + }); + await refresh(); + const historique = await financeApi.getHistoriqueContrat(contratId); + setHistoriques((before) => ({ ...before, [contratId]: historique })); + setStatutEnCours(null); + setCommentaireStatut(""); + setDateResiliation(""); + setMotifResiliation(""); + } catch (err) { + alert(err instanceof Error ? err.message : "Échec du changement de statut."); + } + }; + + const totalPages = Math.max(1, Math.ceil(contracts.length / PAGE_SIZE)); + const pageContracts = contracts.slice((page - 1) * PAGE_SIZE, page * PAGE_SIZE); + + return ( +
+ + +
+
+
+

Achats · contrats

+

Contrats fournisseurs

+
+ {peutGerer && } +
+ + {error &&
{error}
} + + {showForm && ( +
+

Saisir un contrat existant

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + setObjet(e.target.value)} placeholder="Certification des titres 2026" /> +
+
+ + setSignature(e.target.value)} /> +
+
+ + setReference(e.target.value)} placeholder="CT-2026-014" /> +
+
+ + setDebut(e.target.value)} /> +
+
+ + setFin(e.target.value)} /> +
+
+ + setMontantTotal(e.target.value)} placeholder="12 000" /> +
+
+ + +
+
+ +
+
+

+ Échéancier +

+
+ + +
+
+ + + + + + + + + + + + + {echRows.map((row) => ( + + + + + + + + ))} + +
Date d'échéanceMontant (EUR)StatutJustificatif
+ updateEchRow(row.id, { date: e.target.value })} /> + + updateEchRow(row.id, { montant: e.target.value })} /> + + À venir + + + + +
+
+ +
+

+ Documents généraux{" "} + · contrat signé, avenants, CGV — non liés à une échéance +

+ + +
+ {sortedDocs.map((doc) => ( +
+ updateDoc(doc.id, { date: e.target.value })} /> + + {doc.nom} + +
+ ))} +
+
+ +
+ + +
+
+ )} + +
+ {pageContracts.map((c) => { + const paid = c.echeances.filter((e) => e.statut === "PAYEE").length; + const total = c.echeances.length; + const pct = total ? Math.round((paid / total) * 100) : 0; + const isOpen = openIds.has(c.id); + + return ( +
+
toggleOpen(c.id)}> +
+
{c.fournisseur}
+
{c.objet}
+
+
+ {fmtDate(c.dateDebut)} → {fmtDate(c.dateFin)} +
+
{fmtEur(c.montantTotal)}
+
+
+
+
+ + {paid}/{total} + +
+
{c.statut.replaceAll("_", " ")}
+
›
+
+ + {isOpen && ( +
+
+ Statut : {c.statut.replaceAll("_", " ")} + Service : {c.service ?? "Non renseigné"} + Campus : {c.campus ?? "Non renseigné"} +
+ {c.statut === "RESILIE" && ( +

Résilié le {fmtDate(c.dateResiliation ?? "")} — {c.motifResiliation}

+ )} + {peutGerer && ( +
+ {statutEnCours !== c.id ? ( + + ) : ( +
+ + {nouveauStatut === "RESILIE" && <> setDateResiliation(e.target.value)} /> setMotifResiliation(e.target.value)} />} + setCommentaireStatut(e.target.value)} /> + + +
+ )} +
+ )} +

Suivi du contrat

+
+ {(historiques[c.id] ?? []).map((h) =>
{h.nouveauStatut.replaceAll("_", " ")} · {h.utilisateur} · {fmtDate(h.dateAction.slice(0, 10))}{h.commentaire ? ` — ${h.commentaire}` : ""}
)} + {!historiques[c.id]?.length && Historique en cours de chargement…} +
+

+ Échéancier · justificatif rattaché à chaque échéance +

+ + + + + + + + + + + + {c.echeances.map((e) => { + const isEditing = editBuffer?.echeanceId === e.id; + return ( + + {isEditing && editBuffer ? ( + <> + + + + + + + ) : ( + <> + + + + + + + )} + + ); + })} + +
DateMontantStatutJustificatifActions
+ setEditBuffer({ ...editBuffer, date: ev.target.value })} + /> + + setEditBuffer({ ...editBuffer, montant: ev.target.value })} + /> + + + + + +
+ + +
+
{fmtDate(e.date)}{fmtEur(e.montant)} + {e.statut === "PAYEE" ? "Payée" : "À venir"} + + {e.justificatifNom ? ( + ev.preventDefault()}> + {e.justificatifNom} + + ) : ( + — + )} + + {peutGerer &&
+ + +
} +
+ +

+ Documents généraux · non liés à une échéance +

+
+ {[...c.documents] + .sort((a, b) => a.date.localeCompare(b.date)) + .map((doc) => ( +
+ {fmtDate(doc.date)} + {doc.tag} + {doc.nom} + ev.preventDefault()}> + Voir + +
+ ))} + {c.documents.length === 0 && Aucun document.} +
+ +

+ Factures liées · rapprochement, pas de lien automatique à une échéance précise +

+
+ {[...c.facturesLiees] + .sort((a, b) => b.dateFacture.localeCompare(a.dateFacture)) + .map((f) => ( +
+ {fmtDate(f.dateFacture)} + {f.numero} + {fmtEur(f.montantTtc)} +
+ ))} + {c.facturesLiees.length === 0 && Aucune facture rattachée à ce contrat.} +
+
+ )} +
+ ); + })} + {!loading && contracts.length === 0 && ( +
Aucun contrat enregistré.
+ )} +
+ + +
+
+ ); +} + +const css = ` +.cf-root{ + --bg:#F5F4EF; --surface:#FFFFFF; --surface-2:#EFEDE6; + --text:#1C1E1B; --text-2:#65655D; --text-muted:#95948A; --border:#DEDCD3; + --accent:#2E4A6B; --accent-light:#E7EDF3; + --success:#3B6E52; --success-light:#E4EEE7; + --danger:#A63D34; --danger-light:#F5E4E1; + --radius:8px; + --mono:'IBM Plex Mono', ui-monospace, monospace; + --sans:'Inter', system-ui, sans-serif; + background:var(--bg); color:var(--text); font-family:var(--sans); font-size:14px; line-height:1.5; +} +.cf-root *{ box-sizing:border-box; } +.cf-wrap{ max-width:1180px; margin:0 auto; padding:28px 32px 64px; } +.cf-header{ display:flex; justify-content:space-between; align-items:flex-end; margin-bottom:24px; padding-bottom:20px; border-bottom:1px solid var(--border); } +.cf-eyebrow{ font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--text-muted); margin:0 0 6px; } +.cf-h1{ font-size:24px; font-weight:700; margin:0; letter-spacing:-0.01em; } +.cf-btn-primary{ font-family:var(--sans); font-size:13px; padding:9px 16px; border-radius:var(--radius); border:1px solid var(--accent); background:var(--accent); color:#fff; cursor:pointer; } +.cf-btn-primary:hover{ background:#25405C; } +.cf-btn-primary:disabled{ opacity:0.6; cursor:default; } +.cf-btn-ghost{ font-family:var(--sans); font-size:13px; padding:9px 16px; border-radius:var(--radius); border:1px solid var(--border); background:var(--surface); color:var(--text); cursor:pointer; } +.cf-btn-ghost:hover{ border-color:var(--accent); } +.cf-btn-small{ font-family:var(--sans); font-size:12px; padding:5px 10px; border-radius:6px; border:1px solid var(--border); background:var(--surface); color:var(--text-2); cursor:pointer; } +.cf-btn-small:hover{ border-color:var(--accent); color:var(--accent); } +.cf-btn-danger{ border:none; color:var(--danger); background:transparent; } + +.cf-error-banner{ background:var(--danger-light); color:var(--danger); border-radius:var(--radius); padding:10px 14px; font-size:13px; margin-bottom:16px; } + +.cf-contract-card{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); margin-bottom:12px; overflow:hidden; } +.cf-contract-summary{ display:grid; grid-template-columns:1.6fr 1fr 1fr 1fr 0.8fr 40px; align-items:center; gap:12px; padding:14px 20px; cursor:pointer; } +.cf-contract-summary:hover{ background:var(--surface-2); } +.cf-name{ font-weight:500; } +.cf-sub{ font-size:12px; color:var(--text-muted); margin-top:2px; } +.cf-amount{ font-family:var(--mono); } +.cf-amount-cell{ font-family:var(--mono); } +.cf-progress-wrap{ display:flex; align-items:center; gap:8px; } +.cf-progress-track{ background:var(--surface-2); border-radius:4px; height:6px; flex:1; overflow:hidden; } +.cf-progress-fill{ height:100%; background:var(--accent); border-radius:4px; } +.cf-progress-label{ font-size:11px; color:var(--text-muted); white-space:nowrap; } +.cf-chevron{ transition:transform 0.15s ease; color:var(--text-muted); text-align:right; } +.cf-contract-card.open .cf-chevron{ transform:rotate(90deg); } +.cf-detail{ border-top:1px solid var(--border); padding:16px 20px 20px; } +.cf-contract-meta{ display:flex; flex-wrap:wrap; gap:10px 22px; padding:10px 12px; margin-bottom:12px; background:var(--surface-2); border-radius:6px; font-size:12px; color:var(--text-2); } +.cf-resiliation{ margin:0 0 12px; padding:8px 10px; background:var(--danger-light); color:var(--danger); border-radius:6px; font-size:12px; } +.cf-status-action{ margin:0 0 16px; } +.cf-status-form{ display:flex; flex-wrap:wrap; gap:8px; align-items:center; padding:10px; background:var(--accent-light); border-radius:6px; } +.cf-status-form select,.cf-status-form input{ min-height:30px; border:1px solid var(--border); border-radius:5px; padding:4px 8px; font-family:var(--sans); font-size:12px; background:var(--surface); } +.cf-history{ margin:0 0 20px; border-left:2px solid var(--border); padding-left:12px; } +.cf-history-row{ font-size:12px; margin:0 0 7px; color:var(--text-2); } +.cf-detail-label{ font-size:11px; text-transform:uppercase; letter-spacing:0.04em; color:var(--text-muted); margin:0 0 10px; } +.cf-muted-inline{ text-transform:none; color:var(--text-muted); font-weight:400; } + +.cf-table{ width:100%; border-collapse:collapse; font-size:13px; } +.cf-table th{ text-align:left; font-size:11px; color:var(--text-muted); font-weight:500; padding:6px 8px; border-bottom:1px solid var(--border); } +.cf-table td{ padding:8px; border-bottom:1px solid var(--border); } +.cf-status-pill{ font-size:11px; padding:2px 8px; border-radius:20px; background:var(--surface-2); color:var(--text-2); } +.cf-status-pill[data-statut="PAYEE"]{ background:var(--success-light); color:var(--success); } +.cf-status-pill[data-statut="A_VENIR"]{ background:var(--surface-2); color:var(--text-2); } +.cf-ech-justif-link{ font-size:12px; color:var(--accent); text-decoration:none; } +.cf-ech-justif-link:hover{ text-decoration:underline; } +.cf-ech-justif-empty{ font-size:12px; color:var(--text-muted); } + +.cf-justif-list{ display:flex; flex-direction:column; gap:8px; } +.cf-justif-item{ display:flex; align-items:center; gap:8px; background:var(--surface-2); border-radius:6px; padding:8px 10px; font-size:13px; } +.cf-justif-date{ font-family:var(--mono); font-size:12px; color:var(--text-2); width:66px; flex-shrink:0; } +.cf-justif-tag{ font-size:11px; padding:2px 8px; border-radius:20px; background:var(--accent-light); color:var(--accent); flex-shrink:0; } +.cf-justif-name{ flex:1; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; } +.cf-justif-item a{ font-size:12px; color:var(--accent); text-decoration:none; flex-shrink:0; } +.cf-justif-item input[type="date"]{ font-family:var(--mono); font-size:12px; border:none; background:transparent; width:100px; padding:0; flex-shrink:0; } +.cf-doc-tag-select{ border:none; background:transparent; font-size:11px; color:var(--accent); width:auto; padding:0; flex-shrink:0; } + +.cf-panel{ background:var(--surface); border:1px solid var(--border); border-radius:var(--radius); padding:20px 22px; } +.cf-panel-title{ font-size:15px; font-weight:600; margin:0 0 18px; } +.cf-form-grid{ display:grid; grid-template-columns:1fr 1fr; gap:14px 16px; } +.cf-field label{ font-size:12px; color:var(--text-2); display:block; margin-bottom:4px; } +.cf-field input, .cf-field select{ width:100%; font-family:var(--sans); font-size:13px; padding:8px 10px; border-radius:6px; border:1px solid var(--border); background:var(--surface); } +.cf-field input:focus, .cf-field select:focus{ outline:none; border-color:var(--accent); } + +.cf-echeancier-builder{ margin-top:20px; border-top:1px solid var(--border); padding-top:18px; } +.cf-echeancier-header{ display:flex; justify-content:space-between; align-items:center; margin-bottom:12px; } +.cf-table input{ width:100%; font-family:var(--mono); font-size:13px; padding:5px 6px; border:1px solid var(--border); border-radius:4px; } +.cf-table input[type="date"]{ font-family:var(--sans); } +.cf-table td{ padding:4px; } +.cf-inline-select{ font-family:var(--sans); font-size:13px; padding:5px 6px; border:1px solid var(--border); border-radius:4px; background:var(--surface); } +.cf-row-attach-btn{ font-family:var(--sans); font-size:12px; padding:5px 8px; border-radius:4px; border:1px dashed var(--border); background:var(--surface); color:var(--text-muted); cursor:pointer; max-width:100%; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; display:block; } +.cf-row-attach-btn:hover{ border-color:var(--accent); color:var(--accent); } +.cf-row-attach-btn.attached{ border-style:solid; color:var(--accent); border-color:var(--accent-light); background:var(--accent-light); } + +.cf-upload-zone{ margin-top:20px; border-top:1px solid var(--border); padding-top:18px; } +.cf-dropzone{ display:block; border:1.5px dashed var(--border); border-radius:var(--radius); padding:20px; text-align:center; color:var(--text-muted); font-size:13px; cursor:pointer; } +.cf-dropzone:hover{ border-color:var(--accent); color:var(--accent); } + +.cf-form-actions{ display:flex; justify-content:flex-end; gap:8px; margin-top:20px; } + +.cf-pagination{ display:flex; align-items:center; justify-content:center; gap:6px; padding:20px 0 4px; } +.cf-page-btn{ min-width:32px; height:32px; padding:0 8px; border-radius:7px; font-family:var(--sans); font-size:13px; font-weight:500; border:1px solid var(--border); background:var(--surface); color:var(--text-2); cursor:pointer; } +.cf-page-btn:hover:not(:disabled){ border-color:var(--accent); color:var(--accent); } +.cf-page-btn.active{ background:var(--accent); border-color:var(--accent); color:#fff; } +.cf-page-btn:disabled{ opacity:0.4; cursor:default; } +.cf-page-ellipsis{ width:24px; text-align:center; color:var(--text-muted); font-size:13px; } + +@media (max-width:900px){ + .cf-contract-summary{ grid-template-columns:1fr; row-gap:6px; } + .cf-form-grid{ grid-template-columns:1fr; } +} +`; \ No newline at end of file diff --git a/frontend/src/pages/Dashboard.tsx b/frontend/src/pages/Dashboard.tsx new file mode 100644 index 0000000..78d62d4 --- /dev/null +++ b/frontend/src/pages/Dashboard.tsx @@ -0,0 +1,393 @@ +import { useNavigate } from "react-router-dom"; +import { useQuery } from "@tanstack/react-query"; +import { achatsApi, financeApi, nomTypeDepense } from "../data"; +import { useWorkspace } from "../Layout"; +import { useAuth } from "../auth"; + +const c = { + page: "#F7F5EF", + card: "#FFFFFF", + border: "#E8E4D9", + ink: "#1A1A18", + secondary: "#6B6B62", + muted: "#9C9C90", + hover: "#FAF9F5", + + amberText: "#92400E", + amberBg: "#FDF1DC", + greenText: "#2F7D5C", + greenBg: "#E4F0EA", + grayText: "#5B6472", + grayBg: "#EDEEF0", + redText: "#B23A34", + redBg: "#F6E6E4", + blueText: "#1D4ED8", + blueBg: "#E8EEFC", +}; + +const euro = (n: number) => new Intl.NumberFormat("fr-FR", { style: "currency", currency: "EUR", maximumFractionDigits: 0 }).format(n); + +const STATUT: Record = { + BROUILLON: { label: "Brouillon", text: c.grayText, bg: c.grayBg }, + SOUMISE: { label: "À valider", text: c.amberText, bg: c.amberBg }, + EN_VALIDATION: { label: "À valider", text: c.amberText, bg: c.amberBg }, + COMPLEMENT_REQUIS: { label: "Complément", text: c.amberText, bg: c.amberBg }, + VALIDEE: { label: "Validée", text: c.greenText, bg: c.greenBg }, + FACTUREE: { label: "Facturée", text: c.blueText, bg: c.blueBg }, + REFUSEE: { label: "Refusée", text: c.redText, bg: c.redBg }, + ANNULEE: { label: "Annulée", text: c.grayText, bg: c.grayBg }, + ARCHIVEE: { label: "Archivée", text: c.grayText, bg: c.grayBg }, +}; + +function StatutBadge({ statut }: { statut: string }) { + const s = STATUT[statut] ?? { label: statut, text: c.grayText, bg: c.grayBg }; + return ( + + {s.label} + + ); +} + +function Row({ onClick, children }: { onClick?: () => void; children: React.ReactNode }) { + return ( +
(e.currentTarget.style.background = c.hover)} + onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")} + > + {children} +
+ ); +} + +function KpiCard({ label, value, sub, subColor }: { label: string; value: string; sub?: string; subColor?: string }) { + return ( +
+

{label}

+

{value}

+ {sub &&

{sub}

} +
+ ); +} + +function Panel({ title, action, badge, children }: { title: string; action?: React.ReactNode; badge?: number; children: React.ReactNode }) { + return ( +
+
+
+ {title} + {typeof badge === "number" && ( + {badge} + )} +
+ {action} +
+
{children}
+
+ ); +} + +type Severite = "danger" | "warning" | "info" | "primary"; +const SEVERITY: Record = { danger: c.redText, warning: c.amberText, info: c.blueText, primary: c.blueText }; + +function AlerteRow({ a, onClick }: { a: { titre: string; detail: string; severite: Severite }; onClick?: () => void }) { + return ( + + +
+

{a.titre}

+

{a.detail}

+
+
+ ); +} + +function DemandeRow({ d, onClick }: { d: { objet: string; reference: string; type: string; montant: number; statut: string }; onClick?: () => void }) { + return ( + +
+

{d.objet}

+

{d.reference} · {d.type}

+
+ {euro(d.montant)} +
+ +
+
+ ); +} + +type Urgence = "echue" | "proche" | "normale"; +const URGENCE: Record = { + echue: { label: "Échue", text: c.redText, dateText: c.redText }, + proche: { label: "Dans 5 j", text: c.amberText, dateText: c.amberText }, + normale: { label: "À venir", text: c.secondary, dateText: c.ink }, +}; +const MOIS_ABBR = ["JANV", "FÉVR", "MARS", "AVR", "MAI", "JUIN", "JUIL", "AOÛT", "SEPT", "OCT", "NOV", "DÉC"]; + +function EcheanceRow({ e }: { e: { jour: string; mois: string; fournisseur: string; montant: number; urgence: Urgence; joursRestants: number } }) { + const u = URGENCE[e.urgence]; + const label = e.urgence === "proche" ? `Dans ${e.joursRestants} j` : u.label; + return ( + +
+ {e.jour} + {e.mois} +
+
+

{e.fournisseur}

+

{label}

+
+ {euro(e.montant)} +
+ ); +} + +const EN_ATTENTE = new Set(["SOUMISE", "EN_VALIDATION", "COMPLEMENT_REQUIS"]); +const SEUIL_RENOUVELLEMENT_JOURS = 60; + +function trimestreDe(iso: string) { + const d = new Date(iso); + return { annee: d.getFullYear(), trimestre: Math.floor(d.getMonth() / 3) + 1 }; +} + +export default function Dashboard() { + const navigate = useNavigate(); + const { role } = useWorkspace(); + const { roles } = useAuth(); + const hasFinanceRole = roles.includes('FINANCE'); + + // Mêmes queryKey que dans Layout (scope inclus) : React Query dédoublonne les appels + // et sert le cache, donc un retour sur le dashboard s'affiche instantanément. scope=mine + // n'est forcé que pour un utilisateur Finance basculé en espace Collaborateur (vue + // étroite volontaire) — sinon on laisse le backend inclure les demandes des + // collaborateurs dont l'utilisateur est responsable. + const demandesScope = role === 'collaborateur' && hasFinanceRole ? 'mine' : undefined; + const demandesQ = useQuery({ queryKey: ["demandes", demandesScope], queryFn: () => achatsApi.getDemandes(demandesScope) }); + const facturesQ = useQuery({ queryKey: ["factures"], queryFn: financeApi.getFactures }); + const avoirsQ = useQuery({ queryKey: ["avoirs"], queryFn: financeApi.getAvoirs }); + // L'endpoint /api/contrats filtre déjà côté backend par service du responsable + // connecté (cf. ContratsEndpoints.MapGet) — pas besoin de re-filtrer ici. + const contratsQ = useQuery({ queryKey: ["contrats"], queryFn: financeApi.getContrats }); + + const demandes = demandesQ.data ?? []; + const factures = facturesQ.data ?? []; + const avoirs = avoirsQ.data ?? []; + const contrats = contratsQ.data ?? []; + + const loading = demandesQ.isLoading || facturesQ.isLoading || avoirsQ.isLoading || contratsQ.isLoading; + const errorObj = demandesQ.error ?? facturesQ.error ?? avoirsQ.error ?? contratsQ.error; + + if (loading) { + return

Chargement…

; + } + if (errorObj) { + const message = errorObj instanceof Error ? errorObj.message : "Erreur de chargement"; + return

Erreur : {message}

; + } + + const today = new Date(); + today.setHours(0, 0, 0, 0); + + // --- KPI : engagé ce trimestre (demandes validées/facturées) vs trimestre précédent --- + const { annee: anneeCourante, trimestre: trimCourant } = trimestreDe(today.toISOString()); + const demandesEngagees = demandes.filter((d) => d.statut === "VALIDEE" || d.statut === "FACTUREE"); + const sommeTrimestre = (annee: number, trimestre: number) => + demandesEngagees + .filter((d) => { const t = trimestreDe(d.dateCreation); return t.annee === annee && t.trimestre === trimestre; }) + .reduce((s, d) => s + d.montantTtc, 0); + const engageCourant = sommeTrimestre(anneeCourante, trimCourant); + const { annee: anneePrec, trimestre: trimPrec } = trimCourant === 1 ? { annee: anneeCourante - 1, trimestre: 4 } : { annee: anneeCourante, trimestre: trimCourant - 1 }; + const engagePrecedent = sommeTrimestre(anneePrec, trimPrec); + const variationPct = engagePrecedent > 0 ? Math.round(((engageCourant - engagePrecedent) / engagePrecedent) * 1000) / 10 : null; + + // --- KPI : demandes à valider --- + const demandesAValider = demandes.filter((d) => EN_ATTENTE.has(d.statut) && d.peutValider); + const montantAValider = demandesAValider.reduce((s, d) => s + d.montantTtc, 0); + + // --- KPI : à payer (factures validées non encore payées) --- + const facturesAPayer = factures.filter((f) => f.statut === "validee"); + const montantAPayer = facturesAPayer.reduce((s, f) => s + f.montantTtc, 0); + + // --- KPI : échéances à moins de 7 jours + échues --- + const facturesEnCours = factures.filter((f) => f.statut !== "payee"); + const facturesEchues = facturesEnCours.filter((f) => new Date(f.dateEcheance) < today); + const facturesProches = facturesEnCours.filter((f) => { + const jours = Math.ceil((new Date(f.dateEcheance).getTime() - today.getTime()) / 86400000); + return jours >= 0 && jours <= 7; + }); + + const openDemande = (id: number) => navigate("/demandes", { state: { openDemandeId: id } }); + const openContrat = (id: number) => navigate("/contrats-fournisseurs", { state: { openContratId: id } }); + + // --- Demandes récentes --- + const demandesRecentes = [...demandes] + .sort((a, b) => new Date(b.dateCreation).getTime() - new Date(a.dateCreation).getTime()) + .slice(0, 4) + .map((d) => ({ id: d.id, objet: d.objet, reference: d.reference, type: nomTypeDepense(d.typeDepenseId), montant: d.montantTtc, statut: d.statut })); + + // --- Alertes --- + const alertes: { titre: string; detail: string; severite: Severite; lien?: string; demandeId?: number; contratId?: number }[] = []; + for (const f of facturesEchues) { + alertes.push({ + titre: `Échéance dépassée — ${f.numero}`, + detail: `${f.fournisseur} · ${euro(f.montantTtc)} · échoue le ${new Date(f.dateEcheance).toLocaleDateString("fr-FR")} · dossier bloqué`, + severite: "danger", + lien: "/paiements", + }); + } + for (const a of avoirs) { + if (a.soldeDisponible > 0) { + alertes.push({ + titre: `Avoir disponible — ${a.fournisseur}`, + detail: `${euro(a.soldeDisponible)} à affecter avant le prochain paiement`, + severite: "info", + lien: "/factures", + }); + } + } + if (demandesAValider.length > 0) { + alertes.push({ + titre: "Demandes à valider", + detail: `${demandesAValider.length} demande${demandesAValider.length > 1 ? "s" : ""} en attente de votre décision`, + severite: "warning", + lien: demandesAValider.length === 1 ? undefined : "/demandes", + demandeId: demandesAValider.length === 1 ? demandesAValider[0].id : undefined, + }); + } + + // --- Alertes contrats fournisseurs (renouvellement proche/dépassé + échéances impayées en retard) --- + const contratsActifs = contrats.filter((ct) => !["RESILIE", "EXPIRE", "ARCHIVE"].includes(ct.statut)); + for (const ct of contratsActifs) { + const joursAvantFin = Math.ceil((new Date(ct.dateFin).getTime() - today.getTime()) / 86400000); + if (joursAvantFin <= SEUIL_RENOUVELLEMENT_JOURS) { + alertes.push({ + titre: joursAvantFin < 0 ? `Contrat expiré — ${ct.fournisseur}` : `Contrat à renouveler — ${ct.fournisseur}`, + detail: joursAvantFin < 0 + ? `${ct.objet} · expiré depuis le ${new Date(ct.dateFin).toLocaleDateString("fr-FR")}` + : `${ct.objet} · échéance dans ${joursAvantFin} j (${new Date(ct.dateFin).toLocaleDateString("fr-FR")})`, + severite: joursAvantFin < 0 ? "danger" : joursAvantFin <= 30 ? "warning" : "info", + contratId: ct.id, + }); + } + + const echeancesEnRetard = ct.echeances.filter((e) => e.statut === "A_VENIR" && new Date(e.date) < today); + if (echeancesEnRetard.length > 0) { + const montantRetard = echeancesEnRetard.reduce((s, e) => s + e.montant, 0); + alertes.push({ + titre: `Échéance contrat en retard — ${ct.fournisseur}`, + detail: `${echeancesEnRetard.length} échéance${echeancesEnRetard.length > 1 ? "s" : ""} · ${euro(montantRetard)} · ${ct.objet}`, + severite: "danger", + contratId: ct.id, + }); + } + } + + // --- Échéances à venir --- + const echeances = [...facturesEnCours] + .sort((a, b) => new Date(a.dateEcheance).getTime() - new Date(b.dateEcheance).getTime()) + .slice(0, 5) + .map((f) => { + const dt = new Date(f.dateEcheance); + const jours = Math.ceil((dt.getTime() - today.getTime()) / 86400000); + const urgence: Urgence = jours < 0 ? "echue" : jours <= 7 ? "proche" : "normale"; + return { + jour: String(dt.getDate()).padStart(2, "0"), + mois: MOIS_ABBR[dt.getMonth()], + fournisseur: f.fournisseur, + montant: f.montantTtc, + urgence, + joursRestants: jours, + ref: f.numero, + }; + }); + + const premiereADemander = demandesAValider[0]; + + return ( +
+

Tableau de bord

+

Vue d'ensemble du module Achats

+ + {demandesAValider.length > 0 && premiereADemander && ( +
(demandesAValider.length === 1 ? openDemande(premiereADemander.id) : navigate("/demandes"))} + style={{ + display: "flex", alignItems: "center", gap: 12, padding: "14px 20px", borderRadius: 16, marginBottom: 20, cursor: "pointer", + background: c.amberBg, border: `1px solid ${c.amberText}30`, + }} + > + +

+ {demandesAValider.length === 1 ? "1 demande à valider" : `${demandesAValider.length} demandes à valider`} + — {premiereADemander.reference} · {euro(premiereADemander.montantTtc)}{demandesAValider.length > 1 ? " et autres" : ""} +

+ Traiter → +
+ )} + +
+ = 0 ? "+" : ""}${variationPct} % vs trimestre précédent`} + subColor={variationPct === null ? undefined : variationPct >= 0 ? c.greenText : c.redText} + /> + + + 1 ? "s" : ""} échoue${facturesEchues.length > 1 ? "nt" : ""}` : undefined} + subColor={c.redText} + /> +
+ +
+ + {demandesRecentes.length === 0 ? ( +

Aucune demande pour le moment.

+ ) : ( + demandesRecentes.map((d) => openDemande(d.id)} />) + )} +
+ + {alertes.length === 0 ? ( +

Aucune alerte en cours.

+ ) : ( + alertes.map((a, i) => ( + openDemande(a.demandeId!) : + a.contratId != null ? () => openContrat(a.contratId!) : + a.lien ? () => navigate(a.lien!) : undefined + } + /> + )) + )} +
+
+ +
+ + {echeances.length === 0 ? ( +

Aucune échéance en attente.

+ ) : ( + echeances.map((e) => ) + )} +
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/pages/DemandeForm.tsx b/frontend/src/pages/DemandeForm.tsx new file mode 100644 index 0000000..acc52e2 --- /dev/null +++ b/frontend/src/pages/DemandeForm.tsx @@ -0,0 +1,721 @@ +// src/pages/DemandeForm.tsx +import { useEffect, useMemo, useState, type ReactNode } from 'react'; +import { useNavigate, useParams } from 'react-router-dom'; +import { achatsApi, sharepointApi, type Service, type TypeDepense, type ModePaiement, type Fournisseur, type DemandeAchat, type DemandeJustificatif } from '../data'; + +/* + Composant autonome — aucune dépendance externe hormis React. + Mêmes tokens de couleur / structure que ContratsFournisseurs.tsx, + FacturesAvoirs.tsx, Paiements.tsx, Demandes.tsx et Fournisseurs.tsx. + La logique métier (wizard, validation, appel achatsApi) est inchangée. +*/ + +const round2 = (n: number) => Math.round(n * 100) / 100; +const euro = (n: number, digits = 2) => + new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR', maximumFractionDigits: digits }).format(n); + +const ENTREPRISES = ['ENSUP SQY', 'ENSUP CGY', 'ENSUP MRS', 'ENSUP NTE']; +const CATEGORIES_FOURNISSEUR = ['Informatique', 'Mobilier', 'Services', 'Logiciels', 'Fournitures']; +const STEPS = ['Général', 'Articles', 'Justificatifs', 'Planification']; + +interface Ligne { article: string; quantite: number; prixUnitaire: number; } +interface Justif { + libelle: string; date: string; commentaire: string; nom: string; + sharepointUrl?: string; sharepointItemId?: string; tailleOctets?: number; + uploading?: boolean; uploadError?: string; +} + +const isoToFr = (iso: string) => { + const [y, m, d] = iso.split('-'); + return y && m && d ? `${d}/${m}/${y}` : ''; +}; + +// Champ date texte affiché en JJ/MM/AAAA quel que soit la langue du navigateur — +// natif affiche le format selon la locale du navigateur (souvent +// MM/DD/YYYY en anglais), ce qui n'est pas fiable. La valeur interne reste au format +// ISO (yyyy-mm-dd) pour rester compatible avec l'API. +function DateFrInput({ value, onChange }: { value: string; onChange: (iso: string) => void }) { + const [text, setText] = useState(() => isoToFr(value)); + useEffect(() => { setText(isoToFr(value)); }, [value]); + + const handleChange = (raw: string) => { + const digits = raw.replace(/\D/g, '').slice(0, 8); + const formatted = digits.length > 4 + ? `${digits.slice(0, 2)}/${digits.slice(2, 4)}/${digits.slice(4)}` + : digits.length > 2 + ? `${digits.slice(0, 2)}/${digits.slice(2)}` + : digits; + setText(formatted); + if (digits.length === 8) { + onChange(`${digits.slice(4, 8)}-${digits.slice(2, 4)}-${digits.slice(0, 2)}`); + } else if (digits.length === 0) { + onChange(''); + } + }; + + return ( + handleChange(e.target.value)} + placeholder="JJ/MM/AAAA" + inputMode="numeric" + maxLength={10} + /> + ); +} + +function Field({ label, error, children }: { label: string; error?: string; children: ReactNode }) { + return ( +
+ + {children} + {error &&

{error}

} +
+ ); +} + +// Zone de dépôt de fichier (glisser-déposer ou clic) — upload réel vers SharePoint +// (dossier Achats) dès la sélection du fichier, pas seulement à la soumission du formulaire. +function DropZone({ nom, uploading, error, onFile }: { + nom: string; uploading?: boolean; error?: string; onFile: (file: File) => void; +}) { + const [over, setOver] = useState(false); + return ( + + ); +} + +// Combobox de recherche fournisseur (remplace l'Autocomplete MUI) +function FournisseurCombo({ fournisseurs, value, onChange }: { + fournisseurs: Fournisseur[]; value: Fournisseur | null; onChange: (f: Fournisseur | null) => void; +}) { + const [query, setQuery] = useState(value?.raisonSociale ?? ''); + const [open, setOpen] = useState(false); + + useEffect(() => { setQuery(value?.raisonSociale ?? ''); }, [value]); + + const filtered = useMemo( + () => fournisseurs.filter((f) => f.raisonSociale.toLowerCase().includes(query.toLowerCase())), + [fournisseurs, query] + ); + + return ( +
+ { setQuery(e.target.value); onChange(null); setOpen(true); }} + onFocus={() => setOpen(true)} + onBlur={() => setTimeout(() => setOpen(false), 120)} + placeholder="Rechercher un fournisseur…" + /> + {open && filtered.length > 0 && ( +
+ {filtered.map((f) => ( +
{ onChange(f); setQuery(f.raisonSociale); setOpen(false); }}> + {f.raisonSociale} +
+ ))} +
+ )} +
+ ); +} + +function SummaryRow({ label, value }: { label: string; value: string }) { + return ( +
+ {label} + {value} +
+ ); +} + +// Carte d'un justificatif +function JustifCard({ j, index, serviceLibelle, onChange, onRemove }: { + j: Justif; index: number; serviceLibelle?: string; onChange: (patch: Partial) => void; onRemove: () => void; +}) { + const incomplet = !j.nom; + return ( +
+
+ Justificatif {index + 1} + {incomplet ? 'Incomplet' : 'Prêt'} +
+ +
+ + onChange({ date })} /> + + + onChange({ libelle: e.target.value })} placeholder="Ex : Devis Dell France" /> + +
+ + + onChange({ commentaire: e.target.value })} /> + + +

Justificatif *

+ { + onChange({ nom: file.name, uploading: true, uploadError: undefined }); + try { + const result = await sharepointApi.upload('Achats', file, serviceLibelle); + onChange({ nom: result.nom, sharepointUrl: result.webUrl, sharepointItemId: result.itemId, tailleOctets: result.size, uploading: false }); + } catch (err) { + onChange({ uploading: false, uploadError: err instanceof Error ? err.message : "Échec de l'envoi du fichier." }); + } + }} + /> + +
Joindre via mobile (QR Code)
+ +
+ +
+
+ ); +} + +export default function DemandeForm() { + const navigate = useNavigate(); + const { id } = useParams<{ id: string }>(); + const isEdit = !!id; + const [demandeInit, setDemandeInit] = useState(null); + const [existingJustificatifs, setExistingJustificatifs] = useState([]); + const [services, setServices] = useState([]); + const [types, setTypes] = useState([]); + const [modes, setModes] = useState([]); + const [fournisseurs, setFournisseurs] = useState([]); + + const [activeStep, setActiveStep] = useState(0); + + const [entreprise, setEntreprise] = useState(''); + const [serviceId, setServiceId] = useState(''); + const [objet, setObjet] = useState(''); + const [description, setDescription] = useState(''); + const [libreActif, setLibreActif] = useState(false); + const [fournisseurObj, setFournisseurObj] = useState(null); + const [fournisseurLibre, setFournisseurLibre] = useState(''); + const [categorieFournisseurLibre, setCategorieFournisseurLibre] = useState(''); + const [typeDepenseId, setTypeDepenseId] = useState(''); + const [tauxTva, setTauxTva] = useState('20'); + const [datePrevisionnelle, setDatePrevisionnelle] = useState(''); + const [modePaiementId, setModePaiementId] = useState(''); + const [lignes, setLignes] = useState([{ article: '', quantite: 1, prixUnitaire: 0 }]); + const [justificatifs, setJustificatifs] = useState([]); + const [errors, setErrors] = useState>({}); + const [submitting, setSubmitting] = useState(false); + const [submitError, setSubmitError] = useState(null); + + useEffect(() => { + (async () => { + const [s, t, m, f] = await Promise.all([ + achatsApi.getServices(), achatsApi.getTypesDepense(), achatsApi.getModesPaiement(), achatsApi.getFournisseurs(), + ]); + setServices(s); setTypes(t); setModes(m); setFournisseurs(f); + })(); + }, []); + + // Pré-remplissage en mode édition. "entreprise" et les lignes d'articles individuelles + // ne sont pas persistées côté backend (seul le total montantHt l'est) : on synthétise + // une ligne unique représentant le total existant, et on laisse "entreprise" à + // sélectionner à nouveau (champ purement local au wizard, jamais envoyé à l'API). + useEffect(() => { + if (!id) return; + (async () => { + const d = await achatsApi.getDemande(Number(id)); + setDemandeInit(d); + setServiceId(String(d.serviceId)); + setObjet(d.objet); + setDescription(d.description ?? ''); + if (d.fournisseurLibre) { + setLibreActif(true); + setFournisseurLibre(d.fournisseurLibre); + setCategorieFournisseurLibre(d.categorieFournisseurLibre ?? ''); + } + setTypeDepenseId(String(d.typeDepenseId)); + setTauxTva(String(d.tauxTva)); + setDatePrevisionnelle(d.datePrevisionnelle); + setModePaiementId(String(d.modePaiementId)); + setLignes([{ article: d.objet, quantite: 1, prixUnitaire: d.montantHt }]); + setExistingJustificatifs(d.justificatifs); + })(); + }, [id]); + + // Résolution du fournisseur référencé une fois la liste des fournisseurs chargée + // (peut arriver après la demande elle-même). + useEffect(() => { + if (demandeInit?.fournisseurId != null && fournisseurs.length > 0) { + const f = fournisseurs.find((x) => x.id === demandeInit.fournisseurId); + if (f) setFournisseurObj(f); + } + }, [demandeInit, fournisseurs]); + + const setLigne = (i: number, patch: Partial) => setLignes((ls) => ls.map((l, idx) => (idx === i ? { ...l, ...patch } : l))); + const ajouterLigne = () => setLignes((ls) => [...ls, { article: '', quantite: 1, prixUnitaire: 0 }]); + const supprimerLigne = (i: number) => setLignes((ls) => (ls.length > 1 ? ls.filter((_, idx) => idx !== i) : ls)); + + const ajouterJustif = () => setJustificatifs((js) => [...js, { libelle: '', date: '', commentaire: '', nom: '' }]); + const setJustif = (i: number, patch: Partial) => setJustificatifs((js) => js.map((j, idx) => (idx === i ? { ...j, ...patch } : j))); + const supprimerJustif = (i: number) => setJustificatifs((js) => js.filter((_, idx) => idx !== i)); + + const { montantHt, montantTva, montantTtc } = useMemo(() => { + const ht = round2(lignes.reduce((s, l) => s + (l.quantite || 0) * (l.prixUnitaire || 0), 0)); + const tva = round2(ht * ((parseFloat(tauxTva) || 0) / 100)); + return { montantHt: ht, montantTva: tva, montantTtc: round2(ht + tva) }; + }, [lignes, tauxTva]); + + // Validation propre à une étape du wizard (0 = Général … 3 = Planification) + function validateStepFields(step: number): Record { + const e: Record = {}; + if (step === 0) { + if (!entreprise) e.entreprise = 'Entreprise requise'; + if (!serviceId) e.serviceId = 'Service requis'; + if (!typeDepenseId) e.typeDepenseId = 'Catégorie requise'; + if (!objet.trim()) e.objet = 'Objet requis'; + if (libreActif ? !fournisseurLibre.trim() : !fournisseurObj) e.fournisseur = 'Fournisseur requis'; + if (libreActif && !categorieFournisseurLibre) e.categorieFournisseurLibre = 'Catégorie requise'; + } + if (step === 1) { + if (!lignes.some((l) => l.article.trim() && l.prixUnitaire > 0 && l.quantite > 0)) e.lignes = 'Ajoutez au moins un article valide'; + if (montantHt <= 0) e.lignes = 'Le montant total doit être supérieur à 0'; + } + if (step === 3) { + if (!modePaiementId) e.modePaiementId = 'Mode requis'; + if (!datePrevisionnelle) e.datePrevisionnelle = 'Date requise'; + } + return e; + } + + function handleNext() { + const e = validateStepFields(activeStep); + setErrors(e); + if (Object.keys(e).length === 0) setActiveStep((s) => Math.min(STEPS.length - 1, s + 1)); + } + + function handleBack() { + setErrors({}); + setActiveStep((s) => Math.max(0, s - 1)); + } + + // Valide toutes les étapes ; si une étape est en erreur, on s'y replace + function validateAll() { + let allErrors: Record = {}; + let firstErrorStep = -1; + for (let s = 0; s < STEPS.length; s++) { + const e = validateStepFields(s); + if (Object.keys(e).length && firstErrorStep === -1) firstErrorStep = s; + allErrors = { ...allErrors, ...e }; + } + setErrors(allErrors); + if (firstErrorStep !== -1) { setActiveStep(firstErrorStep); return false; } + return true; + } + + async function handleSubmit() { + if (!validateAll()) return; + if (justificatifs.some((j) => j.uploading)) { + setActiveStep(2); + setSubmitError("Attends la fin de l'envoi des justificatifs avant de soumettre."); + return; + } + setSubmitting(true); + setSubmitError(null); + try { + const payload = { + serviceId: Number(serviceId), objet: objet.trim(), description: description.trim() || undefined, + fournisseurId: libreActif ? null : fournisseurObj?.id ?? null, + fournisseurLibre: libreActif ? fournisseurLibre.trim() : null, + categorieFournisseurLibre: libreActif ? categorieFournisseurLibre : null, + typeDepenseId: Number(typeDepenseId), montantHt, + tauxTva: parseFloat(tauxTva), datePrevisionnelle, modePaiementId: Number(modePaiementId), + justificatifs: justificatifs + .filter((j) => j.nom.trim()) + .map((j) => ({ + libelle: j.libelle.trim() || "Justificatif", nom: j.nom.trim(), + sharepointUrl: j.sharepointUrl, sharepointItemId: j.sharepointItemId, tailleOctets: j.tailleOctets, + })), + }; + if (isEdit) { + await achatsApi.modifierDemande(Number(id), payload); + } else { + await achatsApi.createDemande(payload); + } + navigate('/demandes'); + } catch (err) { + setSubmitError(err instanceof Error ? err.message : `Échec de ${isEdit ? 'la modification' : 'la création'} de la demande.`); + } finally { + setSubmitting(false); + } + } + + const isLastStep = activeStep === STEPS.length - 1; + + return ( +
+ +
+
+
+

Achats · demandes

+

{isEdit ? 'Modifier la demande' : "Nouvelle demande d'achat"}

+
+ +
+ +
+
+
+ {STEPS.map((label, i) => ( +
+ {i < activeStep ? '✓' : i + 1} + {label} + {i < STEPS.length - 1 && } +
+ ))} +
+ +
+ {activeStep === 0 && ( + <> + + + + + + + + + + + setObjet(e.target.value)} /> + + +