@things-factory/integration-sellercraft 8.0.0-beta.9 → 8.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. package/dist-server/tsconfig.tsbuildinfo +1 -1
  2. package/package.json +14 -14
  3. package/server/constants/index.ts +2 -0
  4. package/server/constants/order-status-mapping.ts +28 -0
  5. package/server/constants/platform.ts +6 -0
  6. package/server/controllers/index.ts +5 -0
  7. package/server/controllers/sellercraft/apis/add-inbound-order.ts +50 -0
  8. package/server/controllers/sellercraft/apis/echo.ts +14 -0
  9. package/server/controllers/sellercraft/apis/fetch-order-document.ts +18 -0
  10. package/server/controllers/sellercraft/apis/index.ts +8 -0
  11. package/server/controllers/sellercraft/apis/initiate-order-document.ts +17 -0
  12. package/server/controllers/sellercraft/apis/initiate-order-shipment.ts +29 -0
  13. package/server/controllers/sellercraft/apis/pack-marketplace-order.ts +35 -0
  14. package/server/controllers/sellercraft/apis/update-marketplace-order.ts +14 -0
  15. package/server/controllers/sellercraft/apis/update-product.ts +21 -0
  16. package/server/controllers/sellercraft/index.ts +7 -0
  17. package/server/controllers/sellercraft/platform-action.ts +39 -0
  18. package/server/controllers/sellercraft/sellercraft.ts +109 -0
  19. package/server/controllers/sellercraft-api/decorators.ts +52 -0
  20. package/server/controllers/sellercraft-api/index.ts +51 -0
  21. package/server/controllers/sellercraft-api/types.ts +0 -0
  22. package/server/controllers/sellercraft-channel-integration/apis/echo.ts +14 -0
  23. package/server/controllers/sellercraft-channel-integration/apis/index.ts +6 -0
  24. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-categories.ts +37 -0
  25. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-category-attributes.ts +65 -0
  26. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-order-package.ts +62 -0
  27. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-order.ts +92 -0
  28. package/server/controllers/sellercraft-channel-integration/apis/ingest-channel-product.ts +97 -0
  29. package/server/controllers/sellercraft-channel-integration/index.ts +7 -0
  30. package/server/controllers/sellercraft-channel-integration/platform-action.ts +39 -0
  31. package/server/controllers/sellercraft-channel-integration/sellercraft-channel-integration.ts +115 -0
  32. package/server/controllers/sellercraft-channel-integration-api/decorators.ts +45 -0
  33. package/server/controllers/sellercraft-channel-integration-api/index.ts +45 -0
  34. package/server/controllers/sellercraft-channel-integration-api/types.ts +0 -0
  35. package/server/index.ts +7 -0
  36. package/server/middlewares/index.ts +3 -0
  37. package/server/migrations/index.ts +9 -0
  38. package/server/routers/sellercraft-router.ts +326 -0
  39. package/server/routes.ts +32 -0
  40. package/server/service/index.ts +23 -0
  41. package/server/service/marketplace-channel/index.ts +6 -0
  42. package/server/service/marketplace-channel/marketplace-channel-order-mutation.ts +456 -0
  43. package/server/service/marketplace-channel/marketplace-channel-product-mutation.ts +282 -0
  44. package/server/service/marketplace-channel/marketplace-channel.ts +76 -0
  45. package/server/service/sellercraft/index.ts +6 -0
  46. package/server/service/sellercraft/sellercraft-mutation.ts +126 -0
  47. package/server/service/sellercraft/sellercraft-query.ts +43 -0
  48. package/server/service/sellercraft/sellercraft-type.ts +54 -0
  49. package/server/service/sellercraft/sellercraft.ts +96 -0
  50. package/server/utils/tokencraft-util.ts +60 -0
  51. package/tsconfig.json +9 -0
