@silexpert/core 2.0.123-beta.0 → 2.0.123
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/{index-BTRchHCY.d.mts → index-DY6JXj66.d.mts} +51 -3
- package/dist/index-DY6JXj66.d.mts.map +1 -0
- package/dist/index.d.mts +2 -2
- package/dist/index.mjs +3 -3
- package/dist/index.mjs.map +1 -1
- package/dist/server/index.d.mts +1 -1
- package/dist/server/index.d.mts.map +1 -1
- package/dist/server/index.mjs +4 -4
- package/dist/server/index.mjs.map +1 -1
- package/package.json +1 -1
- package/ts/api/resources/zendesk/Ticket.ts +12 -3
- package/ts/types/Enum/Zendesk.ts +28 -0
- package/ts/types/Interface/api/zendesk/QueryTicket.ts +29 -1
- package/ts/types/Interface/api/zendesk/ReadTicket.ts +15 -0
- package/ts/types/Interface/models/IApiError.ts +1 -0
- package/ts/types/Interface/models/IZendeskTicketRead.ts +15 -0
- package/ts/types/index.ts +1 -0
- package/ts/utils/commercialManagement/document.ts +78 -21
- package/ts/utils/commercialManagement/templates/common.ts +2 -2
- package/ts/utils/email.ts +26 -21
- package/ts/utils/tests/document.spec.ts +3 -2
- package/ts/utils/transforms/array.transform.ts +35 -0
- package/dist/index-BTRchHCY.d.mts.map +0 -1
package/package.json
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
import {
|
|
2
|
+
CountUnreadZendeskTickets,
|
|
3
|
+
CountUnreadZendeskTicketsResponse,
|
|
2
4
|
CreateZendeskTicket,
|
|
3
5
|
UpdateZendeskTicket,
|
|
4
6
|
ListZendeskTickets,
|
|
5
|
-
|
|
6
|
-
SearchZendeskResponse,
|
|
7
|
+
ListZendeskTicketsResponse,
|
|
7
8
|
TicketFormEnriched,
|
|
8
9
|
TicketZendesk,
|
|
9
10
|
} from '../../../index.js';
|
|
10
11
|
import { Resource } from '../../Resource.js';
|
|
11
12
|
|
|
12
13
|
export class ZendeskTicket extends Resource {
|
|
13
|
-
getPaginate(query: ListZendeskTickets): Promise<
|
|
14
|
+
getPaginate(query: ListZendeskTickets): Promise<ListZendeskTicketsResponse> {
|
|
14
15
|
return this.axios.$get('connectors/zendesk/tickets', { params: query });
|
|
15
16
|
}
|
|
16
17
|
|
|
@@ -18,6 +19,10 @@ export class ZendeskTicket extends Resource {
|
|
|
18
19
|
return this.axios.$get('connectors/zendesk/tickets/forms');
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
getUnreadCount(query: CountUnreadZendeskTickets): Promise<CountUnreadZendeskTicketsResponse> {
|
|
23
|
+
return this.axios.$get('connectors/zendesk/tickets/unread-count', { params: query });
|
|
24
|
+
}
|
|
25
|
+
|
|
21
26
|
get(id: number): Promise<TicketZendesk> {
|
|
22
27
|
return this.axios.$get(`connectors/zendesk/tickets/${id}`);
|
|
23
28
|
}
|
|
@@ -43,6 +48,10 @@ export class ZendeskTicket extends Resource {
|
|
|
43
48
|
return this.axios.$put(`connectors/zendesk/tickets/${id}`, data);
|
|
44
49
|
}
|
|
45
50
|
|
|
51
|
+
markAsRead(id: number): Promise<void> {
|
|
52
|
+
return this.axios.$put(`connectors/zendesk/tickets/${id}/read`);
|
|
53
|
+
}
|
|
54
|
+
|
|
46
55
|
delete(id: number): Promise<void> {
|
|
47
56
|
return this.axios.$delete(`connectors/zendesk/tickets/${id}`);
|
|
48
57
|
}
|
package/ts/types/Enum/Zendesk.ts
CHANGED
|
@@ -125,3 +125,31 @@ export const ZendeskTicketCategoryList: Array<{ id: ZendeskTicketCategoryEnum; l
|
|
|
125
125
|
{ id: ZendeskTicketCategoryEnum.RELANCE_BILAN, label: 'Relance bilan' },
|
|
126
126
|
{ id: ZendeskTicketCategoryEnum.SUPPORT_APPLICATION, label: 'Support application' },
|
|
127
127
|
];
|
|
128
|
+
|
|
129
|
+
export type ZendeskTicketStatusGroup = 'information_required' | 'in_progress' | 'received' | 'resolved' | 'closed';
|
|
130
|
+
|
|
131
|
+
export enum ZendeskTicketStatusGroupEnum {
|
|
132
|
+
INFORMATION_REQUIRED = 'information_required',
|
|
133
|
+
IN_PROGRESS = 'in_progress',
|
|
134
|
+
RECEIVED = 'received',
|
|
135
|
+
RESOLVED = 'resolved',
|
|
136
|
+
CLOSED = 'closed',
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const ZendeskTicketStatusGroupByStatus: Record<ZendeskTicketStatusEnum, ZendeskTicketStatusGroupEnum> = {
|
|
140
|
+
[ZendeskTicketStatusEnum.NEW]: ZendeskTicketStatusGroupEnum.RECEIVED,
|
|
141
|
+
[ZendeskTicketStatusEnum.OPEN]: ZendeskTicketStatusGroupEnum.IN_PROGRESS,
|
|
142
|
+
[ZendeskTicketStatusEnum.HOLD]: ZendeskTicketStatusGroupEnum.IN_PROGRESS,
|
|
143
|
+
[ZendeskTicketStatusEnum.PENDING]: ZendeskTicketStatusGroupEnum.INFORMATION_REQUIRED,
|
|
144
|
+
[ZendeskTicketStatusEnum.SOLVED]: ZendeskTicketStatusGroupEnum.RESOLVED,
|
|
145
|
+
[ZendeskTicketStatusEnum.CLOSED]: ZendeskTicketStatusGroupEnum.CLOSED,
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// Display order of the support page: groups needing customer action come first.
|
|
149
|
+
export const ZendeskTicketStatusGroupList: Array<{ id: ZendeskTicketStatusGroupEnum; label: string }> = [
|
|
150
|
+
{ id: ZendeskTicketStatusGroupEnum.INFORMATION_REQUIRED, label: 'Information requise' },
|
|
151
|
+
{ id: ZendeskTicketStatusGroupEnum.IN_PROGRESS, label: 'En cours' },
|
|
152
|
+
{ id: ZendeskTicketStatusGroupEnum.RECEIVED, label: 'Demande reçue' },
|
|
153
|
+
{ id: ZendeskTicketStatusGroupEnum.RESOLVED, label: 'Résolu' },
|
|
154
|
+
{ id: ZendeskTicketStatusGroupEnum.CLOSED, label: 'Archivé' },
|
|
155
|
+
];
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Min,
|
|
11
11
|
Max,
|
|
12
12
|
IsDateString,
|
|
13
|
+
IsBoolean,
|
|
13
14
|
} from 'class-validator';
|
|
14
15
|
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
|
|
15
16
|
import {
|
|
@@ -21,9 +22,13 @@ import {
|
|
|
21
22
|
ZendeskTicketPriorityEnum,
|
|
22
23
|
type ZendeskTicketStatus,
|
|
23
24
|
ZendeskTicketStatusEnum,
|
|
25
|
+
type ZendeskTicketStatusGroup,
|
|
26
|
+
ZendeskTicketStatusGroupEnum,
|
|
24
27
|
type ZendeskTicketType,
|
|
25
28
|
ZendeskTicketTypeEnum,
|
|
26
29
|
} from '../../../../types/Enum/Zendesk.js';
|
|
30
|
+
import { ToBoolean } from '../../../../utils/transforms/boolean.transform.js';
|
|
31
|
+
import { ToStringArray } from '../../../../utils/transforms/array.transform.js';
|
|
27
32
|
|
|
28
33
|
export class CreateZendeskTicket {
|
|
29
34
|
@ApiProperty()
|
|
@@ -193,13 +198,36 @@ export class ListZendeskTickets {
|
|
|
193
198
|
@IsDateString()
|
|
194
199
|
endDate?: string;
|
|
195
200
|
|
|
201
|
+
/**
|
|
202
|
+
* @deprecated Tickets are now sorted by status group, unread state and last public message date.
|
|
203
|
+
* Only `sortOrder` is taken into account.
|
|
204
|
+
*/
|
|
196
205
|
@ApiPropertyOptional()
|
|
197
206
|
@IsOptional()
|
|
198
207
|
@IsString()
|
|
199
208
|
sortBy?: string;
|
|
200
209
|
|
|
201
|
-
@ApiPropertyOptional()
|
|
210
|
+
@ApiPropertyOptional({ description: 'Order applied to the last public message date' })
|
|
202
211
|
@IsOptional()
|
|
203
212
|
@IsEnum(['asc', 'desc'])
|
|
204
213
|
sortOrder?: 'asc' | 'desc';
|
|
214
|
+
|
|
215
|
+
@ApiPropertyOptional({ enum: ZendeskTicketStatusGroupEnum, isArray: true })
|
|
216
|
+
@IsOptional()
|
|
217
|
+
@ToStringArray()
|
|
218
|
+
@IsArray()
|
|
219
|
+
@IsEnum(ZendeskTicketStatusGroupEnum, { each: true })
|
|
220
|
+
statusGroups?: ZendeskTicketStatusGroup[];
|
|
221
|
+
|
|
222
|
+
@ApiPropertyOptional()
|
|
223
|
+
@IsOptional()
|
|
224
|
+
@ToBoolean()
|
|
225
|
+
@IsBoolean()
|
|
226
|
+
isUnread?: boolean;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export class CountUnreadZendeskTickets {
|
|
230
|
+
@ApiProperty()
|
|
231
|
+
@IsEnum(ZendeskTicketChannelEnum)
|
|
232
|
+
channel: ZendeskTicketChannel;
|
|
205
233
|
}
|
|
@@ -2,8 +2,10 @@ import {
|
|
|
2
2
|
ZendeskTicketChannel,
|
|
3
3
|
ZendeskTicketPriority,
|
|
4
4
|
ZendeskTicketStatus,
|
|
5
|
+
ZendeskTicketStatusGroup,
|
|
5
6
|
ZendeskTicketType,
|
|
6
7
|
} from '../../../Enum/Zendesk.js';
|
|
8
|
+
import type { BasePaginate } from '../BasePaginate.js';
|
|
7
9
|
|
|
8
10
|
export interface TicketZendesk {
|
|
9
11
|
id: number;
|
|
@@ -22,6 +24,19 @@ export interface TicketZendesk {
|
|
|
22
24
|
organizationId: number | null;
|
|
23
25
|
createdAt: string;
|
|
24
26
|
updatedAt: string;
|
|
27
|
+
// Read-tracking data, only computed on the paginated list
|
|
28
|
+
statusGroup?: ZendeskTicketStatusGroup;
|
|
29
|
+
isUnread?: boolean;
|
|
30
|
+
lastActivityAt?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface ListZendeskTicketsResponse extends BasePaginate<TicketZendesk> {
|
|
34
|
+
// Unread tickets of the channel whatever the filters, to refresh the unread badge with the list.
|
|
35
|
+
unreadCount: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface CountUnreadZendeskTicketsResponse {
|
|
39
|
+
count: number;
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
export interface SearchZendeskResponse {
|
|
@@ -23,6 +23,7 @@ export enum Code {
|
|
|
23
23
|
CONNECTION_ALREADY_EXIST = 'connection_already_exist',
|
|
24
24
|
firstConnectionFromVpn = 'first_connection_from_vpn',
|
|
25
25
|
ENTITY_ALREADY_EXIST = 'entity_already_exist',
|
|
26
|
+
tooManyRequest = 'too_many_requests',
|
|
26
27
|
|
|
27
28
|
// Annotation Error
|
|
28
29
|
failedCheckBalance = 'failed_check_balance',
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { IUser } from './IUser.js';
|
|
2
|
+
|
|
3
|
+
export interface IZendeskTicketRead {
|
|
4
|
+
id?: number;
|
|
5
|
+
idUser: number;
|
|
6
|
+
idTicket: number;
|
|
7
|
+
readAt?: Date | null;
|
|
8
|
+
// Activity snapshot of the Zendesk ticket, refreshed only when `ticketUpdatedAt` changes
|
|
9
|
+
ticketUpdatedAt?: Date | null;
|
|
10
|
+
lastPublicCommentAt?: Date | null;
|
|
11
|
+
lastSupportCommentAt?: Date | null;
|
|
12
|
+
createdAt?: Date;
|
|
13
|
+
updatedAt?: Date;
|
|
14
|
+
user?: IUser;
|
|
15
|
+
}
|
package/ts/types/index.ts
CHANGED
|
@@ -290,6 +290,7 @@ export * from './Interface/models/IRevenueSnapshot.js';
|
|
|
290
290
|
export * from './Interface/models/IEInvoicingEligibilityRequest.js';
|
|
291
291
|
export * from './Interface/models/IUsersHistory.js';
|
|
292
292
|
export * from './Interface/api/teamSystemSignature/index.js';
|
|
293
|
+
export * from './Interface/models/IZendeskTicketRead.js';
|
|
293
294
|
export * from './Interface/models/IEInvoicingKycAndMandate.js';
|
|
294
295
|
export * from './Interface/models/IConnectorTracking.js';
|
|
295
296
|
export * from './Interface/models/IEInvoicing.js';
|
|
@@ -171,28 +171,85 @@ export function generateCommercialDocument(
|
|
|
171
171
|
: payload?.settings?.freeInvoiceText;
|
|
172
172
|
|
|
173
173
|
return {
|
|
174
|
-
header: ejs.render(commonTemplate.header, data),
|
|
175
|
-
body: ejs.render(
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
data
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
174
|
+
header: ejs.render(commonTemplate.header, data, {}),
|
|
175
|
+
body: ejs.render(
|
|
176
|
+
bodyTemplate,
|
|
177
|
+
{
|
|
178
|
+
...data,
|
|
179
|
+
cgv:
|
|
180
|
+
data.cgv && typeof data.cgv === 'string'
|
|
181
|
+
? sanitize(data.cgv, {
|
|
182
|
+
allowedTags: [
|
|
183
|
+
'b',
|
|
184
|
+
'i',
|
|
185
|
+
'em',
|
|
186
|
+
'br',
|
|
187
|
+
'strong',
|
|
188
|
+
'p',
|
|
189
|
+
'span',
|
|
190
|
+
'li',
|
|
191
|
+
'ul',
|
|
192
|
+
'ol',
|
|
193
|
+
'h1',
|
|
194
|
+
'h2',
|
|
195
|
+
'h3',
|
|
196
|
+
'u',
|
|
197
|
+
's',
|
|
198
|
+
'a',
|
|
199
|
+
'blockquote',
|
|
200
|
+
],
|
|
201
|
+
allowedAttributes: {
|
|
202
|
+
a: ['href', 'name', 'target', 'data-list'],
|
|
203
|
+
li: ['data-list'],
|
|
204
|
+
'*': ['class', 'style', 'dir'],
|
|
205
|
+
},
|
|
206
|
+
allowedClasses: {
|
|
207
|
+
'*': [/^ql-/],
|
|
208
|
+
},
|
|
209
|
+
allowedStyles: {
|
|
210
|
+
'*': {
|
|
211
|
+
color: [/^[a-z]+$/i, /^#(?:[0-9a-f]{3}){1,2}$/i, /^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/i],
|
|
212
|
+
'background-color': [
|
|
213
|
+
/^[a-z]+$/i,
|
|
214
|
+
/^#(?:[0-9a-f]{3}){1,2}$/i,
|
|
215
|
+
/^rgb\(\s*\d+\s*,\s*\d+\s*,\s*\d+\s*\)$/i,
|
|
216
|
+
],
|
|
217
|
+
'text-align': [/^left$/i, /^right$/i, /^center$/i, /^justify$/i],
|
|
218
|
+
'white-space': [/^pre-wrap$/i],
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
: null,
|
|
183
223
|
comment:
|
|
184
|
-
|
|
185
|
-
? sanitize(
|
|
224
|
+
data.comment && typeof data.comment === 'string'
|
|
225
|
+
? sanitize(data.comment?.replace(/\n/g, '<br/>'), { allowedTags: ['b', 'i', 'em', 'br', 'strong'] })
|
|
186
226
|
: null,
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
227
|
+
products: data.products.map((p) => ({
|
|
228
|
+
...p,
|
|
229
|
+
comment:
|
|
230
|
+
p.comment && typeof p.comment === 'string'
|
|
231
|
+
? sanitize(p.comment?.replace(/\n/g, '<br/>'), { allowedTags: ['b', 'i', 'em', 'br', 'strong'] })
|
|
232
|
+
: null,
|
|
233
|
+
// @ts-expect-error
|
|
234
|
+
unitFormatted: internationalizationConsistency(locale, p.unitFormatted, 'unit'),
|
|
235
|
+
})),
|
|
236
|
+
vats: forcedVats ?? vats,
|
|
237
|
+
},
|
|
238
|
+
{},
|
|
239
|
+
),
|
|
240
|
+
footer: ejs.render(
|
|
241
|
+
commonTemplate.footer,
|
|
242
|
+
{
|
|
243
|
+
...data,
|
|
244
|
+
freeText:
|
|
245
|
+
documentFreeText && typeof documentFreeText === 'string'
|
|
246
|
+
? sanitize(documentFreeText?.replace(/\n/g, '<br/>'), {
|
|
247
|
+
allowedTags: ['b', 'i', 'em', 'br', 'strong', 'p'],
|
|
248
|
+
})
|
|
249
|
+
: null,
|
|
250
|
+
},
|
|
251
|
+
{},
|
|
252
|
+
),
|
|
196
253
|
data,
|
|
197
254
|
};
|
|
198
255
|
}
|
|
@@ -227,7 +284,7 @@ export function generateMailHeader(
|
|
|
227
284
|
const mailTemplate = new CommercialDocumentMailHTML();
|
|
228
285
|
|
|
229
286
|
return {
|
|
230
|
-
header: ejs.render(mailTemplate.header, data),
|
|
287
|
+
header: ejs.render(mailTemplate.header, data, {}),
|
|
231
288
|
};
|
|
232
289
|
}
|
|
233
290
|
|
|
@@ -334,7 +334,7 @@ export class CommercialDocumentMailHTML {
|
|
|
334
334
|
</style>
|
|
335
335
|
<div style="background-color: <%= brandColors.primary50 %> !important; color: black; width: calc(100% - 48px); display: grid; border-radius: 16px; padding: 24px;">
|
|
336
336
|
<div style="width: max-content; margin: auto; font-size: 16px; font-family: 'circular std', sans-serif; font-weight: 100; line-height: 140%; position: relative;">
|
|
337
|
-
<div style="color: black;"><span style="font-weight: 500;"
|
|
337
|
+
<div style="color: black;"><span style="font-weight: 500;"><%= companyName %></span> <%= languageMail.mail.common.send %>
|
|
338
338
|
<% if(idEstimateInvoiceType) { %>
|
|
339
339
|
<% if ([${EstimateInvoiceType.FACTURE}, ${EstimateInvoiceType.RESTE}].includes(idEstimateInvoiceType)) { %>
|
|
340
340
|
<%= languageMail.mail.invoice.title %>
|
|
@@ -370,7 +370,7 @@ export class CommercialDocumentMailHTML {
|
|
|
370
370
|
<% } else { %>
|
|
371
371
|
<%= languageMail.mail.default.subTitle %>
|
|
372
372
|
<% } %>
|
|
373
|
-
|
|
373
|
+
<%= reference %>
|
|
374
374
|
</div>
|
|
375
375
|
<div style="; color: <%= brandColors.primary500 %>; font-size: 14px;">
|
|
376
376
|
<%= languageMail.mail.common.dueDate %> : <%- dueDateFormatted %>
|
package/ts/utils/email.ts
CHANGED
|
@@ -366,25 +366,30 @@ export function generateSignature(params: any): string {
|
|
|
366
366
|
user.calendlyUrl = calendlyUrl;
|
|
367
367
|
}
|
|
368
368
|
|
|
369
|
-
return ejs.render(
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
369
|
+
return ejs.render(
|
|
370
|
+
signatureTemplate,
|
|
371
|
+
{
|
|
372
|
+
forceRole,
|
|
373
|
+
isFacturation: user.roles ? user.roles.some((r: any) => r.id === Role.FACTURATION) : false,
|
|
374
|
+
isDataOfficer: user.roles ? user.roles.some((r: any) => r.id === Role.DATA_OFFICER) : true,
|
|
375
|
+
brandName,
|
|
376
|
+
brandPostalAddress,
|
|
377
|
+
brandCity,
|
|
378
|
+
idTypeBrand,
|
|
379
|
+
commercialName,
|
|
380
|
+
iban,
|
|
381
|
+
bic,
|
|
382
|
+
hostS3,
|
|
383
|
+
user,
|
|
384
|
+
roleSignature,
|
|
385
|
+
mailSignature,
|
|
386
|
+
phoneSignature:
|
|
387
|
+
commercialName !== 'Comptalib' && phoneSignature ? phoneSignature.replace(/\s+/gm, ' ') : null,
|
|
388
|
+
standardPhoneSignature: commercialName !== 'Comptalib' ? standardPhoneSignature.replace(/\s+/gm, ' ') : null,
|
|
389
|
+
horaires,
|
|
390
|
+
primaryColor,
|
|
391
|
+
supportMail,
|
|
392
|
+
},
|
|
393
|
+
{},
|
|
394
|
+
);
|
|
390
395
|
}
|
|
@@ -24,7 +24,7 @@ const payloadWithoutSellerAddress = {
|
|
|
24
24
|
reference: '202609/94',
|
|
25
25
|
capital: 0,
|
|
26
26
|
isDraft: false,
|
|
27
|
-
cgv: '<p><
|
|
27
|
+
cgv: '<p>Test avec une liste</p><ol><li data-list="bullet"><span class="ql-ui"></span>1</li><li data-list="bullet"><span class="ql-ui"></span>2</li></ol></p>',
|
|
28
28
|
logoUrl: null,
|
|
29
29
|
disableVat: false,
|
|
30
30
|
nafCode: null,
|
|
@@ -94,7 +94,8 @@ describe('generateCommercialDocument', () => {
|
|
|
94
94
|
test.each([true, false])(
|
|
95
95
|
'should render CGV HTML only when invoiceCgv is enabled (%s)',
|
|
96
96
|
(invoiceCgv: boolean): void => {
|
|
97
|
-
const cgv =
|
|
97
|
+
const cgv =
|
|
98
|
+
'<p>Test avec une liste</p><ol><li data-list="bullet"><span class="ql-ui"></span>1</li><li data-list="bullet"><span class="ql-ui"></span>2</li></ol>';
|
|
98
99
|
const payload: CreateInvoiceDocument = {
|
|
99
100
|
...payloadWithoutSellerAddress,
|
|
100
101
|
cgv,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Transform } from 'class-transformer';
|
|
2
|
+
|
|
3
|
+
const valueToStringArray = (value: any) => {
|
|
4
|
+
if (value === null || value === undefined || value === '') {
|
|
5
|
+
return undefined;
|
|
6
|
+
}
|
|
7
|
+
if (Array.isArray(value)) {
|
|
8
|
+
return value.map((v) => String(v).trim());
|
|
9
|
+
}
|
|
10
|
+
return String(value)
|
|
11
|
+
.split(',')
|
|
12
|
+
.map((v) => v.trim())
|
|
13
|
+
.filter((v) => v !== '');
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Accepts repeated query params (`a=x&a=y`) as well as comma-separated values (`a=x,y`).
|
|
17
|
+
const ToStringArray = () => {
|
|
18
|
+
const toPlain = Transform(
|
|
19
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
20
|
+
({ value }) => value,
|
|
21
|
+
{
|
|
22
|
+
toPlainOnly: true,
|
|
23
|
+
},
|
|
24
|
+
);
|
|
25
|
+
const toClass = (target: any, key: string) =>
|
|
26
|
+
Transform(({ obj }) => valueToStringArray(obj[key]), {
|
|
27
|
+
toClassOnly: true,
|
|
28
|
+
})(target, key);
|
|
29
|
+
return (target: any, key: string) => {
|
|
30
|
+
toPlain(target, key);
|
|
31
|
+
toClass(target, key);
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export { ToStringArray };
|