@silexpert/core 1.1.304 → 1.1.306

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/dist/cjs/api/resources/Closing.d.ts +2 -1
  2. package/dist/cjs/api/resources/Closing.d.ts.map +1 -1
  3. package/dist/cjs/api/resources/Closing.js +3 -0
  4. package/dist/cjs/api/resources/Closing.js.map +1 -1
  5. package/dist/cjs/api/resources/Exercice.d.ts +2 -2
  6. package/dist/cjs/api/resources/Exercice.d.ts.map +1 -1
  7. package/dist/cjs/api/resources/Exercice.js +2 -2
  8. package/dist/cjs/api/resources/Exercice.js.map +1 -1
  9. package/dist/cjs/api/resources/IntermediateManagementRebate.d.ts.map +1 -1
  10. package/dist/cjs/api/resources/IntermediateManagementRebate.js +6 -1
  11. package/dist/cjs/api/resources/IntermediateManagementRebate.js.map +1 -1
  12. package/dist/cjs/api/resources/Payment.d.ts +2 -1
  13. package/dist/cjs/api/resources/Payment.d.ts.map +1 -1
  14. package/dist/cjs/api/resources/Payment.js +3 -0
  15. package/dist/cjs/api/resources/Payment.js.map +1 -1
  16. package/dist/cjs/types/Enum/JobTitle.d.ts +2 -1
  17. package/dist/cjs/types/Enum/JobTitle.d.ts.map +1 -1
  18. package/dist/cjs/types/Enum/JobTitle.js +6 -0
  19. package/dist/cjs/types/Enum/JobTitle.js.map +1 -1
  20. package/dist/cjs/types/Interface/api/authentification/ConnectFromSSO.d.ts +4 -0
  21. package/dist/cjs/types/Interface/api/authentification/ConnectFromSSO.d.ts.map +1 -0
  22. package/dist/cjs/types/Interface/api/authentification/ConnectFromSSO.js +23 -0
  23. package/dist/cjs/types/Interface/api/authentification/ConnectFromSSO.js.map +1 -0
  24. package/dist/cjs/types/Interface/api/exercice/QueryExercice.d.ts +3 -0
  25. package/dist/cjs/types/Interface/api/exercice/QueryExercice.d.ts.map +1 -1
  26. package/dist/cjs/types/Interface/api/exercice/QueryExercice.js +11 -1
  27. package/dist/cjs/types/Interface/api/exercice/QueryExercice.js.map +1 -1
  28. package/dist/cjs/types/Interface/api/exercice/UpdateExercice.d.ts +1 -0
  29. package/dist/cjs/types/Interface/api/exercice/UpdateExercice.d.ts.map +1 -1
  30. package/dist/cjs/types/Interface/api/exercice/UpdateExercice.js +7 -0
  31. package/dist/cjs/types/Interface/api/exercice/UpdateExercice.js.map +1 -1
  32. package/dist/cjs/types/Interface/api/payment/Params.d.ts +4 -0
  33. package/dist/cjs/types/Interface/api/payment/Params.d.ts.map +1 -0
  34. package/dist/cjs/types/Interface/api/payment/Params.js +24 -0
  35. package/dist/cjs/types/Interface/api/payment/Params.js.map +1 -0
  36. package/dist/cjs/types/Interface/api/payment/Util.d.ts +25 -0
  37. package/dist/cjs/types/Interface/api/payment/Util.d.ts.map +1 -0
  38. package/dist/cjs/types/Interface/api/payment/Util.js +3 -0
  39. package/dist/cjs/types/Interface/api/payment/Util.js.map +1 -0
  40. package/dist/cjs/types/Interface/api/society/QueryPaginatedSociety.d.ts +1 -0
  41. package/dist/cjs/types/Interface/api/society/QueryPaginatedSociety.d.ts.map +1 -1
  42. package/dist/cjs/types/Interface/api/society/QueryPaginatedSociety.js +8 -0
  43. package/dist/cjs/types/Interface/api/society/QueryPaginatedSociety.js.map +1 -1
  44. package/dist/cjs/types/Interface/api/user/ParamsGetAllUserWithRoles.js +1 -1
  45. package/dist/cjs/types/Interface/api/user/ParamsGetAllUserWithRoles.js.map +1 -1
  46. package/dist/cjs/types/Interface/models/IExercicePrestation.d.ts +1 -0
  47. package/dist/cjs/types/Interface/models/IExercicePrestation.d.ts.map +1 -1
  48. package/dist/cjs/types/Interface/models/IExercicePrestation.js.map +1 -1
  49. package/dist/cjs/types/Interface/models/ISociety.d.ts +2 -1
  50. package/dist/cjs/types/Interface/models/ISociety.d.ts.map +1 -1
  51. package/dist/cjs/types/Interface/models/ISociety.js.map +1 -1
  52. package/dist/cjs/types/index.d.ts +3 -0
  53. package/dist/cjs/types/index.d.ts.map +1 -1
  54. package/dist/cjs/types/index.js +3 -0
  55. package/dist/cjs/types/index.js.map +1 -1
  56. package/dist/cjs/utils/payment.d.ts +14 -2
  57. package/dist/cjs/utils/payment.d.ts.map +1 -1
  58. package/dist/cjs/utils/payment.js +172 -2
  59. package/dist/cjs/utils/payment.js.map +1 -1
  60. package/dist/cjs/utils/tests/payment.spec.d.ts +2 -0
  61. package/dist/cjs/utils/tests/payment.spec.d.ts.map +1 -0
  62. package/dist/cjs/utils/tests/payment.spec.js +458 -0
  63. package/dist/cjs/utils/tests/payment.spec.js.map +1 -0
  64. package/dist/cjs/utils/vat/utils.d.ts +2 -0
  65. package/dist/cjs/utils/vat/utils.d.ts.map +1 -1
  66. package/dist/cjs/utils/vat/utils.js +19 -1
  67. package/dist/cjs/utils/vat/utils.js.map +1 -1
  68. package/dist/mjs/api/resources/Closing.d.ts +2 -1
  69. package/dist/mjs/api/resources/Closing.d.ts.map +1 -1
  70. package/dist/mjs/api/resources/Closing.js +3 -0
  71. package/dist/mjs/api/resources/Closing.js.map +1 -1
  72. package/dist/mjs/api/resources/Exercice.d.ts +2 -2
  73. package/dist/mjs/api/resources/Exercice.d.ts.map +1 -1
  74. package/dist/mjs/api/resources/Exercice.js +2 -2
  75. package/dist/mjs/api/resources/Exercice.js.map +1 -1
  76. package/dist/mjs/api/resources/IntermediateManagementRebate.d.ts.map +1 -1
  77. package/dist/mjs/api/resources/IntermediateManagementRebate.js +6 -1
  78. package/dist/mjs/api/resources/IntermediateManagementRebate.js.map +1 -1
  79. package/dist/mjs/api/resources/Payment.d.ts +2 -1
  80. package/dist/mjs/api/resources/Payment.d.ts.map +1 -1
  81. package/dist/mjs/api/resources/Payment.js +3 -0
  82. package/dist/mjs/api/resources/Payment.js.map +1 -1
  83. package/dist/mjs/types/Enum/JobTitle.d.ts +2 -1
  84. package/dist/mjs/types/Enum/JobTitle.d.ts.map +1 -1
  85. package/dist/mjs/types/Enum/JobTitle.js +6 -0
  86. package/dist/mjs/types/Enum/JobTitle.js.map +1 -1
  87. package/dist/mjs/types/Interface/api/authentification/ConnectFromSSO.d.ts +4 -0
  88. package/dist/mjs/types/Interface/api/authentification/ConnectFromSSO.d.ts.map +1 -0
  89. package/dist/mjs/types/Interface/api/authentification/ConnectFromSSO.js +20 -0
  90. package/dist/mjs/types/Interface/api/authentification/ConnectFromSSO.js.map +1 -0
  91. package/dist/mjs/types/Interface/api/exercice/QueryExercice.d.ts +3 -0
  92. package/dist/mjs/types/Interface/api/exercice/QueryExercice.d.ts.map +1 -1
  93. package/dist/mjs/types/Interface/api/exercice/QueryExercice.js +10 -0
  94. package/dist/mjs/types/Interface/api/exercice/QueryExercice.js.map +1 -1
  95. package/dist/mjs/types/Interface/api/exercice/UpdateExercice.d.ts +1 -0
  96. package/dist/mjs/types/Interface/api/exercice/UpdateExercice.d.ts.map +1 -1
  97. package/dist/mjs/types/Interface/api/exercice/UpdateExercice.js +8 -0
  98. package/dist/mjs/types/Interface/api/exercice/UpdateExercice.js.map +1 -1
  99. package/dist/mjs/types/Interface/api/payment/Params.d.ts +4 -0
  100. package/dist/mjs/types/Interface/api/payment/Params.d.ts.map +1 -0
  101. package/dist/mjs/types/Interface/api/payment/Params.js +21 -0
  102. package/dist/mjs/types/Interface/api/payment/Params.js.map +1 -0
  103. package/dist/mjs/types/Interface/api/payment/Util.d.ts +25 -0
  104. package/dist/mjs/types/Interface/api/payment/Util.d.ts.map +1 -0
  105. package/dist/mjs/types/Interface/api/payment/Util.js +2 -0
  106. package/dist/mjs/types/Interface/api/payment/Util.js.map +1 -0
  107. package/dist/mjs/types/Interface/api/society/QueryPaginatedSociety.d.ts +1 -0
  108. package/dist/mjs/types/Interface/api/society/QueryPaginatedSociety.d.ts.map +1 -1
  109. package/dist/mjs/types/Interface/api/society/QueryPaginatedSociety.js +8 -0
  110. package/dist/mjs/types/Interface/api/society/QueryPaginatedSociety.js.map +1 -1
  111. package/dist/mjs/types/Interface/api/user/ParamsGetAllUserWithRoles.js +1 -1
  112. package/dist/mjs/types/Interface/api/user/ParamsGetAllUserWithRoles.js.map +1 -1
  113. package/dist/mjs/types/Interface/models/IExercicePrestation.d.ts +1 -0
  114. package/dist/mjs/types/Interface/models/IExercicePrestation.d.ts.map +1 -1
  115. package/dist/mjs/types/Interface/models/IExercicePrestation.js.map +1 -1
  116. package/dist/mjs/types/Interface/models/ISociety.d.ts +2 -1
  117. package/dist/mjs/types/Interface/models/ISociety.d.ts.map +1 -1
  118. package/dist/mjs/types/Interface/models/ISociety.js.map +1 -1
  119. package/dist/mjs/types/index.d.ts +3 -0
  120. package/dist/mjs/types/index.d.ts.map +1 -1
  121. package/dist/mjs/types/index.js +3 -0
  122. package/dist/mjs/types/index.js.map +1 -1
  123. package/dist/mjs/utils/payment.d.ts +14 -2
  124. package/dist/mjs/utils/payment.d.ts.map +1 -1
  125. package/dist/mjs/utils/payment.js +170 -1
  126. package/dist/mjs/utils/payment.js.map +1 -1
  127. package/dist/mjs/utils/tests/payment.spec.d.ts +2 -0
  128. package/dist/mjs/utils/tests/payment.spec.d.ts.map +1 -0
  129. package/dist/mjs/utils/tests/payment.spec.js +449 -0
  130. package/dist/mjs/utils/tests/payment.spec.js.map +1 -0
  131. package/dist/mjs/utils/vat/utils.d.ts +2 -0
  132. package/dist/mjs/utils/vat/utils.d.ts.map +1 -1
  133. package/dist/mjs/utils/vat/utils.js +14 -0
  134. package/dist/mjs/utils/vat/utils.js.map +1 -1
  135. package/package.json +1 -1
  136. package/ts/api/resources/Closing.ts +6 -1
  137. package/ts/api/resources/Exercice.ts +3 -3
  138. package/ts/api/resources/IntermediateManagementRebate.ts +6 -1
  139. package/ts/api/resources/Payment.ts +5 -1
  140. package/ts/types/Enum/JobTitle.ts +6 -0
  141. package/ts/types/Interface/api/authentification/ConnectFromSSO.ts +8 -0
  142. package/ts/types/Interface/api/exercice/QueryExercice.ts +8 -0
  143. package/ts/types/Interface/api/exercice/UpdateExercice.ts +6 -0
  144. package/ts/types/Interface/api/payment/Params.ts +9 -0
  145. package/ts/types/Interface/api/payment/Util.ts +26 -0
  146. package/ts/types/Interface/api/society/QueryPaginatedSociety.ts +6 -0
  147. package/ts/types/Interface/api/user/ParamsGetAllUserWithRoles.ts +1 -1
  148. package/ts/types/Interface/models/IExercicePrestation.ts +1 -0
  149. package/ts/types/Interface/models/ISociety.ts +2 -0
  150. package/ts/types/index.ts +3 -0
  151. package/ts/utils/payment.ts +194 -2
  152. package/ts/utils/tests/payment.spec.ts +466 -0
  153. package/ts/utils/vat/utils.ts +16 -0
