refactor(实体和服务): 统一将productSku字段重命名为sku

重构实体类和服务层代码,将productSku字段统一更名为sku以保持命名一致性
修改涉及库存、订单、采购等多个模块的查询和更新逻辑
同时将产品类型默认值从simple改为single,并优化相关条件判断
This commit is contained in:
tikkhun 2025-12-01 10:59:49 +08:00
parent 8b31da07a0
commit e4fc195b8d
13 changed files with 142 additions and 114 deletions

24
output.log Normal file
View File

@ -0,0 +1,24 @@
> my-midway-project@1.0.0 dev
> cross-env NODE_ENV=local mwtsc --watch --run @midwayjs/mock/app.js

[10:37:17 AM] Starting compilation in watch mode...
[10:37:19 AM] Found 0 errors. Watching for file changes.
2025-12-01 10:37:20.106 INFO 58678 [SyncProductJob] start job SyncProductJob
2025-12-01 10:37:20.106 INFO 58678 [SyncShipmentJob] start job SyncShipmentJob
2025-12-01 10:37:20.109 INFO 58678 [SyncProductJob] complete job SyncProductJob
Node.js server started in 732 ms
➜ Local: http://127.0.0.1:7001/
➜ Network: http://192.168.5.100:7001/ 
2025-12-01 10:37:20.110 INFO 58678 [SyncShipmentJob] complete job SyncShipmentJob

View File

