@sommpicks/sommpicks-shopify 24.12.0

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 (71) hide show
  1. package/Logger.ts +18 -0
  2. package/README.md +258 -0
  3. package/addTypings.sh +2 -0
  4. package/bitbucket-pipelines.yml +38 -0
  5. package/index.ts +132 -0
  6. package/package.json +57 -0
  7. package/publish.sh +20 -0
  8. package/services/CacheWrapper.ts +30 -0
  9. package/services/CountryCodeService.ts +507 -0
  10. package/shopify/ShopifyAppService.ts +109 -0
  11. package/shopify/ShopifyAssetService.ts +20 -0
  12. package/shopify/ShopifyBillingService.ts +73 -0
  13. package/shopify/ShopifyCartTrasnformationService.ts +207 -0
  14. package/shopify/ShopifyCollectionService.ts +523 -0
  15. package/shopify/ShopifyCustomerService.ts +472 -0
  16. package/shopify/ShopifyDeliveryCustomisationService.ts +220 -0
  17. package/shopify/ShopifyDiscountService.ts +131 -0
  18. package/shopify/ShopifyDraftOrderService.ts +125 -0
  19. package/shopify/ShopifyFulfillmentService.ts +41 -0
  20. package/shopify/ShopifyFunctionsProductDiscountsService.ts +166 -0
  21. package/shopify/ShopifyInventoryService.ts +415 -0
  22. package/shopify/ShopifyLocationService.ts +29 -0
  23. package/shopify/ShopifyOrderRefundsService.ts +138 -0
  24. package/shopify/ShopifyOrderRiskService.ts +19 -0
  25. package/shopify/ShopifyOrderService.ts +1143 -0
  26. package/shopify/ShopifyPageService.ts +62 -0
  27. package/shopify/ShopifyProductService.ts +772 -0
  28. package/shopify/ShopifyShippingZonesService.ts +37 -0
  29. package/shopify/ShopifyShopService.ts +101 -0
  30. package/shopify/ShopifyTemplateService.ts +30 -0
  31. package/shopify/ShopifyThemeService.ts +33 -0
  32. package/shopify/ShopifyUtils.ts +56 -0
  33. package/shopify/ShopifyWebhookService.ts +110 -0
  34. package/shopify/base/APIVersion.ts +4 -0
  35. package/shopify/base/AbstractService.ts +152 -0
  36. package/shopify/base/ErrorHelper.ts +24 -0
  37. package/shopify/errors/InspiraShopifyCustomError.ts +7 -0
  38. package/shopify/errors/InspiraShopifyError.ts +15 -0
  39. package/shopify/errors/InspiraShopifyUnableToReserveInventoryError.ts +7 -0
  40. package/shopify/helpers/ShopifyProductServiceHelper.ts +450 -0
  41. package/shopify/product/ShopifyProductCountService.ts +110 -0
  42. package/shopify/product/ShopifyProductListService.ts +333 -0
  43. package/shopify/product/ShopifyProductMetafieldsService.ts +405 -0
  44. package/shopify/product/ShopifyProductPublicationsService.ts +112 -0
  45. package/shopify/product/ShopifyVariantService.ts +584 -0
  46. package/shopify/router/ShopifyMandatoryRouter.ts +37 -0
  47. package/shopify/router/ShopifyRouter.ts +85 -0
  48. package/shopify/router/ShopifyRouterBis.ts +85 -0
  49. package/shopify/router/ShopifyRouterBisBis.ts +85 -0
  50. package/shopify/router/ShopifyRouterBisBisBis.ts +85 -0
  51. package/shopify/router/ShopifyRouterBisBisBisBis.ts +85 -0
  52. package/shopify/router/WebhookSkipMiddleware.ts +73 -0
  53. package/shopify/router/services/CryptoService.ts +26 -0
  54. package/shopify/router/services/HmacValidator.ts +36 -0
  55. package/shopify/router/services/OauthService.ts +17 -0
  56. package/shopify/router/services/RestUtils.ts +13 -0
  57. package/shopify/router/services/rateLimiter/MemoryStores.ts +46 -0
  58. package/shopify/router/services/rateLimiter/StoreRateLimiter.ts +46 -0
  59. package/test/README.md +223 -0
  60. package/test/router/ShopifyRouter.test.ts +71 -0
  61. package/test/router/WebhookSkipMiddleware.test.ts +86 -0
  62. package/test/router/services/HmacValidator.test.ts +24 -0
  63. package/test/router/services/RestUtils.test.ts +13 -0
  64. package/test/router/services/rateLimiter/StoreRateLimiter.test.ts +62 -0
  65. package/test/services/CacheWrapper.test.ts +30 -0
  66. package/test/shopify/ShopifyOrderService.test.ts +29 -0
  67. package/test/shopify/ShopifyProductService.test.ts +118 -0
  68. package/test/shopify/ShopifyWebhookService.test.ts +105 -0
  69. package/tsconfig.json +10 -0
  70. package/typings/axios.d.ts +8 -0
  71. package/typings/index.d.ts +1682 -0
