@things-factory/warehouse-base 4.3.367 → 4.3.370

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.
@@ -1,8 +1,18 @@
1
- import { Field, Float, InputType, Int, ObjectType } from 'type-graphql'
1
+ import {
2
+ Field,
3
+ Float,
4
+ InputType,
5
+ Int,
6
+ ObjectType
7
+ } from 'type-graphql'
2
8
 
3
9
  import { User } from '@things-factory/auth-base'
4
10
  import { ProductBundleSetting } from '@things-factory/product-base'
5
- import { Domain, ObjectRef, ScalarDate } from '@things-factory/shell'
11
+ import {
12
+ Domain,
13
+ ObjectRef,
14
+ ScalarDate
15
+ } from '@things-factory/shell'
6
16
 
7
17
  import { Inventory } from './inventory'
8
18
 
@@ -59,6 +69,9 @@ export class InventoryPatch {
59
69
  @Field({ nullable: true })
60
70
  productName?: string
61
71
 
72
+ @Field({ nullable: true })
73
+ productDetailName?: string
74
+
62
75
  @Field({ nullable: true })
63
76
  productDetailId?: string
64
77
 
@@ -358,6 +358,9 @@ export class Inventory {
358
358
  @Field({ nullable: true })
359
359
  condition: string
360
360
 
361
+ @Field({ nullable: true })
362
+ partnerDomain: Domain
363
+
361
364
  constructor(obj?) {
362
365
  if (obj) {
363
366
  this.id = obj.inventory_id
@@ -1,14 +1,42 @@
1
- import { Arg, Ctx, Directive, Mutation, Resolver } from 'type-graphql'
2
- import { EntityManager, In, MoreThan, Not } from 'typeorm'
1
+ import {
2
+ Arg,
3
+ Ctx,
4
+ Directive,
5
+ Mutation,
6
+ Resolver
7
+ } from 'type-graphql'
8
+ import {
9
+ EntityManager,
10
+ In,
11
+ MoreThan,
12
+ Not,
13
+ SelectQueryBuilder
14
+ } from 'typeorm'
3
15
 
4
- import { Application, ApplicationType, User } from '@things-factory/auth-base'
5
- import { Bizplace } from '@things-factory/biz-base'
16
+ import {
17
+ Application,
18
+ ApplicationType,
19
+ User
20
+ } from '@things-factory/auth-base'
21
+ import {
22
+ Bizplace,
23
+ getCompanyBizplace
24
+ } from '@things-factory/biz-base'
6
25
  import { generateId } from '@things-factory/id-rule-base'
7
26
  import { Account } from '@things-factory/integration-accounting'
8
27
  import { MarketplaceStore } from '@things-factory/integration-marketplace'
9
- import { Sellercraft, SellercraftStatus } from '@things-factory/integration-sellercraft'
10
- import { Product, ProductDetail } from '@things-factory/product-base'
11
- import { PartnerSetting, Setting } from '@things-factory/setting-base'
28
+ import {
29
+ Sellercraft,
30
+ SellercraftStatus
31
+ } from '@things-factory/integration-sellercraft'
32
+ import {
33
+ Product,
34
+ ProductDetail
35
+ } from '@things-factory/product-base'
36
+ import {
37
+ PartnerSetting,
38
+ Setting
39
+ } from '@things-factory/setting-base'
12
40
  import { Domain } from '@things-factory/shell'
13
41
 
14
42
  import {
@@ -18,7 +46,12 @@ import {
18
46
  LOCATION_STATUS,
19
47
  LOCATION_TYPE
20
48
  } from '../../constants'
21
- import { EcommerceController, PosController, SellercraftController, PowrupController } from '../../controllers'
49
+ import {
50
+ EcommerceController,
51
+ PosController,
52
+ PowrupController,
53
+ SellercraftController
54
+ } from '../../controllers'
22
55
  import { InventoryNoGenerator } from '../../utils'
23
56
  import { InventoryHistory } from '../inventory-history/inventory-history'
24
57
  import { InventoryItemChange } from '../inventory-item-change/inventory-item-change'
@@ -28,7 +61,11 @@ import { Inventory } from '../inventory/inventory'
28
61
  import { InventoryPatch } from '../inventory/inventory-types'
29
62
  import { Location } from '../location/location'
30
63
  import { InventoryChange } from './inventory-change'
31
- import { InventoryChangeList, InventoryChangePatch, NewInventoryChange } from './inventory-change-types'
64
+ import {
65
+ InventoryChangeList,
66
+ InventoryChangePatch,
67
+ NewInventoryChange
68
+ } from './inventory-change-types'
32
69
 
33
70
  @Resolver(InventoryChange)
34
71
  export class InventoryChangeMutation {
@@ -195,6 +232,13 @@ export class InventoryChangeMutation {
195
232
 
196
233
  const inventoryChangeRepo = tx.getRepository(InventoryChange)
197
234
  const inventoryItemChangeRepo = tx.getRepository(InventoryItemChange)
235
+
236
+ let strictProduct = 'false'
237
+
238
+ let strictProductSelectionSetting: Setting
239
+ let partnerStrictProductSelectionSetting: Setting
240
+
241
+ // new inventory
198
242
  if (_createRecords.length > 0) {
199
243
  for (let i = 0; i < _createRecords.length; i++) {
200
244
  const newRecord = _createRecords[i]
@@ -214,28 +258,126 @@ export class InventoryChangeMutation {
214
258
 
215
259
  newRecord.bizplace = bizplace
216
260
 
217
- var product: Product = await tx.getRepository(Product).findOne(newRecord.product.id)
218
- var productDetail: ProductDetail = await tx.getRepository(ProductDetail).findOne({
219
- where: [
220
- {
221
- product: { id: newRecord.product.id },
222
- packingType: newRecord.packingType,
223
- packingSize: newRecord.packingSize,
261
+ //to get product name
262
+ let customerBizplaceId: string = newRecord.bizplace.id
263
+ const companyBizplace: Bizplace = await getCompanyBizplace(domain, null, customerBizplaceId, tx)
264
+
265
+ const qb: SelectQueryBuilder<ProductDetail> = tx
266
+ .getRepository(ProductDetail)
267
+ .createQueryBuilder('ProductDetail')
268
+ qb.innerJoinAndSelect('ProductDetail.product', 'Product')
269
+ qb.where('Product.deletedAt IS NULL')
270
+
271
+ let product: Product
272
+ let productDetail: ProductDetail
273
+
274
+ //define strictProduct in string
275
+
276
+ if (!newRecord?.product?.id && newRecord.sku) {
277
+ strictProductSelectionSetting = await tx.getRepository(Setting).findOne({
278
+ where: { domain, category: 'id-rule', name: 'strict-product-selection' }
279
+ })
280
+
281
+ if (strictProductSelectionSetting) strictProduct = strictProductSelectionSetting.value
282
+
283
+ partnerStrictProductSelectionSetting = await tx.getRepository(PartnerSetting).findOne({
284
+ where: {
285
+ setting: strictProductSelectionSetting,
286
+ domain,
287
+ partnerDomain: companyBizplace?.domain
288
+ }
289
+ })
290
+
291
+ if (partnerStrictProductSelectionSetting) strictProduct = partnerStrictProductSelectionSetting.value
292
+ }
293
+
294
+ //Condition for interface & if import new only have SKU or SKU and productName in Excel
295
+
296
+ if (newRecord?.productDetailId) {
297
+ qb.andWhere('ProductDetail.id =:productDetailId', { productDetailId: newRecord.productDetailId })
298
+ } else if (newRecord?.sku) {
299
+ qb.andWhere('Product.bizplace =:companyBizplaceId', { companyBizplaceId: companyBizplace.id })
300
+ qb.andWhere('Product.sku =:sku', {
301
+ sku: newRecord.sku
302
+ })
303
+
304
+ //Condition if the strictProduct=false,
305
+ // the user will be allowed to create new PackingType/PackingSize/Uom
306
+
307
+ if (newRecord?.productDetailName) {
308
+ qb.andWhere('ProductDetail.name =:productDetailName', {
309
+ productDetailName: newRecord.productDetailName
310
+ })
311
+ }
312
+
313
+ if (newRecord.packingType) {
314
+ qb.andWhere('ProductDetail.packingType =:packingType', {
315
+ packingType: newRecord.packingType
316
+ })
317
+ }
318
+ if (newRecord.packingSize) {
319
+ qb.andWhere('ProductDetail.packingSize =:packingSize', {
320
+ packingSize: newRecord.packingSize
321
+ })
322
+ }
323
+ if (newRecord.uom) {
324
+ qb.andWhere('ProductDetail.uom =:uom', {
224
325
  uom: newRecord.uom
225
- },
226
- {
227
- product: { id: newRecord.product.id },
228
- isDefault: true
326
+ })
327
+ }
328
+
329
+ productDetail = await qb.getOne()
330
+
331
+ if (strictProduct == 'true') {
332
+ if (newRecord?.packingType && newRecord?.packingType !== productDetail?.packingType) {
333
+ throw new Error('Packing type does not match with product master')
229
334
  }
230
- ],
231
- order: { isDefault: 'DESC' }
232
- })
335
+ if (newRecord?.packingSize && newRecord?.packingSize !== productDetail?.packingSize) {
336
+ throw new Error('Packing size does not match with product master')
337
+ }
338
+ if (newRecord?.uom && newRecord?.uom !== productDetail?.uom) {
339
+ throw new Error('Uom value does not match with product master')
340
+ }
341
+ }
342
+ }
343
+
344
+ //If could not find productDetail, and only have SKU it will take by default
345
+ if (!productDetail) {
346
+ const qb: SelectQueryBuilder<ProductDetail> = tx
347
+ .getRepository(ProductDetail)
348
+ .createQueryBuilder('ProductDetail')
349
+ qb.innerJoinAndSelect('ProductDetail.product', 'Product')
350
+ qb.where('Product.deletedAt IS NULL')
351
+
352
+ if (newRecord?.sku) {
353
+ qb.andWhere('Product.sku =:sku', {
354
+ sku: newRecord.sku
355
+ })
356
+ } else {
357
+ qb.andWhere('ProductDetail.isDefault = true')
358
+ }
359
+
360
+ productDetail = await qb.getOne()
233
361
 
234
- newRecord.product = product
235
- newRecord.productDetail = productDetail
236
- if (!newRecord.unitCost) {
237
- newRecord.unitCost = product.unitCost
362
+ newRecord.product = productDetail.product
363
+ newRecord.productDetail = productDetail
364
+ } else {
365
+ //if the productDetail is found, and take by default
366
+ newRecord.product = productDetail.product
367
+ newRecord.productDetail = productDetail
238
368
  }
369
+
370
+ //Update field
371
+ const fields = ['unitCost', 'packingType', 'packingSize', 'uom']
372
+
373
+ fields.forEach(field => {
374
+ if (!newRecord[field] && field == 'unitCost') {
375
+ newRecord[field] = productDetail.product[field]
376
+ } else if (!newRecord[field]) {
377
+ newRecord[field] = productDetail[field]
378
+ }
379
+ })
380
+
239
381
  newRecord.status = 'PENDING'
240
382
  newRecord.transactionType = 'NEW'
241
383
 
@@ -291,7 +433,7 @@ export class InventoryChangeMutation {
291
433
  // create new inventory items
292
434
  if (newRecord?.inventoryItemChangesJson) {
293
435
  const inventoryItemChanges: InventoryItemChange[] = JSON.parse(newRecord.inventoryItemChangesJson)
294
- if (product?.isRequireSerialNumberScanningInbound) {
436
+ if (productDetail?.product?.isRequireSerialNumberScanningInbound) {
295
437
  if (inventoryItemChanges?.length != newRecord?.qty) {
296
438
  throw new Error('Serial Numbers not tally with quantity')
297
439
  }
@@ -322,6 +464,7 @@ export class InventoryChangeMutation {
322
464
  }
323
465
  }
324
466
 
467
+ // update inventory
325
468
  if (_updateRecords.length > 0) {
326
469
  for (let i = 0; i < _updateRecords.length; i++) {
327
470
  const updateRecord = _updateRecords[i]
@@ -337,10 +480,18 @@ export class InventoryChangeMutation {
337
480
  if (!existingRecord) {
338
481
  existingRecord = await tx.getRepository(Inventory).findOne({
339
482
  where: { domain, id: updateRecord?.id },
340
- relations: ['location', 'warehouse', 'product', 'bizplace', 'bizplace.domain']
483
+ relations: ['location', 'warehouse', 'product', 'productDetail', 'bizplace', 'bizplace.domain']
341
484
  })
342
485
  }
343
486
 
487
+ if (updateRecord.palletId && updateRecord.palletId !== existingRecord.palletId) {
488
+ throw new Error('Updating the Lot ID is not permitted')
489
+ }
490
+
491
+ if (updateRecord.cartonId && updateRecord.cartonId !== existingRecord.cartonId) {
492
+ throw new Error('Updating the Carton ID is not permitted')
493
+ }
494
+
344
495
  if (adjustQty >= 0) {
345
496
  let existingQty: number = existingRecord.qty - existingRecord.lockedQty
346
497
  qtyDiff = existingQty == adjustQty ? existingQty - adjustQty : adjustQty - existingQty
@@ -370,31 +521,98 @@ export class InventoryChangeMutation {
370
521
  updateRecord.bizplace = curBizplace
371
522
  }
372
523
 
524
+ let customerBizplaceId: string = updateRecord?.bizplace?.id || existingRecord?.bizplace?.id
525
+ const companyBizplace: Bizplace = await getCompanyBizplace(domain, null, customerBizplaceId)
526
+
527
+ const qb: SelectQueryBuilder<ProductDetail> = tx
528
+ .getRepository(ProductDetail)
529
+ .createQueryBuilder('ProductDetail')
530
+ qb.innerJoinAndSelect('ProductDetail.product', 'Product')
531
+ qb.where('Product.deletedAt IS NULL')
532
+
373
533
  let product: Product
374
534
  let productDetail: ProductDetail
375
535
 
376
- if (!!updateRecord?.product?.id) {
377
- product = await tx.getRepository(Product).findOne(updateRecord.product.id)
378
- productDetail = await tx.getRepository(ProductDetail).findOne({
379
- where: [
380
- {
381
- product: { id: updateRecord.product.id },
382
- packingType: updateRecord?.packingType ? updateRecord.packingType : existingRecord.packingType,
383
- packingSize: updateRecord?.packingSize ? updateRecord.packingSize : existingRecord.packingSize,
384
- uom: updateRecord?.uom ? updateRecord.uom : existingRecord.uom
385
- },
386
- {
387
- product: { id: updateRecord.product.id },
388
- isDefault: true
389
- }
390
- ],
391
- order: { isDefault: 'DESC' }
536
+ if (!updateRecord?.productDetail?.id && updateRecord.sku) {
537
+ strictProductSelectionSetting = await tx.getRepository(Setting).findOne({
538
+ where: { domain, category: 'id-rule', name: 'strict-product-selection' }
392
539
  })
393
- updateRecord.product = product
394
- updateRecord.productDetail = productDetail
395
- } else {
396
- product = existingRecord.product
397
- productDetail = existingRecord.productDetail
540
+
541
+ if (strictProductSelectionSetting) strictProduct = strictProductSelectionSetting.value
542
+
543
+ partnerStrictProductSelectionSetting = await tx.getRepository(PartnerSetting).findOne({
544
+ where: {
545
+ setting: strictProductSelectionSetting,
546
+ domain,
547
+ partnerDomain: companyBizplace?.domain
548
+ }
549
+ })
550
+
551
+ if (partnerStrictProductSelectionSetting) strictProduct = partnerStrictProductSelectionSetting.value
552
+ }
553
+
554
+ let foundProductDetail: ProductDetail
555
+
556
+ //Condition for interface & if import update only have SKU or SKU and productName in Excel
557
+ if (updateRecord?.productDetailId || updateRecord.sku) {
558
+ if (updateRecord?.productDetailId) {
559
+ qb.andWhere('ProductDetail.id =:productDetailId', { productDetailId: updateRecord.productDetailId })
560
+ } else if (updateRecord.sku) {
561
+ qb.andWhere('Product.bizplace =:companyBizplaceId', { companyBizplaceId: companyBizplace.id })
562
+ qb.andWhere('Product.sku =:sku', {
563
+ sku: updateRecord?.sku ? updateRecord.sku : existingRecord.product.sku
564
+ })
565
+
566
+ //Condition if the strictProduct=false, the user will be allowed to update PackingType/PackingSize/Uom
567
+ //else it will take by default
568
+
569
+ if (updateRecord.productDetailName) {
570
+ qb.andWhere('ProductDetail.name =:productDetailName', {
571
+ productDetailName: updateRecord.productDetailName
572
+ })
573
+ }
574
+ if (strictProduct == 'false') {
575
+ if (updateRecord.packingType) {
576
+ qb.andWhere('ProductDetail.packingType =:packingType', {
577
+ packingType: updateRecord?.packingType ? updateRecord.packingType : existingRecord.packingType
578
+ })
579
+ }
580
+ if (updateRecord.packingSize) {
581
+ qb.andWhere('ProductDetail.packingSize =:packingSize', {
582
+ packingSize: updateRecord?.packingSize ? updateRecord.packingSize : existingRecord.packingSize
583
+ })
584
+ }
585
+ if (updateRecord.uom) {
586
+ qb.andWhere('ProductDetail.uom =:uom', {
587
+ uom: updateRecord?.uom ? updateRecord.uom : existingRecord.uom
588
+ })
589
+ }
590
+ } else {
591
+ qb.andWhere('ProductDetail.isDefault = true')
592
+ }
593
+ }
594
+
595
+ foundProductDetail = await qb.getOne()
596
+
597
+ //Validation if the strictProduct= true, user are not allowed to update PackingType/PackingSize/Uom
598
+ //else it will take existing record from product/productDetail
599
+ if (foundProductDetail) {
600
+ if (strictProduct === 'true') {
601
+ if (updateRecord.packingType && updateRecord.packingType !== foundProductDetail.packingType) {
602
+ throw new Error('Updating the packing type is not permitted')
603
+ }
604
+ if (updateRecord.packingSize && updateRecord.packingSize !== foundProductDetail.packingSize) {
605
+ throw new Error('Updating the packing size is not permitted')
606
+ }
607
+ if (updateRecord.uom && updateRecord.uom !== foundProductDetail.uom) {
608
+ throw new Error('Updating the uom is not permitted')
609
+ }
610
+ }
611
+ updateRecord.productDetail = foundProductDetail
612
+ } else {
613
+ product = existingRecord.product
614
+ productDetail = existingRecord.productDetail
615
+ }
398
616
  }
399
617
 
400
618
  updateRecord.transactionType == 'MISSING'
@@ -409,16 +627,15 @@ export class InventoryChangeMutation {
409
627
  : updateRecord.expirationDate
410
628
 
411
629
  const updatedInventoryChange: InventoryChange = await inventoryChangeRepo.save({
412
- palletId: existingRecord.palletId,
413
- cartonId: existingRecord.cartonId,
414
630
  ...updateRecord,
415
631
  id: undefined,
416
632
  name: InventoryNoGenerator.inventoryName(),
417
633
  inventory: existingRecord,
634
+ palletId: existingRecord.palletId,
635
+ cartonId: existingRecord.cartonId,
418
636
  domain: domain,
419
637
  creator: user,
420
638
  updater: user
421
- //here?
422
639
  })
423
640
 
424
641
  const prevBizAccount: Account = await tx
@@ -569,6 +786,7 @@ export class InventoryChangeMutation {
569
786
  'inventory.warehouse',
570
787
  'bizplace',
571
788
  'product',
789
+ 'productDetail',
572
790
  'location'
573
791
  ]
574
792
  })
@@ -1117,6 +1335,8 @@ export async function approveInventoryChanges(patches: any, context: any) {
1117
1335
 
1118
1336
  if (date > releaseBefore) {
1119
1337
  newRecord.obsolete = true
1338
+ } else {
1339
+ newRecord.obsolete = false
1120
1340
  }
1121
1341
  }
1122
1342
 
@@ -1,4 +1,8 @@
1
- import { Field, ID, ObjectType } from 'type-graphql'
1
+ import {
2
+ Field,
3
+ ID,
4
+ ObjectType
5
+ } from 'type-graphql'
2
6
  import {
3
7
  Column,
4
8
  CreateDateColumn,
@@ -12,8 +16,14 @@ import {
12
16
 
13
17
  import { User } from '@things-factory/auth-base'
14
18
  import { Bizplace } from '@things-factory/biz-base'
15
- import { Product, ProductDetail } from '@things-factory/product-base'
16
- import { Domain, ScalarDate } from '@things-factory/shell'
19
+ import {
20
+ Product,
21
+ ProductDetail
22
+ } from '@things-factory/product-base'
23
+ import {
24
+ Domain,
25
+ ScalarDate
26
+ } from '@things-factory/shell'
17
27
 
18
28
  import { Inventory } from '../inventory/inventory'
19
29
  import { Location } from '../location/location'