@silexpert/core 2.0.108 → 2.0.110
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.
- package/dist/api/resources/FinancialProjection.d.ts +8 -0
- package/dist/api/resources/FinancialProjection.d.ts.map +1 -0
- package/dist/api/resources/FinancialProjection.js +14 -0
- package/dist/api/resources/FinancialProjection.js.map +1 -0
- package/dist/api/resources/Mandat.d.ts +2 -1
- package/dist/api/resources/Mandat.d.ts.map +1 -1
- package/dist/api/resources/Mandat.js +2 -2
- package/dist/api/resources/Mandat.js.map +1 -1
- package/dist/api/resources.d.ts +2 -0
- package/dist/api/resources.d.ts.map +1 -1
- package/dist/api/resources.js +2 -0
- package/dist/api/resources.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/types/Enum/SocietyApproval.js +2 -2
- package/dist/types/Enum/SocietyApproval.js.map +1 -1
- package/dist/types/Interface/api/acd/DeduplicateAccounts.d.ts +5 -0
- package/dist/types/Interface/api/acd/DeduplicateAccounts.d.ts.map +1 -0
- package/dist/types/Interface/api/acd/DeduplicateAccounts.js +34 -0
- package/dist/types/Interface/api/acd/DeduplicateAccounts.js.map +1 -0
- package/dist/types/Interface/api/acd/Vat.d.ts +6 -0
- package/dist/types/Interface/api/acd/Vat.d.ts.map +1 -1
- package/dist/types/Interface/api/acd/Vat.js.map +1 -1
- package/dist/types/Interface/api/commercialManagement/Create.d.ts.map +1 -1
- package/dist/types/Interface/api/commercialManagement/Create.js +3 -1
- package/dist/types/Interface/api/commercialManagement/Create.js.map +1 -1
- package/dist/types/Interface/api/kyc/Create.d.ts +4 -0
- package/dist/types/Interface/api/kyc/Create.d.ts.map +1 -1
- package/dist/types/Interface/api/kyc/Create.js +16 -0
- package/dist/types/Interface/api/kyc/Create.js.map +1 -1
- package/dist/types/Interface/models/ISocietyFinancialProjection.d.ts +12 -0
- package/dist/types/Interface/models/ISocietyFinancialProjection.d.ts.map +1 -0
- package/dist/types/Interface/models/ISocietyFinancialProjection.js +13 -0
- package/dist/types/Interface/models/ISocietyFinancialProjection.js.map +1 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/utils/acdVat.d.ts +3 -3
- package/dist/utils/acdVat.d.ts.map +1 -1
- package/dist/utils/acdVat.js +55 -3
- package/dist/utils/acdVat.js.map +1 -1
- package/dist/utils/commercialManagement/document.d.ts.map +1 -1
- package/dist/utils/commercialManagement/document.js +13 -0
- package/dist/utils/commercialManagement/document.js.map +1 -1
- package/dist/utils/tests/acdVat.spec.js +33 -0
- package/dist/utils/tests/acdVat.spec.js.map +1 -1
- package/dist/utils/tests/document.spec.d.ts +2 -0
- package/dist/utils/tests/document.spec.d.ts.map +1 -0
- package/dist/utils/tests/document.spec.js +71 -0
- package/dist/utils/tests/document.spec.js.map +1 -0
- package/package.json +1 -1
- package/ts/api/resources/FinancialProjection.ts +18 -0
- package/ts/api/resources/Mandat.ts +3 -2
- package/ts/api/resources.ts +2 -0
- package/ts/index.ts +1 -0
- package/ts/types/Enum/SocietyApproval.ts +2 -2
- package/ts/types/Interface/api/acd/DeduplicateAccounts.ts +29 -0
- package/ts/types/Interface/api/acd/Vat.ts +7 -0
- package/ts/types/Interface/api/commercialManagement/Create.ts +3 -1
- package/ts/types/Interface/api/kyc/Create.ts +12 -0
- package/ts/types/Interface/models/ISocietyFinancialProjection.ts +12 -0
- package/ts/types/index.ts +2 -0
- package/ts/utils/acdVat.ts +82 -5
- package/ts/utils/commercialManagement/document.ts +13 -0
- package/ts/utils/tests/acdVat.spec.ts +41 -0
- package/ts/utils/tests/document.spec.ts +75 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// societyFinancialProjections
|
|
2
|
+
export class ISocietyFinancialProjection {
|
|
3
|
+
id?: number;
|
|
4
|
+
monthYear: string; // MM-YYYY
|
|
5
|
+
revenuesAmount: number | null;
|
|
6
|
+
expensesAmount: number | null;
|
|
7
|
+
details: string | null; // JSON string containing categories
|
|
8
|
+
isProjected: boolean;
|
|
9
|
+
idSociety: number;
|
|
10
|
+
createdAt?: Date | null;
|
|
11
|
+
updatedAt?: Date | null;
|
|
12
|
+
}
|
package/ts/types/index.ts
CHANGED
|
@@ -146,6 +146,7 @@ export * from './Interface/models/ISocietyProceding.js';
|
|
|
146
146
|
export * from './Interface/models/ISocietySocial.js';
|
|
147
147
|
export * from './Interface/models/ISocietySoftware.js';
|
|
148
148
|
export * from './Interface/models/ISocietyStat.js';
|
|
149
|
+
export * from './Interface/models/ISocietyFinancialProjection.js';
|
|
149
150
|
export * from './Interface/models/ISocietyVehicle.js';
|
|
150
151
|
export * from './Interface/models/ISoftwareCompta.js';
|
|
151
152
|
export * from './Interface/models/ISource.js';
|
|
@@ -544,6 +545,7 @@ export * from './Enum/Kyc.js';
|
|
|
544
545
|
export * from './Interface/api/accountingTransaction/Create.js';
|
|
545
546
|
export * from './Interface/api/acd/Vat.js';
|
|
546
547
|
export * from './Interface/api/acd/Create.js';
|
|
548
|
+
export * from './Interface/api/acd/DeduplicateAccounts.js';
|
|
547
549
|
export * from './Interface/api/authentification/AuthenticationResponse.js';
|
|
548
550
|
export * from './Interface/api/exercice/ReadExercice.js';
|
|
549
551
|
export * from './Interface/api/authentification/AuthenticationResponse.js';
|
package/ts/utils/acdVat.ts
CHANGED
|
@@ -1,17 +1,49 @@
|
|
|
1
|
-
import { Vat } from '../types/Enum/Vat.js';
|
|
2
|
-
import { AcdVatCode } from '../types/Interface/api/acd/Vat.js';
|
|
1
|
+
import { Vat, type VatType } from '../types/Enum/Vat.js';
|
|
2
|
+
import { AcdVatCode, type AcdVatAccount } from '../types/Interface/api/acd/Vat.js';
|
|
3
3
|
|
|
4
|
-
export function idVatToAcdVatCode(
|
|
4
|
+
export function idVatToAcdVatCode(
|
|
5
|
+
idVat?: number | null,
|
|
6
|
+
acdVatAccounts?: Array<Pick<AcdVatAccount, 'code' | 'rate'>> | null,
|
|
7
|
+
): AcdVatCode | string | null {
|
|
5
8
|
if (!idVat) {
|
|
6
9
|
return null;
|
|
7
10
|
}
|
|
8
11
|
|
|
9
|
-
const
|
|
12
|
+
const vatList = Object.values(Vat) as VatType[];
|
|
13
|
+
const vat = vatList.find((currentVat) => currentVat.id === idVat);
|
|
10
14
|
|
|
11
15
|
if (!vat) {
|
|
12
16
|
return AcdVatCode.NON_AFFECTE;
|
|
13
17
|
}
|
|
14
18
|
|
|
19
|
+
if (acdVatAccounts && acdVatAccounts.length > 0) {
|
|
20
|
+
if (vat.isVatIntracom) {
|
|
21
|
+
const intracomEntry = acdVatAccounts.find((vatAccount) => vatAccount.code === AcdVatCode.INTRACOM);
|
|
22
|
+
if (intracomEntry) {
|
|
23
|
+
return intracomEntry.code as AcdVatCode;
|
|
24
|
+
}
|
|
25
|
+
return AcdVatCode.INTRACOM;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (vat.isVatImportation) {
|
|
29
|
+
const autolEntry = acdVatAccounts.find((vatAccount) => vatAccount.code === AcdVatCode.AUTOL);
|
|
30
|
+
if (autolEntry) {
|
|
31
|
+
return autolEntry.code as AcdVatCode;
|
|
32
|
+
}
|
|
33
|
+
return AcdVatCode.AUTOL;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const matchingRateEntry = acdVatAccounts.find(
|
|
37
|
+
(vatAccount) =>
|
|
38
|
+
vatAccount.rate === vat.value &&
|
|
39
|
+
![AcdVatCode.INTRACOM, AcdVatCode.AUTOL, AcdVatCode.NON_AFFECTE].includes(vatAccount.code as AcdVatCode),
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
if (matchingRateEntry) {
|
|
43
|
+
return matchingRateEntry.code as AcdVatCode;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
15
47
|
if (vat.isVatIntracom) {
|
|
16
48
|
return AcdVatCode.INTRACOM;
|
|
17
49
|
}
|
|
@@ -36,7 +68,51 @@ export function idVatToAcdVatCode(idVat?: number | null): AcdVatCode | null {
|
|
|
36
68
|
}
|
|
37
69
|
}
|
|
38
70
|
|
|
39
|
-
export function acdVatCodeToIdVat(
|
|
71
|
+
export function acdVatCodeToIdVat(
|
|
72
|
+
acdVatCode?: AcdVatCode | string | null,
|
|
73
|
+
acdVatAccounts?: Array<Pick<AcdVatAccount, 'code' | 'rate'>> | null,
|
|
74
|
+
): number | null {
|
|
75
|
+
if (!acdVatCode) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const vatList = Object.values(Vat) as VatType[];
|
|
80
|
+
|
|
81
|
+
if (acdVatAccounts && acdVatAccounts.length > 0) {
|
|
82
|
+
const matchingVat = acdVatAccounts.find((vatAccount) => vatAccount.code === acdVatCode);
|
|
83
|
+
|
|
84
|
+
if (matchingVat && typeof matchingVat.rate === 'number') {
|
|
85
|
+
const rate = matchingVat.rate;
|
|
86
|
+
|
|
87
|
+
if (acdVatCode === AcdVatCode.INTRACOM) {
|
|
88
|
+
const intracomVat = vatList.find((v) => v.isVatIntracom && v.value === rate);
|
|
89
|
+
return intracomVat ? Number(intracomVat.id) : Number(Vat.INTRACOM_WITHOUT_TAXE.id);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (acdVatCode === AcdVatCode.AUTOL) {
|
|
93
|
+
const autolVat = vatList.find((v) => v.isVatImportation && v.value === rate);
|
|
94
|
+
return autolVat ? Number(autolVat.id) : Number(Vat.IMPORTATION_WITHOUT_TAXE.id);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (acdVatCode === AcdVatCode.NON_AFFECTE) {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const standardVat = vatList.find(
|
|
102
|
+
(v) =>
|
|
103
|
+
!v.isVatIntracom &&
|
|
104
|
+
!v.isVatImportation &&
|
|
105
|
+
v.id !== Vat.MARGE.id &&
|
|
106
|
+
v.id !== Vat.MANUAL.id &&
|
|
107
|
+
v.value === rate,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
if (standardVat) {
|
|
111
|
+
return Number(standardVat.id);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
40
116
|
switch (acdVatCode) {
|
|
41
117
|
case AcdVatCode.NORMAL:
|
|
42
118
|
return Vat.TX_20.id;
|
|
@@ -58,6 +134,7 @@ export function acdVatCodeToIdVat(acdVatCode?: AcdVatCode | null): number | null
|
|
|
58
134
|
case AcdVatCode.SPECIAL:
|
|
59
135
|
case null:
|
|
60
136
|
case undefined:
|
|
137
|
+
default:
|
|
61
138
|
return null;
|
|
62
139
|
}
|
|
63
140
|
}
|
|
@@ -89,6 +89,19 @@ export function generateCommercialDocument(
|
|
|
89
89
|
paymentLink: payload.paymentLink ?? '#',
|
|
90
90
|
idEstimateInvoiceType,
|
|
91
91
|
title: payload.title ?? null,
|
|
92
|
+
// EJS `with` blocks throw a ReferenceError when a variable key is entirely absent from the data object, so seller
|
|
93
|
+
// identity/address fields must always be defined even when the payload omits them.
|
|
94
|
+
name: payload.name ?? null,
|
|
95
|
+
number: payload.number ?? null,
|
|
96
|
+
street: payload.street ?? null,
|
|
97
|
+
zipCode: payload.zipCode ?? null,
|
|
98
|
+
city: payload.city ?? null,
|
|
99
|
+
legalForm: payload.legalForm ?? null,
|
|
100
|
+
capital: payload.capital ?? null,
|
|
101
|
+
siren: payload.siren ?? null,
|
|
102
|
+
rcs: payload.rcs ?? null,
|
|
103
|
+
vatCode: payload.vatCode ?? null,
|
|
104
|
+
nafCode: payload.nafCode ?? null,
|
|
92
105
|
penalties: payload.penalties
|
|
93
106
|
? (internationalizationConsistency(locale, payload.penalties, 'penalties') ?? payload.penalties)
|
|
94
107
|
: null,
|
|
@@ -23,4 +23,45 @@ describe('acdVat utils', () => {
|
|
|
23
23
|
expect(acdVatCodeToIdVat(AcdVatCode.AUTOL)).toBe(Vat.IMPORTATION_WITHOUT_TAXE.id);
|
|
24
24
|
expect(acdVatCodeToIdVat(AcdVatCode.NON_AFFECTE)).toBeNull();
|
|
25
25
|
});
|
|
26
|
+
|
|
27
|
+
test('should map ACD VAT codes with custom society VAT rates (e.g. DOM-TOM)', () => {
|
|
28
|
+
const domTomVatAccounts = [
|
|
29
|
+
{ code: 'NON_AFFECTE', rate: 0 },
|
|
30
|
+
{ code: 'TN', rate: 8.5 },
|
|
31
|
+
{ code: 'TR1', rate: 2.1 },
|
|
32
|
+
];
|
|
33
|
+
|
|
34
|
+
expect(acdVatCodeToIdVat('TN', domTomVatAccounts)).toBe(Vat.TX_8_5.id);
|
|
35
|
+
expect(acdVatCodeToIdVat('TR1', domTomVatAccounts)).toBe(Vat.TX_2_1.id);
|
|
36
|
+
expect(acdVatCodeToIdVat('NON_AFFECTE', domTomVatAccounts)).toBeNull();
|
|
37
|
+
|
|
38
|
+
expect(idVatToAcdVatCode(Vat.TX_8_5.id, domTomVatAccounts)).toBe('TN');
|
|
39
|
+
expect(idVatToAcdVatCode(Vat.TX_2_1.id, domTomVatAccounts)).toBe('TR1');
|
|
40
|
+
expect(idVatToAcdVatCode(Vat.WITHOUT_TAXE.id, domTomVatAccounts)).toBeNull();
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('should correctly handle 0% VAT codes (e.g. exoneration, intracom 0%, importation 0%)', () => {
|
|
44
|
+
const vatAccountsWithZeroRates = [
|
|
45
|
+
{ code: 'NON_AFFECTE', rate: 0 },
|
|
46
|
+
{ code: 'EXO', rate: 0 },
|
|
47
|
+
{ code: 'INTRACOM', rate: 0 },
|
|
48
|
+
{ code: 'AUTOL', rate: 0 },
|
|
49
|
+
{ code: 'TN', rate: 20 },
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// Non affecté returns null
|
|
53
|
+
expect(acdVatCodeToIdVat('NON_AFFECTE', vatAccountsWithZeroRates)).toBeNull();
|
|
54
|
+
|
|
55
|
+
// Custom 0% code (e.g. EXO) maps to WITHOUT_TAXE
|
|
56
|
+
expect(acdVatCodeToIdVat('EXO', vatAccountsWithZeroRates)).toBe(Vat.WITHOUT_TAXE.id);
|
|
57
|
+
expect(idVatToAcdVatCode(Vat.WITHOUT_TAXE.id, vatAccountsWithZeroRates)).toBe('EXO');
|
|
58
|
+
|
|
59
|
+
// Intracom at 0%
|
|
60
|
+
expect(acdVatCodeToIdVat('INTRACOM', vatAccountsWithZeroRates)).toBe(Vat.INTRACOM_WITHOUT_TAXE.id);
|
|
61
|
+
expect(idVatToAcdVatCode(Vat.INTRACOM_WITHOUT_TAXE.id, vatAccountsWithZeroRates)).toBe(AcdVatCode.INTRACOM);
|
|
62
|
+
|
|
63
|
+
// Importation at 0%
|
|
64
|
+
expect(acdVatCodeToIdVat('AUTOL', vatAccountsWithZeroRates)).toBe(Vat.IMPORTATION_WITHOUT_TAXE.id);
|
|
65
|
+
expect(idVatToAcdVatCode(Vat.IMPORTATION_WITHOUT_TAXE.id, vatAccountsWithZeroRates)).toBe(AcdVatCode.AUTOL);
|
|
66
|
+
});
|
|
26
67
|
});
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { describe, expect, test } from 'vitest';
|
|
2
|
+
import { generateCommercialDocument } from '../commercialManagement/document.js';
|
|
3
|
+
import { EstimateInvoiceType } from '../../types/Enum/EstimateInvoice.js';
|
|
4
|
+
import { CreateInvoiceDocument } from '../../types/Interface/api/commercialManagement/Create.js';
|
|
5
|
+
|
|
6
|
+
// Payload reproducing a real-world case where the seller address fields (`name`, `street`,
|
|
7
|
+
// `number`, `zipCode`, `city`) are entirely absent from the object, not just `null`.
|
|
8
|
+
// Since EJS `with` blocks throw a ReferenceError for keys missing from the data object,
|
|
9
|
+
// this used to crash `generateCommercialDocument` instead of falling back gracefully.
|
|
10
|
+
const payloadWithoutSellerAddress = {
|
|
11
|
+
penalties: '3 fois le taux légal',
|
|
12
|
+
discount: 'Aucun escompte en cas de paiement anticipé',
|
|
13
|
+
accountingCategory: 2,
|
|
14
|
+
recoveryCost: '40€',
|
|
15
|
+
vatMention: 'TVA: Non concerné',
|
|
16
|
+
expirationDate: null,
|
|
17
|
+
shouldPayTaxAfterDebit: false,
|
|
18
|
+
isStripeAllowed: false,
|
|
19
|
+
vatCode: null,
|
|
20
|
+
paymentType: '',
|
|
21
|
+
reference: '202609/94',
|
|
22
|
+
capital: 0,
|
|
23
|
+
isDraft: false,
|
|
24
|
+
cgv: '<p><br></p>',
|
|
25
|
+
logoUrl: null,
|
|
26
|
+
disableVat: false,
|
|
27
|
+
nafCode: null,
|
|
28
|
+
legalForm: null,
|
|
29
|
+
siren: null,
|
|
30
|
+
products: [
|
|
31
|
+
{
|
|
32
|
+
id: 16979,
|
|
33
|
+
idVat: 4,
|
|
34
|
+
unit: 2,
|
|
35
|
+
itemSaleName: "main d'oeuvre extèrieur",
|
|
36
|
+
priceTaxExcluded: 7000,
|
|
37
|
+
quantity: 1,
|
|
38
|
+
discountType: 1,
|
|
39
|
+
discount2: 0,
|
|
40
|
+
comment: 'Déblocage NORIA, dépose NORIA.',
|
|
41
|
+
reorder: 1,
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
termsOfPayment: 'Exigible sous 30 jours',
|
|
45
|
+
dueDate: '2026-10-22',
|
|
46
|
+
settings: {},
|
|
47
|
+
language: 'fr',
|
|
48
|
+
idSaleDocumentTemplate: 14,
|
|
49
|
+
emissionDate: '2026-09-22',
|
|
50
|
+
thirdParty: {
|
|
51
|
+
id: 156708,
|
|
52
|
+
company: 'S.A.S EMBRUSCALLES',
|
|
53
|
+
idCountry: 66,
|
|
54
|
+
},
|
|
55
|
+
comment: null,
|
|
56
|
+
generalDiscount2: 0,
|
|
57
|
+
title: 'NORIA BLOQUER',
|
|
58
|
+
deposit: 0,
|
|
59
|
+
transmissionType: 'none',
|
|
60
|
+
idEstimateInvoiceType: EstimateInvoiceType.FACTURE,
|
|
61
|
+
idCountry: 66,
|
|
62
|
+
// Seller address fields are intentionally omitted here: `name`, `number`, `street`, `zipCode`, `city`.
|
|
63
|
+
} as unknown as CreateInvoiceDocument;
|
|
64
|
+
|
|
65
|
+
describe('generateCommercialDocument', () => {
|
|
66
|
+
test('should not throw when seller address fields are missing from the payload', () => {
|
|
67
|
+
expect(() => generateCommercialDocument(EstimateInvoiceType.FACTURE, payloadWithoutSellerAddress)).not.toThrow();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('should render an empty seller block when address fields are missing', () => {
|
|
71
|
+
const result = generateCommercialDocument(EstimateInvoiceType.FACTURE, payloadWithoutSellerAddress);
|
|
72
|
+
|
|
73
|
+
expect(result.body).not.toContain('<p>undefined</p>');
|
|
74
|
+
});
|
|
75
|
+
});
|