@unchainedshop/core-delivery 1.1.3

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 (57) hide show
  1. package/.npm/package/README +7 -0
  2. package/.npm/package/npm-shrinkwrap.json +190 -0
  3. package/.versions +54 -0
  4. package/README.md +3 -0
  5. package/lib/db/DeliveryProvidersCollection.d.ts +3 -0
  6. package/lib/db/DeliveryProvidersCollection.js +7 -0
  7. package/lib/db/DeliveryProvidersCollection.js.map +1 -0
  8. package/lib/db/DeliveryProvidersSchema.d.ts +1 -0
  9. package/lib/db/DeliveryProvidersSchema.js +13 -0
  10. package/lib/db/DeliveryProvidersSchema.js.map +1 -0
  11. package/lib/delivery-index.d.ts +8 -0
  12. package/lib/delivery-index.js +9 -0
  13. package/lib/delivery-index.js.map +1 -0
  14. package/lib/delivery-settings.d.ts +2 -0
  15. package/lib/delivery-settings.js +29 -0
  16. package/lib/delivery-settings.js.map +1 -0
  17. package/lib/director/DeliveryAdapter.d.ts +2 -0
  18. package/lib/director/DeliveryAdapter.js +44 -0
  19. package/lib/director/DeliveryAdapter.js.map +1 -0
  20. package/lib/director/DeliveryDirector.d.ts +2 -0
  21. package/lib/director/DeliveryDirector.js +69 -0
  22. package/lib/director/DeliveryDirector.js.map +1 -0
  23. package/lib/director/DeliveryError.d.ts +6 -0
  24. package/lib/director/DeliveryError.js +8 -0
  25. package/lib/director/DeliveryError.js.map +1 -0
  26. package/lib/director/DeliveryPricingAdapter.d.ts +2 -0
  27. package/lib/director/DeliveryPricingAdapter.js +25 -0
  28. package/lib/director/DeliveryPricingAdapter.js.map +1 -0
  29. package/lib/director/DeliveryPricingDirector.d.ts +2 -0
  30. package/lib/director/DeliveryPricingDirector.js +50 -0
  31. package/lib/director/DeliveryPricingDirector.js.map +1 -0
  32. package/lib/director/DeliveryPricingSheet.d.ts +3 -0
  33. package/lib/director/DeliveryPricingSheet.js +92 -0
  34. package/lib/director/DeliveryPricingSheet.js.map +1 -0
  35. package/lib/director/DeliveryProviderType.d.ts +4 -0
  36. package/lib/director/DeliveryProviderType.js +6 -0
  37. package/lib/director/DeliveryProviderType.js.map +1 -0
  38. package/lib/module/configureDeliveryModule.d.ts +3 -0
  39. package/lib/module/configureDeliveryModule.js +137 -0
  40. package/lib/module/configureDeliveryModule.js.map +1 -0
  41. package/package.js +33 -0
  42. package/package.json +43 -0
  43. package/src/db/DeliveryProvidersCollection.ts +11 -0
  44. package/src/db/DeliveryProvidersSchema.js +16 -0
  45. package/src/delivery-index.ts +11 -0
  46. package/src/delivery-settings.ts +46 -0
  47. package/src/director/DeliveryAdapter.ts +56 -0
  48. package/src/director/DeliveryDirector.ts +76 -0
  49. package/src/director/DeliveryError.ts +6 -0
  50. package/src/director/DeliveryPricingAdapter.ts +39 -0
  51. package/src/director/DeliveryPricingDirector.ts +68 -0
  52. package/src/director/DeliveryPricingSheet.ts +112 -0
  53. package/src/director/DeliveryProviderType.ts +4 -0
  54. package/src/module/configureDeliveryModule.ts +206 -0
  55. package/tests/delivery-index.test.ts +22 -0
  56. package/tsconfig.build.json +26 -0
  57. package/tsconfig.json +8 -0
