@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.
- package/dist-server/service/product/product-mutation.js +1 -1
- package/dist-server/service/product/product-mutation.js.map +1 -1
- package/dist-server/service/product/product-query.d.ts +1 -1
- package/dist-server/service/product/product-query.js +5 -5
- package/dist-server/service/product/product-query.js.map +1 -1
- package/dist-server/service/product/product-types.d.ts +4 -2
- package/dist-server/service/product/product-types.js +10 -2
- package/dist-server/service/product/product-types.js.map +1 -1
- package/dist-server/service/product/product.d.ts +2 -1
- package/dist-server/service/product/product.js +6 -1
- package/dist-server/service/product/product.js.map +1 -1
- package/dist-server/tsconfig.tsbuildinfo +1 -1
- package/package.json +5 -5
- package/server/constants/index.ts +1 -0
- package/server/constants/product.ts +24 -0
- package/server/controllers/index.ts +0 -0
- package/server/index.ts +2 -0
- package/server/middlewares/index.ts +0 -0
- package/server/migrations/index.ts +9 -0
- package/server/service/index.ts +61 -0
- package/server/service/product/index.ts +6 -0
- package/server/service/product/product-mutation.ts +407 -0
- package/server/service/product/product-query.ts +336 -0
- package/server/service/product/product-types.ts +458 -0
- package/server/service/product/product.ts +548 -0
- package/server/service/product/validate-product.ts +42 -0
- package/server/service/product-bundle/index.ts +6 -0
- package/server/service/product-bundle/product-bundle-mutation.ts +140 -0
- package/server/service/product-bundle/product-bundle-query.ts +104 -0
- package/server/service/product-bundle/product-bundle-types.ts +51 -0
- package/server/service/product-bundle/product-bundle.ts +102 -0
- package/server/service/product-bundle-setting/index.ts +6 -0
- package/server/service/product-bundle-setting/product-bundle-setting-mutation.ts +168 -0
- package/server/service/product-bundle-setting/product-bundle-setting-query.ts +139 -0
- package/server/service/product-bundle-setting/product-bundle-setting-types.ts +41 -0
- package/server/service/product-bundle-setting/product-bundle-setting.ts +45 -0
- package/server/service/product-combination/index.ts +6 -0
- package/server/service/product-combination/product-combination-mutation.ts +148 -0
- package/server/service/product-combination/product-combination-query.ts +48 -0
- package/server/service/product-combination/product-combination-type.ts +50 -0
- package/server/service/product-combination/product-combination.ts +116 -0
- package/server/service/product-combination-setting/index.ts +6 -0
- package/server/service/product-combination-setting/product-combination-setting-mutation.ts +248 -0
- package/server/service/product-combination-setting/product-combination-setting-query.ts +176 -0
- package/server/service/product-combination-setting/product-combination-setting-type.ts +44 -0
- package/server/service/product-combination-setting/product-combination-setting.ts +77 -0
- package/server/service/product-detail/index.ts +6 -0
- package/server/service/product-detail/product-detail-mutation.ts +238 -0
- package/server/service/product-detail/product-detail-query.ts +213 -0
- package/server/service/product-detail/product-detail-types.ts +280 -0
- package/server/service/product-detail/product-detail.ts +388 -0
- package/server/service/product-detail-bizplace-setting/index.ts +6 -0
- package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting-mutation.ts +118 -0
- package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting-query.ts +90 -0
- package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting-types.ts +62 -0
- package/server/service/product-detail-bizplace-setting/product-detail-bizplace-setting.ts +104 -0
- package/server/service/product-set/index.ts +6 -0
- package/server/service/product-set/product-set-mutation.ts +149 -0
- package/server/service/product-set/product-set-query.ts +114 -0
- package/server/service/product-set/product-set-types.ts +45 -0
- package/server/service/product-set/product-set.ts +95 -0
- package/server/utils/index.ts +1 -0
- 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
|
+
}
|