@things-factory/operato-hub 4.3.543 → 4.3.544

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 (22) hide show
  1. package/dist-server/routers/api/restful-apis/v1/company/get-webhook-details.js +58 -0
  2. package/dist-server/routers/api/restful-apis/v1/company/get-webhook-details.js.map +1 -0
  3. package/dist-server/routers/api/restful-apis/v1/company/index.js +2 -0
  4. package/dist-server/routers/api/restful-apis/v1/company/index.js.map +1 -1
  5. package/dist-server/routers/api/restful-apis/v1/company/upsert-webhooks.js +146 -0
  6. package/dist-server/routers/api/restful-apis/v1/company/upsert-webhooks.js.map +1 -0
  7. package/dist-server/routers/api/restful-apis/v1/utils/params.js +43 -0
  8. package/dist-server/routers/api/restful-apis/v1/utils/params.js.map +1 -1
  9. package/dist-server/routers/api/restful-apis/v1/warehouse/get-inventory-adjustment-details.js +108 -0
  10. package/dist-server/routers/api/restful-apis/v1/warehouse/get-inventory-adjustment-details.js.map +1 -0
  11. package/dist-server/routers/api/restful-apis/v1/warehouse/index.js +1 -0
  12. package/dist-server/routers/api/restful-apis/v1/warehouse/index.js.map +1 -1
  13. package/openapi/v1/Inventory.yaml +221 -0
  14. package/openapi/v1/webhook.yaml +281 -0
  15. package/openapi/v1.yaml +2 -0
  16. package/package.json +14 -14
  17. package/server/routers/api/restful-apis/v1/company/get-webhook-details.ts +64 -0
  18. package/server/routers/api/restful-apis/v1/company/index.ts +2 -0
  19. package/server/routers/api/restful-apis/v1/company/upsert-webhooks.ts +176 -0
  20. package/server/routers/api/restful-apis/v1/utils/params.ts +43 -0
  21. package/server/routers/api/restful-apis/v1/warehouse/get-inventory-adjustment-details.ts +112 -0
  22. package/server/routers/api/restful-apis/v1/warehouse/index.ts +1 -0