@@ -0,0 +1,68 @@
1
+ import {
2
+ DeliveryPricingAdapterContext,
3
+ DeliveryPricingCalculation,
4
+ DeliveryPricingContext,
5
+ IDeliveryPricingAdapter,
6
+ IDeliveryPricingDirector,
7
+ } from '@unchainedshop/types/delivery.pricing';
8
+ import { BasePricingDirector } from '@unchainedshop/utils';
9
+ import { DeliveryPricingSheet } from './DeliveryPricingSheet';
10
+
11
+ const baseDirector = BasePricingDirector<
12
+ DeliveryPricingContext,
13
+ DeliveryPricingAdapterContext,
14
+ DeliveryPricingCalculation,
15
+ IDeliveryPricingAdapter
16
+ >('DeliveryPricingDirector');
17
+
18
+ export const DeliveryPricingDirector: IDeliveryPricingDirector = {
19
+ ...baseDirector,
20
+
21
+ async buildPricingContext({ item, ...context }, requestContext) {
22
+ const { modules } = requestContext;
23
+
24
+ if (!item)
25
+ return {
26
+ discounts: [],
27
+ ...context,
28
+ ...requestContext,
29
+ };
30
+
31
+ const order = await modules.orders.findOrder({
32
+ orderId: item.orderId,
33
+ });
34
+ const provider = await modules.delivery.findProvider({
35
+ deliveryProviderId: item.deliveryProviderId,
36
+ });
37
+ const user = await modules.users.findUserById(order.userId);
38
+ const discounts = await modules.orders.discounts.findOrderDiscounts({
39
+ orderId: item.orderId,
40
+ });
41
+
42
+ return {
43
+ country: order.countryCode,
44
+ currency: order.currency,
45
+ ...context,
46
+ ...requestContext,
47
+ order,
48
+ orderDelivery: item,
49
+ provider,
50
+ user,
51
+ discounts,
52
+ };
53
+ },
54
+
55
+ async actions(pricingContext, requestContext) {
56
+ const actions = await baseDirector.actions(pricingContext, requestContext, this.buildPricingContext);
57
+ return {
58
+ ...actions,
59
+ calculationSheet() {
60
+ const context = actions.getContext();
61
+ return DeliveryPricingSheet({
62
+ calculation: actions.getCalculation(),
63
+ currency: context.currency,
64
+ });
65
+ },
66
+ };
67
+ },
68
+ };
@@ -0,0 +1,112 @@
1
+ import { BasePricingSheet } from '@unchainedshop/utils';
2
+ import {
3
+ DeliveryPricingCalculation,
4
+ DeliveryPricingRowCategory,
5
+ IDeliveryPricingSheet,
6
+ } from '@unchainedshop/types/delivery.pricing';
7
+ import { IBasePricingSheet, PricingSheetParams } from '@unchainedshop/types/pricing';
8
+
9
+ export const DeliveryPricingSheet = (
10
+ params: PricingSheetParams<DeliveryPricingCalculation>,
11
+ ): IDeliveryPricingSheet => {
12
+ const basePricingSheet: IBasePricingSheet<DeliveryPricingCalculation> = BasePricingSheet(params);
13
+
14
+ const pricingSheet: IDeliveryPricingSheet = {
15
+ ...basePricingSheet,
16
+
17
+ addDiscount({ amount, isTaxable, isNetPrice, discountId, meta }) {
18
+ basePricingSheet.calculation.push({
19
+ category: DeliveryPricingRowCategory.Discount,
20
+ amount,
21
+ isTaxable,
22
+ isNetPrice,
23
+ discountId,
24
+ meta,
25
+ });
26
+ },
27
+
28
+ addFee({ amount, isTaxable, isNetPrice, meta }) {
29
+ basePricingSheet.calculation.push({
30
+ amount,
31
+ category: DeliveryPricingRowCategory.Delivery,
32
+ isNetPrice,
33
+ isTaxable,
34
+ meta,
35
+ });
36
+ },
37
+
38
+ addTax({ amount, rate, meta }) {
39
+ basePricingSheet.calculation.push({
40
+ amount,
41
+ category: DeliveryPricingRowCategory.Tax,
42
+ isNetPrice: false,
43
+ isTaxable: false,
44
+ meta,
45
+ rate,
46
+ });
47
+ },
48
+
49
+ taxSum() {
50
+ return basePricingSheet.sum({
51
+ category: DeliveryPricingRowCategory.Tax,
52
+ });
53
+ },
54
+
55
+ feeSum() {
56
+ return basePricingSheet.sum({
57
+ category: DeliveryPricingRowCategory.Delivery,
58
+ });
59
+ },
60
+
61
+ discountSum(discountId) {
62
+ return basePricingSheet.sum({
63
+ category: DeliveryPricingRowCategory.Discount,
64
+ discountId,
65
+ });
66
+ },
67
+
68
+ discountPrices(explicitDiscountId) {
69
+ const discountIds = pricingSheet
70
+ .getDiscountRows(explicitDiscountId)
71
+ .map(({ discountId }) => discountId);
72
+
73
+ return [...new Set(discountIds)]
74
+ .map((discountId) => {
75
+ const amount = basePricingSheet.sum({
76
+ category: DeliveryPricingRowCategory.Discount,
77
+ discountId,
78
+ });
79
+ if (!amount) {
80
+ return null;
81
+ }
82
+ return {
83
+ discountId,
84
+ amount: Math.round(amount),
85
+ currency: basePricingSheet.currency,
86
+ };
87
+ })
88
+ .filter(Boolean);
89
+ },
90
+
91
+ getFeeRows() {
92
+ return basePricingSheet.filterBy({
93
+ category: DeliveryPricingRowCategory.Item,
94
+ });
95
+ },
96
+
97
+ getDiscountRows(discountId) {
98
+ return basePricingSheet.filterBy({
99
+ category: DeliveryPricingRowCategory.Discount,
100
+ discountId,
101
+ });
102
+ },
103
+
104
+ getTaxRows() {
105
+ return basePricingSheet.filterBy({
106
+ category: DeliveryPricingRowCategory.Tax,
107
+ });
108
+ },
109
+ };
110
+
111
+ return pricingSheet;
112
+ };
@@ -0,0 +1,4 @@
1
+ export enum DeliveryProviderType {
2
+ SHIPPING = 'SHIPPING',
3
+ PICKUP = 'PICKUP',
4
+ }
@@ -0,0 +1,206 @@
1
+ import { Context } from '@unchainedshop/types/api';
2
+ import { ModuleInput, ModuleMutations } from '@unchainedshop/types/common';
3
+ import {
4
+ DeliveryContext,
5
+ DeliveryModule,
6
+ DeliveryProvider,
7
+ DeliveryProviderQuery,
8
+ DeliverySettingsOptions,
9
+ } from '@unchainedshop/types/delivery';
10
+ import { PaymentProvider } from '@unchainedshop/types/payments';
11
+ import { emit, registerEvents } from '@unchainedshop/events';
12
+ import { generateDbFilterById, generateDbMutations } from '@unchainedshop/utils';
13
+ import { DeliveryPricingSheet } from '../director/DeliveryPricingSheet';
14
+ import { DeliveryProvidersCollection } from '../db/DeliveryProvidersCollection';
15
+ import { DeliveryProvidersSchema } from '../db/DeliveryProvidersSchema';
16
+ import { deliverySettings } from '../delivery-settings';
17
+ import { DeliveryDirector } from '../director/DeliveryDirector';
18
+ import { DeliveryPricingDirector } from '../director/DeliveryPricingDirector';
19
+
20
+ const DELIVERY_PROVIDER_EVENTS: string[] = [
21
+ 'DELIVERY_PROVIDER_CREATE',
22
+ 'DELIVERY_PROVIDER_UPDATE',
23
+ 'DELIVERY_PROVIDER_REMOVE',
24
+ ];
25
+
26
+ const asyncFilter = async (arr, predicate) => {
27
+ const results = await Promise.all(arr.map(predicate));
28
+
29
+ return arr.filter((_v, index) => results[index]);
30
+ };
31
+
32
+ const buildFindSelector = ({ type }: DeliveryProviderQuery = {}) => {
33
+ return { ...(type ? { type } : {}), deleted: null };
34
+ };
35
+
36
+ export const configureDeliveryModule = async ({
37
+ db,
38
+ options: deliveryOptions = {},
39
+ }: ModuleInput<DeliverySettingsOptions>): Promise<DeliveryModule> => {
40
+ registerEvents(DELIVERY_PROVIDER_EVENTS);
41
+
42
+ deliverySettings.configureSettings(deliveryOptions);
43
+
44
+ const DeliveryProviders = await DeliveryProvidersCollection(db);
45
+
46
+ const mutations = generateDbMutations<DeliveryProvider>(
47
+ DeliveryProviders,
48
+ DeliveryProvidersSchema,
49
+ ) as ModuleMutations<DeliveryProvider>;
50
+
51
+ const getDeliveryAdapter = async (
52
+ deliveryProviderId: string,
53
+ deliveryContext: DeliveryContext,
54
+ requestContext: Context,
55
+ ) => {
56
+ const provider = await DeliveryProviders.findOne(generateDbFilterById(deliveryProviderId), {});
57
+
58
+ return DeliveryDirector.actions(provider, deliveryContext, requestContext);
59
+ };
60
+
61
+ return {
62
+ // Queries
63
+ count: async (query) => {
64
+ const providerCount = await DeliveryProviders.countDocuments(buildFindSelector(query));
65
+ return providerCount;
66
+ },
67
+
68
+ findProvider: async ({ deliveryProviderId, ...query }, options) => {
69
+ return DeliveryProviders.findOne(
70
+ deliveryProviderId ? generateDbFilterById(deliveryProviderId) : query,
71
+ options,
72
+ );
73
+ },
74
+
75
+ findProviders: async (query, options) => {
76
+ const providers = DeliveryProviders.find(buildFindSelector(query), options);
77
+ return providers.toArray();
78
+ },
79
+
80
+ providerExists: async ({ deliveryProviderId }) => {
81
+ const providerCount = await DeliveryProviders.countDocuments(
82
+ generateDbFilterById(deliveryProviderId, { deleted: null }),
83
+ { limit: 1 },
84
+ );
85
+ return !!providerCount;
86
+ },
87
+
88
+ // Delivery Adapter
89
+
90
+ findInterface: (deliveryProvider) => {
91
+ return DeliveryDirector.getAdapter(deliveryProvider.adapterKey);
92
+ },
93
+
94
+ findInterfaces: ({ type }) => {
95
+ return DeliveryDirector.getAdapters({
96
+ adapterFilter: (Adapter) => Adapter.typeSupported(type),
97
+ }).map((Adapter) => ({
98
+ _id: Adapter.key,
99
+ label: Adapter.label,
100
+ version: Adapter.version,
101
+ }));
102
+ },
103
+
104
+ findSupported: async (deliveryContext, requestContext) => {
105
+ const foundProviders = await DeliveryProviders.find(buildFindSelector({})).toArray();
106
+ const providers: PaymentProvider[] = await asyncFilter(
107
+ foundProviders,
108
+ async (provider: DeliveryProvider) => {
109
+ try {
110
+ const director = await DeliveryDirector.actions(provider, deliveryContext, requestContext);
111
+ return director.isActive();
112
+ } catch {
113
+ return false;
114
+ }
115
+ },
116
+ );
117
+
118
+ return deliverySettings.filterSupportedProviders(
119
+ {
120
+ providers,
121
+ order: deliveryContext.order,
122
+ },
123
+ requestContext,
124
+ );
125
+ },
126
+
127
+ determineDefault: async (deliveryProviders, deliveryContext, requestContext) => {
128
+ return deliverySettings.determineDefaultProvider(
129
+ {
130
+ providers: deliveryProviders,
131
+ ...deliveryContext,
132
+ },
133
+ requestContext,
134
+ );
135
+ },
136
+
137
+ configurationError: async (deliveryProvider, requestContext) => {
138
+ const director = await DeliveryDirector.actions(deliveryProvider, {}, requestContext);
139
+ return director.configurationError();
140
+ },
141
+
142
+ isActive: async (deliveryProvider, requestContext) => {
143
+ const director = await DeliveryDirector.actions(deliveryProvider, {}, requestContext);
144
+ return Boolean(director.isActive());
145
+ },
146
+
147
+ isAutoReleaseAllowed: async (deliveryProvider, requestContext) => {
148
+ const director = await DeliveryDirector.actions(deliveryProvider, {}, requestContext);
149
+ return Boolean(director.isAutoReleaseAllowed());
150
+ },
151
+
152
+ calculate: async (pricingContext, requestContext) => {
153
+ const pricing = await DeliveryPricingDirector.actions(pricingContext, requestContext);
154
+ return pricing.calculate();
155
+ },
156
+
157
+ send: async (deliveryProviderId, deliveryContext, requestContext) => {
158
+ const adapter = await getDeliveryAdapter(deliveryProviderId, deliveryContext, requestContext);
159
+ return adapter.send();
160
+ },
161
+
162
+ pricingSheet: (params) => {
163
+ return DeliveryPricingSheet(params);
164
+ },
165
+
166
+ // Mutations
167
+ create: async (doc, userId) => {
168
+ const Adapter = DeliveryDirector.getAdapter(doc.adapterKey);
169
+ if (!Adapter) return null;
170
+
171
+ const deliveryProviderId = await mutations.create(
172
+ {
173
+ configuration: Adapter.initialConfiguration,
174
+ ...doc,
175
+ },
176
+ userId,
177
+ );
178
+ const deliveryProvider = await DeliveryProviders.findOne(
179
+ generateDbFilterById(deliveryProviderId),
180
+ {},
181
+ );
182
+ emit('DELIVERY_PROVIDER_CREATE', { deliveryProvider });
183
+ return deliveryProvider;
184
+ },
185
+
186
+ update: async (_id: string, doc: DeliveryProvider, userId: string) => {
187
+ await mutations.update(_id, doc, userId);
188
+ const deliveryProvider = await DeliveryProviders.findOne(generateDbFilterById(_id), {});
189
+ emit('DELIVERY_PROVIDER_UPDATE', { deliveryProvider });
190
+ return deliveryProvider;
191
+ },
192
+
193
+ delete: async (_id, userId) => {
194
+ await mutations.delete(_id, userId);
195
+ const deliveryProvider = await DeliveryProviders.findOne(generateDbFilterById(_id), {});
196
+ emit('DELIVERY_PROVIDER_REMOVE', { deliveryProvider });
197
+ return deliveryProvider;
198
+ },
199
+
200
+ deletePermanently: async (_id, userId) => {
201
+ const deliveryProvider = await DeliveryProviders.findOne(generateDbFilterById(_id), {});
202
+ await mutations.deletePermanently(_id, userId);
203
+ return deliveryProvider;
204
+ },
205
+ };
206
+ };
@@ -0,0 +1,22 @@
1
+ import { assert } from 'chai';
2
+ import { initDb } from 'meteor/unchained:mongodb';
3
+ import { configureDeliveryModule } from '../src/delivery-index';
4
+
5
+ describe('Test exports', () => {
6
+ let module;
7
+
8
+ before(async () => {
9
+ const db = await initDb();
10
+ module = await configureDeliveryModule({ db });
11
+ });
12
+
13
+ it('Check Delivery module', async () => {
14
+ assert.ok(module);
15
+ assert.isFunction(module.findProvider);
16
+ assert.isFunction(module.findDelivery);
17
+ assert.isFunction(module.providerExists);
18
+ assert.isFunction(module.create);
19
+ assert.isFunction(module.update);
20
+ assert.isFunction(module.delete);
21
+ });
22
+ });
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "allowJs": true,
4
+ "allowSyntheticDefaultImports": true,
5
+ "declaration": true,
6
+ "esModuleInterop": true,
7
+ "experimentalDecorators": true,
8
+ "forceConsistentCasingInFileNames": true,
9
+ "lib": ["esnext"],
10
+ "module": "esnext",
11
+ "moduleResolution": "node",
12
+ "noImplicitReturns": true,
13
+ "noUnusedLocals": false,
14
+ "outDir": "lib",
15
+ "preserveWatchOutput": true,
16
+ "skipLibCheck": true,
17
+ "sourceMap": true,
18
+ "target": "esnext",
19
+ "types": ["node"],
20
+ "baseUrl": ".", // This must be specified if "paths" is.
21
+ "paths": {
22
+ "meteor/unchained:*": ["node_modules/@unchainedshop/types/index.d.ts"]
23
+ }
24
+ },
25
+ "include": ["src"]
26
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,8 @@
1
+ {
2
+ "extends": "./tsconfig.build.json",
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "types": ["node", "mocha"]
6
+ },
7
+ "include": ["src", "tests", "plugins"]
8
+ }