@ -24,13 +24,13 @@ export class QueryStockDTO {
@ApiProperty()
@Rule(RuleType.string())
productSku: string;
sku: string;
@ApiProperty({ description: '按库存点ID排序', required: false })
@Rule(RuleType.number().allow(null))
sortPointId?: number;
@ApiProperty({ description: '排序对象,格式如 { productName: "asc", productSku: "desc" }', required: false })
@ApiProperty({ description: '排序对象,格式如 { productName: "asc", sku: "desc" }', required: false })
@Rule(RuleType.object().allow(null))
order?: Record<string, 'asc' | 'desc'>;
}
@ -58,7 +58,7 @@ export class QueryStockRecordDTO {
@ApiProperty()
@Rule(RuleType.string())
productSku: string;
sku: string;
@ApiProperty()
@Rule(RuleType.string())
@ -132,7 +132,7 @@ export class UpdateStockDTO {
@ApiProperty()
@Rule(RuleType.string())
productSku: string;
sku: string;
@ApiProperty()
@Rule(RuleType.number())

View File

@ -23,6 +23,11 @@ export class Product {
@PrimaryGeneratedColumn()
id: number;
// 类型 主要用来区分混装和单品 单品死
@ApiProperty({ description: '类型' })
@Column({ length: 16, default: 'single' })
type: string;
@ApiProperty({
example: 'ZYN 6MG WINTERGREEN',
description: '产品名称',
@ -49,10 +54,7 @@ export class Product {
@ApiProperty({ description: '价格', example: 99.99 })
@Column({ type: 'decimal', precision: 10, scale: 2, default: 0 })
price: number;
// 类型 主要用来区分混装和单品 单品死
@ApiProperty({ description: '类型' })
@Column({ length: 16, default: 'simple' })
type: string;
// 促销价格
@ApiProperty({ description: '促销价格', example: 99.99 })
@Column({ type: 'decimal', precision: 10, scale: 2, default: 0 })

View File

@ -14,7 +14,7 @@ export class ProductStockComponent {
@ApiProperty({ description: '组件所关联的 SKU', type: 'string' })
@Column({ type: 'varchar', length: 64 })
productSku: string;
sku: string;
@ApiProperty({ type: Number, description: '组成数量' })
@Column({ type: 'int', default: 1 })

View File

@ -10,7 +10,7 @@ export class PurchaseOrderItem {
@ApiProperty({ type: String })
@Column()
productSku: string;
sku: string;
@ApiProperty({ type: String })
@Column()

View File

@ -20,7 +20,7 @@ export class Stock {
@ApiProperty({ type: String })
@Column()
productSku: string;
sku: string;
@ApiProperty({ type: Number })
@Column()

View File

@ -20,7 +20,7 @@ export class StockRecord {
@ApiProperty({ type: String })
@Column()
productSku: string;
sku: string;
@ApiProperty({ type: StockRecordOperationType })
@Column({ type: 'enum', enum: StockRecordOperationType })

View File

@ -9,7 +9,7 @@ export class TransferItem {
@ApiProperty({ type: String })
@Column()
productSku: string;
sku: string;
@ApiProperty({ type: String })
@Column()

View File

@ -527,14 +527,14 @@ export class LogisticsService {
const stock = await stockRepo.findOne({
where: {
stockPointId: orderShipments[0].stockPointId,
productSku: item.sku,
sku: item.sku,
},
});
stock.quantity += item.quantity;
await stockRepo.save(stock);
await stockRecordRepo.save({
stockPointId: orderShipments[0].stockPointId,
productSku: item.sku,
sku: item.sku,
operationType: StockRecordOperationType.IN,
quantityChange: item.quantity,
operatorId: userId,

View File

@ -217,7 +217,7 @@ export class OrderService {
for (const item of items) {
const updateStock = new UpdateStockDTO();
updateStock.stockPointId = stockPointId;
updateStock.productSku = item.sku;
updateStock.sku = item.sku;
updateStock.quantityChange = item.quantity;
updateStock.operationType = StockRecordOperationType.OUT;
updateStock.operatorId = 1;

View File

@ -177,17 +177,19 @@ export class ProductService {
const [items, total] = await qb.getManyAndCount();
// 中文注释:根据类型填充组成信息
for (const p of items) {
if (p.type === 'simple') {
for (const product of items) {
if (product.type === 'single') {
// 中文注释:单品不持久化组成,这里仅返回一个基于 SKU 的虚拟组成
const comp = new ProductStockComponent();
comp.productId = p.id;
comp.productSku = p.sku;
comp.quantity = 1;
p.components = [comp];
const component = new ProductStockComponent();
component.productId = product.id;
component.sku = product.sku;
component.quantity = 1;
product.components = [component];
} else {
// 中文注释:混装商品返回持久化的 SKU 组成
p.components = await this.productStockComponentModel.find({ where: { productId: p.id } });
product.components = await this.productStockComponentModel.find({
where: { productId: product.id },
});
}
}
@ -273,7 +275,7 @@ export class ProductService {
product.description = description;
product.attributes = resolvedAttributes;
// 条件判断(中文注释:设置商品类型,默认 simple
product.type = (createProductDTO.type as any) || 'simple';
product.type = (createProductDTO.type as any) || 'single';
// 生成或设置 SKU中文注释基于属性字典项的 name 生成)
if (sku) {
@ -387,10 +389,10 @@ export class ProductService {
let components: ProductStockComponent[] = [];
// 条件判断(中文注释:单品 simple 不持久化组成,按 SKU 动态返回单条组成)
if (product.type === 'simple') {
if (product.type === 'single') {
const comp = new ProductStockComponent();
comp.productId = productId;
comp.productSku = product.sku;
comp.sku = product.sku;
comp.quantity = 1;
components = [comp];
} else {
@ -399,14 +401,14 @@ export class ProductService {
}
// 中文注释:获取所有组件的 SKU 列表
const skus = components.map(c => c.productSku);
const skus = components.map(c => c.sku);
if (skus.length === 0) {
return components;
}
// 中文注释:查询这些 SKU 的库存信息
const stocks = await this.stockModel.find({
where: { productSku: In(skus) },
where: { sku: In(skus) },
});
// 中文注释:获取所有相关的库存点 ID
@ -419,12 +421,12 @@ export class ProductService {
// 中文注释:将库存信息按 SKU 分组
const stockMap = stocks.reduce((map, stock) => {
if (!map[stock.productSku]) {
map[stock.productSku] = [];
if (!map[stock.sku]) {
map[stock.sku] = [];
}
const stockPoint = stockPointMap[stock.stockPointId];
if (stockPoint) {
map[stock.productSku].push({
map[stock.sku].push({
name: stockPoint.name,
quantity: stock.quantity,
});
@ -436,7 +438,7 @@ export class ProductService {
const componentsWithStock = components.map(comp => {
return {
...comp,
stock: stockMap[comp.productSku] || [],
stock: stockMap[comp.sku] || [],
};
});
@ -452,7 +454,7 @@ export class ProductService {
const product = await this.productModel.findOne({ where: { id: productId } });
if (!product) throw new Error(`产品 ID ${productId} 不存在`);
// 条件判断(中文注释:单品 simple 不允许手动设置组成)
if (product.type === 'simple') {
if (product.type === 'single') {
throw new Error('单品无需设置组成');
}
@ -472,7 +474,7 @@ export class ProductService {
}
const comp = new ProductStockComponent();
comp.productId = productId;
comp.productSku = i.sku;
comp.sku = i.sku;
comp.quantity = i.quantity;
created.push(await this.productStockComponentModel.save(comp));
}
@ -486,19 +488,19 @@ export class ProductService {
if (!product) throw new Error(`产品 ID ${productId} 不存在`);
// 中文注释:按 SKU 自动绑定
// 条件判断simple 类型不持久化组成,直接返回单条基于 SKU 的组成
if (product.type === 'simple') {
if (product.type === 'single') {
const comp = new ProductStockComponent();
comp.productId = productId;
comp.productSku = product.sku;
comp.sku = product.sku;
comp.quantity = 1; // 默认数量 1
return [comp];
}
// bundle 类型:若不存在则持久化一条基于 SKU 的组成
const exist = await this.productStockComponentModel.findOne({ where: { productId, productSku: product.sku } });
const exist = await this.productStockComponentModel.findOne({ where: { productId, sku: product.sku } });
if (!exist) {
const comp = new ProductStockComponent();
comp.productId = productId;
comp.productSku = product.sku;
comp.sku = product.sku;
comp.quantity = 1;
await this.productStockComponentModel.save(comp);
}
@ -525,13 +527,13 @@ export class ProductService {
if (!product) {
throw new Error(`产品 ID ${id} 不存在`);
}
const productSku = product.sku;
const sku = product.sku;
// 查询 wp_product 表中是否存在与该 SKU 关联的产品
const wpProduct = await this.wpProductModel
.createQueryBuilder('wp_product')
.where('JSON_CONTAINS(wp_product.constitution, :sku)', {
sku: JSON.stringify({ sku: productSku }),
sku: JSON.stringify({ sku: sku }),
})
.getOne();
if (wpProduct) {
@ -541,7 +543,7 @@ export class ProductService {
const variation = await this.variationModel
.createQueryBuilder('variation')
.where('JSON_CONTAINS(variation.constitution, :sku)', {
sku: JSON.stringify({ sku: productSku }),
sku: JSON.stringify({ sku: sku }),
})
.getOne();

View File

@ -656,10 +656,10 @@ export class StatisticsService {
const offset = (current - 1) * pageSize;
const countSql = `
WITH product_list AS (
SELECT DISTINCT s.productSku
SELECT DISTINCT s.sku
FROM stock s
LEFT JOIN stock_point sp ON s.stockPointId = sp.id
LEFT JOIN product p ON s.productSku = p.sku
LEFT JOIN product p ON s.sku = p.sku
WHERE sp.ignore = FALSE
${countnameFilter}
)
@ -674,27 +674,27 @@ export class StatisticsService {
const sql = `
WITH stock_summary AS (
SELECT
s.productSku,
s.sku,
JSON_ARRAYAGG(JSON_OBJECT('id', sp.id, 'quantity', s.quantity)) AS stockDetails,
SUM(s.quantity) AS totalStock,
SUM(CASE WHEN sp.inCanada THEN s.quantity ELSE 0 END) AS caTotalStock
FROM stock s
JOIN stock_point sp ON s.stockPointId = sp.id
WHERE sp.ignore = FALSE
GROUP BY s.productSku
GROUP BY s.sku
),
transfer_stock AS (
SELECT
ti.productSku,
ti.sku,
SUM(ti.quantity) AS transitStock
FROM transfer_item ti
JOIN transfer t ON ti.transferId = t.id
WHERE t.isCancel = FALSE AND t.isArrived = FALSE
GROUP BY ti.productSku
GROUP BY ti.sku
),
30_sales_summary AS (
SELECT
os.sku AS productSku,
os.sku AS sku,
SUM(os.quantity) AS totalSales
FROM order_sale os
JOIN \`order\` o ON os.orderId = o.id
@ -704,7 +704,7 @@ export class StatisticsService {
),
15_sales_summary AS (
SELECT
os.sku AS productSku,
os.sku AS sku,
2 * SUM(os.quantity) AS totalSales
FROM order_sale os
JOIN \`order\` o ON os.orderId = o.id
@ -714,36 +714,36 @@ export class StatisticsService {
),
sales_max_summary AS (
SELECT
s30.productSku AS productSku,
s30.sku AS sku,
COALESCE(s30.totalSales, 0) AS totalSales_30,
COALESCE(s15.totalSales, 0) AS totalSales_15,
GREATEST(COALESCE(s30.totalSales, 0), COALESCE(s15.totalSales, 0)) AS maxSales
FROM 30_sales_summary s30
LEFT JOIN 15_sales_summary s15
ON s30.productSku = s15.productSku
ON s30.sku = s15.sku
UNION ALL
SELECT
s15.productSku AS productSku,
s15.sku AS sku,
0 AS totalSales_30,
COALESCE(s15.totalSales, 0) AS totalSales_15,
COALESCE(s15.totalSales, 0) AS maxSales
FROM 15_sales_summary s15
LEFT JOIN 30_sales_summary s30
ON s30.productSku = s15.productSku
WHERE s30.productSku IS NULL
ON s30.sku = s15.sku
WHERE s30.sku IS NULL
),
product_name_summary AS (
SELECT
p.sku AS productSku,
p.sku AS sku,
COALESCE(MAX(os.name), MAX(p.name)) AS productName
FROM product p
LEFT JOIN order_sale os ON p.sku = os.sku
GROUP BY p.sku
)
SELECT
ss.productSku,
ss.sku,
ss.stockDetails,
COALESCE(ts.transitStock, 0) AS transitStock,
(COALESCE(ss.totalStock, 0) + COALESCE(ts.transitStock, 0)) AS totalStock,
@ -761,9 +761,9 @@ export class StatisticsService {
sales.maxSales * 4 AS restockQuantity,
pns.productName
FROM stock_summary ss
LEFT JOIN transfer_stock ts ON ss.productSku = ts.productSku
LEFT JOIN sales_max_summary sales ON ss.productSku = sales.productSku
LEFT JOIN product_name_summary pns ON ss.productSku = pns.productSku
LEFT JOIN transfer_stock ts ON ss.sku = ts.sku
LEFT JOIN sales_max_summary sales ON ss.sku = sales.sku
LEFT JOIN product_name_summary pns ON ss.sku = pns.sku
WHERE 1 = 1
${nameFilter}
ORDER BY caAvailableDays
@ -791,10 +791,10 @@ export class StatisticsService {
const offset = (current - 1) * pageSize;
const countSql = `
WITH product_list AS (
SELECT DISTINCT s.productSku
SELECT DISTINCT s.sku
FROM stock s
LEFT JOIN stock_point sp ON s.stockPointId = sp.id
LEFT JOIN product p ON s.productSku = p.sku
LEFT JOIN product p ON s.sku = p.sku
WHERE sp.ignore = FALSE
${countnameFilter}
)
@ -810,36 +810,36 @@ export class StatisticsService {
const sql = `
WITH stock_summary AS (
SELECT
s.productSku,
s.sku,
SUM(s.quantity) AS totalStock
FROM stock s
JOIN stock_point sp ON s.stockPointId = sp.id
WHERE sp.ignore = FALSE
GROUP BY s.productSku
GROUP BY s.sku
),
transfer_stock AS (
SELECT
ti.productSku,
ti.sku,
SUM(ti.quantity) AS transitStock
FROM transfer_item ti
JOIN transfer t ON ti.transferId = t.id
WHERE t.isCancel = FALSE AND t.isArrived = FALSE
GROUP BY ti.productSku
GROUP BY ti.sku
),
b_sales_data_raw As (
SELECT
sr.productSku,
sr.sku,
DATE_FORMAT(sr.createdAt, '%Y-%m') AS month,
SUM(sr.quantityChange) AS sales
FROM stock_record sr
JOIN stock_point sp ON sr.stockPointId = sp.id
WHERE sp.isB
AND sr.createdAt >= DATE_FORMAT(NOW() - INTERVAL 2 MONTH, '%Y-%m-01')
GROUP BY sr.productSku, month
GROUP BY sr.sku, month
),
sales_data_raw AS (
SELECT
os.sku AS productSku,
os.sku AS sku,
DATE_FORMAT(o.date_paid, '%Y-%m') AS month,
SUM(CASE WHEN DAY(o.date_paid) <= 10 THEN os.quantity ELSE 0 END) AS early_sales,
SUM(CASE WHEN DAY(o.date_paid) > 10 AND DAY(o.date_paid) <= 20 THEN os.quantity ELSE 0 END) AS mid_sales,
@ -852,7 +852,7 @@ export class StatisticsService {
),
monthly_sales_summary AS (
SELECT
sdr.productSku,
sdr.sku,
JSON_ARRAYAGG(
JSON_OBJECT(
'month', sdr.month,
@ -863,12 +863,12 @@ export class StatisticsService {
)
) AS sales_data
FROM sales_data_raw sdr
LEFT JOIN b_sales_data_raw b ON sdr.productSku = b.productSku AND sdr.month = b.month
GROUP BY sdr.productSku
LEFT JOIN b_sales_data_raw b ON sdr.sku = b.sku AND sdr.month = b.month
GROUP BY sdr.sku
),
sales_summary AS (
SELECT
os.sku AS productSku,
os.sku AS sku,
SUM(CASE WHEN o.date_paid >= CURDATE() - INTERVAL 30 DAY THEN os.quantity ELSE 0 END) AS last_30_days_sales,
SUM(CASE WHEN o.date_paid >= CURDATE() - INTERVAL 15 DAY THEN os.quantity ELSE 0 END) AS last_15_days_sales,
SUM(CASE WHEN DATE_FORMAT(o.date_paid, '%Y-%m') = DATE_FORMAT(CURDATE() - INTERVAL 1 MONTH, '%Y-%m') THEN os.quantity ELSE 0 END) AS last_month_sales
@ -880,14 +880,14 @@ export class StatisticsService {
),
product_name_summary AS (
SELECT
p.sku AS productSku,
p.sku AS sku,
COALESCE(MAX(os.name), MAX(p.name)) AS productName
FROM product p
LEFT JOIN order_sale os ON p.sku = os.sku
GROUP BY p.sku
)
SELECT
ss.productSku,
ss.sku,
(COALESCE(ss.totalStock, 0) + COALESCE(ts.transitStock, 0)) AS totalStock,
ms.sales_data AS monthlySalesData,
pns.productName,
@ -900,10 +900,10 @@ export class StatisticsService {
ELSE NULL
END AS stock_ratio
FROM stock_summary ss
LEFT JOIN transfer_stock ts ON ss.productSku = ts.productSku
LEFT JOIN monthly_sales_summary ms ON ss.productSku = ms.productSku
LEFT JOIN product_name_summary pns ON ss.productSku = pns.productSku
LEFT JOIN sales_summary ssum ON ss.productSku = ssum.productSku
LEFT JOIN transfer_stock ts ON ss.sku = ts.sku
LEFT JOIN monthly_sales_summary ms ON ss.sku = ms.sku
LEFT JOIN product_name_summary pns ON ss.sku = pns.sku
LEFT JOIN sales_summary ssum ON ss.sku = ssum.sku
WHERE 1 = 1
${nameFilter}
ORDER BY

View File

@ -167,7 +167,7 @@ export class StockService {
qb
.select([
'poi.purchaseOrderId AS purchaseOrderId',
"JSON_ARRAYAGG(JSON_OBJECT('id', poi.id, 'productName', poi.productName,'productSku', poi.productSku, 'quantity', poi.quantity, 'price', poi.price)) AS items",
"JSON_ARRAYAGG(JSON_OBJECT('id', poi.id, 'productName', poi.productName,'sku', poi.sku, 'quantity', poi.quantity, 'price', poi.price)) AS items",
])
.from(PurchaseOrderItem, 'poi')
.groupBy('poi.purchaseOrderId'),
@ -191,7 +191,7 @@ export class StockService {
async hasStockBySku(sku: string): Promise<boolean> {
const count = await this.stockModel
.createQueryBuilder('stock')
.where('stock.productSku = :sku', { sku })
.where('stock.sku = :sku', { sku })
.andWhere('stock.quantity > 0')
.getCount();
return count > 0;
@ -217,7 +217,7 @@ export class StockService {
for (const item of items) {
const updateStock = new UpdateStockDTO();
updateStock.stockPointId = purchaseOrder.stockPointId;
updateStock.productSku = item.productSku;
updateStock.sku = item.sku;
updateStock.quantityChange = item.quantity;
updateStock.operationType = StockRecordOperationType.IN;
updateStock.operatorId = userId;
@ -240,7 +240,7 @@ export class StockService {
// 获取库存列表
async getStocks(query: QueryStockDTO) {
const { current = 1, pageSize = 10, productName, productSku } = query;
const { current = 1, pageSize = 10, productName, sku } = query;
const nameKeywords = productName
? productName.split(' ').filter(Boolean)
: [];
@ -249,31 +249,31 @@ export class StockService {
.createQueryBuilder('stock')
.select([
// 'stock.id as id',
'stock.productSku as productSku',
'stock.sku as sku',
'product.name as productName',
'product.nameCn as productNameCn',
'JSON_ARRAYAGG(JSON_OBJECT("id", stock.stockPointId, "quantity", stock.quantity)) as stockPoint',
'MIN(stock.updatedAt) as updatedAt',
'MAX(stock.createdAt) as createdAt',
])
.leftJoin(Product, 'product', 'product.sku = stock.productSku')
.groupBy('stock.productSku')
.leftJoin(Product, 'product', 'product.sku = stock.sku')
.groupBy('stock.sku')
.addGroupBy('product.name')
.addGroupBy('product.nameCn');
let totalQueryBuilder = this.stockModel
.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 });
.select('COUNT(DISTINCT stock.sku)', 'count')
.leftJoin(Product, 'product', 'product.sku = stock.sku');
if (sku) {
queryBuilder.andWhere('stock.sku = :sku', { sku });
totalQueryBuilder.andWhere('stock.sku = :sku', { sku });
}
if (nameKeywords.length) {
nameKeywords.forEach((name, index) => {
queryBuilder.andWhere(
`EXISTS (
SELECT 1 FROM product p
WHERE p.sku = stock.productSku
WHERE p.sku = stock.sku
AND p.name LIKE :name${index}
)`,
{ [`name${index}`]: `%${name}%` }
@ -281,7 +281,7 @@ export class StockService {
totalQueryBuilder.andWhere(
`EXISTS (
SELECT 1 FROM product p
WHERE p.sku = stock.productSku
WHERE p.sku = stock.sku
AND p.name LIKE :name${index}
)`,
{ [`name${index}`]: `%${name}%` }
@ -291,7 +291,7 @@ export class StockService {
if (query.order) {
const sortFieldMap: Record<string, string> = {
productName: 'product.name',
productSku: 'stock.productSku',
sku: 'stock.sku',
updatedAt: 'updatedAt',
createdAt: 'createdAt',
};
@ -335,15 +335,15 @@ export class StockService {
const transfer = await this.transferModel
.createQueryBuilder('t')
.select(['ti.productSku as productSku', 'SUM(ti.quantity) as quantity'])
.select(['ti.sku as sku', 'SUM(ti.quantity) as quantity'])
.leftJoin(TransferItem, 'ti', 'ti.transferId = t.id')
.where('!t.isArrived and !t.isCancel and !t.isLost')
.groupBy('ti.productSku')
.groupBy('ti.sku')
.getRawMany();
for (const item of items) {
item.inTransitQuantity =
transfer.find(t => t.productSku === item.productSku)?.quantity || 0;
transfer.find(t => t.sku === item.sku)?.quantity || 0;
}
return {
@ -361,10 +361,10 @@ export class StockService {
const stocks = await this.stockModel
.createQueryBuilder('stock')
.select('stock.productSku', 'productSku')
.select('stock.sku', 'sku')
.addSelect('SUM(stock.quantity)', 'totalQuantity')
.where('stock.productSku IN (:...skus)', { skus })
.groupBy('stock.productSku')
.where('stock.sku IN (:...skus)', { skus })
.groupBy('stock.sku')
.getRawMany();
return stocks;
@ -374,7 +374,7 @@ export class StockService {
async updateStock(data: UpdateStockDTO) {
const {
stockPointId,
productSku,
sku,
quantityChange,
operationType,
operatorId,
@ -383,13 +383,13 @@ export class StockService {
const stock = await this.stockModel.findOneBy({
stockPointId,
productSku,
sku,
});
if (!stock) {
// 如果库存不存在,则直接新增
const newStock = this.stockModel.create({
stockPointId,
productSku,
sku,
quantity: operationType === 'in' ? quantityChange : -quantityChange,
});
await this.stockModel.save(newStock);
@ -406,7 +406,7 @@ export class StockService {
// 记录库存变更日志
const stockRecord = this.stockRecordModel.create({
stockPointId,
productSku,
sku,
operationType,
quantityChange,
operatorId,
@ -421,7 +421,7 @@ export class StockService {
current = 1,
pageSize = 10,
stockPointId,
productSku,
sku,
productName,
operationType,
startDate,
@ -430,14 +430,14 @@ export class StockService {
const where: any = {};
if (stockPointId) where.stockPointId = stockPointId;
if (productSku) where.productSku = productSku;
if (sku) where.sku = sku;
if (operationType) where.operationType = operationType;
if (startDate) where.createdAt = MoreThan(startDate);
if (endDate) where.createdAt = LessThan(endDate);
if (startDate && endDate) where.createdAt = Between(startDate, endDate);
const queryBuilder = this.stockRecordModel
.createQueryBuilder('stock_record')
.leftJoin(Product, 'product', 'product.sku = stock_record.productSku')
.leftJoin(Product, 'product', 'product.sku = stock_record.sku')
.leftJoin(User, 'user', 'stock_record.operatorId = user.id')
.leftJoin(StockPoint, 'sp', 'sp.id = stock_record.stockPointId')
.select([
@ -470,7 +470,7 @@ export class StockService {
// for (const item of items) {
// const stock = await this.stockModel.findOneBy({
// stockPointId: sourceStockPointId,
// productSku: item.productSku,
// sku: item.sku,
// });
// if (!stock || stock.quantity < item.quantity)
// throw new Error(`${item.productName} 库存不足`);
@ -496,7 +496,7 @@ export class StockService {
item.transferId = transfer.id;
const updateStock = new UpdateStockDTO();
updateStock.stockPointId = sourceStockPointId;
updateStock.productSku = item.productSku;
updateStock.sku = item.sku;
updateStock.quantityChange = item.quantity;
updateStock.operationType = StockRecordOperationType.OUT;
updateStock.operatorId = userId;
@ -530,7 +530,7 @@ export class StockService {
qb
.select([
'ti.transferId AS transferId',
"JSON_ARRAYAGG(JSON_OBJECT('id', ti.id, 'productName', ti.productName,'productSku', ti.productSku, 'quantity', ti.quantity)) AS items",
"JSON_ARRAYAGG(JSON_OBJECT('id', ti.id, 'productName', ti.productName,'sku', ti.sku, 'quantity', ti.quantity)) AS items",
])
.from(TransferItem, 'ti')
.groupBy('ti.transferId'),
@ -561,7 +561,7 @@ export class StockService {
for (const item of items) {
const updateStock = new UpdateStockDTO();
updateStock.stockPointId = transfer.sourceStockPointId;
updateStock.productSku = item.productSku;
updateStock.sku = item.sku;
updateStock.quantityChange = item.quantity;
updateStock.operationType = StockRecordOperationType.IN;
updateStock.operatorId = userId;
@ -584,7 +584,7 @@ export class StockService {
for (const item of items) {
const updateStock = new UpdateStockDTO();
updateStock.stockPointId = transfer.destStockPointId;
updateStock.productSku = item.productSku;
updateStock.sku = item.sku;
updateStock.quantityChange = item.quantity;
updateStock.operationType = StockRecordOperationType.IN;
updateStock.operatorId = userId;
@ -619,7 +619,7 @@ export class StockService {
item.transferId = transfer.id;
const updateStock = new UpdateStockDTO();
updateStock.stockPointId = sourceStockPointId;
updateStock.productSku = item.productSku;
updateStock.sku = item.sku;
updateStock.quantityChange = item.quantity;
updateStock.operationType = StockRecordOperationType.IN;
updateStock.operatorId = userId;
@ -632,7 +632,7 @@ export class StockService {
item.transferId = transfer.id;
const updateStock = new UpdateStockDTO();
updateStock.stockPointId = sourceStockPointId;
updateStock.productSku = item.productSku;
updateStock.sku = item.sku;
updateStock.quantityChange = item.quantity;
updateStock.operationType = StockRecordOperationType.OUT;
updateStock.operatorId = userId;