@@ -0,0 +1,176 @@
1
+ import { EntityManager, getConnection, SelectQueryBuilder } from 'typeorm'
2
+
3
+ import { restfulApiRouter as router } from '@things-factory/api'
4
+ import { Bizplace, getPartnersBizplaces } from '@things-factory/biz-base'
5
+ import { Webhook, WebhookEvent, WebhookEventsEnum } from '@things-factory/integration-base'
6
+
7
+ import { businessMiddleware, loggingMiddleware, validationMiddleware } from '../middlewares'
8
+ import { ApiError, ApiErrorHandler, throwInternalServerError } from '../utils/error-util'
9
+
10
+ router.post(
11
+ '/v1/company/upsert-webhooks',
12
+ businessMiddleware,
13
+ validationMiddleware,
14
+ loggingMiddleware,
15
+ async (context, next) => {
16
+ try {
17
+ await getConnection().transaction(async (tx: EntityManager) => {
18
+ const { domain, user } = context.state
19
+ const webhooks = context.request.body.data
20
+ const webhookIds: string[] = []
21
+
22
+ if (!Array.isArray(webhooks)) throw new ApiError('E01', 'data field is not an array')
23
+
24
+ for (const wb of webhooks) {
25
+ const bizplace: Bizplace = await tx.getRepository(Bizplace).findOne({
26
+ where: { id: wb.warehouseBizplaceId },
27
+ relations: ['domain']
28
+ })
29
+
30
+ if (!wb?.events || wb?.events?.length < 1)
31
+ throw new ApiError('E01', 'The "events" field is required and must contain at least one event.')
32
+ const partnerBizplaces: any[] = await getPartnersBizplaces(bizplace.domain, user, tx)
33
+
34
+ const validatedPartnerBiz = validatePartnerBizplaces(partnerBizplaces, wb.partnerBizplaceIds)
35
+
36
+ if (!wb.id) {
37
+ let data = await generateWebook(tx, wb, validatedPartnerBiz, bizplace.domain)
38
+ webhookIds.push(data)
39
+ continue
40
+ }
41
+
42
+ webhookIds.push(wb.id)
43
+ await updateWebhook(tx, wb, validatedPartnerBiz)
44
+ }
45
+
46
+ const qb: SelectQueryBuilder<Webhook> = await tx
47
+ .getRepository(Webhook)
48
+ .createQueryBuilder('webhook')
49
+ .innerJoinAndSelect('webhook.bizplaces', 'bz')
50
+ .innerJoinAndSelect('webhook.webhookEvents', 'webhookEvent')
51
+ .where('webhook.id in (:...webhookIds)', { webhookIds })
52
+
53
+ const result = await qb.getMany()
54
+
55
+ let data = []
56
+ for (const wb of result) {
57
+ let datapoint = {}
58
+ datapoint = {
59
+ id: wb.id,
60
+ name: wb.name,
61
+ active: wb.active,
62
+ targetUrl: wb.targetUrl,
63
+ extensionUrl: wb.extensionUrl,
64
+ events: wb.webhookEvents.map(e => e.name),
65
+ partnerBizplaces: wb.bizplaces.map(b => ({ id: b.id, name: b.name }))
66
+ }
67
+ data.push(datapoint)
68
+ }
69
+
70
+ context.body = {
71
+ data
72
+ }
73
+ })
74
+ } catch (e) {
75
+ if (e instanceof ApiError) ApiErrorHandler(context, e)
76
+ else throwInternalServerError(context, e)
77
+ }
78
+ }
79
+ )
80
+
81
+ function validatePartnerBizplaces(partnerBizplacesIdsDb, partnerBizplaceIds: any[]) {
82
+ const partnerBizplaceIdsSet = new Set(partnerBizplacesIdsDb.map(itm => itm.id))
83
+ const payloadPartnerBizplaceIds = new Set(partnerBizplaceIds.map(itm => itm.id))
84
+
85
+ const notPartnerBizplaces: Bizplace[] = partnerBizplaceIds.filter(itm => !partnerBizplaceIdsSet.has(itm.id))
86
+
87
+ if (notPartnerBizplaces.length > 0) {
88
+ throw new ApiError('E01', `partner bizplaces not found for ${notPartnerBizplaces.join(',')} `)
89
+ }
90
+
91
+ return partnerBizplacesIdsDb.filter(itm => payloadPartnerBizplaceIds.has(itm.id))
92
+ }
93
+
94
+ function generateInsertQuery(tableName: string) {
95
+ return `
96
+ INSERT INTO ${tableName} (name, webhook_id, created_at)
97
+ SELECT e.name, e.webhook_id, e.created_at
98
+ FROM jsonb_to_recordset($1) AS e(name webhook_events_enum, webhook_id UUID, created_at TIMESTAMP)
99
+ RETURNING *;
100
+ `
101
+ }
102
+
103
+ function eventsToInsert(events: WebhookEventsEnum[], webhook: Webhook) {
104
+ const filteredEvents = []
105
+
106
+ for (const [key, values] of Object.entries(WebhookEventsEnum)) {
107
+ if (events.includes(values)) {
108
+ filteredEvents.push(values)
109
+ }
110
+ }
111
+
112
+ return filteredEvents.map(e => ({
113
+ name: e,
114
+ webhook_id: webhook.id,
115
+ created_at: new Date()
116
+ }))
117
+ }
118
+
119
+ async function generateWebook(tx: EntityManager, webhook: Webhook, bizplaces: Bizplace[], warehouseDomain: any) {
120
+ const data = {
121
+ name: webhook.name,
122
+ targetUrl: webhook.targetUrl,
123
+ extensionUrl: webhook.extensionUrl,
124
+ active: webhook.active,
125
+ bizplaces: bizplaces,
126
+ domain: warehouseDomain,
127
+ updatedAt: new Date()
128
+ }
129
+
130
+ const addedWebhook: Webhook = await tx.getRepository(Webhook).save(data)
131
+
132
+ if (webhook.events.length > 0) {
133
+ await tx
134
+ .getRepository(WebhookEvent)
135
+ .query(generateInsertQuery('webhook_events'), [JSON.stringify(eventsToInsert(webhook.events, addedWebhook))])
136
+ }
137
+
138
+ const newWebhook: Webhook = await tx.getRepository(Webhook).findOne({ where: { id: addedWebhook.id } })
139
+
140
+ return newWebhook.id
141
+ }
142
+
143
+ async function updateWebhook(tx: EntityManager, webhook: Webhook, bizplace: Bizplace[]) {
144
+ await tx.getRepository(Webhook).update(webhook.id, {
145
+ name: webhook.name,
146
+ targetUrl: webhook.targetUrl,
147
+ extensionUrl: webhook.extensionUrl,
148
+ active: webhook.active,
149
+ updatedAt: new Date()
150
+ })
151
+
152
+ const webhookEvents: WebhookEvent = await tx
153
+ .getRepository(WebhookEvent)
154
+ .find({ where: { webhook: { id: webhook.id } } })
155
+
156
+ const payloadEvents = new Set(webhook?.events)
157
+ const dbEvents = new Set(webhookEvents.map(itm => itm.name))
158
+
159
+ const { addEvents, removeEvents } = {
160
+ addEvents: webhook?.events.filter(event => !dbEvents.has(event)),
161
+ removeEvents: webhookEvents.filter(event => !payloadEvents.has(event.name))
162
+ }
163
+
164
+ if (addEvents.length > 0) {
165
+ await tx
166
+ .getRepository(WebhookEvent)
167
+ .query(generateInsertQuery('webhook_events'), [JSON.stringify(eventsToInsert(addEvents, { id: webhook.id }))])
168
+ }
169
+
170
+ if (removeEvents.length > 0) {
171
+ const removeEventNames = removeEvents.map(itm => `'${itm.name}'`).join(',')
172
+ await tx
173
+ .getRepository(WebhookEvent)
174
+ .query(`DELETE FROM webhook_events where webhook_id = '${webhook.id}' and name in (${removeEventNames})`)
175
+ }
176
+ }
@@ -27,8 +27,10 @@ export const params = {
27
27
  'get-delivery-order-list': ['bizplaceId', 'doNo', 'vehicleNo', ...fromToDate, ...limitOffset],
28
28
  'get-onhand-inventory-list': ['bizplaceId', 'sku', 'batchId', 'fromDate', 'toDate', ...limitOffset],
29
29
  'get-inventory-product-group-list': ['bizplaceId', 'sku', 'batchId', ...fromToDate, ...limitOffset],
30
+ 'get-inventory-adjustment-details': ['bizplaceId', 'id'],
30
31
  'get-inventory-adjustment-list': ['bizplaceId', ...fromToDate, ...limitOffset, 'sku', 'adjustmentStatus'],
31
32
  'get-manifest-details': ['bizplaceId', 'name', 'id'],
33
+ 'get-webhook-details': ['warehouseBizplaceId', 'webhookId'],
32
34
  'add-contact-points': [
33
35
  'bizplaceId',
34
36
  'name',
@@ -170,6 +172,17 @@ export const params = {
170
172
  'marketplaceProducts.marketplaceProductVariations.inventoryItemId',
171
173
  'marketplaceProducts.marketplaceProductVariations.locationId'
172
174
  ],
175
+ 'upsert-webhooks': [
176
+ 'id',
177
+ 'name',
178
+ 'warehouseBizplaceId',
179
+ 'partnerBizplaceIds',
180
+ 'partnerBizplaceIds.id',
181
+ 'active',
182
+ 'targetUrl',
183
+ 'extensionUrl',
184
+ 'events'
185
+ ],
173
186
  'update-marketplace-product': [
174
187
  'storeId',
175
188
  'id',
@@ -580,7 +593,9 @@ export const reqParams = {
580
593
  'get-onhand-inventory-list': ['bizplaceId'],
581
594
  'get-inventory-product-group-list': ['bizplaceId'],
582
595
  'get-manifest-details': ['bizplaceId'],
596
+ 'get-webhook-details': ['warehouseBizplaceId'],
583
597
  'get-warehouse-bizplace-onhand-inventories': ['bizplaceId'],
598
+ 'get-inventory-adjustment-details': ['id'],
584
599
  'get-inventory-adjustment-list': ['bizplaceId', 'fromDate', 'toDate'],
585
600
  'upsert-product': [
586
601
  {
@@ -611,6 +626,34 @@ export const reqParams = {
611
626
  ]
612
627
  }
613
628
  ],
629
+ 'upsert-webhooks': [
630
+ {
631
+ name: 'warehouseBizplaceId',
632
+ required: true,
633
+ type: 'field'
634
+ },
635
+ {
636
+ name: 'partnerBizplaceIds',
637
+ required: true,
638
+ type: 'array',
639
+ data: [{ name: 'id', required: true, type: 'field' }]
640
+ },
641
+ {
642
+ name: 'active',
643
+ required: true,
644
+ type: 'field'
645
+ },
646
+ {
647
+ name: 'name',
648
+ required: true,
649
+ type: 'field'
650
+ },
651
+ {
652
+ name: 'targetUrl',
653
+ required: true,
654
+ type: 'field'
655
+ }
656
+ ],
614
657
  'upsert-products': [
615
658
  {
616
659
  name: 'sku',
@@ -0,0 +1,112 @@
1
+ import _ from 'lodash'
2
+ import { getRepository } from 'typeorm'
3
+
4
+ import { restfulApiRouter as router } from '@things-factory/api'
5
+ import { InventoryChange } from '@things-factory/warehouse-base'
6
+
7
+ import { businessMiddleware, loggingMiddleware, validationMiddleware } from '../middlewares'
8
+ import { ApiError, ApiErrorHandler, throwInternalServerError } from '../utils/error-util'
9
+
10
+ router.get(
11
+ '/v1/warehouse/get-inventory-adjustment-details',
12
+ businessMiddleware,
13
+ validationMiddleware,
14
+ loggingMiddleware,
15
+ async (context, next) => {
16
+ try {
17
+ // get contact points for that bizplace
18
+ // optional query parameter filter
19
+ const { domain } = context.state
20
+ let filter = { ...context.query, domain: domain.id }
21
+ if (_.has(filter, 'bizplaceId')) {
22
+ filter['bizplace'] = filter['bizplaceId']
23
+ delete filter['bizplaceId']
24
+ }
25
+
26
+ const inventoryChange: InventoryChange = await getRepository(InventoryChange).findOne({
27
+ where: filter,
28
+ relations: ['product', 'productDetail', 'bizplace', 'lastInventoryHistory']
29
+ })
30
+
31
+ if (!inventoryChange) {
32
+ throw new ApiError('E04', 'inventory adjustment not found')
33
+ }
34
+
35
+ // create and format data response
36
+ const data = {
37
+ id: inventoryChange.id,
38
+ current: {
39
+ productDetails: {
40
+ refCode: inventoryChange.productDetail.refCode,
41
+ packingType: inventoryChange.productDetail.packingType,
42
+ packingSize: inventoryChange.productDetail.packingSize,
43
+ uom: inventoryChange.productDetail.uom,
44
+ uomValue: inventoryChange.productDetail.uomValue
45
+ },
46
+ product: {
47
+ sku: inventoryChange.product.sku,
48
+ name: inventoryChange.product.name,
49
+ descrtiption: inventoryChange.product.description
50
+ },
51
+ bizplace: {
52
+ name: inventoryChange.bizplace.name
53
+ },
54
+ qty: inventoryChange.qty,
55
+ uom: inventoryChange.uom,
56
+ uomValue: inventoryChange.uomValue,
57
+ packingType: inventoryChange.packingType,
58
+ packingSize: inventoryChange.packingSize,
59
+ batchId: inventoryChange.batchId,
60
+ batchIdRef: inventoryChange.batchIdRef,
61
+ expirationDate: inventoryChange.expirationDate,
62
+ manufactureYear: inventoryChange.manufactureYear,
63
+ manufactureDate: inventoryChange.manufactureDate,
64
+ palletId: inventoryChange.palletId,
65
+ cartonId: inventoryChange.cartonId,
66
+ },
67
+ previous: {
68
+ productDetails: {
69
+ refCode: inventoryChange.productDetail.refCode,
70
+ packingType: inventoryChange.productDetail.packingType,
71
+ packingSize: inventoryChange.productDetail.packingSize,
72
+ uom: inventoryChange.productDetail.uom,
73
+ uomValue: inventoryChange.productDetail.uomValue
74
+ },
75
+ product: {
76
+ sku: inventoryChange.product.sku,
77
+ name: inventoryChange.product.name,
78
+ descrtiption: inventoryChange.product.description
79
+ },
80
+ bizplace: {
81
+ name: inventoryChange.bizplace.name
82
+ },
83
+ qty: inventoryChange.lastInventoryHistory.qty,
84
+ uom: inventoryChange.lastInventoryHistory.uom,
85
+ uomValue: inventoryChange.lastInventoryHistory.uomValue,
86
+ packingType: inventoryChange.lastInventoryHistory.packingType,
87
+ packingSize: inventoryChange.lastInventoryHistory.packingSize,
88
+ batchId: inventoryChange.lastInventoryHistory.batchId,
89
+ batchIdRef: inventoryChange.lastInventoryHistory.batchIdRef,
90
+ expirationDate: inventoryChange.lastInventoryHistory.expirationDate,
91
+ manufactureYear: inventoryChange.lastInventoryHistory.manufactureYear,
92
+ manufactureDate: inventoryChange.lastInventoryHistory.manufactureDate,
93
+ palletId: inventoryChange.lastInventoryHistory.palletId,
94
+ cartonId: inventoryChange.lastInventoryHistory.cartonId,
95
+ },
96
+ adjustmentCode: inventoryChange.adjustmentCode,
97
+ adjustmentNote: inventoryChange.adjustmentNote,
98
+ type: inventoryChange.transactionType,
99
+ status: inventoryChange.status,
100
+ createdAt: inventoryChange.createdAt,
101
+ updatedAt: inventoryChange.updatedAt
102
+ }
103
+
104
+ context.body = {
105
+ data
106
+ }
107
+ } catch (e) {
108
+ if (e instanceof ApiError) ApiErrorHandler(context, e)
109
+ else throwInternalServerError(context, e)
110
+ }
111
+ }
112
+ )
@@ -20,5 +20,6 @@ import './dispatch-delivery-order'
20
20
  import './update-gan-to-arrived'
21
21
  import './complete-delivery-order'
22
22
  import './get-inventory-adjustment-list'
23
+ import './get-inventory-adjustment-details'
23
24
  import './get-manifest-details'
24
25
  import './update-order-package-details'