@unchainedshop/core 4.5.0 → 4.6.1

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 (35) hide show
  1. package/lib/bulk-exporter/createBulkExporter.d.ts +33 -0
  2. package/lib/bulk-exporter/createBulkExporter.js +46 -0
  3. package/lib/bulk-exporter/handlers/exportAssortmentsHandler.d.ts +38 -0
  4. package/lib/bulk-exporter/handlers/exportAssortmentsHandler.js +137 -0
  5. package/lib/bulk-exporter/handlers/exportFiltersHandler.d.ts +26 -0
  6. package/lib/bulk-exporter/handlers/exportFiltersHandler.js +88 -0
  7. package/lib/bulk-exporter/handlers/exportProductsHandler.d.ts +40 -0
  8. package/lib/bulk-exporter/handlers/exportProductsHandler.js +238 -0
  9. package/lib/bulk-exporter/handlers/exportUsersHandler.d.ts +40 -0
  10. package/lib/bulk-exporter/handlers/exportUsersHandler.js +340 -0
  11. package/lib/bulk-exporter/handlers/generateCSVFileAndUrl.d.ts +13 -0
  12. package/lib/bulk-exporter/handlers/generateCSVFileAndUrl.js +21 -0
  13. package/lib/bulk-exporter/handlers/toCSV.d.ts +2 -0
  14. package/lib/bulk-exporter/handlers/toCSV.js +5 -0
  15. package/lib/bulk-exporter/index.d.ts +3 -0
  16. package/lib/bulk-exporter/index.js +3 -0
  17. package/lib/bulk-importer/handlers/assortment/create.d.ts +0 -2
  18. package/lib/bulk-importer/handlers/assortment/create.js +0 -1
  19. package/lib/bulk-importer/handlers/assortment/update.d.ts +0 -2
  20. package/lib/bulk-importer/handlers/assortment/update.js +0 -1
  21. package/lib/bulk-importer/handlers/product/create.js +2 -2
  22. package/lib/bulk-importer/handlers/product/update.js +2 -2
  23. package/lib/core-index.d.ts +7 -1
  24. package/lib/core-index.js +5 -1
  25. package/lib/directors/EnrollmentDirector.d.ts +4 -1
  26. package/lib/directors/PaymentDirector.js +2 -0
  27. package/lib/directors/WorkerAdapter.d.ts +2 -0
  28. package/lib/services/fulfillQuotation.d.ts +3 -0
  29. package/lib/services/{fullfillQuotation.js → fulfillQuotation.js} +3 -3
  30. package/lib/services/index.d.ts +2 -2
  31. package/lib/services/index.js +2 -2
  32. package/lib/services/processOrder.js +17 -9
  33. package/lib/services/rejectQuotation.js +1 -1
  34. package/package.json +21 -20
  35. package/lib/services/fullfillQuotation.d.ts +0 -3
