@unchainedshop/core-quotations 3.0.0-alpha7 → 3.0.0-rc2

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 (62) hide show
  1. package/lib/db/QuotationsCollection.d.ts +39 -2
  2. package/lib/db/QuotationsCollection.d.ts.map +1 -1
  3. package/lib/db/QuotationsCollection.js +8 -0
  4. package/lib/db/QuotationsCollection.js.map +1 -1
  5. package/lib/director/QuotationDirector.d.ts.map +1 -1
  6. package/lib/director/QuotationDirector.js.map +1 -1
  7. package/lib/module/configureQuotationsModule.d.ts +31 -45
  8. package/lib/module/configureQuotationsModule.d.ts.map +1 -1
  9. package/lib/module/configureQuotationsModule.js +17 -124
  10. package/lib/module/configureQuotationsModule.js.map +1 -1
  11. package/lib/module/configureQuotationsModule_BACKUP_56551.d.ts +52 -0
  12. package/lib/module/configureQuotationsModule_BACKUP_56551.d.ts.map +1 -0
  13. package/lib/module/configureQuotationsModule_BACKUP_56551.js +152 -0
  14. package/lib/module/configureQuotationsModule_BACKUP_56551.js.map +1 -0
  15. package/lib/module/configureQuotationsModule_BASE_56551.d.ts +65 -0
  16. package/lib/module/configureQuotationsModule_BASE_56551.d.ts.map +1 -0
  17. package/lib/module/configureQuotationsModule_BASE_56551.js +259 -0
  18. package/lib/module/configureQuotationsModule_BASE_56551.js.map +1 -0
  19. package/lib/module/configureQuotationsModule_LOCAL_56551.d.ts +51 -0
  20. package/lib/module/configureQuotationsModule_LOCAL_56551.d.ts.map +1 -0
  21. package/lib/module/configureQuotationsModule_LOCAL_56551.js +145 -0
  22. package/lib/module/configureQuotationsModule_LOCAL_56551.js.map +1 -0
  23. package/lib/module/configureQuotationsModule_REMOTE_56551.d.ts +66 -0
  24. package/lib/module/configureQuotationsModule_REMOTE_56551.d.ts.map +1 -0
  25. package/lib/module/configureQuotationsModule_REMOTE_56551.js +266 -0
  26. package/lib/module/configureQuotationsModule_REMOTE_56551.js.map +1 -0
  27. package/lib/quotations-index.d.ts +1 -5
  28. package/lib/quotations-index.d.ts.map +1 -1
  29. package/lib/quotations-index.js +1 -5
  30. package/lib/quotations-index.js.map +1 -1
  31. package/lib/quotations-index_BACKUP_56874.d.ts +4 -0
  32. package/lib/quotations-index_BACKUP_56874.d.ts.map +1 -0
  33. package/lib/quotations-index_BACKUP_56874.js +4 -0
  34. package/lib/quotations-index_BACKUP_56874.js.map +1 -0
  35. package/lib/quotations-index_BASE_56874.d.ts +8 -0
  36. package/lib/quotations-index_BASE_56874.d.ts.map +1 -0
  37. package/lib/quotations-index_BASE_56874.js +8 -0
  38. package/lib/quotations-index_BASE_56874.js.map +1 -0
  39. package/lib/quotations-index_LOCAL_56874.d.ts +4 -0
  40. package/lib/quotations-index_LOCAL_56874.d.ts.map +1 -0
  41. package/lib/quotations-index_LOCAL_56874.js +4 -0
  42. package/lib/quotations-index_LOCAL_56874.js.map +1 -0
  43. package/lib/quotations-index_REMOTE_56874.d.ts +9 -0
  44. package/lib/quotations-index_REMOTE_56874.d.ts.map +1 -0
  45. package/lib/quotations-index_REMOTE_56874.js +9 -0
  46. package/lib/quotations-index_REMOTE_56874.js.map +1 -0
  47. package/lib/quotations-settings.d.ts +1 -1
  48. package/lib/quotations-settings.d.ts.map +1 -1
  49. package/lib/types.d.ts +5 -6
  50. package/lib/types.d.ts.map +1 -1
  51. package/lib/types.js.map +1 -1
  52. package/package.json +9 -12
  53. package/src/db/QuotationsCollection.ts +34 -2
  54. package/src/module/configureQuotationsModule.ts +50 -234
  55. package/src/quotations-index.ts +2 -8
  56. package/src/quotations-settings.ts +1 -1
  57. package/tsconfig.json +5 -7
  58. package/src/db/QuotationStatus.ts +0 -7
  59. package/src/director/QuotationAdapter.ts +0 -51
  60. package/src/director/QuotationDirector.ts +0 -99
  61. package/src/director/QuotationError.ts +0 -6
  62. package/src/types.ts +0 -74
