feat(库存): 重构库存组件为基于SKU的设计
- 将库存组件从基于stockId改为基于productSku - 添加检查SKU库存的接口 - 改进库存查询支持多字段排序 - 优化产品组件查询返回库存点信息 - 允许字典项titleCN为空字符串
This commit is contained in:
parent
af9f49ab58
commit
a5996363c8
|
|
@ -232,12 +232,8 @@ export class ProductController {
|
|||
@Body() body: { title: string; name: string }
|
||||
) {
|
||||
try {
|
||||
const hasItem = await this.productService.hasAttribute(
|
||||
dictName,
|
||||
body.name
|
||||
);
|
||||
if (hasItem) return errorResponse('字典项已存在');
|
||||
const data = await this.productService.createAttribute(dictName, body);
|
||||
// 调用 getOrCreateAttribute 方法,如果不存在则创建,如果存在则返回
|
||||
const data = await this.productService.getOrCreateAttribute(dictName, body.title, body.name);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || error);
|
||||
|
|
|
|||
|
|
@ -176,6 +176,18 @@ export class StockController {
|
|||
}
|
||||
}
|
||||
|
||||
// 中文注释:检查某个 SKU 是否有库存(任一仓库数量大于 0)
|
||||
@ApiOkResponse({ type: BooleanRes })
|
||||
@Get('/has/:sku')
|
||||
async hasStock(@Param('sku') sku: string) {
|
||||
try {
|
||||
const data = await this.stockService.hasStockBySku(sku);
|
||||
return successResponse(data);
|
||||
} catch (error) {
|
||||
return errorResponse(error?.message || '查询失败');
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOkResponse({
|
||||
type: BooleanRes,
|
||||
description: '更新库存(入库、出库、调整)',
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ export class CreateDictItemDTO {
|
|||
@Rule(RuleType.string().required())
|
||||
title: string; // 字典项标题
|
||||
|
||||
@Rule(RuleType.string().allow(null))
|
||||
@Rule(RuleType.string().allow('').allow(null))
|
||||
titleCN?: string; // 字典项中文标题 (可选)
|
||||
|
||||
@Rule(RuleType.number().required())
|
||||
|
|
@ -41,7 +41,7 @@ export class UpdateDictItemDTO {
|
|||
@Rule(RuleType.string())
|
||||
title?: string; // 字典项标题 (可选)
|
||||
|
||||
@Rule(RuleType.string().allow(null))
|
||||
@Rule(RuleType.string().allow('').allow(null))
|
||||
titleCN?: string; // 字典项中文标题 (可选)
|
||||
|
||||
@Rule(RuleType.string().allow(null))
|
||||
|
|
|
|||
|
|
@ -284,9 +284,9 @@ export class BatchSetSkuDTO {
|
|||
|
||||
// 中文注释:产品库存组成项输入
|
||||
export class ProductComponentItemDTO {
|
||||
@ApiProperty({ description: '库存记录ID' })
|
||||
@Rule(RuleType.number().required())
|
||||
stockId: number;
|
||||
@ApiProperty({ description: '组件 SKU' })
|
||||
@Rule(RuleType.string().required())
|
||||
sku: string;
|
||||
|
||||
@ApiProperty({ description: '组成数量', example: 1 })
|
||||
@Rule(RuleType.number().min(1).default(1))
|
||||
|
|
|
|||
|
|
@ -22,13 +22,17 @@ export class QueryStockDTO {
|
|||
@Rule(RuleType.string())
|
||||
productName: string;
|
||||
|
||||
@ApiProperty()
|
||||
@Rule(RuleType.string())
|
||||
productSku: string;
|
||||
|
||||
@ApiProperty({ description: '按库存点ID排序', required: false })
|
||||
@Rule(RuleType.number().allow(null))
|
||||
sortPointId?: number;
|
||||
|
||||
@ApiProperty({ description: '排序方向', enum: ['ascend', 'descend'], required: false })
|
||||
@Rule(RuleType.string().valid('ascend', 'descend').allow(''))
|
||||
sortOrder?: 'ascend' | 'descend' | '';
|
||||
@ApiProperty({ description: '排序对象,格式如 { productName: "asc", productSku: "desc" }', required: false })
|
||||
@Rule(RuleType.object().allow(null))
|
||||
order?: Record<string, 'asc' | 'desc'>;
|
||||
}
|
||||
export class QueryPointDTO {
|
||||
@ApiProperty({ example: '1', description: '页码' })
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { ApiProperty } from '@midwayjs/swagger';
|
||||
import { Column, CreateDateColumn, Entity, ManyToOne, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm';
|
||||
import { Product } from './product.entity';
|
||||
import { Stock } from './stock.entity';
|
||||
|
||||
@Entity('product_stock_component')
|
||||
export class ProductStockComponent {
|
||||
|
|
@ -13,9 +12,9 @@ export class ProductStockComponent {
|
|||
@Column()
|
||||
productId: number;
|
||||
|
||||
@ApiProperty({ type: Number })
|
||||
@Column()
|
||||
stockId: number;
|
||||
@ApiProperty({ description: '组件所关联的 SKU', type: 'string' })
|
||||
@Column({ type: 'varchar', length: 64 })
|
||||
productSku: string;
|
||||
|
||||
@ApiProperty({ type: Number, description: '组成数量' })
|
||||
@Column({ type: 'int', default: 1 })
|
||||
|
|
@ -25,10 +24,6 @@ export class ProductStockComponent {
|
|||
@ManyToOne(() => Product, (product) => product.components, { onDelete: 'CASCADE' })
|
||||
product: Product;
|
||||
|
||||
// 中文注释:多对一,组件引用一个库存记录
|
||||
@ManyToOne(() => Stock, { eager: true, onDelete: 'CASCADE' })
|
||||
stock: Stock;
|
||||
|
||||
@ApiProperty({ description: '创建时间' })
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
|
|
@ -37,4 +32,3 @@ export class ProductStockComponent {
|
|||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ export class DictService {
|
|||
|
||||
// 生成并返回字典项的XLSX模板
|
||||
getDictItemXLSXTemplate() {
|
||||
const headers = ['name', 'title', 'value', 'sort'];
|
||||
const headers = ['name', 'title', 'titleCN', 'value', 'sort'];
|
||||
const ws = xlsx.utils.aoa_to_sheet([headers]);
|
||||
const wb = xlsx.utils.book_new();
|
||||
xlsx.utils.book_append_sheet(wb, ws, 'DictItems');
|
||||
|
|
@ -71,12 +71,14 @@ export class DictService {
|
|||
const wb = xlsx.read(buffer, { type: 'buffer' });
|
||||
const wsname = wb.SheetNames[0];
|
||||
const ws = wb.Sheets[wsname];
|
||||
const data = xlsx.utils.sheet_to_json(ws, { header: ['name', 'title', 'value', 'sort'] }).slice(1);
|
||||
// 支持titleCN字段的导入
|
||||
const data = xlsx.utils.sheet_to_json(ws, { header: ['name', 'title', 'titleCN', 'value', 'sort'] }).slice(1);
|
||||
|
||||
const items = data.map((row: any) => {
|
||||
const item = new DictItem();
|
||||
item.name = row.name;
|
||||
item.title = row.title;
|
||||
item.titleCN = row.titleCN; // 保存中文名称
|
||||
item.value = row.value;
|
||||
item.sort = row.sort || 0;
|
||||
item.dict = dict;
|
||||
|
|
@ -143,6 +145,7 @@ export class DictService {
|
|||
const item = new DictItem();
|
||||
item.name = createDictItemDTO.name;
|
||||
item.title = createDictItemDTO.title;
|
||||
item.titleCN = createDictItemDTO.titleCN; // 保存中文名称
|
||||
item.dict = dict;
|
||||
return this.dictItemModel.save(item);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import { Context } from '@midwayjs/koa';
|
|||
import { TemplateService } from './template.service';
|
||||
import { StockService } from './stock.service';
|
||||
import { Stock } from '../entity/stock.entity';
|
||||
import { StockPoint } from '../entity/stock_point.entity';
|
||||
import { ProductStockComponent } from '../entity/product_stock_component.entity';
|
||||
|
||||
@Provide()
|
||||
|
|
@ -62,6 +63,9 @@ export class ProductService {
|
|||
@InjectEntityModel(Stock)
|
||||
stockModel: Repository<Stock>;
|
||||
|
||||
@InjectEntityModel(StockPoint)
|
||||
stockPointModel: Repository<StockPoint>;
|
||||
|
||||
@InjectEntityModel(ProductStockComponent)
|
||||
productStockComponentModel: Repository<ProductStockComponent>;
|
||||
|
||||
|
|
@ -175,16 +179,14 @@ export class ProductService {
|
|||
// 中文注释:根据类型填充组成信息
|
||||
for (const p of items) {
|
||||
if (p.type === 'simple') {
|
||||
const stocks = await this.stockModel.find({ where: { productSku: p.sku } });
|
||||
p.components = stocks.map(s => {
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = p.id;
|
||||
comp.stockId = s.id;
|
||||
comp.quantity = 1;
|
||||
comp.stock = s as any;
|
||||
return comp;
|
||||
});
|
||||
// 中文注释:单品不持久化组成,这里仅返回一个基于 SKU 的虚拟组成
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = p.id;
|
||||
comp.productSku = p.sku;
|
||||
comp.quantity = 1;
|
||||
p.components = [comp];
|
||||
} else {
|
||||
// 中文注释:混装商品返回持久化的 SKU 组成
|
||||
p.components = await this.productStockComponentModel.find({ where: { productId: p.id } });
|
||||
}
|
||||
}
|
||||
|
|
@ -207,16 +209,18 @@ export class ProductService {
|
|||
throw new Error(`字典 '${dictName}' 不存在`);
|
||||
}
|
||||
|
||||
const nameForLookup = itemName || itemTitle;
|
||||
|
||||
// 查找字典项
|
||||
let item = await this.dictItemModel.findOne({
|
||||
where: { title: itemTitle, dict: { id: dict.id } },
|
||||
where: { name: nameForLookup, dict: { id: dict.id } },
|
||||
});
|
||||
|
||||
// 如果字典项不存在,则创建
|
||||
if (!item) {
|
||||
item = new DictItem();
|
||||
item.title = itemTitle;
|
||||
item.name = itemName || itemTitle; // 如果没有提供 name,则使用 title
|
||||
item.name = nameForLookup;
|
||||
item.dict = dict;
|
||||
await this.dictItemModel.save(item);
|
||||
}
|
||||
|
|
@ -376,31 +380,73 @@ export class ProductService {
|
|||
}
|
||||
|
||||
// 中文注释:获取产品的库存组成列表(表关联版本)
|
||||
async getProductComponents(productId: number): Promise<ProductStockComponent[]> {
|
||||
async getProductComponents(productId: number): Promise<any[]> {
|
||||
// 条件判断:确保产品存在
|
||||
const product = await this.productModel.findOne({ where: { id: productId } });
|
||||
if (!product) throw new Error(`产品 ID ${productId} 不存在`);
|
||||
// 条件判断(中文注释:单品 simple 不持久化组成,按 sku 动态生成)
|
||||
|
||||
let components: ProductStockComponent[] = [];
|
||||
// 条件判断(中文注释:单品 simple 不持久化组成,按 SKU 动态返回单条组成)
|
||||
if (product.type === 'simple') {
|
||||
const stocks = await this.stockModel.find({ where: { productSku: product.sku } });
|
||||
// 中文注释:将同 sku 的库存映射为组成信息(数量默认为 1)
|
||||
return stocks.map(s => {
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = productId;
|
||||
comp.stockId = s.id;
|
||||
comp.quantity = 1;
|
||||
comp.stock = s as any;
|
||||
return comp;
|
||||
});
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = productId;
|
||||
comp.productSku = product.sku;
|
||||
comp.quantity = 1;
|
||||
components = [comp];
|
||||
} else {
|
||||
// 混装 bundle:返回已保存的 SKU 组成
|
||||
components = await this.productStockComponentModel.find({ where: { productId } });
|
||||
}
|
||||
// 混装 bundle:返回已保存的组成
|
||||
return await this.productStockComponentModel.find({ where: { productId } });
|
||||
|
||||
// 中文注释:获取所有组件的 SKU 列表
|
||||
const skus = components.map(c => c.productSku);
|
||||
if (skus.length === 0) {
|
||||
return components;
|
||||
}
|
||||
|
||||
// 中文注释:查询这些 SKU 的库存信息
|
||||
const stocks = await this.stockModel.find({
|
||||
where: { productSku: In(skus) },
|
||||
});
|
||||
|
||||
// 中文注释:获取所有相关的库存点 ID
|
||||
const stockPointIds = [...new Set(stocks.map(s => s.stockPointId))];
|
||||
const stockPoints = await this.stockPointModel.find({ where: { id: In(stockPointIds) } });
|
||||
const stockPointMap = stockPoints.reduce((map, sp) => {
|
||||
map[sp.id] = sp;
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
// 中文注释:将库存信息按 SKU 分组
|
||||
const stockMap = stocks.reduce((map, stock) => {
|
||||
if (!map[stock.productSku]) {
|
||||
map[stock.productSku] = [];
|
||||
}
|
||||
const stockPoint = stockPointMap[stock.stockPointId];
|
||||
if (stockPoint) {
|
||||
map[stock.productSku].push({
|
||||
name: stockPoint.name,
|
||||
quantity: stock.quantity,
|
||||
});
|
||||
}
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
// 中文注释:将库存信息附加到组件上
|
||||
const componentsWithStock = components.map(comp => {
|
||||
return {
|
||||
...comp,
|
||||
stock: stockMap[comp.productSku] || [],
|
||||
};
|
||||
});
|
||||
|
||||
return componentsWithStock;
|
||||
}
|
||||
|
||||
// 中文注释:设置产品的库存组成(覆盖式,表关联版本)
|
||||
async setProductComponents(
|
||||
productId: number,
|
||||
items: { stockId: number; quantity: number }[]
|
||||
items: { sku: string; quantity: number }[]
|
||||
): Promise<ProductStockComponent[]> {
|
||||
// 条件判断:确保产品存在
|
||||
const product = await this.productModel.findOne({ where: { id: productId } });
|
||||
|
|
@ -411,8 +457,8 @@ export class ProductService {
|
|||
}
|
||||
|
||||
const validItems = (items || [])
|
||||
.filter(i => i && i.stockId && i.quantity && i.quantity > 0)
|
||||
.map(i => ({ stockId: Number(i.stockId), quantity: Number(i.quantity) }));
|
||||
.filter(i => i && i.sku && i.quantity && i.quantity > 0)
|
||||
.map(i => ({ sku: String(i.sku), quantity: Number(i.quantity) }));
|
||||
|
||||
// 删除旧的组成
|
||||
await this.productStockComponentModel.delete({ productId });
|
||||
|
|
@ -420,13 +466,14 @@ export class ProductService {
|
|||
// 插入新的组成
|
||||
const created: ProductStockComponent[] = [];
|
||||
for (const i of validItems) {
|
||||
const stock = await this.stockModel.findOne({ where: { id: i.stockId } });
|
||||
if (!stock) throw new Error(`库存 ID ${i.stockId} 不存在`);
|
||||
// 中文注释:校验 SKU 格式,允许不存在库存但必须非空
|
||||
if (!i.sku || i.sku.trim().length === 0) {
|
||||
throw new Error('SKU 不能为空');
|
||||
}
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = productId;
|
||||
comp.stockId = i.stockId;
|
||||
comp.productSku = i.sku;
|
||||
comp.quantity = i.quantity;
|
||||
comp.stock = stock;
|
||||
created.push(await this.productStockComponentModel.save(comp));
|
||||
}
|
||||
return created;
|
||||
|
|
@ -437,28 +484,22 @@ export class ProductService {
|
|||
// 条件判断:确保产品存在
|
||||
const product = await this.productModel.findOne({ where: { id: productId } });
|
||||
if (!product) throw new Error(`产品 ID ${productId} 不存在`);
|
||||
const stocks = await this.stockModel.find({ where: { productSku: product.sku } });
|
||||
if (stocks.length === 0) return [];
|
||||
// 条件判断(中文注释:simple 类型不持久化组成,直接返回动态映射)
|
||||
// 中文注释:按 SKU 自动绑定
|
||||
// 条件判断:simple 类型不持久化组成,直接返回单条基于 SKU 的组成
|
||||
if (product.type === 'simple') {
|
||||
return stocks.map(s => {
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = productId;
|
||||
comp.stockId = s.id;
|
||||
comp.quantity = 1; // 默认数量 1
|
||||
comp.stock = s as any;
|
||||
return comp;
|
||||
});
|
||||
}
|
||||
// bundle 类型:持久化组成
|
||||
for (const stock of stocks) {
|
||||
const exist = await this.productStockComponentModel.findOne({ where: { productId, stockId: stock.id } });
|
||||
if (exist) continue;
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = productId;
|
||||
comp.stockId = stock.id;
|
||||
comp.productSku = product.sku;
|
||||
comp.quantity = 1; // 默认数量 1
|
||||
return [comp];
|
||||
}
|
||||
// bundle 类型:若不存在则持久化一条基于 SKU 的组成
|
||||
const exist = await this.productStockComponentModel.findOne({ where: { productId, productSku: product.sku } });
|
||||
if (!exist) {
|
||||
const comp = new ProductStockComponent();
|
||||
comp.productId = productId;
|
||||
comp.productSku = product.sku;
|
||||
comp.quantity = 1;
|
||||
comp.stock = stock as any;
|
||||
await this.productStockComponentModel.save(comp);
|
||||
}
|
||||
return await this.getProductComponents(productId);
|
||||
|
|
|
|||
|
|
@ -187,6 +187,16 @@ export class StockService {
|
|||
);
|
||||
}
|
||||
|
||||
// 中文注释:检查指定 SKU 是否在任一仓库有库存(数量大于 0)
|
||||
async hasStockBySku(sku: string): Promise<boolean> {
|
||||
const count = await this.stockModel
|
||||
.createQueryBuilder('stock')
|
||||
.where('stock.productSku = :sku', { sku })
|
||||
.andWhere('stock.quantity > 0')
|
||||
.getCount();
|
||||
return count > 0;
|
||||
}
|
||||
|
||||
async delPurchaseOrder(id: number) {
|
||||
const purchaseOrder = await this.purchaseOrderModel.findOneBy({ id });
|
||||
if (!purchaseOrder) throw new Error(`采购订单 ID ${id} 不存在`);
|
||||
|
|
@ -230,7 +240,7 @@ export class StockService {
|
|||
|
||||
// 获取库存列表
|
||||
async getStocks(query: QueryStockDTO) {
|
||||
const { current = 1, pageSize = 10, productName, sortPointId, sortOrder } = query;
|
||||
const { current = 1, pageSize = 10, productName, productSku } = query;
|
||||
const nameKeywords = productName
|
||||
? productName.split(' ').filter(Boolean)
|
||||
: [];
|
||||
|
|
@ -254,6 +264,10 @@ export class StockService {
|
|||
.createQueryBuilder('stock')
|
||||
.select('COUNT(DISTINCT stock.productSku)', 'count')
|
||||
.leftJoin(Product, 'product', 'product.sku = stock.productSku');
|
||||
if (productSku) {
|
||||
queryBuilder.andWhere('stock.productSku = :productSku', { productSku });
|
||||
totalQueryBuilder.andWhere('stock.productSku = :productSku', { productSku });
|
||||
}
|
||||
if (nameKeywords.length) {
|
||||
nameKeywords.forEach((name, index) => {
|
||||
queryBuilder.andWhere(
|
||||
|
|
@ -274,14 +288,51 @@ export class StockService {
|
|||
);
|
||||
});
|
||||
}
|
||||
if (sortPointId && sortOrder) {
|
||||
const sortExpr = `SUM(CASE WHEN stock.stockPointId = :sortPointId THEN stock.quantity ELSE 0 END)`;
|
||||
queryBuilder.addSelect(sortExpr, 'pointSort').setParameter('sortPointId', Number(sortPointId));
|
||||
queryBuilder.orderBy('pointSort', sortOrder === 'ascend' ? 'ASC' : 'DESC');
|
||||
if (query.order) {
|
||||
const sortFieldMap: Record<string, string> = {
|
||||
productName: 'product.name',
|
||||
productSku: 'stock.productSku',
|
||||
updatedAt: 'updatedAt',
|
||||
createdAt: 'createdAt',
|
||||
};
|
||||
let isFirstSort = true;
|
||||
Object.entries(query.order).forEach(([field, direction]) => {
|
||||
const orderDirection = direction === 'asc' ? 'ASC' : 'DESC';
|
||||
if (field.startsWith('point_')) {
|
||||
const pointId = field.split('_')[1];
|
||||
const sortExpr = `SUM(CASE WHEN stock.stockPointId = :pointId THEN stock.quantity ELSE 0 END)`;
|
||||
const sortAlias = `pointSort_${pointId}`;
|
||||
queryBuilder
|
||||
.addSelect(sortExpr, sortAlias)
|
||||
.setParameter('pointId', Number(pointId));
|
||||
if (isFirstSort) {
|
||||
queryBuilder.orderBy(sortAlias, orderDirection);
|
||||
isFirstSort = false;
|
||||
} else {
|
||||
queryBuilder.addOrderBy(sortAlias, orderDirection);
|
||||
}
|
||||
} else {
|
||||
const actualSortField = sortFieldMap[field] || field;
|
||||
if (isFirstSort) {
|
||||
queryBuilder.orderBy(actualSortField, orderDirection);
|
||||
isFirstSort = false;
|
||||
} else {
|
||||
queryBuilder.addOrderBy(actualSortField, orderDirection);
|
||||
}
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// 默认按产品名称排序
|
||||
queryBuilder.orderBy('product.name', 'ASC');
|
||||
}
|
||||
|
||||
const items = await queryBuilder.getRawMany();
|
||||
const total = await totalQueryBuilder.getRawOne();
|
||||
const items = await queryBuilder
|
||||
.offset((current - 1) * pageSize)
|
||||
.limit(pageSize)
|
||||
.getRawMany();
|
||||
const totalResult = await totalQueryBuilder.getRawOne();
|
||||
const total = parseInt(totalResult.count, 10);
|
||||
|
||||
const transfer = await this.transferModel
|
||||
.createQueryBuilder('t')
|
||||
.select(['ti.productSku as productSku', 'SUM(ti.quantity) as quantity'])
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
{"root":["./src/configuration.ts","./src/interface.ts","./src/config/config.default.ts","./src/config/config.local.ts","./src/config/config.unittest.ts","./src/controller/api.controller.ts","./src/controller/area.controller.ts","./src/controller/customer.controller.ts","./src/controller/dict.controller.ts","./src/controller/locale.controller.ts","./src/controller/logistics.controller.ts","./src/controller/order.controller.ts","./src/controller/product.controller.ts","./src/controller/site.controller.ts","./src/controller/statistics.controller.ts","./src/controller/stock.controller.ts","./src/controller/subscription.controller.ts","./src/controller/template.controller.ts","./src/controller/user.controller.ts","./src/controller/webhook.controller.ts","./src/controller/wp_product.controller.ts","./src/db/datasource.ts","./src/db/migrations/1764238434984-product-dict-item-many-to-many.ts","./src/db/migrations/1764294088896-area.ts","./src/db/migrations/1764299629279-productstock.ts","./src/db/seeds/area.seeder.ts","./src/db/seeds/dict.seeder.ts","./src/db/seeds/template.seeder.ts","./src/decorator/user.decorator.ts","./src/dto/area.dto.ts","./src/dto/customer.dto.ts","./src/dto/dict.dto.ts","./src/dto/freightcom.dto.ts","./src/dto/logistics.dto.ts","./src/dto/order.dto.ts","./src/dto/product.dto.ts","./src/dto/reponse.dto.ts","./src/dto/site.dto.ts","./src/dto/statistics.dto.ts","./src/dto/stock.dto.ts","./src/dto/subscription.dto.ts","./src/dto/template.dto.ts","./src/dto/user.dto.ts","./src/dto/wp_product.dto.ts","./src/entity/area.entity.ts","./src/entity/auth_code.ts","./src/entity/customer.entity.ts","./src/entity/customer_tag.entity.ts","./src/entity/device_whitelist.ts","./src/entity/dict.entity.ts","./src/entity/dict_item.entity.ts","./src/entity/order.entity.ts","./src/entity/order_coupon.entity.ts","./src/entity/order_fee.entity.ts","./src/entity/order_item.entity.ts","./src/entity/order_item_original.entity.ts","./src/entity/order_items_original.entity.ts","./src/entity/order_note.entity.ts","./src/entity/order_refund.entity.ts","./src/entity/order_refund_item.entity.ts","./src/entity/order_sale.entity.ts","./src/entity/order_shipment.entity.ts","./src/entity/order_shipping.entity.ts","./src/entity/product.entity.ts","./src/entity/product_stock_component.entity.ts","./src/entity/purchase_order.entity.ts","./src/entity/purchase_order_item.entity.ts","./src/entity/service.entity.ts","./src/entity/shipment.entity.ts","./src/entity/shipment_item.entity.ts","./src/entity/shipping_address.entity.ts","./src/entity/site.entity.ts","./src/entity/stock.entity.ts","./src/entity/stock_point.entity.ts","./src/entity/stock_record.entity.ts","./src/entity/subscription.entity.ts","./src/entity/template.entity.ts","./src/entity/transfer.entity.ts","./src/entity/transfer_item.entity.ts","./src/entity/user.entity.ts","./src/entity/variation.entity.ts","./src/entity/wp_product.entity.ts","./src/enums/base.enum.ts","./src/filter/default.filter.ts","./src/filter/notfound.filter.ts","./src/job/sync_products.job.ts","./src/job/sync_shipment.job.ts","./src/middleware/auth.middleware.ts","./src/middleware/report.middleware.ts","./src/service/area.service.ts","./src/service/authcode.service.ts","./src/service/canadapost.service.ts","./src/service/customer.service.ts","./src/service/devicewhitelist.service.ts","./src/service/dict.service.ts","./src/service/freightcom.service.ts","./src/service/logistics.service.ts","./src/service/mail.service.ts","./src/service/order.service.ts","./src/service/product.service.ts","./src/service/site.service.ts","./src/service/statistics.service.ts","./src/service/stock.service.ts","./src/service/subscription.service.ts","./src/service/template.service.ts","./src/service/uni_express.service.ts","./src/service/user.service.ts","./src/service/wp.service.ts","./src/service/wp_product.service.ts","./src/utils/helper.util.ts","./src/utils/object-transform.util.ts","./src/utils/paginate.util.ts","./src/utils/paginated-response.util.ts","./src/utils/response-wrapper.util.ts","./src/utils/response.util.ts"],"version":"5.9.3"}
|
||||
Loading…
Reference in New Issue