@@ -0,0 +1,40 @@
1
+ import type { UnchainedCore } from '../../core-index.ts';
2
+ import { z } from 'zod';
3
+ export declare const UserExportPayloadSchema: z.ZodObject<{
4
+ exportReviews: z.ZodOptional<z.ZodBoolean>;
5
+ exportOrders: z.ZodOptional<z.ZodBoolean>;
6
+ exportBookmarks: z.ZodOptional<z.ZodBoolean>;
7
+ exportEvents: z.ZodOptional<z.ZodBoolean>;
8
+ exportQuotations: z.ZodOptional<z.ZodBoolean>;
9
+ exportEnrollments: z.ZodOptional<z.ZodBoolean>;
10
+ userId: z.ZodOptional<z.ZodString>;
11
+ }, z.core.$strip>;
12
+ export interface UserExportParams {
13
+ exportReviews?: boolean;
14
+ exportOrders?: boolean;
15
+ exportBookmarks?: boolean;
16
+ exportEvents?: boolean;
17
+ exportQuotations?: boolean;
18
+ exportEnrollments?: boolean;
19
+ userId: string;
20
+ }
21
+ declare const exportUsersHandler: {
22
+ ({ userId, ...options }: UserExportParams, _: any, unchainedAPI: UnchainedCore): Promise<{
23
+ user: import("./generateCSVFileAndUrl.ts").CSVFileResult;
24
+ bookmarks: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
25
+ orders: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
26
+ reviews: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
27
+ quotations: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
28
+ enrollments: import("./generateCSVFileAndUrl.ts").CSVFileResult | null;
29
+ }>;
30
+ payloadSchema: z.ZodObject<{
31
+ exportReviews: z.ZodOptional<z.ZodBoolean>;
32
+ exportOrders: z.ZodOptional<z.ZodBoolean>;
33
+ exportBookmarks: z.ZodOptional<z.ZodBoolean>;
34
+ exportEvents: z.ZodOptional<z.ZodBoolean>;
35
+ exportQuotations: z.ZodOptional<z.ZodBoolean>;
36
+ exportEnrollments: z.ZodOptional<z.ZodBoolean>;
37
+ userId: z.ZodOptional<z.ZodString>;
38
+ }, z.core.$strip>;
39
+ };
40
+ export default exportUsersHandler;
@@ -0,0 +1,340 @@
1
+ import generateCSVFileAndURL from "./generateCSVFileAndUrl.js";
2
+ import { z } from 'zod';
3
+ import { EXPORTS_DIRECTORY } from "../createBulkExporter.js";
4
+ export const UserExportPayloadSchema = z.object({
5
+ exportReviews: z.boolean().optional(),
6
+ exportOrders: z.boolean().optional(),
7
+ exportBookmarks: z.boolean().optional(),
8
+ exportEvents: z.boolean().optional(),
9
+ exportQuotations: z.boolean().optional(),
10
+ exportEnrollments: z.boolean().optional(),
11
+ userId: z.string().optional(),
12
+ });
13
+ const USER_CSV_SCHEMA = {
14
+ userFields: [
15
+ '_id',
16
+ 'emailAddresses',
17
+ 'tags',
18
+ 'roles',
19
+ 'username',
20
+ 'created',
21
+ 'isGuest',
22
+ 'displayName',
23
+ 'birthday',
24
+ 'phoneMobile',
25
+ 'gender',
26
+ 'address.addressLine',
27
+ 'address.addressLine2',
28
+ 'address.city',
29
+ 'address.company',
30
+ 'address.countryCode',
31
+ 'address.firstName',
32
+ 'address.lastName',
33
+ 'address.postalCode',
34
+ 'address.regionCode',
35
+ 'meta',
36
+ 'lastBillingAddress',
37
+ 'lastContact',
38
+ 'lastLogin',
39
+ ],
40
+ bookmarkFields: ['_id', 'productId', 'userId'],
41
+ orderFields: [
42
+ '_id',
43
+ 'userId',
44
+ 'orderNumber',
45
+ 'status',
46
+ 'billingAddress',
47
+ 'contact',
48
+ 'countryCode',
49
+ 'currencyCode',
50
+ 'deliveryId',
51
+ 'paymentId',
52
+ 'confirmed',
53
+ 'ordered',
54
+ 'fulfilled',
55
+ 'products',
56
+ ],
57
+ reviewFields: [
58
+ '_id',
59
+ 'productId',
60
+ 'authorId',
61
+ 'rating',
62
+ 'title',
63
+ 'review',
64
+ 'vote.type',
65
+ 'vote.timestamp',
66
+ 'vote.meta',
67
+ 'meta',
68
+ ],
69
+ quotationFields: [
70
+ '_id',
71
+ 'userId',
72
+ 'quotationNumber',
73
+ 'productId',
74
+ 'status',
75
+ 'price',
76
+ 'expires',
77
+ 'fulfilled',
78
+ 'rejected',
79
+ 'deleted',
80
+ 'meta',
81
+ 'configuration',
82
+ ],
83
+ enrollmentFields: [
84
+ '_id',
85
+ 'userId',
86
+ 'productId',
87
+ 'enrollmentNumber',
88
+ 'status',
89
+ 'countryCode',
90
+ 'currencyCode',
91
+ 'quantity',
92
+ 'created',
93
+ 'deleted',
94
+ 'expires',
95
+ 'configuration',
96
+ 'billingAddress',
97
+ 'contact.emailAddress',
98
+ 'contact.telNumber',
99
+ 'delivery.providerId',
100
+ 'payment.providerId',
101
+ 'delivery.meta',
102
+ 'payment.meta',
103
+ 'meta',
104
+ ],
105
+ };
106
+ const exportUsersHandler = async ({ userId, ...options }, _, unchainedAPI) => {
107
+ const { modules } = unchainedAPI;
108
+ const user = await modules.users.findUserById(userId);
109
+ const userRows = [];
110
+ const orderRows = [];
111
+ const bookmarkRows = [];
112
+ const quotationRows = [];
113
+ const reviewRows = [];
114
+ const enrollmentRows = [];
115
+ if (!user)
116
+ throw new Error(`User with ID ${userId} not found`);
117
+ userRows.push({
118
+ _id: user._id,
119
+ emailAddresses: user.emails ? user.emails.map((email) => email.address).join('; ') : '',
120
+ created: new Date(user.created).getTime(),
121
+ isGuest: user.guest || false,
122
+ tags: user.tags ? user.tags.join('; ') : '',
123
+ roles: user.roles ? user.roles.join('; ') : '',
124
+ username: user.username || '',
125
+ displayName: user.profile?.displayName || '',
126
+ birthday: user.profile?.birthday ? new Date(user.profile?.birthday).getTime() : '',
127
+ phoneMobile: user.profile?.phoneMobile || '',
128
+ gender: user.profile?.gender || '',
129
+ 'address.addressLine': user.profile?.address?.addressLine || '',
130
+ 'address.addressLine2': user.profile?.address?.addressLine2 || '',
131
+ 'address.city': user.profile?.address?.city || '',
132
+ 'address.company': user.profile?.address?.company || '',
133
+ 'address.countryCode': user.profile?.address?.countryCode || '',
134
+ 'address.firstName': user.profile?.address?.firstName || '',
135
+ 'address.lastName': user.profile?.address?.lastName || '',
136
+ 'address.postalCode': user.profile?.address?.postalCode || '',
137
+ 'address.regionCode': user.profile?.address?.regionCode || '',
138
+ meta: user.meta ? JSON.stringify(user.meta) : '',
139
+ lastBillingAddress: user.lastBillingAddress ? JSON.stringify(user.lastBillingAddress) : '',
140
+ lastContact: user.lastContact ? JSON.stringify(user.lastContact) : '',
141
+ lastLogin: user.lastLogin ? JSON.stringify(user.lastLogin) : '',
142
+ });
143
+ if (options.exportBookmarks) {
144
+ const bookmarks = await modules.bookmarks.findBookmarksByUserId(userId);
145
+ for (const bookmark of bookmarks) {
146
+ const row = {};
147
+ USER_CSV_SCHEMA.bookmarkFields.forEach((field) => {
148
+ row[field] = bookmark[field] || '';
149
+ });
150
+ bookmarkRows.push(row);
151
+ }
152
+ }
153
+ if (options.exportOrders) {
154
+ const orders = await modules.orders.findOrders({ userId });
155
+ for await (const order of orders) {
156
+ const positions = await modules.orders.positions.findOrderPositions({ orderId: order._id });
157
+ const row = {};
158
+ USER_CSV_SCHEMA.orderFields.forEach((field) => {
159
+ if ((field === 'ordered' || field === 'confirmed' || field === 'fulfilled') && order[field]) {
160
+ row[field] = new Date(order[field]).getTime();
161
+ }
162
+ else if (field === 'products') {
163
+ row[field] = positions.map((pos) => `${pos.productId}~${pos.quantity}`).join('; ');
164
+ }
165
+ else if (field === 'billingAddress' && order.billingAddress) {
166
+ row[field] = JSON.stringify(order.billingAddress);
167
+ }
168
+ else if (field === 'contact' && order.contact) {
169
+ row[field] = JSON.stringify(order.contact);
170
+ }
171
+ else {
172
+ row[field] = order[field] || '';
173
+ }
174
+ });
175
+ orderRows.push(row);
176
+ }
177
+ }
178
+ if (options.exportReviews) {
179
+ const reviews = await modules.products.reviews.findProductReviews({
180
+ authorId: userId,
181
+ });
182
+ for (const review of reviews) {
183
+ const row = {};
184
+ USER_CSV_SCHEMA.reviewFields.forEach((field) => {
185
+ if (field.startsWith('vote.')) {
186
+ const voteField = field.split('.')[1];
187
+ if (voteField === 'timestamp' && review.votes[0][voteField]) {
188
+ row[field] = new Date(review.votes[0][voteField]).getTime();
189
+ }
190
+ else if (voteField === 'meta' && review.votes[0][voteField]) {
191
+ row[field] = JSON.stringify(review.votes[0][voteField]);
192
+ }
193
+ else {
194
+ row[field] = review.votes[0][voteField] || '';
195
+ }
196
+ }
197
+ else {
198
+ if (field === 'meta' && review.meta) {
199
+ row[field] = JSON.stringify(review.meta);
200
+ }
201
+ else {
202
+ row[field] = review[field] || '';
203
+ }
204
+ }
205
+ });
206
+ reviewRows.push(row);
207
+ }
208
+ }
209
+ if (options.exportQuotations) {
210
+ const quotations = await modules.quotations.findQuotations({
211
+ userId,
212
+ });
213
+ for (const quotation of quotations) {
214
+ const row = {};
215
+ USER_CSV_SCHEMA.quotationFields.forEach((field) => {
216
+ if ((field === 'expires' ||
217
+ field === 'fulfilled' ||
218
+ field === 'rejected' ||
219
+ field === 'deleted') &&
220
+ quotation[field]) {
221
+ row[field] = new Date(quotation[field]).getTime();
222
+ }
223
+ else if (field === 'configuration' || (field === 'meta' && quotation[field])) {
224
+ row[field] = JSON.stringify(quotation.configuration);
225
+ }
226
+ else {
227
+ row[field] = quotation[field] || '';
228
+ }
229
+ });
230
+ quotationRows.push(row);
231
+ }
232
+ }
233
+ if (options.exportEnrollments) {
234
+ const enrollments = await modules.enrollments.findEnrollments({
235
+ userId,
236
+ });
237
+ for (const enrollment of enrollments) {
238
+ const row = {};
239
+ USER_CSV_SCHEMA.enrollmentFields.forEach((field) => {
240
+ if ((field === 'created' || field === 'deleted' || field === 'expires') && enrollment[field]) {
241
+ row[field] = new Date(enrollment[field]).getTime();
242
+ }
243
+ else if (field === 'billingAddress' && enrollment[field]) {
244
+ row[field] = JSON.stringify(enrollment[field]);
245
+ }
246
+ else if (field === 'contact.emailAddress' && enrollment.contact) {
247
+ row[field] = enrollment.contact.emailAddress || '';
248
+ }
249
+ else if (field === 'contact.telNumber' && enrollment.contact) {
250
+ row[field] = enrollment.contact.telNumber || '';
251
+ }
252
+ else if (field === 'delivery.providerId' && enrollment.delivery) {
253
+ row[field] = enrollment.delivery.deliveryProviderId || '';
254
+ }
255
+ else if (field === 'payment.providerId' && enrollment.payment) {
256
+ row[field] = enrollment.payment.paymentProviderId || '';
257
+ }
258
+ else if (field === 'delivery.meta' && enrollment.delivery) {
259
+ row[field] = enrollment.delivery.meta
260
+ ? JSON.stringify(enrollment.delivery.meta)
261
+ : '';
262
+ }
263
+ else if (field === 'payment.meta' && enrollment.payment) {
264
+ row[field] = enrollment.payment.meta
265
+ ? JSON.stringify(enrollment.payment.meta)
266
+ : '';
267
+ }
268
+ else if (field === 'configuration' || (field === 'meta' && enrollment[field])) {
269
+ row[field] = JSON.stringify(enrollment.configuration);
270
+ }
271
+ else {
272
+ row[field] = enrollment[field] || '';
273
+ }
274
+ });
275
+ enrollmentRows.push(row);
276
+ }
277
+ }
278
+ const userCSV = await generateCSVFileAndURL({
279
+ headers: USER_CSV_SCHEMA.userFields,
280
+ rows: userRows,
281
+ directoryName: EXPORTS_DIRECTORY,
282
+ fileName: 'user_export.csv',
283
+ unchainedAPI,
284
+ });
285
+ const reviewCSV = options.exportReviews
286
+ ? await generateCSVFileAndURL({
287
+ headers: USER_CSV_SCHEMA.reviewFields,
288
+ rows: reviewRows,
289
+ directoryName: EXPORTS_DIRECTORY,
290
+ fileName: 'user_reviews_export.csv',
291
+ unchainedAPI,
292
+ })
293
+ : null;
294
+ const quotationCSV = options.exportQuotations
295
+ ? await generateCSVFileAndURL({
296
+ headers: USER_CSV_SCHEMA.quotationFields,
297
+ rows: quotationRows,
298
+ directoryName: EXPORTS_DIRECTORY,
299
+ fileName: 'user_quotations_export.csv',
300
+ unchainedAPI,
301
+ })
302
+ : null;
303
+ const bookmarksCSV = options.exportBookmarks
304
+ ? await generateCSVFileAndURL({
305
+ headers: USER_CSV_SCHEMA.bookmarkFields,
306
+ rows: bookmarkRows,
307
+ directoryName: EXPORTS_DIRECTORY,
308
+ fileName: 'user_bookmarks_export.csv',
309
+ unchainedAPI,
310
+ })
311
+ : null;
312
+ const ordersCSV = options.exportOrders
313
+ ? await generateCSVFileAndURL({
314
+ headers: USER_CSV_SCHEMA.orderFields,
315
+ rows: orderRows,
316
+ directoryName: EXPORTS_DIRECTORY,
317
+ fileName: 'user_orders_export.csv',
318
+ unchainedAPI,
319
+ })
320
+ : null;
321
+ const enrollmentCSV = options.exportEnrollments
322
+ ? await generateCSVFileAndURL({
323
+ headers: USER_CSV_SCHEMA.enrollmentFields,
324
+ rows: enrollmentRows,
325
+ directoryName: EXPORTS_DIRECTORY,
326
+ fileName: 'user_enrollments_export.csv',
327
+ unchainedAPI,
328
+ })
329
+ : null;
330
+ return {
331
+ user: userCSV,
332
+ bookmarks: bookmarksCSV,
333
+ orders: ordersCSV,
334
+ reviews: reviewCSV,
335
+ quotations: quotationCSV,
336
+ enrollments: enrollmentCSV,
337
+ };
338
+ };
339
+ export default exportUsersHandler;
340
+ exportUsersHandler.payloadSchema = UserExportPayloadSchema;
@@ -0,0 +1,13 @@
1
+ import type { UnchainedCore } from '../../core-index.ts';
2
+ export interface CSVFileResult {
3
+ url: string;
4
+ expires: number;
5
+ }
6
+ declare const generateCSVFileAndURL: ({ rows, headers, directoryName, fileName, unchainedAPI, }: {
7
+ rows: Record<string, unknown>[];
8
+ headers: string[];
9
+ directoryName: string;
10
+ fileName: string;
11
+ unchainedAPI: UnchainedCore;
12
+ }, expires?: number) => Promise<CSVFileResult>;
13
+ export default generateCSVFileAndURL;
@@ -0,0 +1,21 @@
1
+ import toCSV from "./toCSV.js";
2
+ const generateCSVFileAndURL = async ({ rows, headers, directoryName, fileName, unchainedAPI, }, expires = 3600000) => {
3
+ if (!rows.length)
4
+ return { url: '', expires: 0 };
5
+ const csvString = toCSV(headers, rows);
6
+ const uploaded = await unchainedAPI.services.files.uploadFileFromStream({
7
+ directoryName,
8
+ rawFile: { filename: fileName, buffer: Buffer.from(csvString).toString('base64') },
9
+ meta: { isPrivate: true },
10
+ });
11
+ const expiresAt = Date.now() + expires;
12
+ const url = await unchainedAPI.services.files.createFileDownloadURL({
13
+ file: uploaded,
14
+ expires: expiresAt,
15
+ });
16
+ if (!url) {
17
+ throw new Error(`Failed to generate download URL for ${fileName}`);
18
+ }
19
+ return { url, expires: expiresAt };
20
+ };
21
+ export default generateCSVFileAndURL;
@@ -0,0 +1,2 @@
1
+ declare const toCSV: (headers: string[], rows: Record<string, any>[]) => string;
2
+ export default toCSV;
@@ -0,0 +1,5 @@
1
+ const toCSV = (headers, rows) => {
2
+ const escape = (v) => `"${String(v ?? '').replace(/"/g, '""')}"`;
3
+ return [headers.join(','), ...rows.map((r) => headers.map((h) => escape(r[h])).join(','))].join('\n');
4
+ };
5
+ export default toCSV;
@@ -0,0 +1,3 @@
1
+ import { default as createBulkExporterFactory } from './createBulkExporter.ts';
2
+ export * from './createBulkExporter.ts';
3
+ export default createBulkExporterFactory;
@@ -0,0 +1,3 @@
1
+ import { default as createBulkExporterFactory } from "./createBulkExporter.js";
2
+ export * from "./createBulkExporter.js";
3
+ export default createBulkExporterFactory;
@@ -5,7 +5,6 @@ export declare const AssortmentCreatePayloadSchema: z.ZodObject<{
5
5
  _id: z.ZodString;
6
6
  specification: z.ZodObject<{
7
7
  isActive: z.ZodBoolean;
8
- isBase: z.ZodOptional<z.ZodBoolean>;
9
8
  isRoot: z.ZodOptional<z.ZodBoolean>;
10
9
  sequence: z.ZodNumber;
11
10
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -71,7 +70,6 @@ declare namespace createAssortment {
71
70
  _id: z.ZodString;
72
71
  specification: z.ZodObject<{
73
72
  isActive: z.ZodBoolean;
74
- isBase: z.ZodOptional<z.ZodBoolean>;
75
73
  isRoot: z.ZodOptional<z.ZodBoolean>;
76
74
  sequence: z.ZodNumber;
77
75
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -8,7 +8,6 @@ export const AssortmentCreatePayloadSchema = z.object({
8
8
  _id: z.string(),
9
9
  specification: z.object({
10
10
  isActive: z.boolean(),
11
- isBase: z.boolean().optional(),
12
11
  isRoot: z.boolean().optional(),
13
12
  sequence: z.number(),
14
13
  tags: z.array(z.string()).optional(),
@@ -5,7 +5,6 @@ export declare const AssortmentUpdatePayloadSchema: z.ZodObject<{
5
5
  _id: z.ZodString;
6
6
  specification: z.ZodOptional<z.ZodObject<{
7
7
  isActive: z.ZodOptional<z.ZodBoolean>;
8
- isBase: z.ZodOptional<z.ZodBoolean>;
9
8
  isRoot: z.ZodOptional<z.ZodBoolean>;
10
9
  sequence: z.ZodOptional<z.ZodNumber>;
11
10
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -72,7 +71,6 @@ declare namespace updateAssortment {
72
71
  _id: z.ZodString;
73
72
  specification: z.ZodOptional<z.ZodObject<{
74
73
  isActive: z.ZodOptional<z.ZodBoolean>;
75
- isBase: z.ZodOptional<z.ZodBoolean>;
76
74
  isRoot: z.ZodOptional<z.ZodBoolean>;
77
75
  sequence: z.ZodOptional<z.ZodNumber>;
78
76
  tags: z.ZodOptional<z.ZodArray<z.ZodString>>;
@@ -10,7 +10,6 @@ export const AssortmentUpdatePayloadSchema = z.object({
10
10
  specification: z
11
11
  .object({
12
12
  isActive: z.boolean().optional(),
13
- isBase: z.boolean().optional(),
14
13
  isRoot: z.boolean().optional(),
15
14
  sequence: z.number().optional(),
16
15
  tags: z.array(z.string()).optional(),
@@ -17,8 +17,8 @@ export const ProductCreateSpecificationSchema = z.object({
17
17
  maxQuantity: z.number().optional(),
18
18
  isTaxable: z.boolean().optional(),
19
19
  isNetPrice: z.boolean().optional(),
20
- currencyCode: z.string(),
21
- countryCode: z.string(),
20
+ currencyCode: z.string().min(1, 'currencyCode is required'),
21
+ countryCode: z.string().min(1, 'countryCode is required'),
22
22
  })),
23
23
  })
24
24
  .optional(),
@@ -16,8 +16,8 @@ export const ProductUpdateSpecificationSchema = z.object({
16
16
  maxQuantity: z.number().optional(),
17
17
  isTaxable: z.boolean().optional(),
18
18
  isNetPrice: z.boolean().optional(),
19
- currencyCode: z.string(),
20
- countryCode: z.string(),
19
+ currencyCode: z.string().min(1, 'currencyCode is required'),
20
+ countryCode: z.string().min(1, 'countryCode is required'),
21
21
  })),
22
22
  })
23
23
  .optional(),
@@ -3,10 +3,12 @@ import { type CustomServices, type Services } from './services/index.ts';
3
3
  import { type Modules, type ModuleOptions } from './modules.ts';
4
4
  import { type BulkImporter, type BulkImportHandler } from './bulk-importer/index.ts';
5
5
  import type { IBaseAdapter } from '@unchainedshop/utils';
6
+ import { type BulkExporter, type BulkExportHandler } from './bulk-exporter/index.ts';
6
7
  export * from './bulk-importer/index.ts';
7
8
  export * from './services/index.ts';
8
9
  export * from './directors/index.ts';
9
10
  export * from './factory/index.ts';
11
+ export * from './bulk-exporter/index.ts';
10
12
  export { default as schedule, type ScheduleData } from './utils/schedule.ts';
11
13
  export interface UnchainedCoreOptions {
12
14
  db: mongodb.Db;
@@ -14,6 +16,9 @@ export interface UnchainedCoreOptions {
14
16
  bulkImporter?: {
15
17
  handlers?: Record<string, BulkImportHandler<UnchainedCore>>;
16
18
  };
19
+ bulkExporter?: {
20
+ handlers?: Record<string, BulkExportHandler<UnchainedCore>>;
21
+ };
17
22
  modules?: Record<string, {
18
23
  configure: (params: ModuleInput<any>) => any;
19
24
  }>;
@@ -25,6 +30,7 @@ export interface UnchainedCore {
25
30
  services: Services;
26
31
  bulkImporter: BulkImporter;
27
32
  options: ModuleOptions;
33
+ bulkExporter: BulkExporter;
28
34
  }
29
- export declare const initCore: ({ db, migrationRepository, bulkImporter: bulkImporterOptions, modules: customModules, services: customServices, options, }: UnchainedCoreOptions) => Promise<UnchainedCore>;
35
+ export declare const initCore: ({ db, migrationRepository, bulkImporter: bulkImporterOptions, modules: customModules, services: customServices, options, bulkExporter: bulkExporterOptions, }: UnchainedCoreOptions) => Promise<UnchainedCore>;
30
36
  export declare const getAllAdapters: () => IBaseAdapter[];
package/lib/core-index.js CHANGED
@@ -3,13 +3,16 @@ import initServices, {} from "./services/index.js";
3
3
  import initModules, {} from "./modules.js";
4
4
  import createBulkImporterFactory, {} from "./bulk-importer/index.js";
5
5
  import { WorkerDirector, DeliveryDirector, DeliveryPricingDirector, EnrollmentDirector, FilterDirector, OrderDiscountDirector, OrderPricingDirector, PaymentDirector, PaymentPricingDirector, ProductDiscountDirector, ProductPricingDirector, QuotationDirector, WarehousingDirector, } from "./directors/index.js";
6
+ import createBulkExporterFactory, {} from "./bulk-exporter/index.js";
6
7
  export * from "./bulk-importer/index.js";
7
8
  export * from "./services/index.js";
8
9
  export * from "./directors/index.js";
9
10
  export * from "./factory/index.js";
11
+ export * from "./bulk-exporter/index.js";
10
12
  export { default as schedule } from "./utils/schedule.js";
11
- export const initCore = async ({ db, migrationRepository, bulkImporter: bulkImporterOptions = {}, modules: customModules = {}, services: customServices = {}, options = {}, }) => {
13
+ export const initCore = async ({ db, migrationRepository, bulkImporter: bulkImporterOptions = {}, modules: customModules = {}, services: customServices = {}, options = {}, bulkExporter: bulkExporterOptions = {}, }) => {
12
14
  const bulkImporter = createBulkImporterFactory(db, bulkImporterOptions);
15
+ const bulkExporter = createBulkExporterFactory(bulkExporterOptions);
13
16
  const modules = await initModules({ db, migrationRepository, options }, customModules);
14
17
  const services = initServices(modules, customServices);
15
18
  return {
@@ -17,6 +20,7 @@ export const initCore = async ({ db, migrationRepository, bulkImporter: bulkImpo
17
20
  services,
18
21
  bulkImporter,
19
22
  options,
23
+ bulkExporter,
20
24
  };
21
25
  };
22
26
  export const getAllAdapters = () => {
@@ -3,11 +3,14 @@ import type { EnrollmentAdapterActions, EnrollmentContext, IEnrollmentAdapter }
3
3
  import type { OrderPosition } from '@unchainedshop/core-orders';
4
4
  import type { Product } from '@unchainedshop/core-products';
5
5
  import type { Enrollment } from '@unchainedshop/core-enrollments';
6
+ import type { Modules } from '../modules.ts';
6
7
  export type IEnrollmentDirector = IBaseDirector<IEnrollmentAdapter> & {
7
8
  transformOrderItemToEnrollment: (item: {
8
9
  orderPosition: OrderPosition;
9
10
  product: Product;
10
11
  }, doc: Omit<Enrollment, 'configuration' | 'productId' | 'quantity' | 'status' | 'periods' | 'log' | '_id' | 'created'>, unchainedAPI: any) => Promise<Omit<Enrollment, 'status' | 'periods' | 'log' | '_id' | 'created'> & Pick<Partial<Enrollment>, '_id' | 'created'>>;
11
- actions: (enrollmentContext: EnrollmentContext, unchainedAPI: any) => Promise<EnrollmentAdapterActions>;
12
+ actions: (enrollmentContext: EnrollmentContext, unchainedAPI: {
13
+ modules: Modules;
14
+ }) => Promise<EnrollmentAdapterActions>;
12
15
  };
13
16
  export declare const EnrollmentDirector: IEnrollmentDirector;
@@ -150,6 +150,8 @@ export const PaymentDirector = {
150
150
  const paymentProvider = await modules.payment.paymentProviders.findProvider({
151
151
  paymentProviderId: orderPayment.paymentProviderId,
152
152
  });
153
+ if (!paymentProvider)
154
+ throw new Error('Payment provider not found: ' + orderPayment.paymentProviderId);
153
155
  const actions = await PaymentDirector.actions(paymentProvider, buildPaymentProviderActionsContext(orderPayment, {
154
156
  ...context,
155
157
  transactionContext: {
@@ -3,6 +3,7 @@ import type { WorkResult } from '@unchainedshop/core-worker';
3
3
  import type { ModuleOptions, Modules } from '../modules.ts';
4
4
  import type { Services } from '../services/index.ts';
5
5
  import type { BulkImporter } from '../bulk-importer/index.ts';
6
+ import type { BulkExporter } from '../bulk-exporter/index.ts';
6
7
  export type IWorkerAdapter<Input, Output> = IBaseAdapter & {
7
8
  type: string;
8
9
  external: boolean;
@@ -12,6 +13,7 @@ export type IWorkerAdapter<Input, Output> = IBaseAdapter & {
12
13
  services: Services;
13
14
  bulkImporter: BulkImporter;
14
15
  options: ModuleOptions;
16
+ bulkExporter: BulkExporter;
15
17
  }, workId: string) => Promise<WorkResult<Output>>;
16
18
  };
17
19
  export declare const WorkerAdapter: Omit<IWorkerAdapter<any, void>, 'key' | 'label' | 'type' | 'version'>;
@@ -0,0 +1,3 @@
1
+ import { type Quotation } from '@unchainedshop/core-quotations';
2
+ import type { Modules } from '../modules.ts';
3
+ export declare function fulfillQuotationService(this: Modules, quotation: Quotation, info: any): Promise<Quotation>;
@@ -1,10 +1,10 @@
1
1
  import { QuotationStatus } from '@unchainedshop/core-quotations';
2
2
  import { processQuotationService } from "./processQuotation.js";
3
- export async function fullfillQuotationService(quotation, info) {
4
- if (quotation.status === QuotationStatus.FULLFILLED)
3
+ export async function fulfillQuotationService(quotation, info) {
4
+ if (quotation.status === QuotationStatus.FULFILLED)
5
5
  return quotation;
6
6
  const updatedQuotation = await this.quotations.updateStatus(quotation._id, {
7
- status: QuotationStatus.FULLFILLED,
7
+ status: QuotationStatus.FULFILLED,
8
8
  info: JSON.stringify(info),
9
9
  });
10
10
  return processQuotationService.bind(this)(updatedQuotation, {});
@@ -32,7 +32,7 @@ import { initializeEnrollmentService } from './initializeEnrollment.ts';
32
32
  import { activateEnrollmentService } from './activateEnrollment.ts';
33
33
  import { terminateEnrollmentService } from './terminateEnrollment.ts';
34
34
  import { invalidateFilterCacheService } from './invalidateFilterCache.ts';
35
- import { fullfillQuotationService } from './fullfillQuotation.ts';
35
+ import { fulfillQuotationService } from './fulfillQuotation.ts';
36
36
  import { processQuotationService } from './processQuotation.ts';
37
37
  import { proposeQuotationService } from './proposeQuotation.ts';
38
38
  import { rejectQuotationService } from './rejectQuotation.ts';
@@ -116,7 +116,7 @@ export default function initServices(modules: Modules, customServices?: CustomSe
116
116
  terminateEnrollment: Bound<typeof terminateEnrollmentService>;
117
117
  };
118
118
  quotations: {
119
- fullfillQuotation: Bound<typeof fullfillQuotationService>;
119
+ fulfillQuotation: Bound<typeof fulfillQuotationService>;
120
120
  processQuotation: Bound<typeof processQuotationService>;
121
121
  proposeQuotation: Bound<typeof proposeQuotationService>;
122
122
  rejectQuotation: Bound<typeof rejectQuotationService>;