# Pagga.Remuneration - Rapports F/H
# Contrat d'API
Services dédié pour les rapports d'égalité H/F
Note importante : on prend l'établissement de la personne à la date de fin de période
## Filtres & modèle de rapport
#### Dto
```csharp
abstract class ReportTemplate {
int Id;
string Name;
abstract ReportType ReportType;
}
class ReportTemplateTop : ReportTemplate {
List<Establishment> Establishments; // Simplified Establishments
List<Department> Departments; // Simplified Dept
List<OccupationCategory> Occu
List<VariableRemunerationNature> VariableRemunerationNatures;
List<FixedRemunerationNature> FixedRemunerationNatures;
List<WorkContractType> WorkContractTypes
}
class ReportTemplateGenderGap : ReportTemplate{
OccupationCategoryGroups OccupationCategoryGroups;
List<Establishment> Establishments; // Simplified Establishments
List<Department> Departments; // Simplified Dept
List<VariableRemunerationNature> VariableRemunerationNatures;
List<FixedRemunerationNature> FixedRemunerationNatures;
List<WorkContractType> WorkContractTypes
}
class OccupationCategoryGroups {
List<Group<OccupationCategories>> OccupationCategoryGroups;
}
class Group<T> { // Si trop complexe avec le swagger, on passe sur une classe par groupe
int Id,
string Name,
T[] Items
}
enum ReportType {
Top,
GenderGap
}
```
### Apis
#### GET - /remuneration/api/report-templates/top
- Response : ReportTemplateTop[]
#### GET - /remuneration/api/report-templates/top/{id}
- Response : ReportTemplateTop
#### GET - /remuneration/api/report-templates/gender-gap
- Response : ReportTemplateGenderGap[]
#### GET - /remuneration/api/report-templates/gender-gap/{id}
- Response : ReportTemplateGenderGap
#### POST - /remuneration/api/report-templates/gender-gap
- Payload
```json=
{
name: "nom du modèle",
establishmentIds: [1, 2, 3],
departmentIds: [1, 2],
...
}
```
#### PUT - /remuneration/api/report-templates/gender-gap/{id}
- Payload
```json=
{
id : 1,
name: "nom du modèle",
establishmentIds: [1, 2, 3],
departmentIds: [1, 2],
...
}
```
#### POST - /remuneration/api/report-templates/top
- Payload
```json=
{
name: "nom du modèle",
establishmentIds: [1, 2, 3],
departmentIds: [1, 2],
occupationCategoryGroups: [ // A revoir ce point là -> l'interface de gestion des groupes serait interessante, notament pour les Apis
{
name : "groupe 1",
ids : [1, 2, 3]
}
],
...
}
```
#### PUT - /remuneration/api/report-templates/top/{id}
- Payload : idem Post
#### DELETE - /remuneration/api/report-templates/{id}
### Notes pour le front
#### APIs a utiliser
- modèles de contrat :
https://demoprem1.ilucca.net/directory/api/work-contract-types
- établissements : https://demoprem1.ilucca.net/organization/structure/api/establishments
- departements : https://demoprem1.ilucca.net/api/v3/departments/tree?fields=id,name,code,head[id,name,dtContractEnd],isActive,currentUsersCount&isActive=true
- CSP : https://demoprem1.ilucca.net/organization/structure/api/occupation-categories
- natures EFP : https://demoprem1.ilucca.net/api/v3/fixedRemunerationNatures
- natures EVP : https://demoprem1.ilucca.net/api/v3/variableRemunerationNatures?typeId=1
Todo :
- Voir le picker du departement de Poplee-Engagement @f2x
- Faire un establishmentPicker custom Bu-Pay pour prendre en compte les LU et les devises
## Générer un rapport - Top
**⚠️Pas de pagination sur cette API⚠️**
### Apis
#### POST - /remuneration/api/reports/top
- payload
```json=
{
endDate : "2023-01-03",
template :
{
name: "nom du modèle",
establishmentIds: [1, 2, 3],
departmentIds: [1, 2],
...
}
}
```
- response : TopReport
### Dto
```csharp
class TopReport {
DateOnly StartDate;
DateOnly EndDate;
List<TopRankedRemuneration> TopRankedRemunerations;
}
class TopRankedRemuneration {
User User;
Establishment Establishment;
int Rank;
RemunerationAmount RemunerationAmount;
}
class RemunerationAmount {
decimal Amount;
Currency Currency
}
```
## Générer un rapport - Gender Gap
**⚠️Pas de pagination sur cette API⚠️**
### Apis
#### POST - /remuneration/api/reports/gender-gap
- payload
```json=
{
endDate : "2023-01-03",
template :
{
name: "nom du modèle",
establishmentIds: [1, 2, 3],
departmentIds: [1, 2],
occupationCategoryGroups: [ // A revoir ce point là -> l'interface de gestion des groupes serait interessante, notament pour les Apis
{
name : "groupe 1",
ids : [1, 2, 3]
}
],
...
}
}
```
#### GET - /remuneration/api/reports/gender-gap/details?OccupationCategoryIds=1,2,3,4&ageMin=18&ageMax=55
- RequiredField : OccupationCategoryIds & ageMin
- response : UserRemunerationPage
### Dto
```csharp
class GenderGapReport {
DateOnly StartDate;
DateOnly EndDate;
List<GenderGapReportItem> Items;
GenderGapReportMetrics Total
}
class GenderGapReportItem {
OccupationCategoryCriteria OccupationCategory;
AgeRangeCriterion AgeRange;
GenderGapReportMetrics Metrics;
}
class GenderGapReportMetrics {
GenderMetrics MaleMetrics;
GenderMetrics FemaleMetrics;
decimal PercentageGap
}
class AgeRangeCriterion {
int Min,
int? Max
}
class OccupationCategoryCriteria {
string Name;
List<OccupationCategory> OccupationCategories; // liste simplifiée de OccupationCategory
}
class GenderMetrics {
RemunerationAmount AverageAmount,
int Count
}
class RemunerationAmount {
decimal Amount;
Currency Currency
}
class UserRemuneration {
User User;
RemunerationAmount Amount;
}
```
### APIS des warnings
#### POST - /remuneration/api/reports/gender-gap/warnings
- payload
```json=
{
endDate : "2023-01-03",
template :
{
name: "nom du modèle",
establishmentIds: [1, 2, 3],
departmentIds: [1, 2],
occupationCategoryGroups: [ // A revoir ce point là -> l'interface de gestion des groupes serait interessante, notament pour les Apis
{
name : "groupe 1",
ids : [1, 2, 3]
}
],
...
}
}
```
- response : GenderGapWarningPage
#### Dto
```csharp
class GenderGapWarningPage : Page<GenderGapWarning> {
int MissingOccupationCategoryCount;
int UndefinedGenderCount;
}
class GenderGapWarning {
User User;
GenderGapWarningType WarningType;
}
enum GenderGapWarningType {
MissingOccupationCategory,
UndefinedGender
}
```
# Moteur de calcul de Remuneration
## Découpage des services
- Service propre au rapport (un pour chaque rapport)
- Service de filtrage de population
- Service d'extraction des données nécessaires au moteur
- Service de calcul des rémunerations (moteur de calcul)
```plantuml
@startuml
database "Database" {
(RemunerationDataFunction)
}
package "Lucca.Core" {
[Infra PopulationFilter]
}
package "RemunerationCalculations" {
[Domain Calculation]
[Infra Calculation] --> (RemunerationDataFunction)
}
package "Reports/GenderGap" {
[Domain GenderGap] <--> [Infra GenderGap]
[Infra GenderGap] <--> [Infra PopulationFilter]
[Domain GenderGap] <--> [Domain Calculation]
[Infra GenderGap] --> [Infra Calculation]
cloud {
[Web GenderGap] <--> [Domain GenderGap]
}
}
package "Reports/Top" {
[Domain Top] <--> [Infra Top]
[Infra Top] <--> [Infra PopulationFilter]
[Domain Top] <--> [Domain Calculation]
[Infra Top] --> [Infra Calculation]
cloud {
[Web Top] <--> [Domain Top]
}
}
@enduml
```
## Filtrage de Population
Filtre de population :
- nombre de jour de présence sur l'année (> 182 jours). présence calendaire/contractuelle
### Entrées
On donne les filtres correspondant au rapport en question.
Uniquement les données associées aux workcontracts :
- Establishments
- Departments
- OccupationCategories
- WorkContractTypes
- Qualication (pas MVP)
- Période sur laquelle les contrats sont éligibles (Date de début/date de fin)
- Collaborateurs présent sur la fin de la période (true/false)
### Sortie possible
- Liste de workcontracts (pas nécessaire pour notre cas ?)
- Liste de workcontracts paginée (pas nécessaire pour notre cas ?)
- Expression/Prédicat permettant de récupérer cette liste des workcontracts
## Récupération des données de rémunérations
On va utiliser une fonction SQL pour récupérer les données de rémunération (EVP/EFP) qui sont nécessaires au moteur de calcul. Cette fonction nous permet de récupérer les données de manière "propre".
POC de cette fonction
**Entrée**
- Date de début
- Date de fin
**Sortie**
wc1 efp1 2000 12
wc2 efp1 2000 15
wc2 evp2 500 15
## Moteur de calcul
### Entrée du moteur
- liste d'élements permettant le calcul
- liste de règles de transformation d'éléments optionnelles
- une règle d'aggrégation
- une liste de règles de transformations d'aggregats optionnelles
### Sortie du moteur
- Aggregation
### Process d'éxecution du moteur
Pour chaque ligne, on applique toute les règles chainables.
Une fois le chainage terminé, on applique la règle d'aggrégation.
Pour terminer on applique les règles de transformations.
*3 types de règles :*
- Item Transformation
- Aggregation
- Aggregate transformation
Toutes les règles *Item Transformation* ont les mêmes interfaces
- Entrée : Une ligne (ComputedRemunerationItem)
- `ComputedRemunerationItem`
- Sortie : La même ligne (ComputedRemunerationItem) avec le montant calculé mis à jour
- `ComputedRemunerationItem`
### Nos règles pour notre Remuneration sans nom
- Règle 1 (*Item Transformation*) : Reconstitué equivalent temps plein
- Calcul : `Montant / ETP`
- Condition d'application : sur tous les éléments (EFP & EVP) et si ETP différent de 0
- Règle 2 (*Item Transformation*) : Proratisation Calendaire
- Calcul : `Montant / (présence contractuelle calendaire sur la période / Nombre de jour calendaire de la période)`
- Condition d'application : Uniquement pour les EFP et si il ne dure pas une période (mois) complete
- Règle 3 (*aggregation*) : Reconstitution annuelle
- Calcul :
- reconstituer les tranches possibles par rapports à tous les éléments de rémunération sur la période (voir exemple plus bas)
- Par tranche on prend tous les éléments qui intersect cette tranche.
- Somme de tous les élements par tranche et on renvois le nombre de tranche (weight)
- Condition d'application : n/a
- Entrée : Une liste de lignes (ComputedRemunerationItem) (un contrat (ou user) / une période)
- `List<ComputedRemunerationItem>`
- Sortie : Sortie custom AggregatedRemunerationItem
exemple règle 3 :
> Mois de Janvier (1 user / contrat)
>
> - EFP SB 2000 du 1 au 8
>
> - EFP SB 3000 du 9 au 31
>
> - EFP AN 100 du 1 au 12
>
> - EFP AN 150 du 13 au 20
>
> - EFP AN 200 du 21 au 31
>
> 0 8 8 31 0 12 12 20 20 31
>
> 0 8 12 20 31
>
> 2000 + 3000 + 3000 + 100 + 150 + 200
>
> 1 période => 4 périodes
### Modèle de donnée
```csharp
class ComputedRemunerationItem {
decimal ComputedAmount; // montant utilisé par les règles
RemunerationItem SourceItem;
}
class AggregatedRemunerationItem {
int Weight; // A rediscuter au moment de l'implem
decimal Sum;
}
interface IItemTransformationRule {
ComputedRemunerationItem Apply(ComputedRemunerationItem item);
}
interface IAggregateRule {
AggregatedRemunerationItem Apply(ComputedRemunerationItem item);
}
interface IAggregateTransformationRule {
AggregatedRemunerationItem Apply(AggregatedRemunerationItem aggregatedItem);
}
```
## Service Top-Ranked
### Entrées
```csharp
class PeriodRemunerationItem {
WorkContract WorkContract;
RemunerationItem Item;
Period Period;
}
class RemunerationItem {
decimal Amount;
Nature RemunerationNature;
}
abstract class RemunerationNature {
int Id;
string Name;
ValueType ValueType;
abstract NatureType NatureType;
abstract IsInActualRemuneration;
abstract IsInTheoricalRemuneration;
abstract NoFullTimeEquivalent;
abstract IsPonctual;
}
enum NatureType {
Fixed,
Variable
}
enum ValueType
{
Amount,
Number
}
```
### Sortie possible
- une liste de UserRemuneration
```csharp
class UserRemuneration {
User User;
RemunerationAmount Amount;
}
```
## Service Gender-Gap