@@ -1,6 +1,5 @@
1
1
  import { SortDirection, SortOption } from '@unchainedshop/utils';
2
- import { UnchainedCore } from '@unchainedshop/core';
3
- import { Quotation, QuotationItemConfiguration, QuotationProposal } from '../types.js';
2
+ import { Quotation, QuotationStatus } from '../db/QuotationsCollection.js';
4
3
  import { emit, registerEvents } from '@unchainedshop/events';
5
4
  import {
6
5
  generateDbFilterById,
@@ -10,81 +9,18 @@ import {
10
9
  ModuleInput,
11
10
  } from '@unchainedshop/mongodb';
12
11
  import { QuotationsCollection } from '../db/QuotationsCollection.js';
13
- import { QuotationStatus } from '../db/QuotationStatus.js';
14
- import { QuotationDirector } from '../quotations-index.js';
15
12
  import { quotationsSettings, QuotationsSettingsOptions } from '../quotations-settings.js';
16
- import { resolveBestCurrency } from '@unchainedshop/utils';
17
13
 
18
14
  export type QuotationQuery = {
19
15
  userId?: string;
20
16
  queryString?: string;
21
17
  };
22
- export interface QuotationQueries {
23
- findQuotation: (query: { quotationId: string }, options?: mongodb.FindOptions) => Promise<Quotation>;
24
- findQuotations: (
25
- query: QuotationQuery & {
26
- limit?: number;
27
- offset?: number;
28
- sort?: Array<SortOption>;
29
- },
30
- options?: mongodb.FindOptions,
31
- ) => Promise<Array<Quotation>>;
32
- count: (query: QuotationQuery) => Promise<number>;
33
- openQuotationWithProduct: (param: { productId: string }) => Promise<Quotation | null>;
34
- }
35
-
36
- // Transformations
37
-
38
- export interface QuotationTransformations {
39
- isExpired: (quotation: Quotation, params?: { referenceDate: Date }) => boolean;
40
- isProposalValid: (quotation: Quotation) => boolean;
41
- normalizedStatus: (quotation: Quotation) => string;
42
- }
43
-
44
- // Processing
45
-
46
- export type QuotationContextParams = (
47
- quotation: Quotation,
48
- params: { quotationContext?: any },
49
- unchainedAPI: UnchainedCore,
50
- ) => Promise<Quotation>;
51
-
52
- // Mutations
53
18
  export interface QuotationData {
54
19
  configuration?: Array<{ key: string; value: string }>;
55
20
  countryCode?: string;
56
21
  productId: string;
57
22
  userId: string;
58
23
  }
59
- export interface QuotationMutations {
60
- create: (doc: QuotationData, unchainedAPI: UnchainedCore) => Promise<Quotation>;
61
-
62
- updateContext: (quotationId: string, context: any) => Promise<Quotation | null>;
63
-
64
- updateProposal: (quotationId: string, proposal: QuotationProposal) => Promise<Quotation>;
65
-
66
- updateStatus: (
67
- quotationId: string,
68
- params: { status: QuotationStatus; info?: string },
69
- ) => Promise<Quotation>;
70
- }
71
-
72
- export interface QuotationProcessing {
73
- fullfillQuotation: (quotationId: string, info: any, unchainedAPI: UnchainedCore) => Promise<Quotation>;
74
- proposeQuotation: QuotationContextParams;
75
- rejectQuotation: QuotationContextParams;
76
- verifyQuotation: QuotationContextParams;
77
- transformItemConfiguration: (
78
- quotation: Quotation,
79
- configuration: QuotationItemConfiguration,
80
- unchainedAPI: UnchainedCore,
81
- ) => Promise<QuotationItemConfiguration>;
82
- }
83
-
84
- export type QuotationsModule = QuotationQueries &
85
- QuotationTransformations &
86
- QuotationProcessing &
87
- QuotationMutations;
88
24
 
89
25
  const QUOTATION_EVENTS: string[] = ['QUOTATION_REQUEST_CREATE', 'QUOTATION_REMOVE', 'QUOTATION_UPDATE'];
90
26
 
@@ -100,17 +36,10 @@ export const buildFindSelector = (query: QuotationQuery = {}) => {
100
36
  return selector;
101
37
  };
102
38
 
103
- const isExpired: QuotationsModule['isExpired'] = (quotation, { referenceDate }) => {
104
- const relevantDate = referenceDate ? new Date(referenceDate) : new Date();
105
- const expiryDate = new Date(quotation.expires);
106
- const isQuotationExpired = relevantDate.getTime() > expiryDate.getTime();
107
- return isQuotationExpired;
108
- };
109
-
110
39
  export const configureQuotationsModule = async ({
111
40
  db,
112
41
  options: quotationsOptions = {},
113
- }: ModuleInput<QuotationsSettingsOptions>): Promise<QuotationsModule> => {
42
+ }: ModuleInput<QuotationsSettingsOptions>) => {
114
43
  registerEvents(QUOTATION_EVENTS);
115
44
 
116
45
  quotationsSettings.configureSettings(quotationsOptions);
@@ -125,27 +54,10 @@ export const configureQuotationsModule = async ({
125
54
  return findNewQuotationNumber(quotation, index + 1);
126
55
  };
127
56
 
128
- const findNextStatus = async (
129
- quotation: Quotation,
130
- unchainedAPI: UnchainedCore,
131
- ): Promise<QuotationStatus> => {
132
- let status = quotation.status as QuotationStatus;
133
- const director = await QuotationDirector.actions({ quotation }, unchainedAPI);
134
-
135
- if (status === QuotationStatus.REQUESTED) {
136
- if (!(await director.isManualRequestVerificationRequired())) {
137
- status = QuotationStatus.PROCESSING;
138
- }
139
- }
140
- if (status === QuotationStatus.PROCESSING) {
141
- if (!(await director.isManualProposalRequired())) {
142
- status = QuotationStatus.PROPOSED;
143
- }
144
- }
145
- return status;
146
- };
147
-
148
- const updateStatus: QuotationsModule['updateStatus'] = async (quotationId, { status, info = '' }) => {
57
+ const updateStatus = async (
58
+ quotationId: string,
59
+ { status, info = '' }: { status: QuotationStatus; info?: string },
60
+ ): Promise<Quotation> => {
149
61
  const selector = generateDbFilterById(quotationId);
150
62
  const quotation = await Quotations.findOne(selector, {});
151
63
 
@@ -197,66 +109,9 @@ export const configureQuotationsModule = async ({
197
109
  return updatedQuotation;
198
110
  };
199
111
 
200
- const processQuotation = async (
201
- initialQuotation: Quotation,
202
- params: { quotationContext?: any },
203
- unchainedAPI: UnchainedCore,
204
- ) => {
205
- const { modules } = unchainedAPI;
206
-
207
- const quotationId = initialQuotation._id;
208
- let quotation = initialQuotation;
209
- let nextStatus = await findNextStatus(quotation, unchainedAPI);
210
- const director = await QuotationDirector.actions({ quotation }, unchainedAPI);
211
-
212
- if (quotation.status === QuotationStatus.REQUESTED && nextStatus !== QuotationStatus.REQUESTED) {
213
- await director.submitRequest(params.quotationContext);
214
- }
215
-
216
- quotation = await modules.quotations.findQuotation({ quotationId });
217
- nextStatus = await findNextStatus(quotation, unchainedAPI);
218
- if (nextStatus !== QuotationStatus.PROCESSING) {
219
- await director.verifyRequest(params.quotationContext);
220
- }
221
-
222
- quotation = await modules.quotations.findQuotation({ quotationId });
223
- nextStatus = await findNextStatus(quotation, unchainedAPI);
224
- if (nextStatus === QuotationStatus.REJECTED) {
225
- await director.rejectRequest(params.quotationContext);
226
- }
227
-
228
- quotation = await modules.quotations.findQuotation({ quotationId });
229
- nextStatus = await findNextStatus(quotation, unchainedAPI);
230
- if (nextStatus === QuotationStatus.PROPOSED) {
231
- const proposal = await director.quote();
232
- quotation = await modules.quotations.updateProposal(quotation._id, proposal);
233
- nextStatus = await findNextStatus(quotation, unchainedAPI);
234
- }
235
-
236
- return updateStatus(quotation._id, { status: nextStatus, info: 'quotation processed' });
237
- };
238
-
239
- const sendStatusToCustomer = async (quotation: Quotation, unchainedAPI: UnchainedCore) => {
240
- const { modules } = unchainedAPI;
241
-
242
- const user = await modules.users.findUserById(quotation.userId);
243
- const locale = modules.users.userLocale(user);
244
-
245
- await modules.worker.addWork({
246
- type: 'MESSAGE',
247
- retries: 0,
248
- input: {
249
- locale,
250
- template: 'QUOTATION_STATUS',
251
- quotationId: quotation._id,
252
- },
253
- });
254
-
255
- return quotation;
256
- };
257
-
258
112
  const updateQuotationFields =
259
- (fieldKeys: Array<string>) => async (quotationId: string, values: any) => {
113
+ (fieldKeys: Array<string>) =>
114
+ async (quotationId: string, values: any): Promise<Quotation> => {
260
115
  const quotation = await Quotations.findOneAndUpdate(generateDbFilterById(quotationId), {
261
116
  $set: {
262
117
  updated: new Date(),
@@ -271,23 +126,38 @@ export const configureQuotationsModule = async ({
271
126
 
272
127
  return {
273
128
  // Queries
274
- count: async (query) => {
129
+ count: async (query: QuotationQuery): Promise<number> => {
275
130
  const quotationCount = await Quotations.countDocuments(buildFindSelector(query));
276
131
  return quotationCount;
277
132
  },
278
- openQuotationWithProduct: async ({ productId }) => {
133
+ openQuotationWithProduct: async ({ productId }: { productId: string }): Promise<Quotation> => {
279
134
  const selector: mongodb.Filter<Quotation> = { productId };
280
135
  selector.status = { $in: [QuotationStatus.REQUESTED, QuotationStatus.PROPOSED] };
281
136
 
282
137
  return Quotations.findOne(selector);
283
138
  },
284
139
 
285
- findQuotation: async ({ quotationId }, options) => {
140
+ findQuotation: async (
141
+ { quotationId }: { quotationId: string },
142
+ options?: mongodb.FindOptions<Quotation>,
143
+ ): Promise<Quotation> => {
286
144
  const selector = generateDbFilterById(quotationId);
287
145
  return Quotations.findOne(selector, options);
288
146
  },
289
147
 
290
- findQuotations: async ({ limit, offset, sort, ...query }, options) => {
148
+ findQuotations: async (
149
+ {
150
+ limit,
151
+ offset,
152
+ sort,
153
+ ...query
154
+ }: QuotationQuery & {
155
+ limit?: number;
156
+ offset?: number;
157
+ sort?: Array<SortOption>;
158
+ },
159
+ options?: mongodb.FindOptions<Quotation>,
160
+ ): Promise<Array<Quotation>> => {
291
161
  const defaultSortOption: Array<SortOption> = [{ key: 'created', value: SortDirection.ASC }];
292
162
  const quotations = Quotations.find(buildFindSelector(query), {
293
163
  limit,
@@ -300,87 +170,29 @@ export const configureQuotationsModule = async ({
300
170
  },
301
171
 
302
172
  // Transformations
303
- normalizedStatus: (quotation) => {
173
+ normalizedStatus: (quotation: Quotation): QuotationStatus => {
304
174
  return quotation.status === null
305
175
  ? QuotationStatus.REQUESTED
306
176
  : (quotation.status as QuotationStatus);
307
177
  },
308
178
 
309
- isExpired,
310
-
311
- isProposalValid: (quotation) => {
312
- return quotation.status === QuotationStatus.PROPOSED && !isExpired(quotation);
313
- },
314
-
315
- // Processing
316
- fullfillQuotation: async (quotationId, info, unchainedAPI) => {
317
- const selector = generateDbFilterById(quotationId);
318
- const quotation = await Quotations.findOne(selector, {});
319
-
320
- if (quotation.status === QuotationStatus.FULLFILLED) return quotation;
321
-
322
- let updatedQuotation = await updateStatus(quotation._id, {
323
- status: QuotationStatus.FULLFILLED,
324
- info: JSON.stringify(info),
325
- });
326
-
327
- updatedQuotation = await processQuotation(updatedQuotation, {}, unchainedAPI);
328
-
329
- return sendStatusToCustomer(updatedQuotation, unchainedAPI);
179
+ isExpired(quotation: Quotation, { referenceDate }: { referenceDate: Date }) {
180
+ const relevantDate = referenceDate ? new Date(referenceDate) : new Date();
181
+ const expiryDate = new Date(quotation.expires);
182
+ const isQuotationExpired = relevantDate.getTime() > expiryDate.getTime();
183
+ return isQuotationExpired;
330
184
  },
331
185
 
332
- proposeQuotation: async (quotation, { quotationContext }, unchainedAPI) => {
333
- if (quotation.status !== QuotationStatus.PROCESSING) return quotation;
334
-
335
- let updatedQuotation = await updateStatus(quotation._id, {
336
- status: QuotationStatus.PROPOSED,
337
- info: 'proposed manually',
338
- });
339
-
340
- updatedQuotation = await processQuotation(updatedQuotation, { quotationContext }, unchainedAPI);
341
-
342
- return sendStatusToCustomer(updatedQuotation, unchainedAPI);
343
- },
344
-
345
- rejectQuotation: async (quotation, { quotationContext }, unchainedAPI) => {
346
- if (quotation.status === QuotationStatus.FULLFILLED) return quotation;
347
-
348
- let updatedQuotation = await updateStatus(quotation._id, {
349
- status: QuotationStatus.REJECTED,
350
- info: 'rejected manually',
351
- });
352
-
353
- updatedQuotation = await processQuotation(updatedQuotation, { quotationContext }, unchainedAPI);
354
-
355
- return sendStatusToCustomer(updatedQuotation, unchainedAPI);
356
- },
357
-
358
- verifyQuotation: async (quotation, { quotationContext }, unchainedAPI) => {
359
- if (quotation.status !== QuotationStatus.REQUESTED) return quotation;
360
-
361
- let updatedQuotation = await updateStatus(quotation._id, {
362
- status: QuotationStatus.PROCESSING,
363
- info: 'verified elligibility manually',
364
- });
365
-
366
- updatedQuotation = await processQuotation(updatedQuotation, { quotationContext }, unchainedAPI);
367
-
368
- return sendStatusToCustomer(updatedQuotation, unchainedAPI);
369
- },
370
-
371
- transformItemConfiguration: async (quotation, configuration, unchainedAPI) => {
372
- const director = await QuotationDirector.actions({ quotation }, unchainedAPI);
373
- return director.transformItemConfiguration(configuration);
186
+ isProposalValid(quotation: Quotation): boolean {
187
+ return quotation.status === QuotationStatus.PROPOSED && !this.isExpired(quotation);
374
188
  },
375
189
 
376
190
  // Mutations
377
- create: async ({ countryCode, ...quotationData }, unchainedAPI) => {
378
- const { modules } = unchainedAPI;
379
-
380
- const countryObject = await modules.countries.findCountry({ isoCode: countryCode });
381
- const currencies = await modules.currencies.findCurrencies({ includeInactive: false });
382
- const currency = resolveBestCurrency(countryObject.defaultCurrencyCode, currencies);
383
-
191
+ create: async ({
192
+ countryCode,
193
+ currency,
194
+ ...quotationData
195
+ }: QuotationData & { currency: string }): Promise<Quotation> => {
384
196
  const { insertedId: quotationId } = await Quotations.insertOne({
385
197
  _id: generateDbObjectId(),
386
198
  created: new Date(),
@@ -392,20 +204,24 @@ export const configureQuotationsModule = async ({
392
204
  status: QuotationStatus.REQUESTED,
393
205
  });
394
206
 
395
- const newQuotation = await Quotations.findOne(generateDbFilterById(quotationId), {});
396
-
397
- let quotation = await processQuotation(newQuotation, {}, unchainedAPI);
398
-
399
- quotation = await sendStatusToCustomer(quotation, unchainedAPI);
207
+ const quotation = await Quotations.findOne(generateDbFilterById(quotationId), {});
400
208
 
401
209
  await emit('QUOTATION_REQUEST_CREATE', { quotation });
402
210
 
403
211
  return quotation;
404
212
  },
405
-
213
+ deleteRequestedUserQuotations: async (userId: string) => {
214
+ const { deletedCount } = await Quotations.deleteMany({
215
+ userId,
216
+ status: { $in: [QuotationStatus.REQUESTED, null] },
217
+ });
218
+ return deletedCount;
219
+ },
406
220
  updateContext: updateQuotationFields(['context']),
407
221
  updateProposal: updateQuotationFields(['price', 'expires', 'meta']),
408
222
 
409
223
  updateStatus,
410
224
  };
411
225
  };
226
+
227
+ export type QuotationsModule = Awaited<ReturnType<typeof configureQuotationsModule>>;
@@ -1,10 +1,4 @@
1
- export * from './types.js';
1
+ export * from './db/QuotationsCollection.js';
2
+
2
3
  export * from './module/configureQuotationsModule.js';
3
4
  export * from './quotations-settings.js';
4
-
5
- export { QuotationStatus } from './db/QuotationStatus.js';
6
-
7
- export { QuotationAdapter } from './director/QuotationAdapter.js';
8
- export { QuotationDirector } from './director/QuotationDirector.js';
9
-
10
- export { QuotationError } from './director/QuotationError.js';
@@ -1,5 +1,5 @@
1
1
  import { generateRandomHash } from '@unchainedshop/utils';
2
- import { Quotation } from './types.js';
2
+ import { Quotation } from './db/QuotationsCollection.js';
3
3
 
4
4
  export interface QuotationsSettingsOptions {
5
5
  quotationNumberHashFn?: (quotation: Quotation, index: number) => string;
package/tsconfig.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "extends": "../shared/base.tsconfig.json",
3
3
  "compilerOptions": {
4
- "rootDir": "src",
5
- "outDir": "lib",
4
+ "declarationDir": "./lib",
5
+ "rootDir": "./src",
6
+ "outDir": "./lib"
6
7
  },
7
- "exclude": ["**/*.test.ts", "**/*.test.js", "tests"],
8
- "include": [
9
- "./src"
10
- ]
11
- }
8
+ "exclude": ["**/*.test.ts", "**/*.test.js", "tests", "lib"]
9
+ }
@@ -1,7 +0,0 @@
1
- export enum QuotationStatus {
2
- REQUESTED = 'REQUESTED',
3
- PROCESSING = 'PROCESSING',
4
- PROPOSED = 'PROPOSED',
5
- FULLFILLED = 'FULLFILLED',
6
- REJECTED = 'REJECTED',
7
- }
@@ -1,51 +0,0 @@
1
- import { log, LogLevel } from '@unchainedshop/logger';
2
- import { IQuotationAdapter } from '../types.js';
3
- import { QuotationError } from './QuotationError.js';
4
-
5
- export const QuotationAdapter: Omit<IQuotationAdapter, 'key' | 'label' | 'version'> = {
6
- orderIndex: 0,
7
-
8
- isActivatedFor: () => {
9
- return false;
10
- },
11
-
12
- actions: () => {
13
- return {
14
- configurationError: () => {
15
- return QuotationError.NOT_IMPLEMENTED;
16
- },
17
-
18
- isManualRequestVerificationRequired: async () => {
19
- return true;
20
- },
21
-
22
- isManualProposalRequired: async () => {
23
- return true;
24
- },
25
-
26
- quote: async () => {
27
- return {};
28
- },
29
-
30
- rejectRequest: async () => {
31
- return true;
32
- },
33
-
34
- submitRequest: async () => {
35
- return true;
36
- },
37
-
38
- verifyRequest: async () => {
39
- return true;
40
- },
41
-
42
- transformItemConfiguration: async ({ quantity, configuration }) => {
43
- return { quantity, configuration };
44
- },
45
- };
46
- },
47
-
48
- log(message: string, { level = LogLevel.Debug, ...options } = {}) {
49
- return log(message, { level, ...options });
50
- },
51
- };
@@ -1,99 +0,0 @@
1
- import { LogLevel, log } from '@unchainedshop/logger';
2
- import { IQuotationAdapter, IQuotationDirector, QuotationContext } from '../types.js';
3
- import { BaseDirector } from '@unchainedshop/utils';
4
- import { UnchainedCore } from '@unchainedshop/core';
5
- import { QuotationError } from './QuotationError.js';
6
-
7
- const baseDirector = BaseDirector<IQuotationAdapter>('QuotationDirector', {
8
- adapterSortKey: 'orderIndex',
9
- });
10
-
11
- const findAppropriateAdapters = (quotationContext: QuotationContext, unchainedAPI: UnchainedCore) =>
12
- baseDirector.getAdapters({
13
- adapterFilter: (Adapter: IQuotationAdapter) => {
14
- const activated = Adapter.isActivatedFor(quotationContext, unchainedAPI);
15
- if (!activated) {
16
- log(`Quotation Director -> ${Adapter.key} (${Adapter.version}) skipped`, {
17
- level: LogLevel.Warning,
18
- });
19
- }
20
- return activated;
21
- },
22
- });
23
-
24
- export const QuotationDirector: IQuotationDirector = {
25
- ...baseDirector,
26
-
27
- actions: async (quotationContext, unchainedAPI) => {
28
- const context = { ...quotationContext, ...unchainedAPI };
29
-
30
- const Adapter = findAppropriateAdapters(quotationContext, unchainedAPI)?.shift();
31
-
32
- if (!Adapter) {
33
- throw new Error('No suitable quotation plugin available for this context');
34
- }
35
-
36
- const adapter = Adapter.actions(context);
37
-
38
- return {
39
- configurationError: () => {
40
- try {
41
- return adapter.configurationError();
42
- } catch (error) {
43
- log('QuotationDirector -> Error while checking for configurationError', {
44
- level: LogLevel.Warning,
45
- ...error,
46
- });
47
- return QuotationError.ADAPTER_NOT_FOUND;
48
- }
49
- },
50
-
51
- isManualRequestVerificationRequired: async () => {
52
- try {
53
- const isRequired = await adapter.isManualRequestVerificationRequired();
54
- return isRequired;
55
- } catch (error) {
56
- log('QuotationDirector -> Error while checking if is manual request verification required', {
57
- level: LogLevel.Error,
58
- ...error,
59
- });
60
- return null;
61
- }
62
- },
63
-
64
- isManualProposalRequired: async () => {
65
- try {
66
- const isRequired = await adapter.isManualProposalRequired();
67
- return isRequired;
68
- } catch (error) {
69
- log('QuotationDirector -> Error while checking if is manual proposal required', {
70
- level: LogLevel.Error,
71
- ...error,
72
- });
73
- return null;
74
- }
75
- },
76
-
77
- quote: adapter.quote,
78
- rejectRequest: adapter.rejectRequest,
79
- submitRequest: adapter.submitRequest,
80
- verifyRequest: adapter.verifyRequest,
81
-
82
- transformItemConfiguration: async ({ quantity, configuration }) => {
83
- try {
84
- const itemConfiguration = await adapter.transformItemConfiguration({
85
- quantity,
86
- configuration,
87
- });
88
- return itemConfiguration;
89
- } catch (error) {
90
- log('QuotationDirector -> Error while transforming item configuration', {
91
- level: LogLevel.Error,
92
- ...error,
93
- });
94
- return null;
95
- }
96
- },
97
- };
98
- },
99
- };
@@ -1,6 +0,0 @@
1
- export enum QuotationError {
2
- ADAPTER_NOT_FOUND = 'ADAPTER_NOT_FOUND',
3
- NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
4
- INCOMPLETE_CONFIGURATION = 'INCOMPLETE_CONFIGURATION',
5
- WRONG_CREDENTIALS = 'WRONG_CREDENTIALS',
6
- }
package/src/types.ts DELETED
@@ -1,74 +0,0 @@
1
- import type { IBaseAdapter, IBaseDirector } from '@unchainedshop/utils';
2
- import type { TimestampFields, LogFields } from '@unchainedshop/mongodb';
3
- import type { UnchainedCore } from '@unchainedshop/core';
4
-
5
- export enum QuotationStatus {
6
- REQUESTED = 'REQUESTED',
7
- PROCESSING = 'PROCESSING',
8
- PROPOSED = 'PROPOSED',
9
- FULLFILLED = 'FULLFILLED',
10
- REJECTED = 'REJECTED',
11
- }
12
-
13
- export enum QuotationError {
14
- ADAPTER_NOT_FOUND = 'ADAPTER_NOT_FOUND',
15
- NOT_IMPLEMENTED = 'NOT_IMPLEMENTED',
16
- INCOMPLETE_CONFIGURATION = 'INCOMPLETE_CONFIGURATION',
17
- WRONG_CREDENTIALS = 'WRONG_CREDENTIALS',
18
- }
19
-
20
- export type Quotation = {
21
- _id?: string;
22
- configuration?: Array<{ key: string; value: string }>;
23
- context?: any;
24
- countryCode?: string;
25
- currency?: string;
26
- expires?: Date;
27
- fullfilled?: Date;
28
- meta?: any;
29
- price?: number;
30
- productId: string;
31
- quotationNumber?: string;
32
- rejected?: Date;
33
- status: string;
34
- userId: string;
35
- } & LogFields &
36
- TimestampFields;
37
-
38
- export type QuotationProposal = { price?: number; expires?: Date; meta?: any };
39
-
40
- export interface QuotationItemConfiguration {
41
- quantity?: number;
42
- configuration: Array<{ key: string; value: string }>;
43
- }
44
-
45
- export type QuotationContext = {
46
- quotation?: Quotation;
47
- };
48
-
49
- export interface QuotationAdapterActions {
50
- configurationError: () => QuotationError;
51
- isManualProposalRequired: () => Promise<boolean>;
52
- isManualRequestVerificationRequired: () => Promise<boolean>;
53
- quote: () => Promise<QuotationProposal>;
54
- rejectRequest: (unchainedAPI?: any) => Promise<boolean>;
55
- submitRequest: (unchainedAPI?: any) => Promise<boolean>;
56
- verifyRequest: (unchainedAPI?: any) => Promise<boolean>;
57
-
58
- transformItemConfiguration: (
59
- params: QuotationItemConfiguration,
60
- ) => Promise<QuotationItemConfiguration>;
61
- }
62
-
63
- export type IQuotationAdapter = IBaseAdapter & {
64
- orderIndex: number;
65
- isActivatedFor: (quotationContext: QuotationContext, unchainedAPI: UnchainedCore) => boolean;
66
- actions: (params: QuotationContext & UnchainedCore) => QuotationAdapterActions;
67
- };
68
-
69
- export type IQuotationDirector = IBaseDirector<IQuotationAdapter> & {
70
- actions: (
71
- quotationContext: QuotationContext,
72
- unchainedAPI: UnchainedCore,
73
- ) => Promise<QuotationAdapterActions>;
74
- };