Passed
Pull Request — master (#220)
by
unknown
01:50
created

server/src/Infrastructure/HumanResource/Templates/PayrollElements/docx.ts   A

Complexity

Total Complexity 4
Complexity/F 0

Size

Lines of Code 281
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
wmc 4
eloc 182
mnd 4
bc 4
fnc 0
dl 0
loc 281
rs 10
bpm 0
cpm 0
noi 0
c 0
b 0
f 0
1
import {
2
  Document,
3
  HeadingLevel,
4
  Paragraph,
5
  TextRun,
6
  Table,
7
  TableRow,
8
  TableCell,
9
  WidthType
10
} from 'docx';
11
import { ArrayUtils } from 'src/Infrastructure/Common/Utils/ArrayUtils';
12
import { DocxFunction } from 'src/Infrastructure/docx.interfaces';
13
import { PayrollElementsLocals } from '../../PayrollElements/DTO/PayrollElementsDTO';
14
import { PayrollElementsView } from 'src/Application/HumanResource/PayrollElements/View/PayrollElementsView';
15
16
export const fn: DocxFunction = (locals: PayrollElementsLocals) => {
17
  const { now, dateUtils } = locals;
18
19
  return new Document({
20
    sections: [
21
      {
22
        children: [
23
          new Paragraph({
24
            text: 'Fairness - Éléments de paie',
25
            heading: HeadingLevel.HEADING_1
26
          }),
27
28
          new Paragraph({
29
            children: [
30
              new TextRun({ text: 'Période', bold: true }),
31
              new TextRun(`: ${dateUtils.format(now, 'MM/y')}`)
32
            ]
33
          }),
34
35
          // Aggregates
36
37
          new Paragraph({
38
            text: 'Totaux',
39
            heading: HeadingLevel.HEADING_2
40
          }),
41
42
          new Table({
43
            width: { size: 50, type: WidthType.PERCENTAGE },
44
            rows: makeAggregateRows(locals)
45
          }),
46
47
          // Employee list
48
49
          new Paragraph({
50
            text: 'Salariés',
51
            heading: HeadingLevel.HEADING_2
52
          }),
53
54
          ...ArrayUtils.flatMap(makeEmployeeTables(locals), table => [
55
            table,
56
            new Paragraph('')
57
          ])
58
        ]
59
      }
60
    ]
61
  });
62
};
63
64
const makeAggregateRows = (locals: PayrollElementsLocals): TableRow[] => {
65
  const { elements, formatMoney } = locals;
66
67
  return [
68
    new TableRow({
69
      children: [
70
        new TableCell({
71
          children: [
72
            new Paragraph({
73
              children: [new TextRun({ text: 'Salaires bruts', bold: true })]
74
            })
75
          ]
76
        }),
77
        new TableCell({
78
          children: [
79
            new Paragraph(
80
              formatMoney(
81
                elements.map(el => el.monthlyEarnings).reduce((a, b) => a + b)
82
              )
83
            )
84
          ]
85
        })
86
      ]
87
    }),
88
    new TableRow({
89
      children: [
90
        new TableCell({
91
          children: [
92
            new Paragraph({
93
              children: [new TextRun({ text: 'Transport', bold: true })]
94
            })
95
          ]
96
        }),
97
        new TableCell({
98
          children: [
99
            new Paragraph(
100
              formatMoney(
101
                elements.map(el => el.transportFee).reduce((a, b) => a + b)
102
              )
103
            )
104
          ]
105
        })
106
      ]
107
    }),
108
    new TableRow({
109
      children: [
110
        new TableCell({
111
          children: [
112
            new Paragraph({
113
              children: [
114
                new TextRun({ text: 'Tickets restaurant', bold: true })
115
              ]
116
            })
117
          ]
118
        }),
119
        new TableCell({
120
          children: [
121
            new Paragraph(
122
              elements
123
                .map(el => el.mealTickets)
124
                .reduce((a, b) => a + b)
125
                .toString()
126
            )
127
          ]
128
        })
129
      ]
130
    })
131
  ];
132
};
133
134
const _chopIntoGroupsOfAtMost = <T>(size: number, items: T[]) => {
135
  const numGroups = Math.ceil(items.length / size);
136
  return new Array(numGroups)
137
    .fill('')
138
    .map((_, i) => items.slice(i * size, (i + 1) * size));
139
};
140
141
const makeEmployeeTables = (locals: PayrollElementsLocals): Table[] => {
142
  const { elements, dateUtils, formatMoney } = locals;
143
144
  const simpleColumns: {
145
    [K in keyof PayrollElementsView]?: [
146
      string,
147
      (v: PayrollElementsView[K]) => string
148
    ];
149
  } = {
150
    lastName: ['Nom', v => v],
151
    firstName: ['Prénom', v => v],
152
    executivePosition: ['Statut', v => (v ? 'Cadre' : 'Non-cadre')],
153
    joiningDate: ["Date d'entrée", v => dateUtils.format(v, 'MM/y')],
154
    leavingDate: [
155
      'Date de sortie',
156
      v => (v ? dateUtils.format(v, 'MM/y') : '')
157
    ],
158
    annualEarnings: ['Salaire brut annuel', formatMoney],
159
    monthlyEarnings: ['Salaire brut mensuel', formatMoney],
160
    workingTime: ['TC/TP', v => (v === 'full_time' ? 'TC' : 'TP')],
161
    transportFee: ['Transport', formatMoney],
162
    mealTickets: ['Tickets restaurant', v => `${v}`],
163
    healthInsurance: ['Mutuelle', v => (v ? 'Oui' : 'Non')],
164
    totalPaidLeaves: ['Congés pays', v => `${v}`],
165
    totalUnpaidLeaves: ['Congés sans solde', v => `${v}`],
166
    totalMedicalLeaves: ['Congés maladie', v => `${v}`],
167
    totalSpecialLeaves: ['Congés exceptionnels', v => `${v}`]
168
  };
169
170
  const makeEmployeeTable = (els: PayrollElementsView[]): Table => {
171
    const rows = Object.keys(simpleColumns).map(column => {
172
      const [label, format]: [string, (v: Object) => string] = simpleColumns[
173
        column
174
      ];
175
      return new TableRow({
176
        children: [
177
          new TableCell({
178
            children: [
179
              new Paragraph({
180
                children: [new TextRun({ text: label, bold: true })]
181
              })
182
            ]
183
          }),
184
          ...els.map(
185
            el =>
186
              new TableCell({ children: [new Paragraph(format(el[column]))] })
187
          )
188
        ]
189
      });
190
    });
191
192
    // Add leaves
193
    rows.push(
194
      new TableRow({
195
        children: [
196
          new TableCell({
197
            children: [
198
              new Paragraph({
199
                children: [new TextRun({ text: 'Congés', bold: true })]
200
              })
201
            ]
202
          }),
203
          ...els.map(
204
            el =>
205
              new TableCell({
206
                children: [
207
                  new Table({
208
                    width: {
209
                      size: 100,
210
                      type: WidthType.PERCENTAGE
211
                    },
212
                    rows: [
213
                      new TableRow({
214
                        children: [
215
                          new TableCell({ children: [new Paragraph('Début')] }),
216
                          new TableCell({ children: [new Paragraph('Fin')] })
217
                        ]
218
                      }),
219
                      ...el.leaves.map(
220
                        leave =>
221
                          new TableRow({
222
                            children: [
223
                              new TableCell({
224
                                children: [
225
                                  new Paragraph(
226
                                    dateUtils.format(leave.startDate, 'dd/MM/Y')
227
                                  )
228
                                ]
229
                              }),
230
                              new TableCell({
231
                                children: [
232
                                  new Paragraph(
233
                                    dateUtils.format(leave.endDate, 'dd/MM/Y')
234
                                  )
235
                                ]
236
                              })
237
                            ]
238
                          })
239
                      )
240
                    ]
241
                  })
242
                ]
243
              })
244
          )
245
        ]
246
      })
247
    );
248
249
    // Add comment
250
    rows.push(
251
      new TableRow({
252
        children: [
253
          new TableCell({
254
            children: [
255
              new Paragraph({
256
                children: [new TextRun({ text: 'Commentaire', bold: true })]
257
              })
258
            ]
259
          }),
260
          ...els.map(_ => new TableCell({ children: [new Paragraph('')] }))
261
        ]
262
      })
263
    );
264
265
    return new Table({
266
      width: {
267
        size: ((1 + els.length) * 100) / (1 + numEmployeeColumns),
268
        type: WidthType.PERCENTAGE
269
      },
270
      rows
271
    });
272
  };
273
274
  // Ensure tables have a reasonable number of columns by splitting
275
  // employees (shown in columns) across multiple tables.
276
  const numEmployeeColumns = 3;
277
  return _chopIntoGroupsOfAtMost(numEmployeeColumns, elements).map(els =>
278
    makeEmployeeTable(els)
279
  );
280
};
281