@@ -0,0 +1,220 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { print } from 'graphql';
3
+ import gql from 'graphql-tag';
4
+ import { Logger } from '../Logger';
5
+ import { AbstractService } from './base/AbstractService';
6
+ import ErrorHelper from './base/ErrorHelper';
7
+ import InspiraShopifyError from './errors/InspiraShopifyError';
8
+
9
+ export class ShopifyDeliveryCustomisationService extends AbstractService{
10
+
11
+ constructor(private axiosInstance: AxiosInstance) {
12
+ super();
13
+ }
14
+
15
+ /**
16
+ * Creates Delivery Customization
17
+ *
18
+ * @param {string} functionId
19
+ * @param {string} message
20
+ * @param {boolean} enabled
21
+ * @param {string} metaKey
22
+ * @param {string} metafieldVal
23
+ * @returns Promise
24
+ */
25
+ public create = async (functionId: string, message: string, enabled: boolean, metaKey: string, metafieldVal: string): Promise<{id: number;}> => {
26
+ return new Promise<{id: number;}>(async (resolve, reject) => {
27
+ try {
28
+ Logger.info(`Creating customisation: ${functionId} and message ${message} and metafield val ${metafieldVal}`);
29
+ const query = gql `mutation DeliveryCustomizationCreate($input: DeliveryCustomizationInput!) {
30
+ deliveryCustomizationCreate(deliveryCustomization: $input) {
31
+ deliveryCustomization {
32
+ id
33
+ }
34
+ userErrors {
35
+ message
36
+ }
37
+ }
38
+ }`;
39
+ const response = await this.axiosInstance.post('/graphql.json', { query: print(query),
40
+ variables: { input: { functionId: functionId, title: message, enabled: enabled } }}, { query_cost: 803 });
41
+
42
+ if(response && response.data && response.data.data && response.data.data.deliveryCustomizationCreate && response.data.data.deliveryCustomizationCreate.deliveryCustomization ){
43
+ const customisationGraph = response.data.data.deliveryCustomizationCreate.deliveryCustomization;
44
+ Logger.info(`Customisation created with ${JSON.stringify(customisationGraph)}`);
45
+ const customizationId = customisationGraph.id;
46
+ Logger.info(`Customisation created with id ${customizationId}`);
47
+ const metafieldQuery = gql `mutation MetafieldsSet($customizationId: ID!, $configurationValue: String!) {
48
+ metafieldsSet(metafields: [
49
+ {
50
+ ownerId: $customizationId
51
+ namespace: "$app:delivery-customization"
52
+ key: "${metaKey}"
53
+ value: $configurationValue
54
+ type: "json"
55
+ }
56
+ ]) {
57
+ metafields {
58
+ id
59
+ }
60
+ userErrors {
61
+ message
62
+ }
63
+ }
64
+ }`;
65
+
66
+ const metafieldResponse = await this.axiosInstance.post('/graphql.json', { query: print(metafieldQuery),
67
+ variables: { customizationId: customizationId, configurationValue: metafieldVal }}, { query_cost: 803 });
68
+
69
+ Logger.info(`Meta response ${JSON.stringify(metafieldResponse.data)}`);
70
+ resolve({ id: this.getIdFromGraphId(customizationId) });
71
+ } else {
72
+ Logger.error(`Create customisation. Error is ${JSON.stringify(response.data.errors)}. All data is ${JSON.stringify(response.data)}` );
73
+ resolve(null);
74
+ }
75
+ } catch (error) { Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
76
+ });
77
+ };
78
+
79
+ /**
80
+ * Updates Delivery Customization
81
+ *
82
+ * @param {string} functionId
83
+ * @param {string} message
84
+ * @param {string} metafieldVal
85
+ * @returns Promise
86
+ */
87
+ public update = async (customisationId: number, functionId: string, message: string, metakey: string, metafieldVal: string, updateCustomisation: boolean): Promise<{id: number;}> => {
88
+ return new Promise<{id: number;}>(async (resolve, reject) => {
89
+ try {
90
+ Logger.info(`Update customisation: ${functionId} and key ${metakey}`);
91
+ if(metafieldVal.length > 9500) {
92
+ throw new Error(`ShopifyDeliveryCustomisationService -> update - The metafield value exceeds 9000 chars. It has ${metafieldVal.length}`);
93
+ } else {
94
+ let isSuccess = false;
95
+ let errors = '';
96
+ if(updateCustomisation) {
97
+
98
+ const query = gql `mutation DeliveryCustomizationUpdate($id: ID!, $input: DeliveryCustomizationInput!) {
99
+ deliveryCustomizationUpdate(id: $id, deliveryCustomization: $input) {
100
+ deliveryCustomization {
101
+ id
102
+ }
103
+ userErrors {
104
+ message
105
+ }
106
+ }
107
+ }`;
108
+ const response = await this.axiosInstance.post('/graphql.json', { query: print(query),
109
+ variables: { input: { functionId: functionId, title: message, enabled: true }, id: this.getGraphDeliveryCustomisationIdFromId(customisationId) }}, { query_cost: 803 });
110
+ if(response && response.data && response.data.data && response.data.data.deliveryCustomizationUpdate ){
111
+ isSuccess = true;
112
+ Logger.info(`Customisation updated with ${JSON.stringify(response.data.data.deliveryCustomizationUpdate)}`);
113
+ } else {
114
+ errors = JSON.stringify(response.data.errors);
115
+ Logger.error(`Update customisation. Error is ${JSON.stringify(response.data.errors)}` );
116
+ }
117
+ } else {
118
+ isSuccess = true;
119
+ }
120
+
121
+ if(isSuccess) {
122
+ const metafieldQuery = gql `mutation MetafieldsSet($customizationId: ID!, $configurationValue: String!) {
123
+ metafieldsSet(metafields: [
124
+ {
125
+ ownerId: $customizationId
126
+ namespace: "$app:delivery-customization"
127
+ key: "${metakey}"
128
+ value: $configurationValue
129
+ type: "json"
130
+ }
131
+ ]) {
132
+ metafields {
133
+ id
134
+ }
135
+ userErrors {
136
+ message
137
+ }
138
+ }
139
+ }`;
140
+
141
+ const metafieldResponse = await this.axiosInstance.post('/graphql.json', { query: print(metafieldQuery),
142
+ variables: { customizationId: this.getGraphDeliveryCustomisationIdFromId(customisationId), configurationValue: metafieldVal }}, { query_cost: 803 });
143
+
144
+ Logger.info(`Meta response ${JSON.stringify(metafieldResponse.data)}`);
145
+ resolve({ id: customisationId });
146
+ } else {
147
+ Logger.error(`Update customisation. Error is ${errors}` );
148
+ resolve(null);
149
+ }
150
+ }
151
+ } catch (error) { Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
152
+ });
153
+ };
154
+
155
+ public getDeliveryCustomisation = async (customisationId: number, namespace: string, key: string): Promise<IDeliveryCustomisation> => {
156
+ const query = gql `query DeliveryCustomization($id: ID!) {
157
+ deliveryCustomization(id: $id) {
158
+ id
159
+ title
160
+ enabled
161
+ metafield(namespace: "${namespace}", key: "${key}") {
162
+ value
163
+ }
164
+ }
165
+ }`;
166
+ const variables = {
167
+ id: `gid://shopify/DeliveryCustomization/${customisationId}`
168
+ };
169
+ const customisationResponse = await this.axiosInstance.post('/graphql.json', { query: print(query), variables: variables }, { query_cost: 10 });
170
+ if(customisationResponse && customisationResponse.data && customisationResponse.data.data && customisationResponse.data.data.deliveryCustomization) {
171
+ console.log('getDeliveryCustomisation', customisationResponse.data.data);
172
+ customisationResponse.data.data.deliveryCustomization.id = customisationId;
173
+ return customisationResponse.data.data.deliveryCustomization;
174
+ } else if (customisationResponse && customisationResponse.data && customisationResponse.data.errors ){
175
+ Logger.error(`getDeliveryCustomisation ${JSON.stringify(customisationResponse.data.errors)}`);
176
+ } else {
177
+ return null;
178
+ }
179
+ };
180
+
181
+ /**
182
+ * setMetafield in Delivery Customisation
183
+ *
184
+ * @param {number} customisationId
185
+ * @param {string} metafieldVal
186
+ * @param {string} key
187
+ * @returns Promise
188
+ */
189
+ public setMetafield = async (customisationId: number, metafieldVal: string, key: string): Promise<{id: number;}> => {
190
+ return new Promise<{id: number;}>(async (resolve, reject) => {
191
+ try {
192
+ Logger.info(`Cart transform updated with ${customisationId}`);
193
+ const metafieldQuery = gql `mutation MetafieldsSet($customizationId: ID!, $configurationValue: String!) {
194
+ metafieldsSet(metafields: [
195
+ {
196
+ ownerId: $customizationId
197
+ namespace: "$app:delivery-customization"
198
+ key: "${key}"
199
+ value: $configurationValue
200
+ type: "json"
201
+ }
202
+ ]) {
203
+ metafields {
204
+ id
205
+ }
206
+ userErrors {
207
+ message
208
+ }
209
+ }
210
+ }`;
211
+
212
+ const metafieldResponse = await this.axiosInstance.post('/graphql.json', { query: print(metafieldQuery),
213
+ variables: { customizationId: this.getGraphDeliveryCustomisationIdFromId(customisationId), configurationValue: metafieldVal }}, { query_cost: 803 });
214
+
215
+ Logger.info(`Meta response ${JSON.stringify(metafieldResponse.data)}`);
216
+ resolve({ id: customisationId });
217
+ } catch (error) { Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
218
+ });
219
+ };
220
+ }
@@ -0,0 +1,131 @@
1
+ import gql from 'graphql-tag';
2
+ import { print } from 'graphql';
3
+ import { AxiosInstance } from 'axios';
4
+ import { Logger } from '../Logger';
5
+ import ErrorHelper from './base/ErrorHelper';
6
+ import * as moment from 'moment';
7
+ import InspiraShopifyError from './errors/InspiraShopifyError';
8
+ import { AbstractService } from './base/AbstractService';
9
+
10
+ export class ShopifyDiscountService extends AbstractService {
11
+
12
+ constructor(private axiosInstance: AxiosInstance) {
13
+ super();
14
+ }
15
+
16
+ public deleteAutomaticDiscount = async (id: number): Promise<void> => {
17
+ const mutation = gql`mutation {
18
+ discountAutomaticDelete(id: "${this.getGraphAutomaticDiscountIdFromId(id)}") {
19
+ deletedAutomaticDiscountId
20
+ userErrors {
21
+ field
22
+ message
23
+ }
24
+ }
25
+ }`;
26
+ const response = await this.axiosInstance.post('/graphql.json', { query: print(mutation) }, { query_cost: 5 });
27
+ if(response.data.errors && response.data.errors.length > 0) {
28
+ throw new Error(JSON.stringify(response.data.errors));
29
+ }
30
+ if(response.data.discountAutomaticDelete && response.data.discountAutomaticDelete.userErrors.length > 0) {
31
+ throw new Error(JSON.stringify(response.data.discountAutomaticDelete.userError));
32
+ }
33
+ };
34
+
35
+ public getAutomaticDiscount = async (id: number): Promise<{ id: number; }> => {
36
+ const query = gql`{
37
+ automaticDiscountNode(id: "gid://shopify/DiscountAutomaticNode/${id}") {
38
+ id
39
+ automaticDiscount {
40
+ __typename
41
+ ... on DiscountAutomaticBasic {
42
+ status
43
+ }
44
+ ... on DiscountAutomaticBxgy {
45
+ status
46
+ }
47
+ }
48
+ }
49
+ }`;
50
+ const response = await this.axiosInstance.post('/graphql.json', { query: print(query) }, { query_cost: 5 });
51
+
52
+ if(response.data.errors && response.data.errors.length > 0) {
53
+ throw new Error(JSON.stringify(response.data.errors));
54
+ }
55
+ return { id: response.data.automaticDiscountNode && response.data.automaticDiscountNode.id ? this.getIdFromGraphId(response.data.automaticDiscountNode.id) : null };
56
+ };
57
+ /**
58
+ * Creates a discount code. For a percentage on a product with a minimum quantity.
59
+ *
60
+ * @param {string} title and code of the discount
61
+ * @param {number} percentageOff % value off the item
62
+ * @param {number} productId where to be applied.
63
+ * @param {number} minItemsRequired minimum quantity of items of the product to be present for the discount
64
+ * @returns Promise
65
+ */
66
+ public getPercentageDiscountCodeForItem = async (title: string, percentageOff: number, productId: number, minItemsRequired: number): Promise<IDiscount> => {
67
+ return new Promise<IDiscount>(async (resolve, reject) => {
68
+ try {
69
+ const priceRule = { title: title, value: -percentageOff, value_type: 'percentage',
70
+ prerequisite_quantity_range: {greater_than_or_equal_to: minItemsRequired },
71
+ target_selection: 'entitled', entitled_product_ids: [productId], allocation_method: 'each',
72
+ customer_selection: 'all', target_type: 'line_item', starts_at: moment().format('YYYY-MM-DDTHH:mm:ss') + 'Z'};
73
+ resolve(await this.createRuleAndCode(priceRule));
74
+ } catch (error) { Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
75
+ });
76
+ };
77
+
78
+ /**
79
+ * Creates a 1 time discount code. For a Fixed amount on the order total for a number of products with a total minimum quantity.
80
+ *
81
+ * @param {string} title and code of the discount
82
+ * @param {number} amount to be discounted on the order total.
83
+ * @param {number} productId where to be applied.
84
+ * @param {number} minItemsRequired minimum quantity of discounted items of the product to be present for the discount.
85
+ * @param {number} customerId where the discount can be applied
86
+ * @returns Promise
87
+ */
88
+ public getFixedDiscountCodeForOrder = async (title: string, amount: number, productIds: number[], minItemsRequired: number, customerId: number): Promise<IDiscount> => {
89
+ return new Promise<IDiscount>(async (resolve, reject) => {
90
+ try {
91
+ const priceRule = { title: title, value: -amount, value_type: 'fixed_amount',
92
+ prerequisite_quantity_range: {greater_than_or_equal_to: minItemsRequired },
93
+ target_selection: 'entitled', entitled_product_ids: productIds, allocation_method: 'across',
94
+ customer_selection: 'prerequisite', prerequisite_customer_ids: [customerId], target_type: 'line_item',
95
+ usage_limit: 1, once_per_customer: true, starts_at: moment().format('YYYY-MM-DDTHH:mm:ss') + 'Z'};
96
+ resolve(await this.createRuleAndCode(priceRule));
97
+ } catch (error) { Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
98
+ });
99
+ };
100
+
101
+ /**
102
+ * Removes Rule and Discount code
103
+ *
104
+ * @param {number} ruleId
105
+ * @param {number} codeId
106
+ * @returns Promise
107
+ */
108
+ public removeDiscount = async (ruleId: number, codeId: number): Promise<void> => {
109
+ return new Promise<void>(async (resolve, reject) => {
110
+ try {
111
+ Logger.info(`removeDiscount code ${codeId}`);
112
+ await this.axiosInstance.delete(`/price_rules/${ruleId}/discount_codes/${codeId}.json`);
113
+ Logger.info(`removeDiscount rule ${ruleId}`);
114
+ await this.axiosInstance.delete(`/price_rules/${ruleId}.json`);
115
+ Logger.info(`Discount removed. Rule ${ruleId} and code ${codeId}`);
116
+ resolve();
117
+ } catch (error) { Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
118
+ });
119
+ };
120
+
121
+ private createRuleAndCode = async (priceRule: { title: string; value: number; value_type: string; prerequisite_quantity_range: { greater_than_or_equal_to: number; }; target_selection: string; entitled_product_ids: number[]; allocation_method: string; }): Promise<IDiscount> => {
122
+ Logger.info(`getDiscountCodeForItem for ${JSON.stringify(priceRule)}`);
123
+ const ruleResponse = await this.axiosInstance.post('/price_rules.json', { price_rule: priceRule });
124
+ const createdRule: IPriceRule = ruleResponse.data.price_rule;
125
+ Logger.info(`Hash of the discount for ${priceRule.title}`);
126
+ const codeResponse = await this.axiosInstance.post(`/price_rules/${createdRule.id}/discount_codes.json`, { discount_code: { code: priceRule.title } });
127
+ Logger.info(`Code response ${codeResponse}`);
128
+ return { code: codeResponse.data.discount_code, rule: createdRule };
129
+ };
130
+
131
+ }
@@ -0,0 +1,125 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { Logger } from '../Logger';
3
+ import ErrorHelper from './base/ErrorHelper';
4
+ import InspiraShopifyError from './errors/InspiraShopifyError';
5
+
6
+ export class ShopifyDraftOrderService {
7
+
8
+ constructor(private axiosInstance: AxiosInstance) { }
9
+
10
+ public create = async (order: IOrderToCreate): Promise<IDraftOrder> => {
11
+ return new Promise<IDraftOrder>(async (resolve, reject) => {
12
+ try {
13
+ Logger.info('Creating draft order', order);
14
+ const response = await this.axiosInstance.post('/draft_orders.json', { draft_order: order });
15
+ resolve(response.data.draft_order);
16
+ } catch (error) {
17
+ const extractedErrorFromResponse = ErrorHelper.getErrorFromResponse(error);
18
+ Logger.error('Error while creating draft order', error, extractedErrorFromResponse);
19
+ reject(Error(extractedErrorFromResponse));
20
+ }
21
+ });
22
+ };
23
+
24
+ /**
25
+ * Sends Shopify invoice to customer
26
+ *
27
+ * @param orderId {number}
28
+ * @returns
29
+ */
30
+ public send = async (orderId: number): Promise<void> => {
31
+ return new Promise<void>(async (resolve, reject) => {
32
+ try {
33
+ Logger.info(`Sending invoice for Draft Order: ${orderId}`);
34
+ await this.axiosInstance.post(`/draft_orders/${orderId}/send_invoice.json`, { draft_order_invoice: {} });
35
+ resolve();
36
+ } catch (error) { Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
37
+ });
38
+ };
39
+
40
+ public createAndSendInvoice = async (order: IOrderToCreate): Promise<IDraftOrder> => {
41
+ return new Promise<IDraftOrder>(async (resolve, reject) => {
42
+ try {
43
+ Logger.info('Creating draft order and sending invoice');
44
+ const orderCreated = await this.create(order);
45
+ await this.send(orderCreated.id);
46
+ resolve(orderCreated);
47
+ } catch (error) {
48
+ const extractedErrorFromResponse = ErrorHelper.getErrorFromResponse(error);
49
+ Logger.error('Error while creating draft order and sending invoice', error, extractedErrorFromResponse);
50
+ reject(reject(new InspiraShopifyError(error)));
51
+ }
52
+ });
53
+ };
54
+
55
+ /**
56
+ * Updates Shipping Address in a draft order.
57
+ *
58
+ * @param {number} orderId
59
+ * @param {IAddress} shippingAddress
60
+ * @returns Promise
61
+ */
62
+ public updateShippingAddress = async (orderId: number, shippingAddress: IAddress): Promise<IDraftOrder> => {
63
+ return new Promise<IDraftOrder>(async (resolve, reject) => {
64
+ try {
65
+ Logger.info(`Updating draft order shipping address with id ${orderId}`, shippingAddress);
66
+ const response = await this.axiosInstance.put(`/draft_orders/${orderId}.json`, { draft_order: { id: orderId, shipping_address: shippingAddress} });
67
+ resolve(response.data.draft_order);
68
+ } catch (error) { Logger.error('Error updating order shipping address', error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
69
+ });
70
+ };
71
+
72
+ /**
73
+ * Deletes a draft order.
74
+ *
75
+ * @param {number} orderId
76
+ * @returns Promise
77
+ */
78
+ public delete = async (orderId: number): Promise<void> => {
79
+ return new Promise<void>(async (resolve, reject) => {
80
+ try {
81
+ Logger.info(`Updating draft order shipping address with id ${orderId}`);
82
+ await this.axiosInstance.delete(`/draft_orders/${orderId}.json`);
83
+ resolve();
84
+ } catch (error) { Logger.error('Error deleting draft order', error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
85
+ });
86
+ };
87
+
88
+ /**
89
+ * Transforms a Cart to a draft Order. If the cart has the line items applied_discount field filled it will create the draft order with the discount applied.
90
+ *
91
+ * @param {ICart} cart
92
+ * @param {number} customerId
93
+ * @returns IDraftOrder
94
+ */
95
+ public fromCartToDraft = (cart: ICart, customerId: number): IDraftOrder => {
96
+ const draftorder: IDraftOrder = {
97
+ customer: { id: customerId } as ICustomer,
98
+ note: cart.note,
99
+ note_attributes: [],
100
+ line_items: cart.items.map( (it) => ({ price: (it.price / 100).toFixed(2), title: it.title, quantity: it.quantity, taxable: it.taxable, product_id: it.product_id, variant_id: it.variant_id,
101
+ properties: (it.properties && it.properties.length) ? it.properties : [], requires_shipping: it.requires_shipping, sku: it.sku, applied_discount: it.applied_discount } as ILineItem))
102
+ };
103
+ if(cart.attributes && cart.attributes !== undefined) {
104
+ for (const [key, val] of Object.entries(cart.attributes)) {
105
+ draftorder.note_attributes.push({ name: key, value: val as string });
106
+ }
107
+ }
108
+ return draftorder;
109
+ };
110
+
111
+ public createFromCart = async (cart: ICart, customerId: number): Promise<IDraftOrder> => {
112
+ return new Promise<IDraftOrder>(async (resolve, reject) => {
113
+ try {
114
+ Logger.info('Creating draft order from cart', cart);
115
+ const draftOrder = this.fromCartToDraft(cart, customerId);
116
+ const createdDraftOrder = await this.create(draftOrder);
117
+ resolve(createdDraftOrder);
118
+ } catch (error) {
119
+ const extractedErrorFromResponse = ErrorHelper.getErrorFromResponse(error);
120
+ Logger.error('Error while creating draft order', error, extractedErrorFromResponse);
121
+ reject(Error(extractedErrorFromResponse));
122
+ }
123
+ });
124
+ };
125
+ }
@@ -0,0 +1,41 @@
1
+ import { AxiosInstance } from 'axios';
2
+ import { Logger } from '../Logger';
3
+ import ErrorHelper from './base/ErrorHelper';
4
+ import InspiraShopifyError from './errors/InspiraShopifyError';
5
+
6
+ export class ShopifyFulfillmentService {
7
+
8
+ constructor(private axiosInstance: AxiosInstance) { }
9
+
10
+ public create = async (fulfillment: IFulfillmentService): Promise<IFulfillmentService> => {
11
+ return new Promise<IFulfillmentService>(async (resolve, reject) => {
12
+ try {
13
+ Logger.info('setting fulfillment service', fulfillment);
14
+ const response = await this.axiosInstance.post('/fulfillment_services.json', { fulfillment_service: fulfillment });
15
+ resolve(response.data.fulfillment_service);
16
+ } catch (error) {Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
17
+ });
18
+ };
19
+
20
+ public addTrackingInfoToFulfillment = async (fulfillmentId: number, company: string, trackingNumber: string, trackingUrl: string, notifyCustomer: boolean): Promise<IFulFillment> => {
21
+ return new Promise<IFulFillment>(async (resolve, reject) => {
22
+ try {
23
+ Logger.info(`Adding tracking information to fulfillment ${fulfillmentId}`);
24
+ const fulfillmentUpdate = { fulfillment: { notify_customer: notifyCustomer, tracking_info: { number: trackingNumber, url: trackingUrl, company: company } }};
25
+ Logger.info(`New tracking information to fulfillment ${fulfillmentId}`, fulfillmentUpdate);
26
+ const response = await this.axiosInstance.post(`/fulfillments/${fulfillmentId}/update_tracking.json`, fulfillmentUpdate);
27
+ resolve(response.data.fulfillment);
28
+ } catch (error) {Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
29
+ });
30
+ };
31
+
32
+ public getAll = async (): Promise<IFulfillmentService[]> => {
33
+ return new Promise<IFulfillmentService[]>(async (resolve, reject) => {
34
+ try {
35
+ Logger.info('getting fulfillment services');
36
+ const response = await this.axiosInstance.get('/fulfillment_services.json');
37
+ resolve(response.data.fulfillment_services);
38
+ } catch (error) {Logger.error(error, ErrorHelper.getErrorFromResponse(error)); reject(new InspiraShopifyError(error)); }
39
+ });
40
+ };
41
+ }