@@ -0,0 +1,326 @@
1
+ import Router from 'koa-router'
2
+ import { v4 as uuidv4 } from 'uuid'
3
+
4
+ import { StoreAPI } from '@things-factory/integration-marketplace'
5
+ import { createPayloadLog, PayloadType } from '@things-factory/integration-base'
6
+ import { sleep } from '@things-factory/utils'
7
+
8
+ import { SHIPPING_TYPE } from '../constants'
9
+ import { getShop } from '../utils/tokencraft-util'
10
+
11
+ const debug = require('debug')('things-factory:integration-sellercraft:sellercraft-router')
12
+
13
+ export const sellercraftRouter = new Router()
14
+
15
+ sellercraftRouter.post('/sellercraft/store/update-product-price', async (context, next) => {
16
+ const requestBody = context.request.body
17
+
18
+ for (var i = 0; i < requestBody.length; i++) {
19
+ let mappedStore: any = await getShop(context, requestBody[i].channel_id, requestBody[i].shop_id)
20
+ var result
21
+
22
+ try {
23
+ if (requestBody[i].variant.native_variant_id != requestBody[i].native_product_id) {
24
+ const req = {
25
+ costPrice: requestBody[i].variant.full_price,
26
+ sellPrice: requestBody[i].variant.sale_price,
27
+ productId: requestBody[i].native_product_id,
28
+ variationId: requestBody[i].variant.native_variant_id,
29
+ variationSku: requestBody[i].variant.sku,
30
+ context: { state: { domain: null } }
31
+ }
32
+ result = await StoreAPI.updateStoreProductVariationPrice(mappedStore, req)
33
+ } else {
34
+ const req = {
35
+ costPrice: requestBody[i].variant.full_price,
36
+ sellPrice: requestBody[i].variant.sale_price,
37
+ productId: requestBody[i].native_product_id,
38
+ variationSku: requestBody[i].variant.sku,
39
+ context: { state: { domain: null } }
40
+ }
41
+ result = await StoreAPI.updateStoreProductPrice(mappedStore, req)
42
+ }
43
+
44
+ debug(result)
45
+
46
+ context.type = 'application/json'
47
+ context.status = 200
48
+ context.body = 'Succeeded'
49
+ } catch (e) {
50
+ context.type = 'application/json'
51
+ context.status = 500
52
+ context.body = e.message
53
+ }
54
+ }
55
+ })
56
+
57
+ sellercraftRouter.post('/sellercraft/store/update-product-stock', async (context, next) => {
58
+ const requestBody = context.request.body
59
+ for (var i = 0; i < requestBody.length; i++) {
60
+ let mappedStore: any = await getShop(context, requestBody[i].channel_id, requestBody[i].shop_id)
61
+ var result
62
+
63
+ try {
64
+ if (requestBody[i].variant.native_variant_id != requestBody[i].native_product_id) {
65
+ const req = {
66
+ qty: requestBody[i].variant.stock,
67
+ itemId: requestBody[i].native_product_id,
68
+ variationId: requestBody[i].variant.native_variant_id,
69
+ distributors: requestBody[i].distributors || [], // for Magento
70
+ variationSku: requestBody[i].variant.sku,
71
+ locationId: requestBody[i]?.variant?.extra_metadata?.locationId, // for Shopify
72
+ inventoryItemId: requestBody[i]?.variant?.extra_metadata?.inventoryItemId, // for Shopify
73
+ context: { state: { domain: null } }
74
+ }
75
+ if (req.locationId) {
76
+ let location = JSON.parse(req.locationId)
77
+ req.qty -= await StoreAPI.getStoreProductVariationStock(mappedStore, { variantId: req.variationId })
78
+ req.locationId = location[0].location_id
79
+ }
80
+ result = await StoreAPI.updateStoreProductVariationStock(mappedStore, req)
81
+ } else {
82
+ const req = {
83
+ qty: requestBody[i].variant.stock,
84
+ itemId: requestBody[i].native_product_id,
85
+ distributors: requestBody[i].distributors || [], // for Magento
86
+ variationSku: requestBody[i].variant.sku,
87
+ context: { state: { domain: null } }
88
+ }
89
+ result = await StoreAPI.updateStoreProductStock(mappedStore, req)
90
+ }
91
+
92
+ debug(result)
93
+
94
+ context.type = 'application/json'
95
+ context.status = 200
96
+ context.body = 'Succeeded'
97
+ } catch (e) {
98
+ context.type = 'application/json'
99
+ context.status = 500
100
+ context.body = e.message
101
+ }
102
+ }
103
+ })
104
+
105
+ sellercraftRouter.post('/sellercraft/store/update-order-status', async (context, next) => {
106
+ try {
107
+ const requestBody = context.request.body
108
+ var result: any = {}
109
+ let mappedStore: any = await getShop(context, requestBody.channel_id, requestBody.shop_id)
110
+ let packageIds: any =
111
+ mappedStore.platform == 'tiktok'
112
+ ? requestBody?.order_items.map(oi => {
113
+ return oi.package_id
114
+ })
115
+ : []
116
+
117
+ const reqBody = {
118
+ orderId: requestBody.native_order_id,
119
+ status: requestBody.order_status,
120
+ carrier: requestBody?.shipper_last_mile,
121
+ trackingNo: requestBody?.tracking_number,
122
+ orderItems: unmapOrderItems(
123
+ requestBody?.order_items.map(oi => {
124
+ return oi.native_order_item_id
125
+ }) || []
126
+ ),
127
+ isSOF: requestBody?.seller_logistics,
128
+ packageIds: [...new Set(packageIds)],
129
+ locations:
130
+ mappedStore.platform == 'shopify'
131
+ ? requestBody?.order_items.map(oi => {
132
+ let jsonLocation = oi.extra_metadata.locationId
133
+ return JSON.parse(jsonLocation)[0].location_id
134
+ })
135
+ : null,
136
+ context: { state: { domain: null } }
137
+ }
138
+
139
+ let responseBody: any = { operation_id: uuidv4() }
140
+ try {
141
+ try {
142
+ if (mappedStore.platform == 'tiktok') {
143
+ for (let packageId of reqBody.packageIds) {
144
+ result = (await StoreAPI.updateOrderStatus(mappedStore, { packageId, status: reqBody.status })) || {}
145
+ }
146
+ } else {
147
+ result = (await StoreAPI.updateOrderStatus(mappedStore, reqBody)) || {}
148
+ }
149
+
150
+ debug(result)
151
+ } catch (e) {
152
+ if (e?.requiredDocument) result.requiredDocument = e.requiredDocument
153
+ }
154
+
155
+ if (result?.requiredDocument) {
156
+ // call document api and ingest channel order package
157
+ const order: any = await StoreAPI.getStoreOrder(mappedStore, { orderId: reqBody.orderId })
158
+ let orderPackage: any = order ? (order[0]?.orderPackage ? order[0].orderPackage : {}) : {}
159
+ if (orderPackage) {
160
+ const packageId: string = orderPackage.packageId
161
+
162
+ let orderDocument: any = orderPackage.orderDocument
163
+ if (orderDocument?.length == 0) {
164
+ let hasDocument: boolean = false
165
+ let retries = 0
166
+ while (!hasDocument && retries < 10) {
167
+ orderDocument = await StoreAPI.getStoreOrderDocument(mappedStore, { packageId, documentType: 1 })
168
+ if (orderDocument?.length > 0) {
169
+ hasDocument = true
170
+ break
171
+ }
172
+ await sleep(1000)
173
+ retries++
174
+ }
175
+ }
176
+
177
+ // Order Package Payload
178
+ let newOrderPackage: any = {
179
+ channel_shop_id: mappedStore.channelShopId,
180
+ native_order_id: reqBody.orderId,
181
+ native_package_id: orderPackage.packageId.toString(),
182
+ shipping_tracking_code: orderPackage.trackingNumber,
183
+ shipping_type_value: orderPackage?.shippingType ? orderPackage.shippingType : SHIPPING_TYPE.DROP_SHIPPING,
184
+ warehouse_code: SHIPPING_TYPE.DROP_SHIPPING,
185
+ shipper: {
186
+ name: orderPackage.shippingProvider,
187
+ is_cod_supported: orderPackage?.isCodSupport ? orderPackage.isCodSupport : false
188
+ },
189
+ documents:
190
+ orderDocument?.map(doc => {
191
+ return {
192
+ file: doc.file,
193
+ file_type_value: doc.fileTypeValue,
194
+ mime_type: doc.mimeType
195
+ }
196
+ }) || [],
197
+ shipper_last_mile: {
198
+ name: orderPackage.shippingProvider,
199
+ is_cod_supported: orderPackage?.isCodSupport ? orderPackage.isCodSupport : false
200
+ },
201
+ order_item_ids: orderPackage?.orderListIdList
202
+ ? orderPackage.orderListIdList
203
+ : reqBody.orderItems.map(oi => {
204
+ return oi.order_item_id
205
+ })
206
+ }
207
+
208
+ responseBody.package = newOrderPackage
209
+ }
210
+ responseBody.response_code = 'E0'
211
+ responseBody.message = 'Success'
212
+ }
213
+
214
+ responseBody.time = Math.floor(new Date(new Date().toUTCString()).getTime() / 1000)
215
+
216
+ try {
217
+ createPayloadLog(
218
+ mappedStore.channelShopId,
219
+ '/sellercraft/store/update-order-status',
220
+ requestBody,
221
+ responseBody,
222
+ null,
223
+ PayloadType.INGESTION,
224
+ 'hub-api'
225
+ )
226
+ } catch (e) {}
227
+
228
+ context.type = 'application/json'
229
+ context.status = 200
230
+ context.body = responseBody
231
+
232
+ await next()
233
+ } catch (e) {
234
+ responseBody.response_code = 'E1'
235
+ responseBody.message = e.message
236
+
237
+ context.type = 'application/json'
238
+ context.status = 500
239
+ context.body = responseBody
240
+ try {
241
+ createPayloadLog(
242
+ mappedStore?.channelShopId,
243
+ '/sellercraft/store/update-order-status',
244
+ requestBody,
245
+ responseBody,
246
+ null,
247
+ PayloadType.INGESTION,
248
+ 'hub-api'
249
+ )
250
+ } catch (e) {}
251
+ }
252
+ } catch (e) {}
253
+ })
254
+
255
+ sellercraftRouter.post('/sellercraft/store/update-product-attribute', async (context, next) => {
256
+ const requestBody = context.request.body
257
+ let mappedStore: any = await getShop(context, requestBody.channel_id, requestBody.shop_id)
258
+
259
+ let { product_sku: productSku, native_product_id: itemId, variants } = requestBody
260
+
261
+ variants.map(variant => {
262
+ let { variant_sku: variantSku, native_variant_id: variantId, attributes } = variant
263
+
264
+ attributes.map(attribute => {
265
+ let { native_attribute_id: id, attribute_key: name, attribute_value: value } = attribute
266
+
267
+ return {
268
+ id,
269
+ name,
270
+ value
271
+ }
272
+ })
273
+
274
+ return {
275
+ variantSku,
276
+ variantId,
277
+ attributes
278
+ }
279
+ })
280
+
281
+ for (var i = 0; i < variants.length; i++) {
282
+ try {
283
+ let variant = variants[i]
284
+ await StoreAPI.updateProductAttribute(mappedStore, {
285
+ productSku,
286
+ itemId,
287
+ variant,
288
+ context: { state: { domain: null } }
289
+ })
290
+ context.type = 'application/json'
291
+ context.status = 200
292
+ context.body = 'Succeeded'
293
+ } catch (e) {
294
+ context.type = 'application/json'
295
+ context.status = 500
296
+ context.body = e.message
297
+ }
298
+ }
299
+ })
300
+
301
+ function unmapOrderItems(orderItems) {
302
+ let itemsList = []
303
+ let res = orderItems.map(e => {
304
+ return e.split('-')[0]
305
+ })
306
+
307
+ for (let item of res) {
308
+ let count = 0
309
+
310
+ res.forEach(element => {
311
+ if (element === item) {
312
+ count += 1
313
+ }
314
+ })
315
+
316
+ itemsList.push({
317
+ order_item_id: item,
318
+ qty: count
319
+ })
320
+ }
321
+
322
+ itemsList = itemsList.filter(
323
+ (value, index, self) => index === self.findIndex(t => t.order_item_id === value.order_item_id)
324
+ )
325
+ return itemsList
326
+ }
@@ -0,0 +1,32 @@
1
+ import { sellercraftRouter } from './routers/sellercraft-router'
2
+
3
+ const debug = require('debug')('things-factory:integration-sellercraft:routes')
4
+
5
+ process.on('bootstrap-module-global-public-route' as any, (app, globalPublicRouter) => {
6
+ /*
7
+ * can add global public routes to application (auth not required, tenancy not required)
8
+ *
9
+ * ex) routes.get('/path', async(context, next) => {})
10
+ * ex) routes.post('/path', async(context, next) => {})
11
+ */
12
+
13
+ globalPublicRouter.use(sellercraftRouter.routes(), sellercraftRouter.allowedMethods())
14
+ })
15
+
16
+ process.on('bootstrap-module-global-private-route' as any, (app, globalPrivateRouter) => {
17
+ /*
18
+ * can add global private routes to application (auth required, tenancy not required)
19
+ */
20
+ })
21
+
22
+ process.on('bootstrap-module-domain-public-route' as any, (app, domainPublicRouter) => {
23
+ /*
24
+ * can add domain public routes to application (auth not required, tenancy required)
25
+ */
26
+ })
27
+
28
+ process.on('bootstrap-module-domain-private-route' as any, (app, domainPrivateRouter) => {
29
+ /*
30
+ * can add domain private routes to application (auth required, tenancy required)
31
+ */
32
+ })
@@ -0,0 +1,23 @@
1
+ /* EXPORT ENTITY TYPES */
2
+ export * from './sellercraft/sellercraft'
3
+
4
+ /* IMPORT ENTITIES AND RESOLVERS */
5
+ import { entities as SellercraftEntities, resolvers as SellercraftResolvers } from './sellercraft'
6
+ import { entities as MarketplaceChannelEntities, resolvers as MarketplaceSellercraftResolvers } from './marketplace-channel'
7
+
8
+ export const entities = [
9
+ /* ENTITIES */
10
+ ...SellercraftEntities,
11
+ ...MarketplaceChannelEntities
12
+ ]
13
+
14
+
15
+ export const schema = {
16
+ resolverClasses: [
17
+ /* RESOLVER CLASSES */
18
+ ...SellercraftResolvers,
19
+ ...MarketplaceSellercraftResolvers
20
+ ]
21
+ }
22
+
23
+ export { MarketplaceSellercraftResolvers }
@@ -0,0 +1,6 @@
1
+ import { MarketplaceChannel } from './marketplace-channel'
2
+ import { MarketplaceChannelOrderMutation } from './marketplace-channel-order-mutation'
3
+ import { MarketplaceChannelProductMutation } from './marketplace-channel-product-mutation'
4
+
5
+ export const entities = [MarketplaceChannel]
6
+ export const resolvers = [MarketplaceChannelOrderMutation, MarketplaceChannelProductMutation]