@things-factory/product-base 8.0.0-beta.9 → 8.0.2

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 (63) hide show
  1. package/dist-server/service/product/product-mutation.js +1 -1
  2. package/dist-server/service/product/product-mutation.js.map +1 -1
  3. package/dist-server/service/product/product-query.d.ts +1 -1
  4. package/dist-server/service/product/product-query.js +5 -5
  5. package/dist-server/service/product/product-query.js.map +1 -1
  6. package/dist-server/service/product/product-types.d.ts +4 -2
  7. package/dist-server/service/product/product-types.js +10 -2
  8. package/dist-server/service/product/product-types.js.map +1 -1
  9. package/dist-server/service/product/product.d.ts +2 -1
  10. package/dist-server/service/product/product.js +6 -1
  11. package/dist-server/service/product/product.js.map +1 -1
  12. package/dist-server/tsconfig.tsbuildinfo +1 -1
  13. package/package.json +5 -5
  14. package/server/constants/index.ts +1 -0
  15. package/server/constants/product.ts +24 -0
  16. package/server/controllers/index.ts +0 -0
  17. package/server/index.ts +2 -0
  18. package/server/middlewares/index.ts +0 -0
  19. package/server/migrations/index.ts +9 -0
  20. package/server/service/index.ts +61 -0
  21. package/server/service/product/index.ts +6 -0
  22. package/server/service/product/product-mutation.ts +407 -0
  23. package/server/service/product/product-query.ts +336 -0
  24. package/server/service/product/product-types.ts +458 -0
  25. package/server/service/product/product.ts +548 -0
  26. package/server/service/product/validate-product.ts +42 -0
  27. package/server/service/product-bundle/index.ts +6 -0
  28. package/server/service/product-bundle/product-bundle-mutation.ts +140 -0
  29. package/server/service/product-bundle/product-bundle-query.ts +104 -0
  30. package/server/service/product-bundle/product-bundle-types.ts +51 -0
  31. package/server/service/product-bundle/product-bundle.ts +102 -0
  32. package/server/service/product-bundle-setting/index.ts +6 -0
  33. package/server/service/product-bundle-setting/product-bundle-setting-mutation.ts +168 -0
  34. package/server/service/product-bundle-setting/product-bundle-setting-query.ts +139 -0
  35. package/server/service/product-bundle-setting/product-bundle-setting-types.ts +41 -0
  36. package/server/service/product-bundle-setting/product-bundle-setting.ts +45 -0
  37. package/server/service/product-combination/index.ts +6 -0
  38. package/server/service/product-combination/product-combination-mutation.ts +148 -0
  39. package/server/service/product-combination/product-combination-query.ts +48 -0
  40. package/server/service/product-combination/product-combination-type.ts +50 -0
  41. package/server/service/product-combination/product-combination.ts +116 -0
  42. package/server/service/product-combination-setting/index.ts +6 -0
  43. package/server/service/product-combination-setting/product-combination-setting-mutation.ts +248 -0
  44. package/server/service/product-combination-setting/product-combination-setting-query.ts +176 -0
  45. package/server/service/product-combination-setting/product-combination-setting-type.ts +44 -0
  46. package/server/service/product-combination-setting/product-combination-setting.ts +77 -0
  47. package/server/service/product-detail/index.ts +6 -0
  48. package/server/service/product-detail/product-detail-mutation.ts +238 -0
  49. package/server/service/product-detail/product-detail-query.ts +213 -0
  50. package/server/service/product-detail/product-detail-types.ts +280 -0
  51. package/server/service/product-detail/product-detail.ts +388 -0
  52. package/server/service/product-detail-bizplace-setting/index.ts +6 -0
  53. package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting-mutation.ts +118 -0
  54. package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting-query.ts +90 -0
  55. package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting-types.ts +62 -0
  56. package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting.ts +104 -0
  57. package/server/service/product-set/index.ts +6 -0
  58. package/server/service/product-set/product-set-mutation.ts +149 -0
  59. package/server/service/product-set/product-set-query.ts +114 -0
  60. package/server/service/product-set/product-set-types.ts +45 -0
  61. package/server/service/product-set/product-set.ts +95 -0
  62. package/server/utils/index.ts +1 -0
  63. package/server/utils/product-util.ts +11 -0
