feat(area): 重构区域模块,使用i18n-iso-countries管理国家数据

refactor: 统一将productName字段重命名为name
chore: 添加i18n-iso-countries依赖
style: 优化字典名称格式化逻辑
This commit is contained in:
tikkhun 2025-12-01 23:53:12 +08:00
parent 10b42eca7a
commit f20f4727f6
15 changed files with 186 additions and 224 deletions

19
package-lock.json generated
View File

@ -27,6 +27,7 @@
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"csv-parse": "^6.1.0", "csv-parse": "^6.1.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"i18n-iso-countries": "^7.14.0",
"mysql2": "^3.15.3", "mysql2": "^3.15.3",
"nodemailer": "^7.0.5", "nodemailer": "^7.0.5",
"npm-check-updates": "^19.1.2", "npm-check-updates": "^19.1.2",
@ -2044,6 +2045,12 @@
"wrappy": "1" "wrappy": "1"
} }
}, },
"node_modules/diacritics": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/diacritics/-/diacritics-1.3.0.tgz",
"integrity": "sha512-wlwEkqcsaxvPJML+rDh/2iS824jbREk6DUMUKkEaSlxdYHeS43cClJtsWglvw2RfeXGm6ohKDqsXteJ5sP5enA==",
"license": "MIT"
},
"node_modules/dir-glob": { "node_modules/dir-glob": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz", "resolved": "https://registry.npmmirror.com/dir-glob/-/dir-glob-3.0.1.tgz",
@ -2691,6 +2698,18 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/i18n-iso-countries": {
"version": "7.14.0",
"resolved": "https://registry.npmjs.org/i18n-iso-countries/-/i18n-iso-countries-7.14.0.tgz",
"integrity": "sha512-nXHJZYtNrfsi1UQbyRqm3Gou431elgLjKl//CYlnBGt5aTWdRPH1PiS2T/p/n8Q8LnqYqzQJik3Q7mkwvLokeg==",
"license": "MIT",
"dependencies": {
"diacritics": "1.3.0"
},
"engines": {
"node": ">= 12"
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.7.0", "version": "0.7.0",
"resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz", "resolved": "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.7.0.tgz",

View File

@ -22,6 +22,7 @@
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"csv-parse": "^6.1.0", "csv-parse": "^6.1.0",
"dayjs": "^1.11.13", "dayjs": "^1.11.13",
"i18n-iso-countries": "^7.14.0",
"mysql2": "^3.15.3", "mysql2": "^3.15.3",
"nodemailer": "^7.0.5", "nodemailer": "^7.0.5",
"npm-check-updates": "^19.1.2", "npm-check-updates": "^19.1.2",

View File

@ -1,15 +1,5 @@
import { Inject } from '@midwayjs/core'; import { Body, Context, Controller, Del, Get, Inject, Param, Post, Put, Query } from '@midwayjs/core';
import {
Body,
Controller,
Del,
Get,
Param,
Post,
Put,
Query,
} from '@midwayjs/decorator';
import { import {
ApiBearerAuth, ApiBearerAuth,
ApiBody, ApiBody,
@ -22,14 +12,39 @@ import { AreaService } from '../service/area.service';
import { CreateAreaDTO, QueryAreaDTO, UpdateAreaDTO } from '../dto/area.dto'; import { CreateAreaDTO, QueryAreaDTO, UpdateAreaDTO } from '../dto/area.dto';
import { errorResponse, successResponse } from '../utils/response.util'; import { errorResponse, successResponse } from '../utils/response.util';
import { Area } from '../entity/area.entity'; import { Area } from '../entity/area.entity';
import * as countries from 'i18n-iso-countries';
@ApiBearerAuth() @ApiBearerAuth()
@ApiTags('Area') @ApiTags('Area')
@Controller('/api/area') @Controller('/area')
export class AreaController { export class AreaController {
@Inject()
ctx: Context;
@Inject() @Inject()
areaService: AreaService; areaService: AreaService;
@ApiOperation({ summary: '获取国家列表' })
@ApiOkResponse({ description: '国家列表' })
@Get('/countries')
async getCountries() {
try {
// 注册中文语言包
countries.registerLocale(require('i18n-iso-countries/langs/zh.json'));
// 获取所有国家的中文名称
const countryNames = countries.getNames('zh', { select: 'official' });
// 格式化为 { code, name } 的数组
const countryList = Object.keys(countryNames).map(code => ({
code,
name: countryNames[code],
}));
return successResponse(countryList, '查询成功');
} catch (error) {
console.log(error);
return errorResponse(error?.message || error);
}
}
@ApiOperation({ summary: '创建区域' }) @ApiOperation({ summary: '创建区域' })
@ApiBody({ type: CreateAreaDTO }) @ApiBody({ type: CreateAreaDTO })
@ApiOkResponse({ type: Area, description: '成功创建的区域' }) @ApiOkResponse({ type: Area, description: '成功创建的区域' })
@ -39,7 +54,8 @@ export class AreaController {
const newArea = await this.areaService.createArea(area); const newArea = await this.areaService.createArea(area);
return successResponse(newArea, '创建成功'); return successResponse(newArea, '创建成功');
} catch (error) { } catch (error) {
return errorResponse(error.message); console.log(error);
return errorResponse(error?.message || error);
} }
} }
@ -52,7 +68,8 @@ export class AreaController {
const updatedArea = await this.areaService.updateArea(id, area); const updatedArea = await this.areaService.updateArea(id, area);
return successResponse(updatedArea, '更新成功'); return successResponse(updatedArea, '更新成功');
} catch (error) { } catch (error) {
return errorResponse(error.message); console.log(error);
return errorResponse(error?.message || error);
} }
} }
@ -64,7 +81,8 @@ export class AreaController {
await this.areaService.deleteArea(id); await this.areaService.deleteArea(id);
return successResponse(null, '删除成功'); return successResponse(null, '删除成功');
} catch (error) { } catch (error) {
return errorResponse(error.message); console.log(error);
return errorResponse(error?.message || error);
} }
} }
@ -77,19 +95,8 @@ export class AreaController {
const { list, total } = await this.areaService.getAreaList(query); const { list, total } = await this.areaService.getAreaList(query);
return successResponse({ list, total }, '查询成功'); return successResponse({ list, total }, '查询成功');
} catch (error) { } catch (error) {
return errorResponse(error.message); console.log(error);
} return errorResponse(error?.message || error);
}
@ApiOperation({ summary: '获取所有区域' })
@ApiOkResponse({ type: [Area], description: '所有区域列表' })
@Get('/all')
async getAllAreas() {
try {
const areas = await this.areaService.getAllAreas();
return successResponse(areas, '查询成功');
} catch (error) {
return errorResponse(error.message);
} }
} }
@ -104,7 +111,8 @@ export class AreaController {
} }
return successResponse(area, '查询成功'); return successResponse(area, '查询成功');
} catch (error) { } catch (error) {
return errorResponse(error.message); console.log(error);
return errorResponse(error?.message || error);
} }
} }
} }