@@ -0,0 +1,466 @@
1
+ import * as dayjs from 'dayjs';
2
+ import { IRegulation, Vat, PaymentMethod } from '../../types';
3
+ import MockDate from 'mockdate';
4
+
5
+ import { calculatePaymentsAuto } from '../payment';
6
+ import { PartialExercicePrestationExpress } from '../../types/Interface/api/payment/Util';
7
+
8
+ describe('Payment', () => {
9
+ describe('calculatePayments', () => {
10
+ const idPrestation = 1;
11
+ const idSociety = 1;
12
+ const yearlyAmountNonExpress = 1800;
13
+ const appCost = 14.9;
14
+
15
+ const firstExerciceNonExpresssEndDate = dayjs().add(11, 'month').endOf('month').toDate();
16
+ const prestationList = {
17
+ id: 34,
18
+ name: 'Compta Pro',
19
+ isComptable: true,
20
+ isJuridique: false,
21
+ isSocial: false,
22
+ isAttestation: false,
23
+ inAverageCart: true,
24
+ isBack: false,
25
+ minimumPrice: null,
26
+ price: null,
27
+ isCatalog: false,
28
+ isPlannable: false,
29
+ hidden: false,
30
+ isComplementary: false,
31
+ signaturePosition: '400,54,569,90',
32
+ signaturePage: 7,
33
+ };
34
+ const idVat = Vat.TX_10.id;
35
+ const vatRate = Object.values(Vat).find(v => v.id === idVat)?.value ?? 20;
36
+ const listPartialExercicePrestationExpress:PartialExercicePrestationExpress[] = [
37
+ {
38
+ startDate: dayjs().subtract(2, 'year').startOf('month').toDate(),
39
+ endDate: dayjs().subtract(13, 'month').endOf('month').toDate(),
40
+ countExpressMonthlyPayment: 2,
41
+ totalAmount: 1200,
42
+ },
43
+ {
44
+ startDate: dayjs().subtract(1, 'year').startOf('month').toDate(),
45
+ endDate: dayjs().subtract(1, 'month').endOf('month').toDate(),
46
+ countExpressMonthlyPayment: 2,
47
+ totalAmount: 1800,
48
+ },
49
+ ];
50
+ const listRegulations:IRegulation[] = [{
51
+ amount: 600,
52
+ amountUsed: 0,
53
+ date: new Date(),
54
+ processed: true,
55
+ idPaymentMethod: PaymentMethod.BANK_CARD.id,
56
+ idSociety,
57
+ }, {
58
+ amount: 300,
59
+ amountUsed: 0,
60
+ date: new Date(),
61
+ processed: true,
62
+ idPaymentMethod: PaymentMethod.BANK_CARD.id,
63
+ idSociety,
64
+ }, {
65
+ amount: 300,
66
+ amountUsed: 300,
67
+ date: new Date(),
68
+ processed: true,
69
+ idPaymentMethod: PaymentMethod.BANK_CARD.id,
70
+ idSociety,
71
+ }];
72
+
73
+ // =====================
74
+ // errors handling
75
+ // =====================
76
+ test('should return error if prestationList doesn\'t correspond (!hidden, isCatalog, isComptable)', () => {
77
+ expect.assertions(2);
78
+ try {
79
+ prestationList.hidden = true;
80
+ calculatePaymentsAuto({
81
+ goToClient: dayjs().toDate(),
82
+ firstExerciceNonExpresssEndDate,
83
+ idPrestation,
84
+ prestationList: {
85
+ ...prestationList,
86
+ isCatalog: true,
87
+ },
88
+ idVat,
89
+ listPartialExercicePrestationExpress,
90
+ listRegulations,
91
+ yearlyAmountNonExpress });
92
+ } catch (error) {
93
+ expect(error).toBeInstanceOf(Error);
94
+ // @ts-ignore
95
+ expect(error.message).toStrictEqual('Ce type de prestation ne correspond pas aux conditions attendues.');
96
+ }
97
+ });
98
+ test('should return error if listRegulations is empty', () => {
99
+ expect.assertions(2);
100
+ try {
101
+ calculatePaymentsAuto({
102
+ goToClient: dayjs().toDate(),
103
+ firstExerciceNonExpresssEndDate,
104
+ idPrestation,
105
+ prestationList,
106
+ idVat,
107
+ listPartialExercicePrestationExpress,
108
+ listRegulations: [],
109
+ yearlyAmountNonExpress });
110
+ } catch (error) {
111
+ expect(error).toBeInstanceOf(Error);
112
+ // @ts-ignore
113
+ expect(error.message).toStrictEqual('List of regulations can not be empty');
114
+ }
115
+ });
116
+ test('should listRegulations contain amount and amountUsed', () => {
117
+ expect.assertions(2);
118
+ try {
119
+ calculatePaymentsAuto({
120
+ goToClient: dayjs().toDate(),
121
+ firstExerciceNonExpresssEndDate,
122
+ idPrestation,
123
+ prestationList,
124
+ idVat,
125
+ listPartialExercicePrestationExpress,
126
+ listRegulations: [{ processed: true } as IRegulation],
127
+ yearlyAmountNonExpress });
128
+ } catch (error) {
129
+ expect(error).toBeInstanceOf(Error);
130
+ // @ts-ignore
131
+ expect(error.message).toStrictEqual('Amount and amountUsed are missing for regulations');
132
+ }
133
+ });
134
+
135
+ // =====================
136
+ // logical
137
+ // =====================
138
+ // express
139
+ test('if only one express, monthly payments of actual exo should be in parallel of express monthly payments, smoothed in number of remaining months (mini 3 months) (if not in 3 last month)', async () => {
140
+ const payments = calculatePaymentsAuto({
141
+ goToClient: dayjs().toDate(),
142
+ firstExerciceNonExpresssEndDate,
143
+ idPrestation,
144
+ prestationList,
145
+ idVat,
146
+ listPartialExercicePrestationExpress: [listPartialExercicePrestationExpress[0]],
147
+ listRegulations,
148
+ yearlyAmountNonExpress });
149
+
150
+ const firstExpressDatePayment = payments.filter(p => dayjs(p.closingDate).diff(firstExerciceNonExpresssEndDate) < 0)[0].datePayment;
151
+ const firstNonExpressDatePayment = payments.filter(p => dayjs(p.closingDate).diff(firstExerciceNonExpresssEndDate) === 0)[0].datePayment;
152
+ expect(dayjs(firstNonExpressDatePayment).diff(firstExpressDatePayment, 'month')).toStrictEqual(0);
153
+ });
154
+ test('if more than one express, monthly payments of actual exo should be in a row of express monthly payments and smoothed, smoothed in number of remaining months after express monthly payments (mini 3 months) (if in 3 last month of exercice)', async () => {
155
+ const payments = calculatePaymentsAuto({
156
+ goToClient: dayjs().toDate(),
157
+ firstExerciceNonExpresssEndDate,
158
+ idPrestation,
159
+ prestationList,
160
+ idVat,
161
+ listPartialExercicePrestationExpress,
162
+ listRegulations,
163
+ yearlyAmountNonExpress });
164
+
165
+ const lastExpressDatePayment = payments.filter(p => dayjs(p.closingDate).diff(firstExerciceNonExpresssEndDate) < 0).splice(-1)?.[0]?.datePayment;
166
+ const firstNonExpressDatePayment = payments.filter(p => dayjs(p.closingDate).diff(firstExerciceNonExpresssEndDate) === 0)?.[0]?.datePayment;
167
+ expect(dayjs(firstNonExpressDatePayment).diff(lastExpressDatePayment, 'month')).toStrictEqual(1);
168
+ });
169
+ // non express
170
+ test('should return a number of payments equal to nb of remaining months in exercice (if not in 3 last months)', async () => {
171
+ const mockedDate = dayjs('2024-05-15').toDate();
172
+ MockDate.set(mockedDate);
173
+
174
+ const nbMonthDelay = 6;
175
+ const newFirstExerciceNonExpresssEndDate = dayjs().add(nbMonthDelay, 'month').endOf('month').toDate();
176
+
177
+ const payments = calculatePaymentsAuto({
178
+ goToClient: dayjs().toDate(),
179
+ firstExerciceNonExpresssEndDate: newFirstExerciceNonExpresssEndDate,
180
+ idPrestation,
181
+ prestationList,
182
+ idVat,
183
+ listPartialExercicePrestationExpress: [],
184
+ listRegulations: [{
185
+ amount: 50,
186
+ amountUsed: 0,
187
+ } as IRegulation],
188
+ yearlyAmountNonExpress });
189
+
190
+ // const listNonExpressPayments = payments.filter(p => p.closingDate === newFirstExerciceNonExpresssEndDate);
191
+ const listNonExpressPayments = payments.filter(p => dayjs(p.closingDate).diff(newFirstExerciceNonExpresssEndDate) === 0);
192
+ expect(listNonExpressPayments.length).toStrictEqual(nbMonthDelay);
193
+
194
+ MockDate.reset();
195
+ });
196
+ test('should return a number of payments equal to 3 (if in 3 last month of exercice)', async () => {
197
+ const nbMonthDelay = 1;
198
+ const newFirstExerciceNonExpresssEndDate = dayjs().add(nbMonthDelay, 'month').endOf('month').toDate();
199
+
200
+ const payments = calculatePaymentsAuto({
201
+ goToClient: dayjs().toDate(),
202
+ firstExerciceNonExpresssEndDate: newFirstExerciceNonExpresssEndDate,
203
+ idPrestation,
204
+ prestationList,
205
+ idVat,
206
+ listPartialExercicePrestationExpress: [],
207
+ listRegulations: [{
208
+ amount: 50,
209
+ amountUsed: 0,
210
+ } as IRegulation],
211
+ yearlyAmountNonExpress });
212
+
213
+ const listNonExpressPayments = payments.filter(p => dayjs(p.closingDate).diff(newFirstExerciceNonExpresssEndDate) === 0);
214
+ expect(listNonExpressPayments.length).toStrictEqual(3);
215
+ });
216
+ test('should (yearlyAmountNonExpress - sum of listRegulations) / count of monthly payments be equal to monthly payment amount (if not in 3 last month)', async () => {
217
+ const mockedDate = dayjs('2024-05-15').toDate();
218
+ MockDate.set(mockedDate);
219
+
220
+ const nbMonthDelay = 6;
221
+ const newFirstExerciceNonExpresssEndDate = dayjs().add(nbMonthDelay, 'month').endOf('month').toDate();
222
+ const newListRegulations = [{
223
+ amount: 50,
224
+ amountUsed: 0,
225
+ } as IRegulation];
226
+ const payments = calculatePaymentsAuto({
227
+ goToClient: dayjs().toDate(),
228
+ firstExerciceNonExpresssEndDate: newFirstExerciceNonExpresssEndDate,
229
+ idPrestation,
230
+ prestationList,
231
+ idVat,
232
+ listPartialExercicePrestationExpress: [],
233
+ listRegulations: newListRegulations,
234
+ yearlyAmountNonExpress });
235
+
236
+ const diff = yearlyAmountNonExpress - newListRegulations.reduce((acc, r:IRegulation) => acc + r.amount - r.amountUsed, 0);
237
+ const amount = Math.round(100 * diff / nbMonthDelay) / 100;
238
+
239
+ expect(payments[0].amount).toStrictEqual(Math.round(100 * amount * ((100 + vatRate) / 100)) / 100);
240
+
241
+ MockDate.reset();
242
+ });
243
+ test('should (totalAmount - firstPaymentAmount) / 3 be equal to monthly payment amount (if in 3 last month of exercice)', async () => {
244
+ const nbMonthDelay = 1;
245
+ const newFirstExerciceNonExpresssEndDate = dayjs().add(nbMonthDelay, 'month').endOf('month').toDate();
246
+ const newListRegulations = [{
247
+ amount: 50,
248
+ amountUsed: 0,
249
+ } as IRegulation];
250
+ const payments = calculatePaymentsAuto({
251
+ goToClient: dayjs().toDate(),
252
+ firstExerciceNonExpresssEndDate: newFirstExerciceNonExpresssEndDate,
253
+ idPrestation,
254
+ prestationList,
255
+ idVat,
256
+ listPartialExercicePrestationExpress: [],
257
+ listRegulations: newListRegulations,
258
+ yearlyAmountNonExpress });
259
+
260
+ const diff = yearlyAmountNonExpress - newListRegulations.reduce((acc, r:IRegulation) => acc + r.amount - r.amountUsed, 0);
261
+ const amount = Math.round(100 * diff / 3) / 100;
262
+
263
+ expect(payments[0].amount).toStrictEqual(Math.round(100 * amount * ((100 + vatRate) / 100)) / 100);
264
+ });
265
+ // annual price increase
266
+ test('should apply annual price increase (2,75%) if signed before last 3 months of civil year', async () => {
267
+ const mockedDate = dayjs('2024-08-15').toDate();
268
+ MockDate.set(mockedDate);
269
+
270
+ const newFirstExerciceNonExpresssEndDate = dayjs().add(11, 'month').endOf('month').toDate();
271
+
272
+ const payments = calculatePaymentsAuto({
273
+ goToClient: dayjs().toDate(),
274
+ firstExerciceNonExpresssEndDate: newFirstExerciceNonExpresssEndDate,
275
+ idPrestation,
276
+ prestationList,
277
+ idVat,
278
+ listPartialExercicePrestationExpress: [],
279
+ listRegulations: [{
280
+ amount: 50,
281
+ amountUsed: 0,
282
+ } as IRegulation],
283
+ yearlyAmountNonExpress });
284
+
285
+ const listNonExpressPayments = payments.filter(p => dayjs(p.closingDate).diff(newFirstExerciceNonExpresssEndDate) === 0);
286
+ expect(Math.round(100 * listNonExpressPayments[0].amount * 1.0275) / 100).toStrictEqual(listNonExpressPayments.splice(-2)[0].amount);
287
+
288
+ MockDate.reset();
289
+ });
290
+ test('should not apply annual price increase (2,75%) if signed in last 3 months of civil year', async () => {
291
+ const mockedDate = dayjs('2024-11-15').toDate();
292
+ MockDate.set(mockedDate);
293
+
294
+ const newFirstExerciceNonExpresssEndDate = dayjs().add(11, 'month').endOf('month').toDate();
295
+
296
+ const payments = calculatePaymentsAuto({
297
+ goToClient: dayjs().toDate(),
298
+ firstExerciceNonExpresssEndDate: newFirstExerciceNonExpresssEndDate,
299
+ idPrestation,
300
+ prestationList,
301
+ idVat,
302
+ listPartialExercicePrestationExpress: [],
303
+ listRegulations: [{
304
+ amount: 50,
305
+ amountUsed: 0,
306
+ } as IRegulation],
307
+ yearlyAmountNonExpress });
308
+
309
+ const listNonExpressPayments = payments.filter(p => dayjs(p.datePayment).diff(dayjs('2025-06-01')) === 0);
310
+ expect(listNonExpressPayments[0].amount).toStrictEqual(listNonExpressPayments.splice(-2)[0].amount);
311
+
312
+ MockDate.reset();
313
+ });
314
+
315
+ test('should apply 14,9€ HT increase for the second exercice created (only offered on first exo) (no rules for 3 last months)', async () => {
316
+ const mockedDate = dayjs('2024-06-15').toDate();
317
+ MockDate.set(mockedDate);
318
+
319
+ const newFirstExerciceNonExpresssEndDate = dayjs().add(12, 'month').endOf('month').toDate();
320
+
321
+ const payments = calculatePaymentsAuto({
322
+ goToClient: dayjs().toDate(),
323
+ firstExerciceNonExpresssEndDate: newFirstExerciceNonExpresssEndDate,
324
+ idPrestation,
325
+ prestationList,
326
+ idVat,
327
+ listPartialExercicePrestationExpress: [],
328
+ listRegulations: [{
329
+ amount: 50,
330
+ amountUsed: 50,
331
+ } as IRegulation],
332
+ yearlyAmountNonExpress });
333
+
334
+ const paymentsOfFirstExo = payments.filter(p => dayjs(p.datePayment).diff(dayjs('2025-06-01')) === 0).splice(-1)[0].amount ?? 0;
335
+ const paymentsOfSecondExo = payments.filter(p => dayjs(p.datePayment).diff(dayjs('2025-07-01')) === 0).splice(-1)[0].amount ?? 0;
336
+ expect(paymentsOfFirstExo + appCost * ((100 + vatRate) / 100)).toStrictEqual(paymentsOfSecondExo);
337
+
338
+ MockDate.reset();
339
+ });
340
+
341
+ // misc
342
+ test('should payments been generated sorted by exercices start date', async () => {
343
+ const listPartialExercicePrestationExpressCloned = [...listPartialExercicePrestationExpress];
344
+ listPartialExercicePrestationExpressCloned[0] = {
345
+ ...listPartialExercicePrestationExpress[0],
346
+ startDate: dayjs().add(1, 'year').startOf('month').toDate(),
347
+ };
348
+ listPartialExercicePrestationExpressCloned[1] = {
349
+ ...listPartialExercicePrestationExpress[1],
350
+ startDate: dayjs().startOf('month').toDate(),
351
+ };
352
+ const payments = calculatePaymentsAuto({
353
+ goToClient: dayjs().toDate(),
354
+ firstExerciceNonExpresssEndDate,
355
+ idPrestation,
356
+ prestationList,
357
+ idVat,
358
+ listPartialExercicePrestationExpress: listPartialExercicePrestationExpressCloned,
359
+ listRegulations,
360
+ yearlyAmountNonExpress });
361
+ expect(payments.length).toBeGreaterThanOrEqual(4);
362
+ expect(dayjs(payments[payments.length - 1].datePayment).diff(dayjs(payments?.[0]?.datePayment), 'month')).toBeGreaterThan(0);
363
+ });
364
+ test('if regulations are bigger than sum exercice prestation amounts, should pass some exercice payments', async () => {
365
+ const mockedDate = dayjs('2024-03-15').toDate();
366
+ MockDate.set(mockedDate);
367
+
368
+ const payments = calculatePaymentsAuto({
369
+ goToClient: dayjs().toDate(),
370
+ firstExerciceNonExpresssEndDate,
371
+ idPrestation,
372
+ prestationList,
373
+ idVat,
374
+ listPartialExercicePrestationExpress,
375
+ listRegulations: [{
376
+ amount: 2000,
377
+ amountUsed: 0,
378
+ date: new Date(),
379
+ processed: true,
380
+ idPaymentMethod: PaymentMethod.BANK_CARD.id,
381
+ idSociety,
382
+ }],
383
+ yearlyAmountNonExpress });
384
+ expect(payments.length).toBeGreaterThanOrEqual(2);
385
+ expect(payments[0]).toMatchObject(
386
+ {
387
+ amount: 550,
388
+ datePayment: dayjs(mockedDate).add(1, 'month').startOf('month').toDate(),
389
+ idPrestation: 1,
390
+ idPaymentMethod: 1,
391
+ });
392
+ expect(payments[1]).toMatchObject(
393
+ {
394
+ amount: 550,
395
+ datePayment: dayjs(mockedDate).add(2, 'month').startOf('month').toDate(),
396
+ idPrestation: 1,
397
+ idPaymentMethod: 1,
398
+ });
399
+ MockDate.reset();
400
+ });
401
+ test('should not consider actual month if signed in last 3 days of month', async () => {
402
+ const mockedDate = dayjs('2024-03-29').toDate();
403
+ MockDate.set(mockedDate);
404
+
405
+ const payments = calculatePaymentsAuto({
406
+ goToClient: dayjs().toDate(),
407
+ firstExerciceNonExpresssEndDate,
408
+ idPrestation,
409
+ prestationList,
410
+ idVat,
411
+ listPartialExercicePrestationExpress,
412
+ listRegulations,
413
+ yearlyAmountNonExpress });
414
+ expect(payments.length).toBeGreaterThanOrEqual(4);
415
+ expect(payments?.[0]?.datePayment).toStrictEqual(dayjs(mockedDate).add(2, 'month').startOf('month').toDate());
416
+ MockDate.reset();
417
+ });
418
+ test('last payment returned should be last of a year (12/XX)', async () => {
419
+ const mockedDate = dayjs('2024-03-29').toDate();
420
+ MockDate.set(mockedDate);
421
+
422
+ const payments = calculatePaymentsAuto({
423
+ goToClient: dayjs().toDate(),
424
+ firstExerciceNonExpresssEndDate,
425
+ idPrestation,
426
+ prestationList,
427
+ idVat,
428
+ listPartialExercicePrestationExpress,
429
+ listRegulations,
430
+ yearlyAmountNonExpress });
431
+ expect(payments.length).toBeGreaterThanOrEqual(4);
432
+ expect(dayjs(payments[payments.length - 1].datePayment).month()).toStrictEqual(11);
433
+ });
434
+ test('first payment and last payment should have at least one year difference (ex. : 23 - 24)', async () => {
435
+ const payments = calculatePaymentsAuto({
436
+ goToClient: dayjs().toDate(),
437
+ firstExerciceNonExpresssEndDate,
438
+ idPrestation,
439
+ prestationList,
440
+ idVat,
441
+ listPartialExercicePrestationExpress: [],
442
+ listRegulations,
443
+ yearlyAmountNonExpress });
444
+ expect(dayjs(payments.slice(-1)?.[0]?.datePayment).diff(dayjs(payments?.[0]?.datePayment), 'year', true)).toBeGreaterThan(1);
445
+ });
446
+ test('real case : idSociety 114440', async () => {
447
+ const payments = calculatePaymentsAuto({
448
+ goToClient: dayjs().toDate(),
449
+ firstExerciceNonExpresssEndDate: dayjs('2024-12-31').toDate(),
450
+ idPrestation,
451
+ prestationList,
452
+ idVat: Vat.TX_20.id,
453
+ listPartialExercicePrestationExpress: [],
454
+ listRegulations: [{
455
+ amount: 1006.8,
456
+ amountUsed: 0,
457
+ date: dayjs('2024-06-06').toDate(),
458
+ processed: false,
459
+ idPaymentMethod: PaymentMethod.BANK_CARD.id,
460
+ idSociety,
461
+ }],
462
+ yearlyAmountNonExpress: 1433 });
463
+ expect(dayjs(payments.slice(-1)?.[0]?.datePayment).diff(dayjs(payments?.[0]?.datePayment), 'year', true)).toBeGreaterThan(1);
464
+ });
465
+ });
466
+ });
@@ -58,4 +58,20 @@ export function textFormatting(value: any, length: number | null = null): string
58
58
  return formattedValue;
59
59
  }
60
60
  return null;
61
+ }
62
+
63
+ export function getTaxesIncludedAmount(amountHt: number, idVat: number, isRounded = true) {
64
+ const vatRate = Object.values(Vat).find(v => v.id === idVat)?.value ?? 20;
65
+ const amountNotRounded = amountHt * ((100 + vatRate) / 100);
66
+
67
+ if (!isRounded) return amountNotRounded;
68
+ return Math.round(amountNotRounded * 100) / 100 ?? 0;
69
+ }
70
+
71
+ export function getTaxesExcludedAmount(amountTtc: number, idVat: number, isRounded = true) {
72
+ const vatRate = Object.values(Vat).find(v => v.id === idVat)?.value ?? 20;
73
+ const amountNotRounded = amountTtc / ((100 + vatRate) / 100);
74
+
75
+ if (!isRounded) return amountNotRounded;
76
+ return Math.round(amountNotRounded * 100) / 100 ?? 0;
61
77
  }