@@ -0,0 +1,336 @@
1
+ import { Arg, Args, Ctx, Directive, FieldResolver, Query, Resolver, Root } from 'type-graphql'
2
+ import { Brackets, In, SelectQueryBuilder } from 'typeorm'
3
+
4
+ import { Attachment } from '@things-factory/attachment-base'
5
+ import { User } from '@things-factory/auth-base'
6
+ import {
7
+ Bizplace,
8
+ getCompaniesBizplaces,
9
+ getCompanyBizplace,
10
+ getMyBizplace,
11
+ getPartnersCompanyBizplaces,
12
+ getPermittedBizplaceIds
13
+ } from '@things-factory/biz-base'
14
+ import { logger } from '@things-factory/env'
15
+ import { buildCondition, Domain, getQueryBuilderFromListParams, getRepository, ListParam } from '@things-factory/shell'
16
+
17
+ import { ProductDetail } from '../product-detail/product-detail'
18
+ import { Product } from './product'
19
+ import { ProductList } from './product-types'
20
+
21
+ const debug = require('debug')('things-factory:product-base:product')
22
+
23
+ @Resolver(Product)
24
+ export class ProductQuery {
25
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
26
+ @Query(returns => Product)
27
+ async product(@Arg('sku') sku: string, @Ctx() context: ResolverContext): Promise<Product> {
28
+ const { domain, user, bizplace } = context.state
29
+
30
+ if (bizplace) {
31
+ return await getRepository(Product).findOne({
32
+ where: {
33
+ domain: { id: domain.id },
34
+ sku,
35
+ bizplace: { id: bizplace.id }
36
+ },
37
+ relations: ['domain', 'bizplace', 'productRef', 'creator', 'updater']
38
+ })
39
+ } else {
40
+ return await getRepository(Product).findOne({
41
+ where: {
42
+ domain: { id: domain.id },
43
+ sku,
44
+ bizplace: { id: In(await getPermittedBizplaceIds(domain, user)) }
45
+ },
46
+ relations: ['domain', 'bizplace', 'productRef', 'creator', 'updater']
47
+ })
48
+ }
49
+ }
50
+
51
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
52
+ @Query(returns => Product)
53
+ async productById(@Arg('id') id: string, @Ctx() context: ResolverContext): Promise<Product> {
54
+ const { domain, user } = context.state
55
+ return await getRepository(Product).findOne({
56
+ where: { domain: { id: domain.id }, id },
57
+ relations: ['domain', 'bizplace', 'productRef', 'creator', 'updater']
58
+ })
59
+ }
60
+
61
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
62
+ @Query(returns => ProductList)
63
+ async products(@Args(type => ListParam) params: ListParam, @Ctx() context: ResolverContext): Promise<ProductList> {
64
+ const { domain, user } = context.state
65
+ const { domains }: { domains: Domain[] } = user as any /* TODO clarify this */
66
+ let bizplaces: Bizplace[]
67
+
68
+ if (!params.filters.find((filter: any) => filter.name === 'bizplace_id')) {
69
+ const warehouseDomain: Domain = domain
70
+ const companiesBizplaces: Bizplace[] = await getCompaniesBizplaces(domains, warehouseDomain)
71
+ const partnersCompanyBizplaces: Bizplace[] = await getPartnersCompanyBizplaces(domain, user)
72
+
73
+ bizplaces = [...companiesBizplaces, ...partnersCompanyBizplaces]
74
+ } else {
75
+ const bizplaceId: string[] = params.filters.filter(param => param.name == 'bizplace_id').map(item => item.value)
76
+
77
+ const foundBizplace: Bizplace = await getRepository(Bizplace).findOne({
78
+ where: { id: bizplaceId[0] },
79
+ relations: ['company', 'company.domain']
80
+ })
81
+
82
+ if (foundBizplace) {
83
+ const companyDomain: Domain = foundBizplace.company?.domain
84
+ const companyBizplace: Bizplace = await getRepository(Bizplace).findOne({
85
+ where: { domain: { id: companyDomain.id } }
86
+ })
87
+
88
+ bizplaces = [companyBizplace, foundBizplace]
89
+ }
90
+ }
91
+
92
+ const qb: SelectQueryBuilder<Product> = buildCustomConditions(params, context, bizplaces)
93
+
94
+ let [items, total] = await qb
95
+ .leftJoinAndSelect('Product.productDetails', 'ProductDetails')
96
+ .leftJoinAndSelect('ProductDetails.childProductDetail', 'ChildProductDetail')
97
+ .getManyAndCount()
98
+
99
+ return { items, total }
100
+ }
101
+
102
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
103
+ @Query(returns => ProductList)
104
+ async myBizplaceProducts(
105
+ @Args(type => ListParam) params: ListParam,
106
+ @Ctx() context: ResolverContext
107
+ ): Promise<ProductList> {
108
+ try {
109
+ const { domain, user }: { domain: Domain; user: User } = context.state
110
+ const bizplaceFilter = params.filters.find(filter => filter.name === 'bizplace_id')
111
+
112
+ let bizplaces: Bizplace[] = []
113
+ if (!bizplaceFilter) {
114
+ bizplaces.push(await getRepository(Bizplace).findOne({ where: { domain: { id: domain.id } } }))
115
+ }
116
+
117
+ const qb: SelectQueryBuilder<Product> = buildCustomConditions(params, context, bizplaces)
118
+
119
+ let [items, total] = await qb
120
+ .leftJoinAndSelect('Product.productDetails', 'ProductDetails')
121
+ .leftJoinAndSelect('Product.routing', 'Routing')
122
+ .getManyAndCount()
123
+
124
+ items = items.map(itm => {
125
+ let defaultProductDetail = itm.productDetails.find(itm => itm.isDefault)
126
+ return {
127
+ ...itm,
128
+ ...new ProductDetail(defaultProductDetail, true)
129
+ }
130
+ })
131
+
132
+ return { items, total }
133
+ } catch (error) {
134
+ logger.error(`product-query[myBizplaceProducts]: ${error}`)
135
+ throw error
136
+ }
137
+ }
138
+
139
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
140
+ @Query(returns => ProductList)
141
+ async productsByBizplace(
142
+ @Args(type => ListParam) params: ListParam,
143
+ @Ctx() context: ResolverContext
144
+ ): Promise<ProductList> {
145
+ const { domain, user } = context.state
146
+ const bizplaceFilter = params.filters.find(x => x.name == 'bizplace')
147
+
148
+ try {
149
+ let bizplaces: Bizplace[]
150
+ if (!bizplaceFilter || bizplaceFilter.value == '') {
151
+ bizplaces = [await getMyBizplace(domain, user), await getCompanyBizplace(domain, user)]
152
+ } else {
153
+ let myBizplace: Bizplace = await getRepository(Bizplace).findOne({
154
+ where: { id: bizplaceFilter.value },
155
+ relations: ['company', 'company.domain']
156
+ })
157
+
158
+ bizplaces = [
159
+ myBizplace,
160
+ await getRepository(Bizplace).findOne({
161
+ where: { domain: { id: myBizplace.company.domain.id } }
162
+ })
163
+ ]
164
+ }
165
+
166
+ const qb: SelectQueryBuilder<Product> = buildCustomConditions(params, context, bizplaces)
167
+
168
+ let [items, total] = await qb.leftJoinAndSelect('Product.productDetails', 'ProductDetails').getManyAndCount()
169
+
170
+ items = items.map(itm => {
171
+ let defaultProductDetail = itm.productDetails?.find(itm => itm.isDefault)
172
+ return {
173
+ ...itm,
174
+ ...new ProductDetail(defaultProductDetail, true)
175
+ }
176
+ })
177
+
178
+ return { items, total }
179
+ } catch (error) {
180
+ throw error
181
+ }
182
+ }
183
+
184
+ /**
185
+ * @description this resolver was specially made to return huge products data
186
+ * without dropping off performance due to typegraphql limitations
187
+ * @returns pure product data only. there will be no other table joined to avoid
188
+ * ORM lagging issue (converting to object extremely withdraws the performance)
189
+ * */
190
+ @Directive('@privilege(category: "order", privilege: "query", domainOwnerGranted: true)')
191
+ @Query(returns => ProductList)
192
+ async pureProductsByBizplace(
193
+ @Args(type => ListParam) params: ListParam,
194
+ @Ctx() context: ResolverContext
195
+ ): Promise<ProductList> {
196
+ const { domain, user } = context.state
197
+ const bizplaceFilter = params.filters.find(x => x.name == 'bizplace')
198
+
199
+ try {
200
+ let bizplaces: Bizplace[]
201
+ if (!bizplaceFilter || bizplaceFilter.value == '') {
202
+ bizplaces = [await getMyBizplace(domain, user), await getCompanyBizplace(domain, user)]
203
+ } else {
204
+ let myBizplace: Bizplace = await getRepository(Bizplace).findOne({
205
+ where: { id: bizplaceFilter.value },
206
+ relations: ['company', 'company.domain']
207
+ })
208
+
209
+ bizplaces = [
210
+ myBizplace,
211
+ await getRepository(Bizplace).findOne({
212
+ where: { domain: { id: myBizplace.company.domain.id } }
213
+ })
214
+ ]
215
+ }
216
+
217
+ const qb: SelectQueryBuilder<Product> = buildCustomConditions(params, context, bizplaces)
218
+ let items = await (await qb.getRawMany()).map(item => new Product(item))
219
+ let total = await qb.getCount()
220
+
221
+ return { items, total }
222
+ } catch (error) {
223
+ throw error
224
+ }
225
+ }
226
+
227
+ @FieldResolver(type => Domain)
228
+ async domain(@Root() product: Product): Promise<Domain> {
229
+ if (product.domainId) return await getRepository(Domain).findOneBy({ id: product.domainId })
230
+ else return null
231
+ }
232
+
233
+ @FieldResolver(type => Bizplace)
234
+ async bizplace(@Root() product: Product): Promise<Bizplace> {
235
+ if (product.bizplaceId) return await getRepository(Bizplace).findOneBy({ id: product.bizplaceId })
236
+ else return null
237
+ }
238
+
239
+ @FieldResolver(type => User)
240
+ async creator(@Root() product: Product): Promise<User> {
241
+ if (product.creatorId) return await getRepository(User).findOneBy({ id: product.creatorId })
242
+ else return null
243
+ }
244
+
245
+ @FieldResolver(type => User)
246
+ async updater(@Root() product: Product): Promise<User> {
247
+ if (product.updaterId) return await getRepository(User).findOneBy({ id: product.updaterId })
248
+ else return null
249
+ }
250
+
251
+ @FieldResolver(type => Product)
252
+ async parentProductRef(@Root() product: Product): Promise<Product> {
253
+ if (product.parentProductRefId) return await getRepository(Product).findOneBy({ id: product.parentProductRefId })
254
+ else return null
255
+ }
256
+
257
+ @FieldResolver(type => Product)
258
+ async productRef(@Root() product: Product): Promise<Product> {
259
+ if (product.productRefId) return await getRepository(Product).findOneBy({ id: product.productRefId })
260
+ else return null
261
+ }
262
+
263
+ @FieldResolver(type => String)
264
+ async thumbnail(@Root() product: Product): Promise<string | undefined> {
265
+ const attachment: Attachment = await getRepository(Attachment).findOne({
266
+ where: {
267
+ domain: { id: product.domainId },
268
+ refBy: product.id
269
+ }
270
+ })
271
+
272
+ return attachment?.fullpath
273
+ }
274
+
275
+ @FieldResolver(type => String)
276
+ productInformation(@Root() product: Product): String {
277
+ return `[${product.sku}] ${product.name}${product.description ? ` - ${product.description}` : ''}`
278
+ }
279
+
280
+ @FieldResolver(type => String, { nullable: true })
281
+ async warehouseName(@Root() product: Product): Promise<String | undefined> {
282
+ if (!product.warehouseId) return null
283
+
284
+ const items = await getRepository(Product).query(`
285
+ SELECT w.name as "warehouseName" FROM products p LEFT JOIN warehouses w ON p.warehouse_id::uuid = w.id and w.domain_id = p.domain_id WHERE p.id = '${product.id}'
286
+ `)
287
+
288
+ return items[0].warehouseName || null
289
+ }
290
+ }
291
+
292
+ function buildCustomConditions(params, context, bizplaces): SelectQueryBuilder<Product> {
293
+ const deletedFilter = params.filters.find(x => x.name == 'deleted')
294
+ const productFilters = params.filters.find(x => x.name == 'product_info')
295
+ const productFilterColumns = ['sku', 'brandSku', 'name', 'description', 'brand', 'subBrand']
296
+
297
+ try {
298
+ params.filters = [
299
+ ...params.filters.filter(x => ['product_info', 'bizplace', 'bizplaceId', 'deleted'].indexOf(x.name) == -1),
300
+ { name: 'deletedAt', operator: deletedFilter?.value ? 'is_not_null' : 'is_null' },
301
+ { name: 'bizplaceId', operator: 'in', value: bizplaces.map(bizplace => bizplace.id) }
302
+ ]
303
+ } catch (err) {
304
+ // error with no bizplace.
305
+ debug('[WARN] params.filters', err.message)
306
+ }
307
+
308
+ const qb = getQueryBuilderFromListParams({
309
+ repository: getRepository(Product),
310
+ alias: 'Product',
311
+ params,
312
+ domain: context.domain,
313
+ searchables: ['sku', 'name', 'description']
314
+ })
315
+
316
+ if (productFilters) {
317
+ qb.andWhere(
318
+ new Brackets(qb2 => {
319
+ productFilterColumns.forEach(filter => {
320
+ const condition = buildCondition(
321
+ qb.alias,
322
+ filter,
323
+ productFilters.operator,
324
+ productFilters.value,
325
+ productFilters.relation,
326
+ Object.keys(qb.getParameters()).length
327
+ )
328
+
329
+ qb2.orWhere(condition.clause, condition.parameters)
330
+ })
331
+ })
332
+ )
333
+ }
334
+
335
+ return qb
336
+ }