View File

@ -134,9 +134,9 @@ export class ProductController {
@ApiOkResponse({ type: ProductRes }) @ApiOkResponse({ type: ProductRes })
@Put('updateNameCn/:id/:nameCn') @Put('updateNameCn/:id/:nameCn')
async updateProductNameCn(@Param('id') id: number, @Param('nameCn') nameCn: string) { async updatenameCn(@Param('id') id: number, @Param('nameCn') nameCn: string) {
try { try {
const data = this.productService.updateProductNameCn(id, nameCn); const data = this.productService.updatenameCn(id, nameCn);
return successResponse(data); return successResponse(data);
} catch (error) { } catch (error) {
return errorResponse(error?.message || error); return errorResponse(error?.message || error);

View File

@ -8,18 +8,21 @@ export default class AreaSeeder implements Seeder {
dataSource: DataSource, dataSource: DataSource,
factoryManager: SeederFactoryManager factoryManager: SeederFactoryManager
): Promise<any> { ): Promise<any> {
const repository = dataSource.getRepository(Area); const areaRepository = dataSource.getRepository(Area);
const areas = [ const areas = [
{ name: '加拿大' }, { name: 'Australia' },
{ name: '澳大利亚' }, { name: 'Canada' },
{ name: '欧洲' }, { name: 'United States' },
{ name: 'Germany' },
{ name: 'Poland' },
]; ];
for (const area of areas) { for (const areaData of areas) {
const existing = await repository.findOne({ where: { name: area.name } }); const existingArea = await areaRepository.findOne({ where: { name: areaData.name } });
if (!existing) { if (!existingArea) {
await repository.insert(area); const newArea = areaRepository.create(areaData);
await areaRepository.save(newArea);
} }
} }
} }

View File

@ -10,8 +10,9 @@ export default class DictSeeder implements Seeder {
* @returns * @returns
*/ */
private formatName(name: string): string { private formatName(name: string): string {
// 将空格、下划线、点统一转换成中划线,并转为小写 // return String(name).replace(/[\_\s.]+/g, '-').toLowerCase();
return String(name).replace(/[_\s.]+/g, '-').toLowerCase(); // 只替换空格和下划线
return String(name).replace(/[\_\s]+/g, '-').toLowerCase();
} }
public async run( public async run(

View File

@ -2,47 +2,28 @@
import { ApiProperty } from '@midwayjs/swagger'; import { ApiProperty } from '@midwayjs/swagger';
import { Rule, RuleType } from '@midwayjs/validate'; import { Rule, RuleType } from '@midwayjs/validate';
// 创建区域的数据传输对象
export class CreateAreaDTO { export class CreateAreaDTO {
@ApiProperty({ type: 'string', description: '区域名称', example: '欧洲' }) @ApiProperty({ description: '编码' })
@Rule(RuleType.string().required()) @Rule(RuleType.string().required())
name: string; code: string;
@ApiProperty({ type: 'number', description: '纬度', example: 48.8566, required: false })
@Rule(RuleType.number().min(-90).max(90).allow(null))
latitude?: number;
@ApiProperty({ type: 'number', description: '经度', example: 2.3522, required: false })
@Rule(RuleType.number().min(-180).max(180).allow(null))
longitude?: number;
} }
// 更新区域的数据传输对象
export class UpdateAreaDTO { export class UpdateAreaDTO {
@ApiProperty({ type: 'string', description: '区域名称', example: '欧洲' }) @ApiProperty({ description: '编码', required: false })
@Rule(RuleType.string()) @Rule(RuleType.string())
name?: string; code?: string;
@ApiProperty({ type: 'number', description: '纬度', example: 48.8566, required: false })
@Rule(RuleType.number().min(-90).max(90).allow(null))
latitude?: number;
@ApiProperty({ type: 'number', description: '经度', example: 2.3522, required: false })
@Rule(RuleType.number().min(-180).max(180).allow(null))
longitude?: number;
} }
// 查询区域的数据传输对象
export class QueryAreaDTO { export class QueryAreaDTO {
@ApiProperty({ type: 'number', description: '当前页码', example: 1 }) @ApiProperty({ description: '当前页', required: false, default: 1 })
@Rule(RuleType.number().min(1).default(1)) @Rule(RuleType.number().integer().min(1).default(1))
currentPage: number; currentPage?: number;
@ApiProperty({ type: 'number', description: '每页数量', example: 10 }) @ApiProperty({ description: '每页数量', required: false, default: 10 })
@Rule(RuleType.number().min(1).max(100).default(10)) @Rule(RuleType.number().integer().min(1).default(10))
pageSize: number; pageSize?: number;
@ApiProperty({ type: 'string', description: '区域名称', example: '欧洲' }) @ApiProperty({ description: '关键词(名称或编码)', required: false })
@Rule(RuleType.string()) @Rule(RuleType.string())
name?: string; keyword?: string;
} }

View File

@ -20,7 +20,7 @@ export class QueryStockDTO {
@ApiProperty() @ApiProperty()
@Rule(RuleType.string()) @Rule(RuleType.string())
productName: string; name: string;
@ApiProperty() @ApiProperty()
@Rule(RuleType.string()) @Rule(RuleType.string())
@ -62,7 +62,7 @@ export class QueryStockRecordDTO {
@ApiProperty() @ApiProperty()
@Rule(RuleType.string()) @Rule(RuleType.string())
productName: string; name: string;
@ApiProperty() @ApiProperty()
@Rule(RuleType.string()) @Rule(RuleType.string())
@ -96,7 +96,7 @@ export class QueryPurchaseOrderDTO {
export class StockDTO extends Stock { export class StockDTO extends Stock {
@ApiProperty() @ApiProperty()
@Rule(RuleType.string()) @Rule(RuleType.string())
productName: string; name: string;
@ApiProperty({ @ApiProperty({
type: 'object', type: 'object',

View File

@ -1,43 +1,17 @@
import { ApiProperty } from '@midwayjs/swagger'; import { ApiProperty } from '@midwayjs/swagger';
import { import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
Column,
CreateDateColumn,
Entity,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('area') @Entity('area')
export class Area { export class Area {
@ApiProperty({ type: 'number' })
@PrimaryGeneratedColumn() @PrimaryGeneratedColumn()
id: number; id: number;
@ApiProperty({ type: 'string', description: '区域名称' }) @ApiProperty({ description: '名称' })
@Column({ unique: true }) @Column()
name: string; name: string;
@ApiProperty({ type: 'number', description: '纬度', required: false }) @ApiProperty({ description: '编码' })
@Column({ type: 'decimal', precision: 10, scale: 6, nullable: true }) @Column({ unique: true })
latitude?: number; code: string;
@ApiProperty({ type: 'number', description: '经度', required: false })
@Column({ type: 'decimal', precision: 10, scale: 6, nullable: true })
longitude?: number;
@ApiProperty({
example: '2022-12-12 11:11:11',
description: '创建时间',
required: true,
})
@CreateDateColumn()
createdAt: Date;
@ApiProperty({
example: '2022-12-12 11:11:11',
description: '更新时间',
required: true,
})
@UpdateDateColumn()
updatedAt: Date;
} }

View File

@ -14,7 +14,7 @@ export class PurchaseOrderItem {
@ApiProperty({ type: String }) @ApiProperty({ type: String })
@Column() @Column()
productName: string; name: string;
@ApiProperty({ type: Number }) @ApiProperty({ type: Number })
@Column() @Column()

View File

@ -13,7 +13,7 @@ export class TransferItem {
@ApiProperty({ type: String }) @ApiProperty({ type: String })
@Column() @Column()
productName: string; name: string;
@ApiProperty({ type: Number }) @ApiProperty({ type: Number })
@Column() @Column()

View File

@ -1,102 +1,80 @@
import { Provide } from '@midwayjs/core'; import { Provide } from '@midwayjs/core';
import { InjectEntityModel } from '@midwayjs/typeorm'; import { InjectEntityModel } from '@midwayjs/typeorm';
import { Repository } from 'typeorm'; import { Like, Repository } from 'typeorm';
import { Area } from '../entity/area.entity'; import { Area } from '../entity/area.entity';
import { CreateAreaDTO, QueryAreaDTO, UpdateAreaDTO } from '../dto/area.dto'; import { CreateAreaDTO, QueryAreaDTO, UpdateAreaDTO } from '../dto/area.dto';
import * as countries from 'i18n-iso-countries';
@Provide() @Provide()
export class AreaService { export class AreaService {
@InjectEntityModel(Area) @InjectEntityModel(Area)
areaModel: Repository<Area>; areaRepository: Repository<Area>;
/** constructor() {
* // 在服务初始化时注册中文语言包
* @param params countries.registerLocale(require('i18n-iso-countries/langs/zh.json'));
*/
async createArea(params: CreateAreaDTO) {
// 检查区域名称是否已存在
const existing = await this.areaModel.findOne({ where: { name: params.name } });
if (existing) {
throw new Error('区域名称已存在');
}
const area = new Area();
area.name = params.name;
if (params.latitude !== undefined) {
area.latitude = params.latitude;
}
if (params.longitude !== undefined) {
area.longitude = params.longitude;
}
return await this.areaModel.save(area);
} }
/**
*
* @param id ID
* @param params
*/
async updateArea(id: number, params: UpdateAreaDTO) {
const area = await this.areaModel.findOneBy({ id });
if (!area) {
throw new Error('区域不存在');
}
if (params.name) {
// 检查新的区域名称是否已存在
const existing = await this.areaModel.findOne({ where: { name: params.name } });
if (existing && existing.id !== id) {
throw new Error('区域名称已存在');
}
area.name = params.name;
}
if (params.latitude !== undefined) {
area.latitude = params.latitude;
}
if (params.longitude !== undefined) {
area.longitude = params.longitude;
}
return await this.areaModel.save(area);
}
/**
*
* @param id ID
*/
async deleteArea(id: number) {
const result = await this.areaModel.delete(id);
return result.affected > 0;
}
/**
*
* @param query
*/
async getAreaList(query: QueryAreaDTO) { async getAreaList(query: QueryAreaDTO) {
const { currentPage, pageSize, name } = query; const { currentPage = 1, pageSize = 10, keyword = '' } = query;
const [list, total] = await this.areaModel.findAndCount({ const [list, total] = await this.areaRepository.findAndCount({
where: name ? { name } : {}, where: [{ name: Like(`%${keyword}%`) }, { code: Like(`%${keyword}%`) }],
skip: (currentPage - 1) * pageSize, skip: (currentPage - 1) * pageSize,
take: pageSize, take: pageSize,
order: {
id: 'DESC',
},
}); });
return { list, total }; return { list, total };
} }
/** async getAreaById(id: number) {
* return this.areaRepository.findOne({ where: { id } });
*/
async getAllAreas() {
return await this.areaModel.find();
} }
/** async createArea(createAreaDTO: CreateAreaDTO) {
* ID获取区域详情 // 根据 code 获取国家中文名称
* @param id ID const name = countries.getName(createAreaDTO.code, 'zh', {
*/ select: 'official',
async getAreaById(id: number) { });
return await this.areaModel.findOneBy({ id });
// 如果找不到对应的国家,则抛出错误
if (!name) {
throw new Error(`无效的国家代码: ${createAreaDTO.code}`);
}
const area = new Area();
area.name = name;
area.code = createAreaDTO.code;
return this.areaRepository.save(area);
}
async updateArea(id: number, updateAreaDTO: UpdateAreaDTO) {
const area = await this.getAreaById(id);
if (!area) {
return null;
}
// 如果 code 发生变化,则更新 name
if (updateAreaDTO.code && updateAreaDTO.code !== area.code) {
const name = countries.getName(updateAreaDTO.code, 'zh', {
select: 'official',
});
if (!name) {
throw new Error(`无效的国家代码: ${updateAreaDTO.code}`);
}
area.name = name;
area.code = updateAreaDTO.code;
}
return this.areaRepository.save(area);
}
async deleteArea(id: number) {
const area = await this.getAreaById(id);
if (!area) {
return false;
}
await this.areaRepository.remove(area);
return true;
} }
} }

View File

@ -18,7 +18,8 @@ export class DictService {
// 格式化名称为 kebab-case // 格式化名称为 kebab-case
private formatName(name: string): string { private formatName(name: string): string {
return String(name).replace(/[_\s.]+/g, '-').toLowerCase(); // 只替换空格和下划线
return String(name).replace(/[_\s]+/g, '-').toLowerCase();
} }
// 生成并返回字典的XLSX模板 // 生成并返回字典的XLSX模板

View File

@ -509,7 +509,7 @@ export class ProductService {
// 重复定义的 getProductList 已合并到前面的实现(中文注释:移除重复) // 重复定义的 getProductList 已合并到前面的实现(中文注释:移除重复)
async updateProductNameCn(id: number, nameCn: string): Promise<Product> { async updatenameCn(id: number, nameCn: string): Promise<Product> {
// 确认产品是否存在 // 确认产品是否存在
const product = await this.productModel.findOneBy({ id }); const product = await this.productModel.findOneBy({ id });
if (!product) { if (!product) {

View File

@ -167,7 +167,7 @@ export class StockService {
qb qb
.select([ .select([
'poi.purchaseOrderId AS purchaseOrderId', 'poi.purchaseOrderId AS purchaseOrderId',
"JSON_ARRAYAGG(JSON_OBJECT('id', poi.id, 'productName', poi.productName,'sku', poi.sku, 'quantity', poi.quantity, 'price', poi.price)) AS items", "JSON_ARRAYAGG(JSON_OBJECT('id', poi.id, 'name', poi.name,'sku', poi.sku, 'quantity', poi.quantity, 'price', poi.price)) AS items",
]) ])
.from(PurchaseOrderItem, 'poi') .from(PurchaseOrderItem, 'poi')
.groupBy('poi.purchaseOrderId'), .groupBy('poi.purchaseOrderId'),
@ -240,9 +240,9 @@ export class StockService {
// 获取库存列表 // 获取库存列表
async getStocks(query: QueryStockDTO) { async getStocks(query: QueryStockDTO) {
const { current = 1, pageSize = 10, productName, sku } = query; const { current = 1, pageSize = 10, name, sku } = query;
const nameKeywords = productName const nameKeywords = name
? productName.split(' ').filter(Boolean) ? name.split(' ').filter(Boolean)
: []; : [];
let queryBuilder = this.stockModel let queryBuilder = this.stockModel
@ -250,8 +250,8 @@ export class StockService {
.select([ .select([
// 'stock.id as id', // 'stock.id as id',
'stock.sku as sku', 'stock.sku as sku',
'product.name as productName', 'product.name as name',
'product.nameCn as productNameCn', 'product.nameCn as nameCn',
'JSON_ARRAYAGG(JSON_OBJECT("id", stock.stockPointId, "quantity", stock.quantity)) as stockPoint', 'JSON_ARRAYAGG(JSON_OBJECT("id", stock.stockPointId, "quantity", stock.quantity)) as stockPoint',
'MIN(stock.updatedAt) as updatedAt', 'MIN(stock.updatedAt) as updatedAt',
'MAX(stock.createdAt) as createdAt', 'MAX(stock.createdAt) as createdAt',
@ -264,33 +264,29 @@ export class StockService {
.createQueryBuilder('stock') .createQueryBuilder('stock')
.select('COUNT(DISTINCT stock.sku)', 'count') .select('COUNT(DISTINCT stock.sku)', 'count')
.leftJoin(Product, 'product', 'product.sku = stock.sku'); .leftJoin(Product, 'product', 'product.sku = stock.sku');
if (sku) { if (sku || nameKeywords.length) {
queryBuilder.andWhere('stock.sku = :sku', { sku }); const conditions = [];
totalQueryBuilder.andWhere('stock.sku = :sku', { sku }); if (sku) {
} conditions.push(`stock.sku LIKE :sku`);
if (nameKeywords.length) { }
nameKeywords.forEach((name, index) => { if (nameKeywords.length) {
queryBuilder.andWhere( nameKeywords.forEach((name, index) => {
`EXISTS ( conditions.push(`product.name LIKE :name${index}`);
SELECT 1 FROM product p });
WHERE p.sku = stock.sku }
AND p.name LIKE :name${index} const whereClause = conditions.join(' OR ');
)`, queryBuilder.andWhere(`(${whereClause})`, {
{ [`name${index}`]: `%${name}%` } sku: `%${sku}%`,
); ...nameKeywords.reduce((acc, name, index) => ({ ...acc, [`name${index}`]: `%${name}%` }), {}),
totalQueryBuilder.andWhere( });
`EXISTS ( totalQueryBuilder.andWhere(`(${whereClause})`, {
SELECT 1 FROM product p sku: `%${sku}%`,
WHERE p.sku = stock.sku ...nameKeywords.reduce((acc, name, index) => ({ ...acc, [`name${index}`]: `%${name}%` }), {}),
AND p.name LIKE :name${index}
)`,
{ [`name${index}`]: `%${name}%` }
);
}); });
} }
if (query.order) { if (query.order) {
const sortFieldMap: Record<string, string> = { const sortFieldMap: Record<string, string> = {
productName: 'product.name', name: 'product.name',
sku: 'stock.sku', sku: 'stock.sku',
updatedAt: 'updatedAt', updatedAt: 'updatedAt',
createdAt: 'createdAt', createdAt: 'createdAt',
@ -422,7 +418,7 @@ export class StockService {
pageSize = 10, pageSize = 10,
stockPointId, stockPointId,
sku, sku,
productName, name,
operationType, operationType,
startDate, startDate,
endDate, endDate,
@ -447,9 +443,9 @@ export class StockService {
'sp.name as stockPointName', 'sp.name as stockPointName',
]) ])
.where(where); .where(where);
if (productName) if (name)
queryBuilder.andWhere('product.name LIKE :name', { queryBuilder.andWhere('product.name LIKE :name', {
name: `%${productName}%`, name: `%${name}%`,
}); });
const items = await queryBuilder const items = await queryBuilder
.orderBy('stock_record.createdAt', 'DESC') .orderBy('stock_record.createdAt', 'DESC')