feat:improve product yarn ratio workflow (#5)
* feat: support product yarn batches * feat: improve product yarn ratio workflow * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
bb212aff36
commit
e3f6fa236d
@ -210,16 +210,14 @@ CREATE TABLE `crm_data_migration_log` (
|
||||
|
||||
CREATE TABLE `inv_product` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`tenant_id` VARCHAR(32) NOT NULL DEFAULT 't_default_001',
|
||||
`product_id` VARCHAR(32) NOT NULL,
|
||||
`product_name` VARCHAR(100) NOT NULL,
|
||||
`image_url` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`spec` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`color` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`unit_pieces` INT NOT NULL DEFAULT 0,
|
||||
`unit_rolls` INT NOT NULL DEFAULT 0,
|
||||
`stock_quantity` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
`location` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`cost_price` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
`color` VARCHAR(50) NOT NULL DEFAULT '本色',
|
||||
`color_no` VARCHAR(20) NOT NULL DEFAULT '',
|
||||
`product_code` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`sales_price` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
`remark` TEXT,
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1:enabled 0:disabled',
|
||||
@ -228,12 +226,96 @@ CREATE TABLE `inv_product` (
|
||||
`deleted_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_product_id` (`product_id`),
|
||||
UNIQUE KEY `uk_product_name_spec_color` (`product_name`, `spec`, `color`),
|
||||
UNIQUE KEY `uk_tenant_product_code` (`tenant_id`, `product_code`),
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_product_name_tenant` (`product_name`, `tenant_id`),
|
||||
KEY `idx_color` (`color`),
|
||||
KEY `idx_location` (`location`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品档案';
|
||||
|
||||
CREATE TABLE `inv_yarn` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`yarn_id` VARCHAR(32) NOT NULL,
|
||||
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`yarn_name` VARCHAR(100) NOT NULL,
|
||||
`color` VARCHAR(50) NOT NULL DEFAULT '原纱本色',
|
||||
`weight_g_m` DECIMAL(10,4) NOT NULL DEFAULT 0.0000,
|
||||
`supplier_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`dye_factory` VARCHAR(100) NOT NULL DEFAULT '无',
|
||||
`image_url` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`remark` TEXT,
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1:enabled 0:disabled',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_yarn_id` (`yarn_id`),
|
||||
UNIQUE KEY `uk_tenant_yarn_name_color_supplier` (`tenant_id`, `yarn_name`, `color`, `supplier_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_supplier_id` (`supplier_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='原料纱档案';
|
||||
|
||||
CREATE TABLE `inv_product_batch` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`batch_id` VARCHAR(32) NOT NULL,
|
||||
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`product_id` VARCHAR(32) NOT NULL,
|
||||
`batch_no` VARCHAR(64) NOT NULL DEFAULT '',
|
||||
`yarn_ratio` JSON DEFAULT NULL,
|
||||
`warp_weight_g_m` DECIMAL(10,4) NOT NULL DEFAULT 0.0000,
|
||||
`weft_weight_g_m` DECIMAL(10,4) NOT NULL DEFAULT 0.0000,
|
||||
`production_process` TEXT,
|
||||
`remark` TEXT,
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1:enabled 0:disabled',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_batch_id` (`batch_id`),
|
||||
UNIQUE KEY `uk_tenant_batch_no` (`tenant_id`, `batch_no`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品批次表';
|
||||
|
||||
CREATE TABLE `inv_product_pan` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`pan_id` VARCHAR(32) NOT NULL,
|
||||
`product_id` VARCHAR(32) NOT NULL,
|
||||
`batch_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`name` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`position` VARCHAR(100) NOT NULL DEFAULT '',
|
||||
`sort_order` INT NOT NULL DEFAULT 0,
|
||||
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_pan_id` (`pan_id`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_batch_id` (`batch_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_position` (`position`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品盘库存';
|
||||
|
||||
CREATE TABLE `inv_product_bolt` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`bolt_id` VARCHAR(32) NOT NULL,
|
||||
`pan_id` VARCHAR(32) NOT NULL,
|
||||
`length_m` DECIMAL(10,2) NOT NULL DEFAULT 0.00,
|
||||
`color` VARCHAR(50) NOT NULL DEFAULT '',
|
||||
`sort_order` INT NOT NULL DEFAULT 0,
|
||||
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_bolt_id` (`bolt_id`),
|
||||
KEY `idx_pan_id` (`pan_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品匹库存';
|
||||
|
||||
CREATE TABLE `inv_stock_import_log` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
|
||||
174
deploy/mysql/migrations/20260703_add_product_yarn_batch.sql
Normal file
174
deploy/mysql/migrations/20260703_add_product_yarn_batch.sql
Normal file
@ -0,0 +1,174 @@
|
||||
-- Product/yarn/batch model migration.
|
||||
-- Adds yarn master data, product batches, frozen product codes, and pan batch ownership.
|
||||
-- Run after the pan/bolt and purchase-management migrations.
|
||||
|
||||
SET @db = DATABASE();
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `inv_yarn` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`yarn_id` VARCHAR(32) NOT NULL COMMENT 'business key, UUID',
|
||||
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`yarn_name` VARCHAR(100) NOT NULL,
|
||||
`color` VARCHAR(50) NOT NULL DEFAULT '原纱本色',
|
||||
`weight_g_m` DECIMAL(10,4) NOT NULL DEFAULT 0.0000 COMMENT 'yarn weight (g/m)',
|
||||
`supplier_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`dye_factory` VARCHAR(100) NOT NULL DEFAULT '无',
|
||||
`image_url` VARCHAR(255) NOT NULL DEFAULT '',
|
||||
`remark` TEXT,
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1:enabled 0:disabled',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_yarn_id` (`yarn_id`),
|
||||
UNIQUE KEY `uk_tenant_yarn_name_color_supplier` (`tenant_id`, `yarn_name`, `color`, `supplier_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_supplier_id` (`supplier_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='原料纱档案';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `inv_product_batch` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`batch_id` VARCHAR(32) NOT NULL COMMENT 'business key, UUID',
|
||||
`tenant_id` VARCHAR(32) NOT NULL DEFAULT '',
|
||||
`product_id` VARCHAR(32) NOT NULL,
|
||||
`batch_no` VARCHAR(64) NOT NULL DEFAULT '' COMMENT 'format: {product_code}-B001',
|
||||
`yarn_ratio` JSON DEFAULT NULL,
|
||||
`warp_weight_g_m` DECIMAL(10,4) NOT NULL DEFAULT 0.0000,
|
||||
`weft_weight_g_m` DECIMAL(10,4) NOT NULL DEFAULT 0.0000,
|
||||
`production_process` TEXT,
|
||||
`remark` TEXT,
|
||||
`status` TINYINT NOT NULL DEFAULT 1 COMMENT '1:enabled 0:disabled',
|
||||
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
`updated_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||
`deleted_at` DATETIME DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_batch_id` (`batch_id`),
|
||||
UNIQUE KEY `uk_tenant_batch_no` (`tenant_id`, `batch_no`),
|
||||
KEY `idx_product_id` (`product_id`),
|
||||
KEY `idx_tenant_id` (`tenant_id`),
|
||||
KEY `idx_status` (`status`),
|
||||
KEY `idx_deleted_at` (`deleted_at`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='产品批次表';
|
||||
|
||||
-- inv_product.color_no
|
||||
SET @c = (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='inv_product' AND COLUMN_NAME='color_no');
|
||||
SET @s = IF(@c=0, 'ALTER TABLE `inv_product` ADD COLUMN `color_no` VARCHAR(20) NOT NULL DEFAULT '''' COMMENT ''色号'' AFTER `color`', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- inv_product.product_code
|
||||
SET @c = (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='inv_product' AND COLUMN_NAME='product_code');
|
||||
SET @s = IF(@c=0, 'ALTER TABLE `inv_product` ADD COLUMN `product_code` VARCHAR(50) NOT NULL DEFAULT '''' COMMENT ''产品码,创建后冻结'' AFTER `color_no`', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- inv_product_pan.batch_id
|
||||
SET @c = (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='inv_product_pan' AND COLUMN_NAME='batch_id');
|
||||
SET @s = IF(@c=0, 'ALTER TABLE `inv_product_pan` ADD COLUMN `batch_id` VARCHAR(32) NOT NULL DEFAULT '''' COMMENT ''FK inv_product_batch.batch_id'' AFTER `product_id`', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @i = (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='inv_product_pan' AND INDEX_NAME='idx_batch_id');
|
||||
SET @s = IF(@i=0, 'ALTER TABLE `inv_product_pan` ADD KEY `idx_batch_id` (`batch_id`)', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- Align older pan/bolt installations with the current model.
|
||||
SET @c = (SELECT COUNT(*) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='inv_product_bolt' AND COLUMN_NAME='color');
|
||||
SET @s = IF(@c=0, 'ALTER TABLE `inv_product_bolt` ADD COLUMN `color` VARCHAR(50) NOT NULL DEFAULT '''' AFTER `length_m`', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- Backfill product defaults.
|
||||
UPDATE `inv_product`
|
||||
SET `color` = '本色'
|
||||
WHERE `deleted_at` IS NULL AND TRIM(COALESCE(`color`, '')) = '';
|
||||
|
||||
UPDATE `inv_product` p
|
||||
JOIN (
|
||||
SELECT id, LPAD(ROW_NUMBER() OVER (PARTITION BY tenant_id, product_name ORDER BY created_at, id), 2, '0') AS generated_color_no
|
||||
FROM `inv_product`
|
||||
WHERE `deleted_at` IS NULL
|
||||
) x ON x.id = p.id
|
||||
SET p.`color_no` = x.generated_color_no
|
||||
WHERE TRIM(COALESCE(p.`color_no`, '')) = '';
|
||||
|
||||
-- Historical SQL cannot reliably derive pinyin initials. For migrated rows with
|
||||
-- non-latin product names, use a stable P{id} prefix; new writes use backend pinyin.
|
||||
UPDATE `inv_product` p
|
||||
JOIN (
|
||||
SELECT id,
|
||||
UPPER(CONCAT(
|
||||
CASE
|
||||
WHEN REGEXP_REPLACE(product_name, '[^0-9A-Za-z]', '') = '' THEN CONCAT('P', id)
|
||||
ELSE REGEXP_REPLACE(product_name, '[^0-9A-Za-z]', '')
|
||||
END,
|
||||
color_no
|
||||
)) AS raw_code
|
||||
FROM `inv_product`
|
||||
WHERE `deleted_at` IS NULL
|
||||
) b ON b.id = p.id
|
||||
SET p.`product_code` = LEFT(b.raw_code, 50)
|
||||
WHERE TRIM(COALESCE(p.`product_code`, '')) = '';
|
||||
|
||||
UPDATE `inv_product` p
|
||||
JOIN (
|
||||
SELECT id,
|
||||
CASE
|
||||
WHEN rn = 1 THEN raw_code
|
||||
ELSE LEFT(CONCAT(raw_code, '-', id), 50)
|
||||
END AS unique_code
|
||||
FROM (
|
||||
SELECT id, tenant_id, product_code AS raw_code,
|
||||
ROW_NUMBER() OVER (PARTITION BY tenant_id, product_code ORDER BY id) AS rn
|
||||
FROM `inv_product`
|
||||
WHERE `deleted_at` IS NULL
|
||||
) d
|
||||
) u ON u.id = p.id
|
||||
SET p.`product_code` = u.unique_code;
|
||||
|
||||
-- Default B001 batch for every historical product.
|
||||
INSERT INTO `inv_product_batch` (
|
||||
`batch_id`, `tenant_id`, `product_id`, `batch_no`,
|
||||
`yarn_ratio`, `warp_weight_g_m`, `weft_weight_g_m`,
|
||||
`production_process`, `remark`, `status`
|
||||
)
|
||||
SELECT
|
||||
MD5(CONCAT('B001:', p.`tenant_id`, ':', p.`product_id`)),
|
||||
p.`tenant_id`,
|
||||
p.`product_id`,
|
||||
CONCAT(p.`product_code`, '-B001'),
|
||||
NULL,
|
||||
0.0000,
|
||||
0.0000,
|
||||
'无',
|
||||
NULL,
|
||||
1
|
||||
FROM `inv_product` p
|
||||
WHERE p.`deleted_at` IS NULL
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM `inv_product_batch` b
|
||||
WHERE b.`tenant_id` = p.`tenant_id`
|
||||
AND b.`product_id` = p.`product_id`
|
||||
AND b.`batch_no` = CONCAT(p.`product_code`, '-B001')
|
||||
);
|
||||
|
||||
UPDATE `inv_product_pan` pan
|
||||
JOIN `inv_product` p ON p.`product_id` = pan.`product_id`
|
||||
JOIN `inv_product_batch` b ON b.`product_id` = p.`product_id`
|
||||
AND b.`tenant_id` = p.`tenant_id`
|
||||
AND b.`batch_no` = CONCAT(p.`product_code`, '-B001')
|
||||
SET pan.`batch_id` = b.`batch_id`
|
||||
WHERE TRIM(COALESCE(pan.`batch_id`, '')) = '';
|
||||
|
||||
-- Replace the old natural key after product_code has been populated.
|
||||
SET @i = (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='inv_product' AND INDEX_NAME='uk_product_name_spec_color');
|
||||
SET @s = IF(@i>0, 'ALTER TABLE `inv_product` DROP INDEX `uk_product_name_spec_color`', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
SET @i = (SELECT COUNT(*) FROM information_schema.STATISTICS WHERE TABLE_SCHEMA=@db AND TABLE_NAME='inv_product' AND INDEX_NAME='uk_tenant_product_code');
|
||||
SET @s = IF(@i=0, 'ALTER TABLE `inv_product` ADD UNIQUE KEY `uk_tenant_product_code` (`tenant_id`, `product_code`)', 'SELECT 1');
|
||||
PREPARE stmt FROM @s; EXECUTE stmt; DEALLOCATE PREPARE stmt;
|
||||
|
||||
-- Verification queries for release runbooks:
|
||||
-- SELECT COUNT(*) AS empty_product_code FROM inv_product WHERE deleted_at IS NULL AND product_code = '';
|
||||
-- SELECT tenant_id, product_code, COUNT(*) FROM inv_product WHERE deleted_at IS NULL GROUP BY tenant_id, product_code HAVING COUNT(*) > 1;
|
||||
-- SELECT p.product_id FROM inv_product p LEFT JOIN inv_product_batch b ON b.product_id = p.product_id AND b.batch_no = CONCAT(p.product_code, '-B001') WHERE p.deleted_at IS NULL AND b.batch_id IS NULL;
|
||||
-- SELECT pan.pan_id FROM inv_product_pan pan WHERE pan.batch_id = '';
|
||||
@ -224,6 +224,8 @@ type (
|
||||
ImageUrl string `json:"imageUrl"`
|
||||
Spec string `json:"spec"`
|
||||
Color string `json:"color"`
|
||||
ColorNo string `json:"colorNo"`
|
||||
ProductCode string `json:"productCode"`
|
||||
SalesPrice string `json:"salesPrice"`
|
||||
Remark string `json:"remark"`
|
||||
Status int64 `json:"status"`
|
||||
@ -235,15 +237,19 @@ type (
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Spec string `json:"spec,optional"`
|
||||
Color string `json:"color,optional"`
|
||||
ColorNo string `json:"colorNo,optional"`
|
||||
ProductCode string `json:"productCode,optional"`
|
||||
SalesPrice string `json:"salesPrice,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
Pans []PanInput `json:"pans,optional"`
|
||||
InitialBatch ProductBatchInput `json:"initialBatch,optional"`
|
||||
}
|
||||
UpdateProductReq {
|
||||
ProductName string `json:"productName,optional"`
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Spec string `json:"spec,optional"`
|
||||
Color string `json:"color,optional"`
|
||||
ColorNo string `json:"colorNo,optional"`
|
||||
SalesPrice string `json:"salesPrice,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
}
|
||||
@ -253,6 +259,8 @@ type (
|
||||
ProductName string `form:"productName,optional"`
|
||||
Spec string `form:"spec,optional"`
|
||||
Color string `form:"color,optional"`
|
||||
ColorNo string `form:"colorNo,optional"`
|
||||
ProductCode string `form:"productCode,optional"`
|
||||
Status int64 `form:"status,optional,default=-1"`
|
||||
}
|
||||
ListProductResp {
|
||||
@ -279,6 +287,8 @@ type (
|
||||
PanInfo {
|
||||
PanId string `json:"panId"`
|
||||
ProductId string `json:"productId"`
|
||||
BatchId string `json:"batchId"`
|
||||
BatchNo string `json:"batchNo"`
|
||||
Name string `json:"name"`
|
||||
Position string `json:"position"`
|
||||
SortOrder int64 `json:"sortOrder"`
|
||||
@ -290,6 +300,7 @@ type (
|
||||
Name string `json:"name"`
|
||||
Position string `json:"position,optional"`
|
||||
BoltLengths []string `json:"boltLengths,optional"`
|
||||
BatchId string `json:"batchId,optional"`
|
||||
}
|
||||
ListPanResp {
|
||||
List []PanInfo `json:"list"`
|
||||
@ -331,6 +342,8 @@ type (
|
||||
ProductId string `json:"productId"`
|
||||
ProductName string `json:"productName"`
|
||||
Color string `json:"color"`
|
||||
ColorNo string `json:"colorNo"`
|
||||
ProductCode string `json:"productCode"`
|
||||
Spec string `json:"spec"`
|
||||
ImageUrl string `json:"imageUrl"`
|
||||
PanCount int64 `json:"panCount"`
|
||||
@ -338,6 +351,7 @@ type (
|
||||
TotalLengthM string `json:"totalLengthM"`
|
||||
Locations string `json:"locations"`
|
||||
SalesPrice string `json:"salesPrice"`
|
||||
BatchCount int64 `json:"batchCount"`
|
||||
}
|
||||
ListColorDetailReq {
|
||||
ProductName string `form:"productName"`
|
||||
@ -347,6 +361,85 @@ type (
|
||||
}
|
||||
)
|
||||
|
||||
// ==================== Yarn & Batch Types ====================
|
||||
type (
|
||||
YarnInfo {
|
||||
YarnId string `json:"yarnId"`
|
||||
YarnName string `json:"yarnName"`
|
||||
Color string `json:"color"`
|
||||
WeightGM string `json:"weightGM"`
|
||||
SupplierId string `json:"supplierId"`
|
||||
SupplierName string `json:"supplierName"`
|
||||
DyeFactory string `json:"dyeFactory"`
|
||||
ImageUrl string `json:"imageUrl"`
|
||||
Remark string `json:"remark"`
|
||||
Status int64 `json:"status"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
CreateYarnReq {
|
||||
YarnName string `json:"yarnName"`
|
||||
Color string `json:"color,optional"`
|
||||
WeightGM string `json:"weightGM"`
|
||||
SupplierId string `json:"supplierId"`
|
||||
DyeFactory string `json:"dyeFactory,optional"`
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
}
|
||||
UpdateYarnReq {
|
||||
YarnName string `json:"yarnName,optional"`
|
||||
Color string `json:"color,optional"`
|
||||
WeightGM string `json:"weightGM,optional"`
|
||||
SupplierId string `json:"supplierId,optional"`
|
||||
DyeFactory string `json:"dyeFactory,optional"`
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
Status int64 `json:"status,optional,default=1"`
|
||||
}
|
||||
ListYarnReq {
|
||||
Page int64 `form:"page,default=1"`
|
||||
PageSize int64 `form:"pageSize,default=20"`
|
||||
YarnName string `form:"yarnName,optional"`
|
||||
SupplierId string `form:"supplierId,optional"`
|
||||
Status int64 `form:"status,optional,default=-1"`
|
||||
}
|
||||
ListYarnResp {
|
||||
Total int64 `json:"total"`
|
||||
List []YarnInfo `json:"list"`
|
||||
}
|
||||
ProductBatchInput {
|
||||
BatchNo string `json:"batchNo,optional"`
|
||||
YarnRatio string `json:"yarnRatio,optional"`
|
||||
WarpWeightGM string `json:"warpWeightGM,optional"`
|
||||
WeftWeightGM string `json:"weftWeightGM,optional"`
|
||||
ProductionProcess string `json:"productionProcess,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
}
|
||||
ProductBatchInfo {
|
||||
BatchId string `json:"batchId"`
|
||||
ProductId string `json:"productId"`
|
||||
BatchNo string `json:"batchNo"`
|
||||
YarnRatio string `json:"yarnRatio"`
|
||||
WarpWeightGM string `json:"warpWeightGM"`
|
||||
WeftWeightGM string `json:"weftWeightGM"`
|
||||
ProductionProcess string `json:"productionProcess"`
|
||||
Remark string `json:"remark"`
|
||||
Status int64 `json:"status"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
CreateProductBatchReq {
|
||||
Batch ProductBatchInput `json:"batch"`
|
||||
}
|
||||
UpdateProductBatchReq {
|
||||
Batch ProductBatchInput `json:"batch"`
|
||||
Status int64 `json:"status,optional,default=1"`
|
||||
}
|
||||
ListProductBatchResp {
|
||||
List []ProductBatchInfo `json:"list"`
|
||||
}
|
||||
)
|
||||
|
||||
// ==================== Stock Statistics Types ====================
|
||||
type (
|
||||
StockSummaryResp {
|
||||
@ -725,6 +818,51 @@ service gateway-api {
|
||||
put /products/:id/pans (SavePansReq) returns (IdResp)
|
||||
}
|
||||
|
||||
// Inventory - Yarn Management
|
||||
@server (
|
||||
prefix: /api/v1/inventory
|
||||
group: inventory/yarn
|
||||
jwt: Auth
|
||||
middleware: AuthorityMiddleware
|
||||
)
|
||||
service gateway-api {
|
||||
@handler ListYarnHandler
|
||||
get /yarns (ListYarnReq) returns (ListYarnResp)
|
||||
|
||||
@handler GetYarnHandler
|
||||
get /yarns/:id returns (YarnInfo)
|
||||
|
||||
@handler CreateYarnHandler
|
||||
post /yarns (CreateYarnReq) returns (IdResp)
|
||||
|
||||
@handler UpdateYarnHandler
|
||||
put /yarns/:id (UpdateYarnReq) returns (IdResp)
|
||||
|
||||
@handler DeleteYarnHandler
|
||||
delete /yarns/:id returns (IdResp)
|
||||
}
|
||||
|
||||
// Inventory - Product Batch Management
|
||||
@server (
|
||||
prefix: /api/v1/inventory
|
||||
group: inventory/productbatch
|
||||
jwt: Auth
|
||||
middleware: AuthorityMiddleware
|
||||
)
|
||||
service gateway-api {
|
||||
@handler ListProductBatchHandler
|
||||
get /products/:id/batches returns (ListProductBatchResp)
|
||||
|
||||
@handler CreateProductBatchHandler
|
||||
post /products/:id/batches (CreateProductBatchReq) returns (IdResp)
|
||||
|
||||
@handler UpdateProductBatchHandler
|
||||
put /products/:id/batches/:batchId (UpdateProductBatchReq) returns (IdResp)
|
||||
|
||||
@handler DeleteProductBatchHandler
|
||||
delete /products/:id/batches/:batchId returns (IdResp)
|
||||
}
|
||||
|
||||
// Inventory - Pan/Bolt Management
|
||||
@server (
|
||||
prefix: /api/v1/inventory
|
||||
@ -805,4 +943,3 @@ service gateway-api {
|
||||
@handler ApproveStockAdjustHandler
|
||||
post /adjusts/:id/approve (ApproveStockAdjustReq) returns (IdResp)
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ package product
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/product"
|
||||
@ -14,11 +15,14 @@ import (
|
||||
func ExportProductHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
l := product.NewExportProductLogic(r.Context(), svcCtx)
|
||||
err := l.ExportProduct()
|
||||
data, filename, err := l.ExportProduct()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.Ok(w)
|
||||
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape(filename))
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,32 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/productbatch"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
)
|
||||
|
||||
func CreateProductBatchHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateProductBatchReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), "pathId", pathvar.Vars(r)["id"])
|
||||
l := productbatch.NewCreateProductBatchLogic(ctx, svcCtx)
|
||||
resp, err := l.CreateProductBatch(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/productbatch"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
)
|
||||
|
||||
func DeleteProductBatchHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
vars := pathvar.Vars(r)
|
||||
ctx := context.WithValue(r.Context(), "pathId", vars["id"])
|
||||
ctx = context.WithValue(ctx, "batchId", vars["batchId"])
|
||||
l := productbatch.NewDeleteProductBatchLogic(ctx, svcCtx)
|
||||
resp, err := l.DeleteProductBatch()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/productbatch"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
)
|
||||
|
||||
func ListProductBatchHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(r.Context(), "pathId", pathvar.Vars(r)["id"])
|
||||
l := productbatch.NewListProductBatchLogic(ctx, svcCtx)
|
||||
resp, err := l.ListProductBatch()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/productbatch"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
)
|
||||
|
||||
func UpdateProductBatchHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateProductBatchReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
vars := pathvar.Vars(r)
|
||||
ctx := context.WithValue(r.Context(), "pathId", vars["id"])
|
||||
ctx = context.WithValue(ctx, "batchId", vars["batchId"])
|
||||
l := productbatch.NewUpdateProductBatchLogic(ctx, svcCtx)
|
||||
resp, err := l.UpdateProductBatch(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
29
gateway/internal/handler/inventory/yarn/createYarnHandler.go
Normal file
29
gateway/internal/handler/inventory/yarn/createYarnHandler.go
Normal file
@ -0,0 +1,29 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/yarn"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
)
|
||||
|
||||
func CreateYarnHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.CreateYarnReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
l := yarn.NewCreateYarnLogic(r.Context(), svcCtx)
|
||||
resp, err := l.CreateYarn(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
26
gateway/internal/handler/inventory/yarn/deleteYarnHandler.go
Normal file
26
gateway/internal/handler/inventory/yarn/deleteYarnHandler.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/yarn"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
)
|
||||
|
||||
func DeleteYarnHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(r.Context(), "pathId", pathvar.Vars(r)["id"])
|
||||
l := yarn.NewDeleteYarnLogic(ctx, svcCtx)
|
||||
resp, err := l.DeleteYarn()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
26
gateway/internal/handler/inventory/yarn/getYarnHandler.go
Normal file
26
gateway/internal/handler/inventory/yarn/getYarnHandler.go
Normal file
@ -0,0 +1,26 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/yarn"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
)
|
||||
|
||||
func GetYarnHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := context.WithValue(r.Context(), "pathId", pathvar.Vars(r)["id"])
|
||||
l := yarn.NewGetYarnLogic(ctx, svcCtx)
|
||||
resp, err := l.GetYarn()
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
29
gateway/internal/handler/inventory/yarn/listYarnHandler.go
Normal file
29
gateway/internal/handler/inventory/yarn/listYarnHandler.go
Normal file
@ -0,0 +1,29 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/yarn"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
)
|
||||
|
||||
func ListYarnHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.ListYarnReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
l := yarn.NewListYarnLogic(r.Context(), svcCtx)
|
||||
resp, err := l.ListYarn(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
32
gateway/internal/handler/inventory/yarn/updateYarnHandler.go
Normal file
32
gateway/internal/handler/inventory/yarn/updateYarnHandler.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Code scaffolded by goctl. Safe to edit.
|
||||
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/zeromicro/go-zero/rest/httpx"
|
||||
"github.com/zeromicro/go-zero/rest/pathvar"
|
||||
"muyu-apiserver/gateway/internal/logic/inventory/yarn"
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
)
|
||||
|
||||
func UpdateYarnHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
var req types.UpdateYarnReq
|
||||
if err := httpx.Parse(r, &req); err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
return
|
||||
}
|
||||
ctx := context.WithValue(r.Context(), "pathId", pathvar.Vars(r)["id"])
|
||||
l := yarn.NewUpdateYarnLogic(ctx, svcCtx)
|
||||
resp, err := l.UpdateYarn(&req)
|
||||
if err != nil {
|
||||
httpx.ErrorCtx(r.Context(), w, err)
|
||||
} else {
|
||||
httpx.OkJsonCtx(r.Context(), w, resp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,9 @@ import (
|
||||
inventorylog "muyu-apiserver/gateway/internal/handler/inventory/log"
|
||||
inventorypanbolt "muyu-apiserver/gateway/internal/handler/inventory/panbolt"
|
||||
inventoryproduct "muyu-apiserver/gateway/internal/handler/inventory/product"
|
||||
inventoryproductbatch "muyu-apiserver/gateway/internal/handler/inventory/productbatch"
|
||||
inventorystock "muyu-apiserver/gateway/internal/handler/inventory/stock"
|
||||
inventoryyarn "muyu-apiserver/gateway/internal/handler/inventory/yarn"
|
||||
productioninbound "muyu-apiserver/gateway/internal/handler/production/inbound"
|
||||
productionplan "muyu-apiserver/gateway/internal/handler/production/plan"
|
||||
productionprocess "muyu-apiserver/gateway/internal/handler/production/process"
|
||||
@ -148,6 +150,71 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
||||
rest.WithPrefix("/api/v1/inventory"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.AuthorityMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/yarns",
|
||||
Handler: inventoryyarn.ListYarnHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/yarns/:id",
|
||||
Handler: inventoryyarn.GetYarnHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/yarns",
|
||||
Handler: inventoryyarn.CreateYarnHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPut,
|
||||
Path: "/yarns/:id",
|
||||
Handler: inventoryyarn.UpdateYarnHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/yarns/:id",
|
||||
Handler: inventoryyarn.DeleteYarnHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/inventory"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.AuthorityMiddleware},
|
||||
[]rest.Route{
|
||||
{
|
||||
Method: http.MethodGet,
|
||||
Path: "/products/:id/batches",
|
||||
Handler: inventoryproductbatch.ListProductBatchHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPost,
|
||||
Path: "/products/:id/batches",
|
||||
Handler: inventoryproductbatch.CreateProductBatchHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodPut,
|
||||
Path: "/products/:id/batches/:batchId",
|
||||
Handler: inventoryproductbatch.UpdateProductBatchHandler(serverCtx),
|
||||
},
|
||||
{
|
||||
Method: http.MethodDelete,
|
||||
Path: "/products/:id/batches/:batchId",
|
||||
Handler: inventoryproductbatch.DeleteProductBatchHandler(serverCtx),
|
||||
},
|
||||
}...,
|
||||
),
|
||||
rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
|
||||
rest.WithPrefix("/api/v1/inventory"),
|
||||
)
|
||||
|
||||
server.AddRoutes(
|
||||
rest.WithMiddlewares(
|
||||
[]rest.Middleware{serverCtx.AuthorityMiddleware},
|
||||
|
||||
@ -40,6 +40,8 @@ func (l *ListPanViewLogic) ListPanView(req *types.ListPanViewReq) (resp *types.L
|
||||
list = append(list, types.PanListItem{
|
||||
PanId: item.PanId,
|
||||
ProductId: item.ProductId,
|
||||
BatchId: item.BatchId,
|
||||
BatchNo: item.BatchNo,
|
||||
Name: item.Name,
|
||||
Position: item.Position,
|
||||
ProductName: item.ProductName,
|
||||
|
||||
@ -30,9 +30,12 @@ func (l *CreateProductLogic) CreateProduct(req *types.CreateProductReq) (resp *t
|
||||
ImageUrl: req.ImageUrl,
|
||||
Spec: req.Spec,
|
||||
Color: req.Color,
|
||||
ColorNo: req.ColorNo,
|
||||
ProductCode: req.ProductCode,
|
||||
SalesPrice: req.SalesPrice,
|
||||
Remark: req.Remark,
|
||||
Pans: panInputsToPb(req.Pans),
|
||||
InitialBatch: productBatchInputToPb(req.InitialBatch),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -51,7 +54,22 @@ func panInputsToPb(pans []types.PanInput) []*pb.PanInput {
|
||||
Name: p.Name,
|
||||
Position: p.Position,
|
||||
BoltLengths: p.BoltLengths,
|
||||
BatchId: p.BatchId,
|
||||
})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func productBatchInputToPb(input types.ProductBatchInput) *pb.ProductBatchInput {
|
||||
if input == (types.ProductBatchInput{}) {
|
||||
return nil
|
||||
}
|
||||
return &pb.ProductBatchInput{
|
||||
BatchNo: input.BatchNo,
|
||||
YarnRatio: input.YarnRatio,
|
||||
WarpWeightGM: input.WarpWeightGM,
|
||||
WeftWeightGM: input.WeftWeightGM,
|
||||
ProductionProcess: input.ProductionProcess,
|
||||
Remark: input.Remark,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,10 +2,13 @@ package product
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/xuri/excelize/v2"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
@ -23,6 +26,99 @@ func NewExportProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Exp
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ExportProductLogic) ExportProduct() error {
|
||||
return errors.New("export product requires response writer, will be implemented in handler layer")
|
||||
func (l *ExportProductLogic) ExportProduct() ([]byte, string, error) {
|
||||
products, err := l.listAllProducts()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
f := excelize.NewFile()
|
||||
defer f.Close()
|
||||
|
||||
sheet := "产品"
|
||||
if err := f.SetSheetName("Sheet1", sheet); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
headers := []interface{}{"产品名称", "规格型号", "颜色", "色号", "产品码", "销售价", "批次号", "纱线配比", "经线克重(g/m)", "纬线克重(g/m)", "生产工艺", "备注"}
|
||||
if err := f.SetSheetRow(sheet, "A1", &headers); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
row := 2
|
||||
for _, product := range products {
|
||||
batchResp, err := l.svcCtx.InventoryRpc.ListProductBatch(l.ctx, &pb.ListProductBatchReq{ProductId: product.ProductId})
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if len(batchResp.List) == 0 {
|
||||
if err := setProductExportRow(f, sheet, row, product, nil); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
row++
|
||||
continue
|
||||
}
|
||||
for _, batch := range batchResp.List {
|
||||
if err := setProductExportRow(f, sheet, row, product, batch); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
row++
|
||||
}
|
||||
}
|
||||
|
||||
if err := f.SetColWidth(sheet, "A", "L", 18); err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
buf, err := f.WriteToBuffer()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
filename := fmt.Sprintf("products-%s.xlsx", time.Now().Format("20060102150405"))
|
||||
return buf.Bytes(), filename, nil
|
||||
}
|
||||
|
||||
func (l *ExportProductLogic) listAllProducts() ([]*pb.ProductInfo, error) {
|
||||
const pageSize int64 = 500
|
||||
var all []*pb.ProductInfo
|
||||
for page := int64(1); ; page++ {
|
||||
resp, err := l.svcCtx.InventoryRpc.ListProduct(l.ctx, &pb.ListProductReq{Page: page, PageSize: pageSize, Status: -1})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
all = append(all, resp.List...)
|
||||
if int64(len(all)) >= resp.Total || len(resp.List) == 0 {
|
||||
return all, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setProductExportRow(f *excelize.File, sheet string, row int, product *pb.ProductInfo, batch *pb.ProductBatchInfo) error {
|
||||
values := []interface{}{
|
||||
product.ProductName,
|
||||
product.Spec,
|
||||
product.Color,
|
||||
product.ColorNo,
|
||||
product.ProductCode,
|
||||
product.SalesPrice,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
product.Remark,
|
||||
}
|
||||
if batch != nil {
|
||||
values[6] = batch.BatchNo
|
||||
values[7] = batch.YarnRatio
|
||||
values[8] = batch.WarpWeightGM
|
||||
values[9] = batch.WeftWeightGM
|
||||
values[10] = batch.ProductionProcess
|
||||
if batch.Remark != "" {
|
||||
values[11] = batch.Remark
|
||||
}
|
||||
}
|
||||
cell, err := excelize.CoordinatesToCellName(1, row)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return f.SetSheetRow(sheet, cell, &values)
|
||||
}
|
||||
|
||||
@ -37,6 +37,8 @@ func (l *GetColorDetailLogic) GetColorDetail(req *types.ListColorDetailReq) (res
|
||||
ProductId: it.ProductId,
|
||||
ProductName: it.ProductName,
|
||||
Color: it.Color,
|
||||
ColorNo: it.ColorNo,
|
||||
ProductCode: it.ProductCode,
|
||||
Spec: it.Spec,
|
||||
ImageUrl: it.ImageUrl,
|
||||
PanCount: it.PanCount,
|
||||
@ -44,6 +46,7 @@ func (l *GetColorDetailLogic) GetColorDetail(req *types.ListColorDetailReq) (res
|
||||
TotalLengthM: it.TotalLengthM,
|
||||
Locations: it.Locations,
|
||||
SalesPrice: it.SalesPrice,
|
||||
BatchCount: it.BatchCount,
|
||||
})
|
||||
}
|
||||
return &types.ColorDetailResp{List: list}, nil
|
||||
|
||||
@ -42,8 +42,17 @@ func (l *ImportProductLogic) ImportProduct(filePath, fileName string) (resp *typ
|
||||
ProductName: p.ProductName,
|
||||
Spec: p.Spec,
|
||||
Color: p.Color,
|
||||
ColorNo: p.ColorNo,
|
||||
ProductCode: p.ProductCode,
|
||||
SalesPrice: p.SalesPrice,
|
||||
Remark: p.Remark,
|
||||
InitialBatch: &pb.ProductBatchInput{
|
||||
BatchNo: p.BatchNo,
|
||||
YarnRatio: p.YarnRatio,
|
||||
WarpWeightGM: p.WarpWeightGM,
|
||||
WeftWeightGM: p.WeftWeightGM,
|
||||
ProductionProcess: p.ProductionProcess,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -57,6 +57,8 @@ func panInfoFromPB(p *pb.PanInfo) types.PanInfo {
|
||||
return types.PanInfo{
|
||||
PanId: p.PanId,
|
||||
ProductId: p.ProductId,
|
||||
BatchId: p.BatchId,
|
||||
BatchNo: p.BatchNo,
|
||||
Name: p.Name,
|
||||
Position: p.Position,
|
||||
SortOrder: p.SortOrder,
|
||||
@ -76,6 +78,8 @@ func productInfoFromPB(p *pb.ProductInfo) types.ProductInfo {
|
||||
ImageUrl: p.ImageUrl,
|
||||
Spec: p.Spec,
|
||||
Color: p.Color,
|
||||
ColorNo: p.ColorNo,
|
||||
ProductCode: p.ProductCode,
|
||||
SalesPrice: p.SalesPrice,
|
||||
Remark: p.Remark,
|
||||
Status: p.Status,
|
||||
|
||||
@ -31,6 +31,8 @@ func (l *ListProductLogic) ListProduct(req *types.ListProductReq) (resp *types.L
|
||||
ProductName: req.ProductName,
|
||||
Spec: req.Spec,
|
||||
Color: req.Color,
|
||||
ColorNo: req.ColorNo,
|
||||
ProductCode: req.ProductCode,
|
||||
Status: req.Status,
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@ -34,6 +34,7 @@ func (l *UpdateProductLogic) UpdateProduct(req *types.UpdateProductReq) (resp *t
|
||||
ImageUrl: req.ImageUrl,
|
||||
Spec: req.Spec,
|
||||
Color: req.Color,
|
||||
ColorNo: req.ColorNo,
|
||||
SalesPrice: req.SalesPrice,
|
||||
Remark: req.Remark,
|
||||
})
|
||||
|
||||
@ -0,0 +1,34 @@
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/pkg/ctxdata"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type CreateProductBatchLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewCreateProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateProductBatchLogic {
|
||||
return &CreateProductBatchLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *CreateProductBatchLogic) CreateProductBatch(req *types.CreateProductBatchReq) (*types.IdResp, error) {
|
||||
productId := ctxdata.GetPathId(l.ctx)
|
||||
resp, err := l.svcCtx.InventoryRpc.CreateProductBatch(l.ctx, &pb.CreateProductBatchReq{
|
||||
ProductId: productId,
|
||||
Batch: batchInputToPB(req.Batch),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.IdResp{Id: resp.Id}, nil
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/pkg/ctxdata"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type DeleteProductBatchLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewDeleteProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteProductBatchLogic {
|
||||
return &DeleteProductBatchLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *DeleteProductBatchLogic) DeleteProductBatch() (*types.IdResp, error) {
|
||||
productId := ctxdata.GetPathId(l.ctx)
|
||||
batchId, _ := l.ctx.Value("batchId").(string)
|
||||
if _, err := l.svcCtx.InventoryRpc.DeleteProductBatch(l.ctx, &pb.DeleteProductBatchReq{ProductId: productId, BatchId: batchId}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.IdResp{Id: batchId}, nil
|
||||
}
|
||||
36
gateway/internal/logic/inventory/productbatch/helpers.go
Normal file
36
gateway/internal/logic/inventory/productbatch/helpers.go
Normal file
@ -0,0 +1,36 @@
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
)
|
||||
|
||||
func batchInputToPB(input types.ProductBatchInput) *pb.ProductBatchInput {
|
||||
return &pb.ProductBatchInput{
|
||||
BatchNo: input.BatchNo,
|
||||
YarnRatio: input.YarnRatio,
|
||||
WarpWeightGM: input.WarpWeightGM,
|
||||
WeftWeightGM: input.WeftWeightGM,
|
||||
ProductionProcess: input.ProductionProcess,
|
||||
Remark: input.Remark,
|
||||
}
|
||||
}
|
||||
|
||||
func batchInfoFromPB(b *pb.ProductBatchInfo) types.ProductBatchInfo {
|
||||
if b == nil {
|
||||
return types.ProductBatchInfo{}
|
||||
}
|
||||
return types.ProductBatchInfo{
|
||||
BatchId: b.BatchId,
|
||||
ProductId: b.ProductId,
|
||||
BatchNo: b.BatchNo,
|
||||
YarnRatio: b.YarnRatio,
|
||||
WarpWeightGM: b.WarpWeightGM,
|
||||
WeftWeightGM: b.WeftWeightGM,
|
||||
ProductionProcess: b.ProductionProcess,
|
||||
Remark: b.Remark,
|
||||
Status: b.Status,
|
||||
CreatedAt: b.CreatedAt,
|
||||
UpdatedAt: b.UpdatedAt,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/pkg/ctxdata"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ListProductBatchLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewListProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListProductBatchLogic {
|
||||
return &ListProductBatchLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *ListProductBatchLogic) ListProductBatch() (*types.ListProductBatchResp, error) {
|
||||
productId := ctxdata.GetPathId(l.ctx)
|
||||
resp, err := l.svcCtx.InventoryRpc.ListProductBatch(l.ctx, &pb.ListProductBatchReq{ProductId: productId})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := make([]types.ProductBatchInfo, 0, len(resp.List))
|
||||
for _, item := range resp.List {
|
||||
list = append(list, batchInfoFromPB(item))
|
||||
}
|
||||
return &types.ListProductBatchResp{List: list}, nil
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
package productbatch
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/pkg/ctxdata"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UpdateProductBatchLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUpdateProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateProductBatchLogic {
|
||||
return &UpdateProductBatchLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *UpdateProductBatchLogic) UpdateProductBatch(req *types.UpdateProductBatchReq) (*types.IdResp, error) {
|
||||
productId := ctxdata.GetPathId(l.ctx)
|
||||
batchId, _ := l.ctx.Value("batchId").(string)
|
||||
_, err := l.svcCtx.InventoryRpc.UpdateProductBatch(l.ctx, &pb.UpdateProductBatchReq{
|
||||
ProductId: productId,
|
||||
BatchId: batchId,
|
||||
Batch: batchInputToPB(req.Batch),
|
||||
Status: req.Status,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.IdResp{Id: batchId}, nil
|
||||
}
|
||||
37
gateway/internal/logic/inventory/yarn/createYarnLogic.go
Normal file
37
gateway/internal/logic/inventory/yarn/createYarnLogic.go
Normal file
@ -0,0 +1,37 @@
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type CreateYarnLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewCreateYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateYarnLogic {
|
||||
return &CreateYarnLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *CreateYarnLogic) CreateYarn(req *types.CreateYarnReq) (*types.IdResp, error) {
|
||||
resp, err := l.svcCtx.InventoryRpc.CreateYarn(l.ctx, &pb.CreateYarnReq{
|
||||
YarnName: req.YarnName,
|
||||
Color: req.Color,
|
||||
WeightGM: req.WeightGM,
|
||||
SupplierId: req.SupplierId,
|
||||
DyeFactory: req.DyeFactory,
|
||||
ImageUrl: req.ImageUrl,
|
||||
Remark: req.Remark,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.IdResp{Id: resp.Id}, nil
|
||||
}
|
||||
30
gateway/internal/logic/inventory/yarn/deleteYarnLogic.go
Normal file
30
gateway/internal/logic/inventory/yarn/deleteYarnLogic.go
Normal file
@ -0,0 +1,30 @@
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/pkg/ctxdata"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type DeleteYarnLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewDeleteYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteYarnLogic {
|
||||
return &DeleteYarnLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *DeleteYarnLogic) DeleteYarn() (*types.IdResp, error) {
|
||||
id := ctxdata.GetPathId(l.ctx)
|
||||
if _, err := l.svcCtx.InventoryRpc.DeleteYarn(l.ctx, &pb.DeleteYarnReq{YarnId: id}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.IdResp{Id: id}, nil
|
||||
}
|
||||
32
gateway/internal/logic/inventory/yarn/getYarnLogic.go
Normal file
32
gateway/internal/logic/inventory/yarn/getYarnLogic.go
Normal file
@ -0,0 +1,32 @@
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/pkg/ctxdata"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type GetYarnLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewGetYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetYarnLogic {
|
||||
return &GetYarnLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *GetYarnLogic) GetYarn() (*types.YarnInfo, error) {
|
||||
id := ctxdata.GetPathId(l.ctx)
|
||||
resp, err := l.svcCtx.InventoryRpc.GetYarn(l.ctx, &pb.GetYarnReq{YarnId: id})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := yarnInfoFromPB(resp)
|
||||
return &out, nil
|
||||
}
|
||||
26
gateway/internal/logic/inventory/yarn/helpers.go
Normal file
26
gateway/internal/logic/inventory/yarn/helpers.go
Normal file
@ -0,0 +1,26 @@
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
)
|
||||
|
||||
func yarnInfoFromPB(y *pb.YarnInfo) types.YarnInfo {
|
||||
if y == nil {
|
||||
return types.YarnInfo{}
|
||||
}
|
||||
return types.YarnInfo{
|
||||
YarnId: y.YarnId,
|
||||
YarnName: y.YarnName,
|
||||
Color: y.Color,
|
||||
WeightGM: y.WeightGM,
|
||||
SupplierId: y.SupplierId,
|
||||
SupplierName: y.SupplierName,
|
||||
DyeFactory: y.DyeFactory,
|
||||
ImageUrl: y.ImageUrl,
|
||||
Remark: y.Remark,
|
||||
Status: y.Status,
|
||||
CreatedAt: y.CreatedAt,
|
||||
UpdatedAt: y.UpdatedAt,
|
||||
}
|
||||
}
|
||||
39
gateway/internal/logic/inventory/yarn/listYarnLogic.go
Normal file
39
gateway/internal/logic/inventory/yarn/listYarnLogic.go
Normal file
@ -0,0 +1,39 @@
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ListYarnLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewListYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListYarnLogic {
|
||||
return &ListYarnLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *ListYarnLogic) ListYarn(req *types.ListYarnReq) (*types.ListYarnResp, error) {
|
||||
resp, err := l.svcCtx.InventoryRpc.ListYarn(l.ctx, &pb.ListYarnReq{
|
||||
Page: req.Page,
|
||||
PageSize: req.PageSize,
|
||||
YarnName: req.YarnName,
|
||||
SupplierId: req.SupplierId,
|
||||
Status: req.Status,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := make([]types.YarnInfo, 0, len(resp.List))
|
||||
for _, item := range resp.List {
|
||||
list = append(list, yarnInfoFromPB(item))
|
||||
}
|
||||
return &types.ListYarnResp{Total: resp.Total, List: list}, nil
|
||||
}
|
||||
41
gateway/internal/logic/inventory/yarn/updateYarnLogic.go
Normal file
41
gateway/internal/logic/inventory/yarn/updateYarnLogic.go
Normal file
@ -0,0 +1,41 @@
|
||||
package yarn
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/gateway/internal/svc"
|
||||
"muyu-apiserver/gateway/internal/types"
|
||||
"muyu-apiserver/pkg/ctxdata"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type UpdateYarnLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUpdateYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateYarnLogic {
|
||||
return &UpdateYarnLogic{Logger: logx.WithContext(ctx), ctx: ctx, svcCtx: svcCtx}
|
||||
}
|
||||
|
||||
func (l *UpdateYarnLogic) UpdateYarn(req *types.UpdateYarnReq) (*types.IdResp, error) {
|
||||
id := ctxdata.GetPathId(l.ctx)
|
||||
_, err := l.svcCtx.InventoryRpc.UpdateYarn(l.ctx, &pb.UpdateYarnReq{
|
||||
YarnId: id,
|
||||
YarnName: req.YarnName,
|
||||
Color: req.Color,
|
||||
WeightGM: req.WeightGM,
|
||||
SupplierId: req.SupplierId,
|
||||
DyeFactory: req.DyeFactory,
|
||||
ImageUrl: req.ImageUrl,
|
||||
Remark: req.Remark,
|
||||
Status: req.Status,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &types.IdResp{Id: id}, nil
|
||||
}
|
||||
@ -38,9 +38,12 @@ type CreateProductReq struct {
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Spec string `json:"spec,optional"`
|
||||
Color string `json:"color,optional"`
|
||||
ColorNo string `json:"colorNo,optional"`
|
||||
ProductCode string `json:"productCode,optional"`
|
||||
SalesPrice string `json:"salesPrice,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
Pans []PanInput `json:"pans,optional"`
|
||||
InitialBatch ProductBatchInput `json:"initialBatch,optional"`
|
||||
}
|
||||
|
||||
type CreateRoleReq struct {
|
||||
@ -114,6 +117,8 @@ type ListProductReq struct {
|
||||
ProductName string `form:"productName,optional"`
|
||||
Spec string `form:"spec,optional"`
|
||||
Color string `form:"color,optional"`
|
||||
ColorNo string `form:"colorNo,optional"`
|
||||
ProductCode string `form:"productCode,optional"`
|
||||
Status int64 `form:"status,optional,default=-1"`
|
||||
}
|
||||
|
||||
@ -230,6 +235,8 @@ type ProductInfo struct {
|
||||
ImageUrl string `json:"imageUrl"`
|
||||
Spec string `json:"spec"`
|
||||
Color string `json:"color"`
|
||||
ColorNo string `json:"colorNo"`
|
||||
ProductCode string `json:"productCode"`
|
||||
SalesPrice string `json:"salesPrice"`
|
||||
Remark string `json:"remark"`
|
||||
Status int64 `json:"status"`
|
||||
@ -247,6 +254,8 @@ type BoltInfo struct {
|
||||
type PanInfo struct {
|
||||
PanId string `json:"panId"`
|
||||
ProductId string `json:"productId"`
|
||||
BatchId string `json:"batchId"`
|
||||
BatchNo string `json:"batchNo"`
|
||||
Name string `json:"name"`
|
||||
Position string `json:"position"`
|
||||
SortOrder int64 `json:"sortOrder"`
|
||||
@ -259,6 +268,7 @@ type PanInput struct {
|
||||
Name string `json:"name"`
|
||||
Position string `json:"position,optional"`
|
||||
BoltLengths []string `json:"boltLengths,optional"`
|
||||
BatchId string `json:"batchId,optional"`
|
||||
}
|
||||
|
||||
type ListPanResp struct {
|
||||
@ -304,6 +314,8 @@ type ColorDetailItem struct {
|
||||
ProductId string `json:"productId"`
|
||||
ProductName string `json:"productName"`
|
||||
Color string `json:"color"`
|
||||
ColorNo string `json:"colorNo"`
|
||||
ProductCode string `json:"productCode"`
|
||||
Spec string `json:"spec"`
|
||||
ImageUrl string `json:"imageUrl"`
|
||||
PanCount int64 `json:"panCount"`
|
||||
@ -311,6 +323,7 @@ type ColorDetailItem struct {
|
||||
TotalLengthM string `json:"totalLengthM"`
|
||||
Locations string `json:"locations"`
|
||||
SalesPrice string `json:"salesPrice"`
|
||||
BatchCount int64 `json:"batchCount"`
|
||||
}
|
||||
|
||||
type ListColorDetailReq struct {
|
||||
@ -321,9 +334,96 @@ type ColorDetailResp struct {
|
||||
List []ColorDetailItem `json:"list"`
|
||||
}
|
||||
|
||||
type YarnInfo struct {
|
||||
YarnId string `json:"yarnId"`
|
||||
YarnName string `json:"yarnName"`
|
||||
Color string `json:"color"`
|
||||
WeightGM string `json:"weightGM"`
|
||||
SupplierId string `json:"supplierId"`
|
||||
SupplierName string `json:"supplierName"`
|
||||
DyeFactory string `json:"dyeFactory"`
|
||||
ImageUrl string `json:"imageUrl"`
|
||||
Remark string `json:"remark"`
|
||||
Status int64 `json:"status"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type CreateYarnReq struct {
|
||||
YarnName string `json:"yarnName"`
|
||||
Color string `json:"color,optional"`
|
||||
WeightGM string `json:"weightGM"`
|
||||
SupplierId string `json:"supplierId"`
|
||||
DyeFactory string `json:"dyeFactory,optional"`
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
}
|
||||
|
||||
type UpdateYarnReq struct {
|
||||
YarnName string `json:"yarnName"`
|
||||
Color string `json:"color,optional"`
|
||||
WeightGM string `json:"weightGM"`
|
||||
SupplierId string `json:"supplierId"`
|
||||
DyeFactory string `json:"dyeFactory,optional"`
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
Status int64 `json:"status,optional,default=1"`
|
||||
}
|
||||
|
||||
type ListYarnReq struct {
|
||||
Page int64 `form:"page,default=1"`
|
||||
PageSize int64 `form:"pageSize,default=20"`
|
||||
YarnName string `form:"yarnName,optional"`
|
||||
SupplierId string `form:"supplierId,optional"`
|
||||
Status int64 `form:"status,optional,default=-1"`
|
||||
}
|
||||
|
||||
type ListYarnResp struct {
|
||||
Total int64 `json:"total"`
|
||||
List []YarnInfo `json:"list"`
|
||||
}
|
||||
|
||||
type ProductBatchInput struct {
|
||||
BatchNo string `json:"batchNo,optional"`
|
||||
YarnRatio string `json:"yarnRatio,optional"`
|
||||
WarpWeightGM string `json:"warpWeightGM,optional"`
|
||||
WeftWeightGM string `json:"weftWeightGM,optional"`
|
||||
ProductionProcess string `json:"productionProcess,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
}
|
||||
|
||||
type ProductBatchInfo struct {
|
||||
BatchId string `json:"batchId"`
|
||||
ProductId string `json:"productId"`
|
||||
BatchNo string `json:"batchNo"`
|
||||
YarnRatio string `json:"yarnRatio"`
|
||||
WarpWeightGM string `json:"warpWeightGM"`
|
||||
WeftWeightGM string `json:"weftWeightGM"`
|
||||
ProductionProcess string `json:"productionProcess"`
|
||||
Remark string `json:"remark"`
|
||||
Status int64 `json:"status"`
|
||||
CreatedAt string `json:"createdAt"`
|
||||
UpdatedAt string `json:"updatedAt"`
|
||||
}
|
||||
|
||||
type CreateProductBatchReq struct {
|
||||
Batch ProductBatchInput `json:"batch"`
|
||||
}
|
||||
|
||||
type UpdateProductBatchReq struct {
|
||||
Batch ProductBatchInput `json:"batch"`
|
||||
Status int64 `json:"status,optional,default=1"`
|
||||
}
|
||||
|
||||
type ListProductBatchResp struct {
|
||||
List []ProductBatchInfo `json:"list"`
|
||||
}
|
||||
|
||||
type PanListItem struct {
|
||||
PanId string `json:"panId"`
|
||||
ProductId string `json:"productId"`
|
||||
BatchId string `json:"batchId"`
|
||||
BatchNo string `json:"batchNo"`
|
||||
Name string `json:"name"`
|
||||
Position string `json:"position"`
|
||||
ProductName string `json:"productName"`
|
||||
@ -484,6 +584,7 @@ type UpdateProductReq struct {
|
||||
ImageUrl string `json:"imageUrl,optional"`
|
||||
Spec string `json:"spec,optional"`
|
||||
Color string `json:"color,optional"`
|
||||
ColorNo string `json:"colorNo,optional"`
|
||||
SalesPrice string `json:"salesPrice,optional"`
|
||||
Remark string `json:"remark,optional"`
|
||||
}
|
||||
|
||||
1
go.mod
1
go.mod
@ -66,6 +66,7 @@ require (
|
||||
github.com/microsoft/go-mssqldb v1.9.5 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mozillazg/go-pinyin v0.21.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/openzipkin/zipkin-go v0.4.3 // indirect
|
||||
|
||||
2
go.sum
2
go.sum
@ -189,6 +189,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/mozillazg/go-pinyin v0.21.0 h1:Wo8/NT45z7P3er/9YSLHA3/kjZzbLz5hR7i+jGeIGao=
|
||||
github.com/mozillazg/go-pinyin v0.21.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
|
||||
81
model/invproductbatchmodel.go
Normal file
81
model/invproductbatchmodel.go
Normal file
@ -0,0 +1,81 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
var _ InvProductBatchModel = (*customInvProductBatchModel)(nil)
|
||||
|
||||
type (
|
||||
InvProductBatchModel interface {
|
||||
invProductBatchModel
|
||||
FindByProductId(ctx context.Context, tenantId, productId string) ([]*InvProductBatch, error)
|
||||
FindLatestByProductId(ctx context.Context, tenantId, productId string) (*InvProductBatch, error)
|
||||
FindOneByProductAndBatchId(ctx context.Context, tenantId, productId, batchId string) (*InvProductBatch, error)
|
||||
NextBatchNo(ctx context.Context, tenantId, productCode string) (string, error)
|
||||
SoftDeleteByBatchId(ctx context.Context, batchId string) error
|
||||
}
|
||||
|
||||
customInvProductBatchModel struct {
|
||||
*defaultInvProductBatchModel
|
||||
}
|
||||
)
|
||||
|
||||
func NewInvProductBatchModel(conn sqlx.SqlConn) InvProductBatchModel {
|
||||
return &customInvProductBatchModel{
|
||||
defaultInvProductBatchModel: newInvProductBatchModel(conn),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *customInvProductBatchModel) FindByProductId(ctx context.Context, tenantId, productId string) ([]*InvProductBatch, error) {
|
||||
var list []*InvProductBatch
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE tenant_id = ? AND product_id = ? AND deleted_at IS NULL ORDER BY batch_no", invProductBatchRows, m.table)
|
||||
if err := m.conn.QueryRowsCtx(ctx, &list, query, tenantId, productId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductBatchModel) FindLatestByProductId(ctx context.Context, tenantId, productId string) (*InvProductBatch, error) {
|
||||
var resp InvProductBatch
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE tenant_id = ? AND product_id = ? AND deleted_at IS NULL AND status = 1 ORDER BY batch_no DESC LIMIT 1", invProductBatchRows, m.table)
|
||||
if err := m.conn.QueryRowCtx(ctx, &resp, query, tenantId, productId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductBatchModel) FindOneByProductAndBatchId(ctx context.Context, tenantId, productId, batchId string) (*InvProductBatch, error) {
|
||||
var resp InvProductBatch
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE tenant_id = ? AND product_id = ? AND batch_id = ? AND deleted_at IS NULL LIMIT 1", invProductBatchRows, m.table)
|
||||
if err := m.conn.QueryRowCtx(ctx, &resp, query, tenantId, productId, batchId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductBatchModel) NextBatchNo(ctx context.Context, tenantId, productCode string) (string, error) {
|
||||
var next int64
|
||||
query := fmt.Sprintf(`SELECT COALESCE(MAX(CAST(SUBSTRING(batch_no, CHAR_LENGTH(?) + 3) AS UNSIGNED)), 0) + 1
|
||||
FROM %s
|
||||
WHERE tenant_id = ? AND batch_no LIKE CONCAT(?, '-B%%')`, m.table)
|
||||
if err := m.conn.QueryRowCtx(ctx, &next, query, productCode, tenantId, productCode); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%s-B%03d", productCode, next), nil
|
||||
}
|
||||
|
||||
func (m *customInvProductBatchModel) SoftDeleteByBatchId(ctx context.Context, batchId string) error {
|
||||
batch, err := m.FindOneByBatchId(ctx, batchId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
batch.Status = 0
|
||||
batch.DeletedAt.Time = time.Now()
|
||||
batch.DeletedAt.Valid = true
|
||||
return m.Update(ctx, batch)
|
||||
}
|
||||
100
model/invproductbatchmodel_gen.go
Normal file
100
model/invproductbatchmodel_gen.go
Normal file
@ -0,0 +1,100 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
)
|
||||
|
||||
var (
|
||||
invProductBatchFieldNames = builder.RawFieldNames(&InvProductBatch{})
|
||||
invProductBatchRows = strings.Join(invProductBatchFieldNames, ",")
|
||||
invProductBatchRowsExpectAutoSet = strings.Join(stringx.Remove(invProductBatchFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
|
||||
invProductBatchRowsWithPlaceHolder = strings.Join(stringx.Remove(invProductBatchFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?"
|
||||
)
|
||||
|
||||
type (
|
||||
invProductBatchModel interface {
|
||||
Insert(ctx context.Context, data *InvProductBatch) (sql.Result, error)
|
||||
FindOne(ctx context.Context, id int64) (*InvProductBatch, error)
|
||||
FindOneByBatchId(ctx context.Context, batchId string) (*InvProductBatch, error)
|
||||
Update(ctx context.Context, data *InvProductBatch) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
defaultInvProductBatchModel struct {
|
||||
conn sqlx.SqlConn
|
||||
table string
|
||||
}
|
||||
|
||||
InvProductBatch struct {
|
||||
Id int64 `db:"id"`
|
||||
BatchId string `db:"batch_id"`
|
||||
TenantId string `db:"tenant_id"`
|
||||
ProductId string `db:"product_id"`
|
||||
BatchNo string `db:"batch_no"`
|
||||
YarnRatio sql.NullString `db:"yarn_ratio"`
|
||||
WarpWeightGM float64 `db:"warp_weight_g_m"`
|
||||
WeftWeightGM float64 `db:"weft_weight_g_m"`
|
||||
ProductionProcess sql.NullString `db:"production_process"`
|
||||
Remark sql.NullString `db:"remark"`
|
||||
Status int64 `db:"status"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
DeletedAt sql.NullTime `db:"deleted_at"`
|
||||
}
|
||||
)
|
||||
|
||||
func newInvProductBatchModel(conn sqlx.SqlConn) *defaultInvProductBatchModel {
|
||||
return &defaultInvProductBatchModel{
|
||||
conn: conn,
|
||||
table: "`inv_product_batch`",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultInvProductBatchModel) Insert(ctx context.Context, data *InvProductBatch) (sql.Result, error) {
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, invProductBatchRowsExpectAutoSet)
|
||||
return m.conn.ExecCtx(ctx, query, data.BatchId, data.TenantId, data.ProductId, data.BatchNo, data.YarnRatio, data.WarpWeightGM, data.WeftWeightGM, data.ProductionProcess, data.Remark, data.Status, data.DeletedAt)
|
||||
}
|
||||
|
||||
func (m *defaultInvProductBatchModel) FindOne(ctx context.Context, id int64) (*InvProductBatch, error) {
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE `id` = ? LIMIT 1", invProductBatchRows, m.table)
|
||||
var resp InvProductBatch
|
||||
err := m.conn.QueryRowCtx(ctx, &resp, query, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *defaultInvProductBatchModel) FindOneByBatchId(ctx context.Context, batchId string) (*InvProductBatch, error) {
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE `batch_id` = ? AND `deleted_at` IS NULL LIMIT 1", invProductBatchRows, m.table)
|
||||
var resp InvProductBatch
|
||||
err := m.conn.QueryRowCtx(ctx, &resp, query, batchId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *defaultInvProductBatchModel) Update(ctx context.Context, data *InvProductBatch) error {
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE `id` = ?", m.table, invProductBatchRowsWithPlaceHolder)
|
||||
_, err := m.conn.ExecCtx(ctx, query, data.BatchId, data.TenantId, data.ProductId, data.BatchNo, data.YarnRatio, data.WarpWeightGM, data.WeftWeightGM, data.ProductionProcess, data.Remark, data.Status, data.DeletedAt, data.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultInvProductBatchModel) Delete(ctx context.Context, id int64) error {
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE `id` = ?", m.table)
|
||||
_, err := m.conn.ExecCtx(ctx, query, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultInvProductBatchModel) tableName() string {
|
||||
return m.table
|
||||
}
|
||||
@ -59,7 +59,7 @@ func (m *customInvProductBoltModel) FindByPanId(ctx context.Context, panId strin
|
||||
func (m *customInvProductBoltModel) FindByProductId(ctx context.Context, productId string) ([]*InvProductBolt, error) {
|
||||
var list []*InvProductBolt
|
||||
query := fmt.Sprintf(
|
||||
"SELECT b.`id`, b.`bolt_id`, b.`pan_id`, b.`length_m`, b.`sort_order`, b.`tenant_id`, b.`created_at`, b.`updated_at` "+
|
||||
"SELECT b.`id`, b.`bolt_id`, b.`pan_id`, b.`length_m`, b.`color`, b.`sort_order`, b.`tenant_id`, b.`created_at`, b.`updated_at` "+
|
||||
"FROM %s b INNER JOIN `inv_product_pan` p ON b.`pan_id` = p.`pan_id` "+
|
||||
"WHERE p.`product_id` = ? ORDER BY p.`sort_order`, b.`sort_order`", m.table)
|
||||
err := m.conn.QueryRowsCtx(ctx, &list, query, productId)
|
||||
|
||||
@ -33,11 +33,14 @@ type ColorDetail struct {
|
||||
ProductId string `db:"product_id"`
|
||||
ProductName string `db:"product_name"`
|
||||
Color string `db:"color"`
|
||||
ColorNo string `db:"color_no"`
|
||||
ProductCode string `db:"product_code"`
|
||||
Spec string `db:"spec"`
|
||||
ImageUrl string `db:"image_url"`
|
||||
SalesPrice float64 `db:"sales_price"`
|
||||
PanCount int64 `db:"pan_count"`
|
||||
BoltCount int64 `db:"bolt_count"`
|
||||
BatchCount int64 `db:"batch_count"`
|
||||
TotalLengthM float64 `db:"total_length_m"`
|
||||
Locations string `db:"locations"`
|
||||
}
|
||||
@ -45,11 +48,13 @@ type ColorDetail struct {
|
||||
type (
|
||||
InvProductModel interface {
|
||||
invProductModel
|
||||
FindList(ctx context.Context, tenantId string, page, pageSize int64, productName, spec, color string, status int64) ([]*InvProduct, int64, error)
|
||||
FindList(ctx context.Context, tenantId string, page, pageSize int64, productName, spec, color, colorNo, productCode string, status int64) ([]*InvProduct, int64, error)
|
||||
FindProductSummary(ctx context.Context, tenantId string) ([]*ProductSummary, error)
|
||||
FindColorDetail(ctx context.Context, tenantId, productName string) ([]*ColorDetail, error)
|
||||
FindOneByTenantProductCode(ctx context.Context, tenantId, productCode string) (*InvProduct, error)
|
||||
FindGroupByColor(ctx context.Context, tenantId string) ([]StockGroupResult, error)
|
||||
FindGroupByLocation(ctx context.Context, tenantId string) ([]StockGroupResult, error)
|
||||
NextColorNo(ctx context.Context, tenantId, productName string) (string, error)
|
||||
BulkInsert(ctx context.Context, products []*InvProduct) (int64, error)
|
||||
}
|
||||
|
||||
@ -79,15 +84,16 @@ func (m *customInvProductModel) BulkInsert(ctx context.Context, products []*InvP
|
||||
}
|
||||
batch := products[i:end]
|
||||
|
||||
placeholder := "(" + strings.Repeat("?,", 9) + "?)"
|
||||
placeholder := "(" + strings.Repeat("?,", 11) + "?)"
|
||||
placeholders := make([]string, len(batch))
|
||||
args := make([]interface{}, 0, len(batch)*10)
|
||||
args := make([]interface{}, 0, len(batch)*12)
|
||||
|
||||
for j, p := range batch {
|
||||
placeholders[j] = placeholder
|
||||
args = append(args,
|
||||
p.ProductId, p.ProductName, p.ImageUrl, p.Spec, p.Color,
|
||||
p.SalesPrice, p.Remark, p.Status, p.TenantId, p.DeletedAt,
|
||||
p.ColorNo, p.ProductCode, p.SalesPrice, p.Remark, p.Status,
|
||||
p.TenantId, p.DeletedAt,
|
||||
)
|
||||
}
|
||||
|
||||
@ -105,7 +111,7 @@ func (m *customInvProductModel) BulkInsert(ctx context.Context, products []*InvP
|
||||
return totalAffected, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductModel) FindList(ctx context.Context, tenantId string, page, pageSize int64, productName, spec, color string, status int64) ([]*InvProduct, int64, error) {
|
||||
func (m *customInvProductModel) FindList(ctx context.Context, tenantId string, page, pageSize int64, productName, spec, color, colorNo, productCode string, status int64) ([]*InvProduct, int64, error) {
|
||||
where := "WHERE deleted_at IS NULL AND tenant_id = ?"
|
||||
args := []interface{}{tenantId}
|
||||
|
||||
@ -121,6 +127,14 @@ func (m *customInvProductModel) FindList(ctx context.Context, tenantId string, p
|
||||
where += " AND color = ?"
|
||||
args = append(args, color)
|
||||
}
|
||||
if colorNo != "" {
|
||||
where += " AND color_no LIKE ?"
|
||||
args = append(args, "%"+colorNo+"%")
|
||||
}
|
||||
if productCode != "" {
|
||||
where += " AND product_code LIKE ?"
|
||||
args = append(args, "%"+productCode+"%")
|
||||
}
|
||||
if status >= 0 {
|
||||
where += " AND status = ?"
|
||||
args = append(args, status)
|
||||
@ -144,6 +158,27 @@ func (m *customInvProductModel) FindList(ctx context.Context, tenantId string, p
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductModel) FindOneByTenantProductCode(ctx context.Context, tenantId, productCode string) (*InvProduct, error) {
|
||||
var resp InvProduct
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE tenant_id = ? AND product_code = ? AND deleted_at IS NULL LIMIT 1", invProductRows, m.table)
|
||||
err := m.QueryRowNoCacheCtx(ctx, &resp, query, tenantId, productCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductModel) NextColorNo(ctx context.Context, tenantId, productName string) (string, error) {
|
||||
var next int64
|
||||
query := fmt.Sprintf(`SELECT COALESCE(MAX(CAST(color_no AS UNSIGNED)), 0) + 1
|
||||
FROM %s
|
||||
WHERE tenant_id = ? AND product_name = ? AND deleted_at IS NULL AND color_no REGEXP '^[0-9]+$'`, m.table)
|
||||
if err := m.QueryRowNoCacheCtx(ctx, &next, query, tenantId, productName); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("%02d", next), nil
|
||||
}
|
||||
|
||||
func (m *customInvProductModel) FindProductSummary(ctx context.Context, tenantId string) ([]*ProductSummary, error) {
|
||||
var list []*ProductSummary
|
||||
query := `SELECT p.product_name, p.spec,
|
||||
@ -200,9 +235,16 @@ func (m *customInvProductModel) FindGroupByLocation(ctx context.Context, tenantI
|
||||
|
||||
func (m *customInvProductModel) FindColorDetail(ctx context.Context, tenantId, productName string) ([]*ColorDetail, error) {
|
||||
var list []*ColorDetail
|
||||
query := `SELECT p.product_id, p.product_name, p.color, p.spec, p.image_url, p.sales_price,
|
||||
query := `SELECT p.product_id, p.product_name, p.color, p.color_no, p.product_code, p.spec, p.image_url, p.sales_price,
|
||||
COUNT(DISTINCT pan.pan_id) AS pan_count,
|
||||
COUNT(bolt.bolt_id) AS bolt_count,
|
||||
(
|
||||
SELECT COUNT(DISTINCT batch.batch_id)
|
||||
FROM ` + "`inv_product_batch`" + ` batch
|
||||
WHERE batch.product_id = p.product_id
|
||||
AND batch.tenant_id = p.tenant_id
|
||||
AND batch.deleted_at IS NULL
|
||||
) AS batch_count,
|
||||
COALESCE(SUM(bolt.length_m), 0) AS total_length_m,
|
||||
COALESCE(GROUP_CONCAT(DISTINCT pan.position ORDER BY pan.position), '') AS locations
|
||||
FROM ` + m.table + ` p
|
||||
|
||||
@ -27,6 +27,7 @@ var (
|
||||
cacheInvProductIdPrefix = "cache:invProduct:id:"
|
||||
cacheInvProductProductIdPrefix = "cache:invProduct:productId:"
|
||||
cacheInvProductProductNamePrefix = "cache:invProduct:productName:"
|
||||
cacheInvProductProductCodePrefix = "cache:invProduct:productCode:"
|
||||
)
|
||||
|
||||
type (
|
||||
@ -35,6 +36,7 @@ type (
|
||||
FindOne(ctx context.Context, id int64) (*InvProduct, error)
|
||||
FindOneByProductId(ctx context.Context, productId string) (*InvProduct, error)
|
||||
FindOneByProductName(ctx context.Context, productName string) (*InvProduct, error)
|
||||
FindOneByProductCode(ctx context.Context, productCode string) (*InvProduct, error)
|
||||
Update(ctx context.Context, data *InvProduct) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
}
|
||||
@ -51,6 +53,8 @@ type (
|
||||
ImageUrl string `db:"image_url"`
|
||||
Spec string `db:"spec"`
|
||||
Color string `db:"color"`
|
||||
ColorNo string `db:"color_no"`
|
||||
ProductCode string `db:"product_code"`
|
||||
SalesPrice float64 `db:"sales_price"`
|
||||
Remark sql.NullString `db:"remark"`
|
||||
Status int64 `db:"status"`
|
||||
@ -77,10 +81,11 @@ func (m *defaultInvProductModel) Delete(ctx context.Context, id int64) error {
|
||||
invProductIdKey := fmt.Sprintf("%s%v", cacheInvProductIdPrefix, id)
|
||||
invProductProductIdKey := fmt.Sprintf("%s%v", cacheInvProductProductIdPrefix, data.ProductId)
|
||||
invProductProductNameKey := fmt.Sprintf("%s%v", cacheInvProductProductNamePrefix, data.ProductName)
|
||||
invProductProductCodeKey := fmt.Sprintf("%s%v", cacheInvProductProductCodePrefix, data.ProductCode)
|
||||
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("delete from %s where `id` = ?", m.table)
|
||||
return conn.ExecCtx(ctx, query, id)
|
||||
}, invProductIdKey, invProductProductIdKey, invProductProductNameKey)
|
||||
}, invProductIdKey, invProductProductIdKey, invProductProductNameKey, invProductProductCodeKey)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -121,6 +126,26 @@ func (m *defaultInvProductModel) FindOneByProductId(ctx context.Context, product
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultInvProductModel) FindOneByProductCode(ctx context.Context, productCode string) (*InvProduct, error) {
|
||||
invProductProductCodeKey := fmt.Sprintf("%s%v", cacheInvProductProductCodePrefix, productCode)
|
||||
var resp InvProduct
|
||||
err := m.QueryRowIndexCtx(ctx, &resp, invProductProductCodeKey, m.formatPrimary, func(ctx context.Context, conn sqlx.SqlConn, v any) (i any, e error) {
|
||||
query := fmt.Sprintf("select %s from %s where `product_code` = ? limit 1", invProductRows, m.table)
|
||||
if err := conn.QueryRowCtx(ctx, &resp, query, productCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Id, nil
|
||||
}, m.queryPrimary)
|
||||
switch err {
|
||||
case nil:
|
||||
return &resp, nil
|
||||
case sqlc.ErrNotFound:
|
||||
return nil, ErrNotFound
|
||||
default:
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultInvProductModel) FindOneByProductName(ctx context.Context, productName string) (*InvProduct, error) {
|
||||
invProductProductNameKey := fmt.Sprintf("%s%v", cacheInvProductProductNamePrefix, productName)
|
||||
var resp InvProduct
|
||||
@ -145,10 +170,11 @@ func (m *defaultInvProductModel) Insert(ctx context.Context, data *InvProduct) (
|
||||
invProductIdKey := fmt.Sprintf("%s%v", cacheInvProductIdPrefix, data.Id)
|
||||
invProductProductIdKey := fmt.Sprintf("%s%v", cacheInvProductProductIdPrefix, data.ProductId)
|
||||
invProductProductNameKey := fmt.Sprintf("%s%v", cacheInvProductProductNamePrefix, data.ProductName)
|
||||
invProductProductCodeKey := fmt.Sprintf("%s%v", cacheInvProductProductCodePrefix, data.ProductCode)
|
||||
ret, err := m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, invProductRowsExpectAutoSet)
|
||||
return conn.ExecCtx(ctx, query, data.ProductId, data.ProductName, data.ImageUrl, data.Spec, data.Color, data.SalesPrice, data.Remark, data.Status, data.TenantId, data.DeletedAt)
|
||||
}, invProductIdKey, invProductProductIdKey, invProductProductNameKey)
|
||||
query := fmt.Sprintf("insert into %s (%s) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, invProductRowsExpectAutoSet)
|
||||
return conn.ExecCtx(ctx, query, data.ProductId, data.ProductName, data.ImageUrl, data.Spec, data.Color, data.ColorNo, data.ProductCode, data.SalesPrice, data.Remark, data.Status, data.TenantId, data.DeletedAt)
|
||||
}, invProductIdKey, invProductProductIdKey, invProductProductNameKey, invProductProductCodeKey)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
@ -161,10 +187,11 @@ func (m *defaultInvProductModel) Update(ctx context.Context, newData *InvProduct
|
||||
invProductIdKey := fmt.Sprintf("%s%v", cacheInvProductIdPrefix, data.Id)
|
||||
invProductProductIdKey := fmt.Sprintf("%s%v", cacheInvProductProductIdPrefix, data.ProductId)
|
||||
invProductProductNameKey := fmt.Sprintf("%s%v", cacheInvProductProductNamePrefix, data.ProductName)
|
||||
invProductProductCodeKey := fmt.Sprintf("%s%v", cacheInvProductProductCodePrefix, data.ProductCode)
|
||||
_, err = m.ExecCtx(ctx, func(ctx context.Context, conn sqlx.SqlConn) (result sql.Result, err error) {
|
||||
query := fmt.Sprintf("update %s set %s where `id` = ?", m.table, invProductRowsWithPlaceHolder)
|
||||
return conn.ExecCtx(ctx, query, newData.ProductId, newData.ProductName, newData.ImageUrl, newData.Spec, newData.Color, newData.SalesPrice, newData.Remark, newData.Status, newData.TenantId, newData.DeletedAt, newData.Id)
|
||||
}, invProductIdKey, invProductProductIdKey, invProductProductNameKey)
|
||||
return conn.ExecCtx(ctx, query, newData.ProductId, newData.ProductName, newData.ImageUrl, newData.Spec, newData.Color, newData.ColorNo, newData.ProductCode, newData.SalesPrice, newData.Remark, newData.Status, newData.TenantId, newData.DeletedAt, newData.Id)
|
||||
}, invProductIdKey, invProductProductIdKey, invProductProductNameKey, invProductProductCodeKey)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
@ -49,6 +49,8 @@ func TestInvProductInsert(t *testing.T) {
|
||||
ImageUrl: "",
|
||||
Spec: "100cm",
|
||||
Color: "red",
|
||||
ColorNo: "01",
|
||||
ProductCode: uniqueID("TPC"),
|
||||
SalesPrice: 99.9,
|
||||
Remark: sql.NullString{String: "test remark", Valid: true},
|
||||
Status: 1,
|
||||
@ -83,6 +85,8 @@ func TestInvProductFindOne(t *testing.T) {
|
||||
ProductName: uniqueID("test_product"),
|
||||
Spec: "200cm",
|
||||
Color: "blue",
|
||||
ColorNo: "01",
|
||||
ProductCode: uniqueID("TPC"),
|
||||
SalesPrice: 49.5,
|
||||
Status: 1,
|
||||
TenantId: "t_default_001",
|
||||
@ -121,6 +125,8 @@ func TestInvProductUpdate(t *testing.T) {
|
||||
ProductName: uniqueID("test_product"),
|
||||
Spec: "50cm",
|
||||
Color: "green",
|
||||
ColorNo: "01",
|
||||
ProductCode: uniqueID("TPC"),
|
||||
SalesPrice: 10.0,
|
||||
Status: 1,
|
||||
TenantId: "t_default_001",
|
||||
@ -172,6 +178,8 @@ func TestInvProductBulkInsert(t *testing.T) {
|
||||
ProductName: uniqueID(fmt.Sprintf("bulk_product_%d", i)),
|
||||
Spec: "10cm",
|
||||
Color: "white",
|
||||
ColorNo: fmt.Sprintf("%02d", i+1),
|
||||
ProductCode: uniqueID(fmt.Sprintf("BTPC%d", i)),
|
||||
SalesPrice: float64(i+1) * 5.0,
|
||||
Status: 1,
|
||||
TenantId: "t_default_001",
|
||||
@ -229,7 +237,8 @@ func TestInvProductSchemaColumns(t *testing.T) {
|
||||
// Columns that the InvProduct struct declares (via db tags).
|
||||
structCols := []string{
|
||||
"id", "product_id", "product_name", "image_url", "spec", "color",
|
||||
"sales_price", "remark", "status", "tenant_id", "created_at", "updated_at", "deleted_at",
|
||||
"color_no", "product_code", "sales_price", "remark", "status", "tenant_id",
|
||||
"created_at", "updated_at", "deleted_at",
|
||||
}
|
||||
|
||||
for _, col := range structCols {
|
||||
@ -267,6 +276,8 @@ func TestFindProductSummaryNoPans(t *testing.T) {
|
||||
ProductName: uniqueID("nopan_product"),
|
||||
Spec: "50cm",
|
||||
Color: "red",
|
||||
ColorNo: "01",
|
||||
ProductCode: uniqueID("TPC"),
|
||||
SalesPrice: 99.0,
|
||||
Status: 1,
|
||||
TenantId: "t_default_001",
|
||||
@ -318,6 +329,8 @@ func TestFindColorDetailNoPans(t *testing.T) {
|
||||
ProductName: uniqueID("color_nopan_product"),
|
||||
Spec: "80cm",
|
||||
Color: "blue",
|
||||
ColorNo: "01",
|
||||
ProductCode: uniqueID("TPC"),
|
||||
SalesPrice: 50.0,
|
||||
Status: 1,
|
||||
TenantId: "t_default_001",
|
||||
@ -343,3 +356,96 @@ func TestFindColorDetailNoPans(t *testing.T) {
|
||||
t.Errorf("Locations: got %q, want empty string", list[0].Locations)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindColorDetailBatchCountDoesNotDuplicateStock(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test in short mode")
|
||||
}
|
||||
|
||||
conn := sqlx.NewMysql(testDSN)
|
||||
m := newTestProductModel(t)
|
||||
panM := newInvProductPanModel(conn)
|
||||
boltM := newInvProductBoltModel(conn)
|
||||
batchM := newInvProductBatchModel(conn)
|
||||
ctx := context.Background()
|
||||
|
||||
productId := uniqueID("pcb")
|
||||
p := &InvProduct{
|
||||
ProductId: productId,
|
||||
ProductName: uniqueID("color_batch_product"),
|
||||
Spec: "120cm",
|
||||
Color: "natural",
|
||||
ColorNo: "01",
|
||||
ProductCode: uniqueID("TPC"),
|
||||
SalesPrice: 20.0,
|
||||
Status: 1,
|
||||
TenantId: "t_default_001",
|
||||
}
|
||||
res, err := m.Insert(ctx, p)
|
||||
if err != nil {
|
||||
t.Fatalf("Insert product: %v", err)
|
||||
}
|
||||
productRowId, _ := res.LastInsertId()
|
||||
t.Cleanup(func() { _ = m.Delete(ctx, productRowId) })
|
||||
|
||||
batchAId := uniqueID("b1")
|
||||
batchBId := uniqueID("b2")
|
||||
for _, batch := range []*InvProductBatch{
|
||||
{BatchId: batchAId, TenantId: p.TenantId, ProductId: productId, BatchNo: p.ProductCode + "-B001", Status: 1},
|
||||
{BatchId: batchBId, TenantId: p.TenantId, ProductId: productId, BatchNo: p.ProductCode + "-B002", Status: 1},
|
||||
} {
|
||||
res, err := batchM.Insert(ctx, batch)
|
||||
if err != nil {
|
||||
t.Fatalf("Insert batch: %v", err)
|
||||
}
|
||||
id, _ := res.LastInsertId()
|
||||
t.Cleanup(func() { _ = batchM.Delete(ctx, id) })
|
||||
}
|
||||
|
||||
pans := []*InvProductPan{
|
||||
{PanId: uniqueID("pa"), ProductId: productId, BatchId: batchAId, Name: "A", Position: "A-01", SortOrder: 1, TenantId: p.TenantId},
|
||||
{PanId: uniqueID("pb"), ProductId: productId, BatchId: batchBId, Name: "B", Position: "B-02", SortOrder: 2, TenantId: p.TenantId},
|
||||
}
|
||||
for _, pan := range pans {
|
||||
res, err := panM.Insert(ctx, pan)
|
||||
if err != nil {
|
||||
t.Fatalf("Insert pan: %v", err)
|
||||
}
|
||||
id, _ := res.LastInsertId()
|
||||
t.Cleanup(func() { _ = panM.Delete(ctx, id) })
|
||||
}
|
||||
|
||||
for _, bolt := range []*InvProductBolt{
|
||||
{BoltId: uniqueID("ba1"), PanId: pans[0].PanId, LengthM: 10.5, Color: p.Color, SortOrder: 1, TenantId: p.TenantId},
|
||||
{BoltId: uniqueID("ba2"), PanId: pans[0].PanId, LengthM: 11.5, Color: p.Color, SortOrder: 2, TenantId: p.TenantId},
|
||||
{BoltId: uniqueID("bb1"), PanId: pans[1].PanId, LengthM: 12.0, Color: p.Color, SortOrder: 1, TenantId: p.TenantId},
|
||||
} {
|
||||
res, err := boltM.Insert(ctx, bolt)
|
||||
if err != nil {
|
||||
t.Fatalf("Insert bolt: %v", err)
|
||||
}
|
||||
id, _ := res.LastInsertId()
|
||||
t.Cleanup(func() { _ = boltM.Delete(ctx, id) })
|
||||
}
|
||||
|
||||
list, err := m.FindColorDetail(ctx, p.TenantId, p.ProductName)
|
||||
if err != nil {
|
||||
t.Fatalf("FindColorDetail: %v", err)
|
||||
}
|
||||
if len(list) != 1 {
|
||||
t.Fatalf("expected 1 color detail row, got %d", len(list))
|
||||
}
|
||||
got := list[0]
|
||||
if got.PanCount != 2 {
|
||||
t.Errorf("PanCount: got %d, want 2", got.PanCount)
|
||||
}
|
||||
if got.BoltCount != 3 {
|
||||
t.Errorf("BoltCount: got %d, want 3", got.BoltCount)
|
||||
}
|
||||
if got.BatchCount != 2 {
|
||||
t.Errorf("BatchCount: got %d, want 2", got.BatchCount)
|
||||
}
|
||||
if got.TotalLengthM != 34.0 {
|
||||
t.Errorf("TotalLengthM: got %.2f, want 34.00", got.TotalLengthM)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,8 @@ var _ InvProductPanModel = (*customInvProductPanModel)(nil)
|
||||
type PanListItem struct {
|
||||
PanId string `db:"pan_id"`
|
||||
ProductId string `db:"product_id"`
|
||||
BatchId string `db:"batch_id"`
|
||||
BatchNo string `db:"batch_no"`
|
||||
Name string `db:"name"`
|
||||
Position string `db:"position"`
|
||||
ProductName string `db:"product_name"`
|
||||
@ -30,6 +32,7 @@ type (
|
||||
DeleteByProductId(ctx context.Context, productId string) error
|
||||
BulkReplace(ctx context.Context, productId, tenantId string, pans []*InvProductPan) error
|
||||
FindPanList(ctx context.Context, tenantId string, page, pageSize int64, productName, position string) ([]*PanListItem, int64, error)
|
||||
CountByBatchId(ctx context.Context, batchId string) (int64, error)
|
||||
}
|
||||
|
||||
customInvProductPanModel struct {
|
||||
@ -79,13 +82,14 @@ func (m *customInvProductPanModel) FindPanList(ctx context.Context, tenantId str
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`SELECT pan.pan_id, pan.product_id, pan.name, pan.position,
|
||||
p.product_name,
|
||||
query := fmt.Sprintf(`SELECT pan.pan_id, pan.product_id, pan.batch_id, COALESCE(batch.batch_no, '') AS batch_no,
|
||||
pan.name, pan.position, p.product_name,
|
||||
COALESCE(GROUP_CONCAT(DISTINCT b.color ORDER BY b.color SEPARATOR ', '), '') AS colors,
|
||||
COUNT(b.bolt_id) AS bolt_count,
|
||||
COALESCE(SUM(b.length_m), 0) AS total_length_m
|
||||
FROM %s pan
|
||||
JOIN inv_product p ON p.product_id = pan.product_id
|
||||
LEFT JOIN inv_product_batch batch ON batch.batch_id = pan.batch_id AND batch.deleted_at IS NULL
|
||||
LEFT JOIN inv_product_bolt b ON b.pan_id = pan.pan_id
|
||||
%s
|
||||
GROUP BY pan.pan_id
|
||||
@ -100,6 +104,15 @@ func (m *customInvProductPanModel) FindPanList(ctx context.Context, tenantId str
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductPanModel) CountByBatchId(ctx context.Context, batchId string) (int64, error) {
|
||||
var total int64
|
||||
query := fmt.Sprintf("SELECT COUNT(*) FROM %s WHERE `batch_id` = ?", m.table)
|
||||
if err := m.conn.QueryRowCtx(ctx, &total, query, batchId); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return total, nil
|
||||
}
|
||||
|
||||
func (m *customInvProductPanModel) BulkReplace(ctx context.Context, productId, tenantId string, pans []*InvProductPan) error {
|
||||
return m.conn.TransactCtx(ctx, func(ctx context.Context, session sqlx.Session) error {
|
||||
delQuery := fmt.Sprintf("DELETE FROM %s WHERE `product_id` = ?", m.table)
|
||||
@ -109,12 +122,12 @@ func (m *customInvProductPanModel) BulkReplace(ctx context.Context, productId, t
|
||||
if len(pans) == 0 {
|
||||
return nil
|
||||
}
|
||||
placeholder := "(?, ?, ?, ?, ?, ?)"
|
||||
placeholder := "(?, ?, ?, ?, ?, ?, ?)"
|
||||
placeholders := make([]string, len(pans))
|
||||
args := make([]interface{}, 0, len(pans)*6)
|
||||
args := make([]interface{}, 0, len(pans)*7)
|
||||
for i, p := range pans {
|
||||
placeholders[i] = placeholder
|
||||
args = append(args, p.PanId, productId, p.Name, p.Position, p.SortOrder, tenantId)
|
||||
args = append(args, p.PanId, productId, p.BatchId, p.Name, p.Position, p.SortOrder, tenantId)
|
||||
}
|
||||
insQuery := fmt.Sprintf("INSERT INTO %s (%s) VALUES %s", m.table, invProductPanRowsExpectAutoSet, strings.Join(placeholders, ","))
|
||||
_, err := session.ExecCtx(ctx, insQuery, args...)
|
||||
|
||||
@ -37,6 +37,7 @@ type (
|
||||
Id int64 `db:"id"`
|
||||
PanId string `db:"pan_id"`
|
||||
ProductId string `db:"product_id"`
|
||||
BatchId string `db:"batch_id"`
|
||||
Name string `db:"name"`
|
||||
Position string `db:"position"`
|
||||
SortOrder int64 `db:"sort_order"`
|
||||
@ -54,8 +55,8 @@ func newInvProductPanModel(conn sqlx.SqlConn) *defaultInvProductPanModel {
|
||||
}
|
||||
|
||||
func (m *defaultInvProductPanModel) Insert(ctx context.Context, data *InvProductPan) (sql.Result, error) {
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?)", m.table, invProductPanRowsExpectAutoSet)
|
||||
return m.conn.ExecCtx(ctx, query, data.PanId, data.ProductId, data.Name, data.Position, data.SortOrder, data.TenantId)
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?, ?)", m.table, invProductPanRowsExpectAutoSet)
|
||||
return m.conn.ExecCtx(ctx, query, data.PanId, data.ProductId, data.BatchId, data.Name, data.Position, data.SortOrder, data.TenantId)
|
||||
}
|
||||
|
||||
func (m *defaultInvProductPanModel) FindOne(ctx context.Context, id int64) (*InvProductPan, error) {
|
||||
@ -80,7 +81,7 @@ func (m *defaultInvProductPanModel) FindOneByPanId(ctx context.Context, panId st
|
||||
|
||||
func (m *defaultInvProductPanModel) Update(ctx context.Context, data *InvProductPan) error {
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE `id` = ?", m.table, invProductPanRowsWithPlaceHolder)
|
||||
_, err := m.conn.ExecCtx(ctx, query, data.PanId, data.ProductId, data.Name, data.Position, data.SortOrder, data.TenantId, data.Id)
|
||||
_, err := m.conn.ExecCtx(ctx, query, data.PanId, data.ProductId, data.BatchId, data.Name, data.Position, data.SortOrder, data.TenantId, data.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
82
model/invyarnmodel.go
Normal file
82
model/invyarnmodel.go
Normal file
@ -0,0 +1,82 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
)
|
||||
|
||||
var _ InvYarnModel = (*customInvYarnModel)(nil)
|
||||
|
||||
type (
|
||||
InvYarnModel interface {
|
||||
invYarnModel
|
||||
FindList(ctx context.Context, tenantId string, page, pageSize int64, yarnName, supplierId string, status int64) ([]*InvYarn, int64, error)
|
||||
FindOneByUnique(ctx context.Context, tenantId, yarnName, color, supplierId string) (*InvYarn, error)
|
||||
SoftDeleteByYarnId(ctx context.Context, yarnId string) error
|
||||
}
|
||||
|
||||
customInvYarnModel struct {
|
||||
*defaultInvYarnModel
|
||||
}
|
||||
)
|
||||
|
||||
func NewInvYarnModel(conn sqlx.SqlConn) InvYarnModel {
|
||||
return &customInvYarnModel{
|
||||
defaultInvYarnModel: newInvYarnModel(conn),
|
||||
}
|
||||
}
|
||||
|
||||
func (m *customInvYarnModel) FindList(ctx context.Context, tenantId string, page, pageSize int64, yarnName, supplierId string, status int64) ([]*InvYarn, int64, error) {
|
||||
where := "WHERE tenant_id = ? AND deleted_at IS NULL"
|
||||
args := []interface{}{tenantId}
|
||||
|
||||
if yarnName != "" {
|
||||
where += " AND yarn_name LIKE ?"
|
||||
args = append(args, "%"+yarnName+"%")
|
||||
}
|
||||
if supplierId != "" {
|
||||
where += " AND supplier_id = ?"
|
||||
args = append(args, supplierId)
|
||||
}
|
||||
if status >= 0 {
|
||||
where += " AND status = ?"
|
||||
args = append(args, status)
|
||||
}
|
||||
|
||||
var total int64
|
||||
countQuery := fmt.Sprintf("SELECT COUNT(*) FROM %s %s", m.table, where)
|
||||
if err := m.conn.QueryRowCtx(ctx, &total, countQuery, args...); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var list []*InvYarn
|
||||
query := fmt.Sprintf("SELECT %s FROM %s %s ORDER BY created_at DESC LIMIT ? OFFSET ?", invYarnRows, m.table, where)
|
||||
args = append(args, pageSize, (page-1)*pageSize)
|
||||
if err := m.conn.QueryRowsCtx(ctx, &list, query, args...); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return list, total, nil
|
||||
}
|
||||
|
||||
func (m *customInvYarnModel) FindOneByUnique(ctx context.Context, tenantId, yarnName, color, supplierId string) (*InvYarn, error) {
|
||||
var resp InvYarn
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE tenant_id = ? AND yarn_name = ? AND color = ? AND supplier_id = ? AND deleted_at IS NULL LIMIT 1", invYarnRows, m.table)
|
||||
if err := m.conn.QueryRowCtx(ctx, &resp, query, tenantId, yarnName, color, supplierId); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *customInvYarnModel) SoftDeleteByYarnId(ctx context.Context, yarnId string) error {
|
||||
yarn, err := m.FindOneByYarnId(ctx, yarnId)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
yarn.Status = 0
|
||||
yarn.DeletedAt.Time = time.Now()
|
||||
yarn.DeletedAt.Valid = true
|
||||
return m.Update(ctx, yarn)
|
||||
}
|
||||
100
model/invyarnmodel_gen.go
Normal file
100
model/invyarnmodel_gen.go
Normal file
@ -0,0 +1,100 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/stores/builder"
|
||||
"github.com/zeromicro/go-zero/core/stores/sqlx"
|
||||
"github.com/zeromicro/go-zero/core/stringx"
|
||||
)
|
||||
|
||||
var (
|
||||
invYarnFieldNames = builder.RawFieldNames(&InvYarn{})
|
||||
invYarnRows = strings.Join(invYarnFieldNames, ",")
|
||||
invYarnRowsExpectAutoSet = strings.Join(stringx.Remove(invYarnFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), ",")
|
||||
invYarnRowsWithPlaceHolder = strings.Join(stringx.Remove(invYarnFieldNames, "`id`", "`create_at`", "`create_time`", "`created_at`", "`update_at`", "`update_time`", "`updated_at`"), "=?,") + "=?"
|
||||
)
|
||||
|
||||
type (
|
||||
invYarnModel interface {
|
||||
Insert(ctx context.Context, data *InvYarn) (sql.Result, error)
|
||||
FindOne(ctx context.Context, id int64) (*InvYarn, error)
|
||||
FindOneByYarnId(ctx context.Context, yarnId string) (*InvYarn, error)
|
||||
Update(ctx context.Context, data *InvYarn) error
|
||||
Delete(ctx context.Context, id int64) error
|
||||
}
|
||||
|
||||
defaultInvYarnModel struct {
|
||||
conn sqlx.SqlConn
|
||||
table string
|
||||
}
|
||||
|
||||
InvYarn struct {
|
||||
Id int64 `db:"id"`
|
||||
YarnId string `db:"yarn_id"`
|
||||
TenantId string `db:"tenant_id"`
|
||||
YarnName string `db:"yarn_name"`
|
||||
Color string `db:"color"`
|
||||
WeightGM float64 `db:"weight_g_m"`
|
||||
SupplierId string `db:"supplier_id"`
|
||||
DyeFactory string `db:"dye_factory"`
|
||||
ImageUrl string `db:"image_url"`
|
||||
Remark sql.NullString `db:"remark"`
|
||||
Status int64 `db:"status"`
|
||||
CreatedAt time.Time `db:"created_at"`
|
||||
UpdatedAt time.Time `db:"updated_at"`
|
||||
DeletedAt sql.NullTime `db:"deleted_at"`
|
||||
}
|
||||
)
|
||||
|
||||
func newInvYarnModel(conn sqlx.SqlConn) *defaultInvYarnModel {
|
||||
return &defaultInvYarnModel{
|
||||
conn: conn,
|
||||
table: "`inv_yarn`",
|
||||
}
|
||||
}
|
||||
|
||||
func (m *defaultInvYarnModel) Insert(ctx context.Context, data *InvYarn) (sql.Result, error) {
|
||||
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", m.table, invYarnRowsExpectAutoSet)
|
||||
return m.conn.ExecCtx(ctx, query, data.YarnId, data.TenantId, data.YarnName, data.Color, data.WeightGM, data.SupplierId, data.DyeFactory, data.ImageUrl, data.Remark, data.Status, data.DeletedAt)
|
||||
}
|
||||
|
||||
func (m *defaultInvYarnModel) FindOne(ctx context.Context, id int64) (*InvYarn, error) {
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE `id` = ? LIMIT 1", invYarnRows, m.table)
|
||||
var resp InvYarn
|
||||
err := m.conn.QueryRowCtx(ctx, &resp, query, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *defaultInvYarnModel) FindOneByYarnId(ctx context.Context, yarnId string) (*InvYarn, error) {
|
||||
query := fmt.Sprintf("SELECT %s FROM %s WHERE `yarn_id` = ? AND `deleted_at` IS NULL LIMIT 1", invYarnRows, m.table)
|
||||
var resp InvYarn
|
||||
err := m.conn.QueryRowCtx(ctx, &resp, query, yarnId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
}
|
||||
|
||||
func (m *defaultInvYarnModel) Update(ctx context.Context, data *InvYarn) error {
|
||||
query := fmt.Sprintf("UPDATE %s SET %s WHERE `id` = ?", m.table, invYarnRowsWithPlaceHolder)
|
||||
_, err := m.conn.ExecCtx(ctx, query, data.YarnId, data.TenantId, data.YarnName, data.Color, data.WeightGM, data.SupplierId, data.DyeFactory, data.ImageUrl, data.Remark, data.Status, data.DeletedAt, data.Id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultInvYarnModel) Delete(ctx context.Context, id int64) error {
|
||||
query := fmt.Sprintf("DELETE FROM %s WHERE `id` = ?", m.table)
|
||||
_, err := m.conn.ExecCtx(ctx, query, id)
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *defaultInvYarnModel) tableName() string {
|
||||
return m.table
|
||||
}
|
||||
86
pkg/excelparse/native.go
Normal file
86
pkg/excelparse/native.go
Normal file
@ -0,0 +1,86 @@
|
||||
package excelparse
|
||||
|
||||
import "strings"
|
||||
|
||||
// NativeProductParser handles the MuYu product import template and product exports.
|
||||
type NativeProductParser struct{}
|
||||
|
||||
func (p *NativeProductParser) Name() string { return "木语产品模板" }
|
||||
|
||||
func (p *NativeProductParser) Match(headers []string) bool {
|
||||
idx := buildIndex(headers)
|
||||
if !hasAny(idx, "产品名称", "名称") {
|
||||
return false
|
||||
}
|
||||
return hasAny(idx, "产品码", "色号", "批次号", "纱线配比", "生产工艺")
|
||||
}
|
||||
|
||||
func (p *NativeProductParser) ParseRows(headers []string, rows [][]string) (*ParseResult, error) {
|
||||
idx := buildIndex(headers)
|
||||
seen := make(map[string]struct{}, len(rows))
|
||||
products := make([]ParsedProduct, 0, len(rows))
|
||||
skipped := 0
|
||||
|
||||
for _, row := range rows {
|
||||
name := firstCol(row, idx, "产品名称", "名称")
|
||||
if name == "" {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
|
||||
productCode := firstCol(row, idx, "产品码", "Product Code", "product_code")
|
||||
key := productCode
|
||||
if key == "" {
|
||||
key = strings.Join([]string{
|
||||
name,
|
||||
firstCol(row, idx, "规格型号", "规格"),
|
||||
firstCol(row, idx, "颜色"),
|
||||
firstCol(row, idx, "色号"),
|
||||
}, "\x00")
|
||||
}
|
||||
if _, ok := seen[key]; ok {
|
||||
skipped++
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
|
||||
products = append(products, ParsedProduct{
|
||||
ProductName: name,
|
||||
Spec: firstCol(row, idx, "规格型号", "规格"),
|
||||
Color: firstCol(row, idx, "颜色"),
|
||||
ColorNo: firstCol(row, idx, "色号"),
|
||||
ProductCode: productCode,
|
||||
SalesPrice: stripCurrency(firstCol(row, idx, "销售价", "参考售价")),
|
||||
BatchNo: firstCol(row, idx, "批次号"),
|
||||
YarnRatio: firstCol(row, idx, "纱线配比"),
|
||||
WarpWeightGM: stripUnit(firstCol(row, idx, "经线克重(g/m)", "经线克重", "经纱克重")),
|
||||
WeftWeightGM: stripUnit(firstCol(row, idx, "纬线克重(g/m)", "纬线克重", "纬纱克重")),
|
||||
ProductionProcess: firstCol(row, idx, "生产工艺"),
|
||||
Remark: firstCol(row, idx, "备注"),
|
||||
})
|
||||
}
|
||||
|
||||
return &ParseResult{Products: products, SkippedRows: skipped}, nil
|
||||
}
|
||||
|
||||
func hasAny(idx map[string]int, names ...string) bool {
|
||||
for _, name := range names {
|
||||
if _, ok := idx[name]; ok {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func firstCol(row []string, idx map[string]int, names ...string) string {
|
||||
for _, name := range names {
|
||||
col, ok := idx[name]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if val := colVal(row, col); val != "" {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
@ -17,6 +17,7 @@ import (
|
||||
var parsers []FormatParser
|
||||
|
||||
func init() {
|
||||
parsers = append(parsers, &NativeProductParser{})
|
||||
parsers = append(parsers, &MiaozhangParser{})
|
||||
}
|
||||
|
||||
|
||||
@ -12,10 +12,17 @@ type ParsedProduct struct {
|
||||
ProductName string
|
||||
Spec string
|
||||
Color string
|
||||
ColorNo string
|
||||
ProductCode string
|
||||
UnitRolls int64
|
||||
StockQuantity string
|
||||
CostPrice string
|
||||
SalesPrice string
|
||||
BatchNo string
|
||||
YarnRatio string
|
||||
WarpWeightGM string
|
||||
WeftWeightGM string
|
||||
ProductionProcess string
|
||||
Remark string
|
||||
}
|
||||
|
||||
|
||||
140
rpc/inventory/internal/logic/batch_helpers.go
Normal file
140
rpc/inventory/internal/logic/batch_helpers.go
Normal file
@ -0,0 +1,140 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
)
|
||||
|
||||
func nullString(v string) sql.NullString {
|
||||
return sql.NullString{String: v, Valid: v != ""}
|
||||
}
|
||||
|
||||
func nullableJSON(v string) sql.NullString {
|
||||
v = strings.TrimSpace(v)
|
||||
return sql.NullString{String: v, Valid: v != ""}
|
||||
}
|
||||
|
||||
func parseDecimalString(v string) float64 {
|
||||
n, _ := strconv.ParseFloat(strings.TrimSpace(v), 64)
|
||||
return n
|
||||
}
|
||||
|
||||
type yarnRatioPayload struct {
|
||||
Warp []yarnRatioItem `json:"warp"`
|
||||
Weft []yarnRatioItem `json:"weft"`
|
||||
}
|
||||
|
||||
type yarnRatioItem struct {
|
||||
YarnID string `json:"yarn_id"`
|
||||
YarnId string `json:"yarnId"`
|
||||
Ratio float64 `json:"ratio"`
|
||||
}
|
||||
|
||||
func (i yarnRatioItem) yarnID() string {
|
||||
if i.YarnID != "" {
|
||||
return i.YarnID
|
||||
}
|
||||
return i.YarnId
|
||||
}
|
||||
|
||||
func validateProductBatchInput(batch *pb.ProductBatchInput) error {
|
||||
if batch == nil {
|
||||
return nil
|
||||
}
|
||||
return validateYarnRatio(batch.YarnRatio)
|
||||
}
|
||||
|
||||
func validateYarnRatio(raw string) error {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return nil
|
||||
}
|
||||
var payload yarnRatioPayload
|
||||
if err := json.Unmarshal([]byte(raw), &payload); err != nil {
|
||||
return fmt.Errorf("纱线配比格式无效")
|
||||
}
|
||||
if err := validateYarnRatioSide("经线", payload.Warp); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateYarnRatioSide("纬线", payload.Weft)
|
||||
}
|
||||
|
||||
func validateYarnRatioSide(label string, items []yarnRatioItem) error {
|
||||
if len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
total := 0
|
||||
for _, item := range items {
|
||||
if strings.TrimSpace(item.yarnID()) == "" {
|
||||
return fmt.Errorf("%s纱线不能为空", label)
|
||||
}
|
||||
if item.Ratio < 1 || item.Ratio > 10 || math.Trunc(item.Ratio) != item.Ratio {
|
||||
return fmt.Errorf("%s使用配比必须为 1-10 的整数", label)
|
||||
}
|
||||
total += int(item.Ratio)
|
||||
}
|
||||
if total != 10 {
|
||||
return fmt.Errorf("%s配比合计必须为 10,当前为 %d", label, total)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func batchInputToModelFields(batch *pb.ProductBatchInput, target *model.InvProductBatch) error {
|
||||
if batch == nil {
|
||||
target.ProductionProcess = nullString(defaultProcess)
|
||||
return nil
|
||||
}
|
||||
if err := validateProductBatchInput(batch); err != nil {
|
||||
return err
|
||||
}
|
||||
target.YarnRatio = nullableJSON(batch.YarnRatio)
|
||||
target.WarpWeightGM = parseDecimalString(batch.WarpWeightGM)
|
||||
target.WeftWeightGM = parseDecimalString(batch.WeftWeightGM)
|
||||
process := strings.TrimSpace(batch.ProductionProcess)
|
||||
if process == "" {
|
||||
process = defaultProcess
|
||||
}
|
||||
target.ProductionProcess = nullString(process)
|
||||
target.Remark = nullString(batch.Remark)
|
||||
return nil
|
||||
}
|
||||
|
||||
func batchToPb(batch *model.InvProductBatch) *pb.ProductBatchInfo {
|
||||
return &pb.ProductBatchInfo{
|
||||
BatchId: batch.BatchId,
|
||||
ProductId: batch.ProductId,
|
||||
BatchNo: batch.BatchNo,
|
||||
YarnRatio: batch.YarnRatio.String,
|
||||
WarpWeightGM: fmt.Sprintf("%.4f", batch.WarpWeightGM),
|
||||
WeftWeightGM: fmt.Sprintf("%.4f", batch.WeftWeightGM),
|
||||
ProductionProcess: batch.ProductionProcess.String,
|
||||
Remark: batch.Remark.String,
|
||||
Status: batch.Status,
|
||||
CreatedAt: batch.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: batch.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
}
|
||||
|
||||
func productToPb(product *model.InvProduct) *pb.ProductInfo {
|
||||
return &pb.ProductInfo{
|
||||
ProductId: product.ProductId,
|
||||
ProductName: product.ProductName,
|
||||
ImageUrl: product.ImageUrl,
|
||||
Spec: product.Spec,
|
||||
Color: product.Color,
|
||||
SalesPrice: fmt.Sprintf("%.2f", product.SalesPrice),
|
||||
Remark: product.Remark.String,
|
||||
Status: product.Status,
|
||||
CreatedAt: product.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: product.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
ColorNo: product.ColorNo,
|
||||
ProductCode: product.ProductCode,
|
||||
}
|
||||
}
|
||||
56
rpc/inventory/internal/logic/batch_helpers_test.go
Normal file
56
rpc/inventory/internal/logic/batch_helpers_test.go
Normal file
@ -0,0 +1,56 @@
|
||||
package logic
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValidateYarnRatio(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
raw string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty ratio is valid",
|
||||
raw: "",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "both sides add up to ten",
|
||||
raw: `{"warp":[{"yarn_id":"y1","ratio":6},{"yarn_id":"y2","ratio":4}],"weft":[{"yarn_id":"y3","ratio":10}]}`,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "one empty side is valid",
|
||||
raw: `{"warp":[{"yarn_id":"y1","ratio":10}],"weft":[]}`,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "side with yarn must add up to ten",
|
||||
raw: `{"warp":[{"yarn_id":"y1","ratio":9}],"weft":[]}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "ratio must be integer",
|
||||
raw: `{"warp":[{"yarn_id":"y1","ratio":9.5}],"weft":[]}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "yarn id is required",
|
||||
raw: `{"warp":[{"ratio":10}],"weft":[]}`,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid json is rejected",
|
||||
raw: `{warp}`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateYarnRatio(tt.raw)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("validateYarnRatio() error = %v, wantErr %v", err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
56
rpc/inventory/internal/logic/createproductbatchlogic.go
Normal file
56
rpc/inventory/internal/logic/createproductbatchlogic.go
Normal file
@ -0,0 +1,56 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/pkg/uid"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type CreateProductBatchLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCreateProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateProductBatchLogic {
|
||||
return &CreateProductBatchLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *CreateProductBatchLogic) CreateProductBatch(in *pb.CreateProductBatchReq) (*pb.IdResp, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
product, err := l.svcCtx.ProductModel.FindOneByProductId(l.ctx, in.ProductId)
|
||||
if err != nil || product.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "product not found")
|
||||
}
|
||||
batchNo, err := l.svcCtx.ProductBatchModel.NextBatchNo(l.ctx, tenantId, product.ProductCode)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
if requestedBatchNo := strings.TrimSpace(in.Batch.GetBatchNo()); requestedBatchNo != "" {
|
||||
batchNo = requestedBatchNo
|
||||
}
|
||||
batchId := uid.Generate()
|
||||
batch := &model.InvProductBatch{
|
||||
BatchId: batchId,
|
||||
TenantId: tenantId,
|
||||
ProductId: product.ProductId,
|
||||
BatchNo: batchNo,
|
||||
Status: 1,
|
||||
}
|
||||
if err := batchInputToModelFields(in.Batch, batch); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
if _, err := l.svcCtx.ProductBatchModel.Insert(l.ctx, batch); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return &pb.IdResp{Id: batchId}, nil
|
||||
}
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
"muyu-apiserver/pkg/metrics"
|
||||
@ -35,23 +36,64 @@ func (l *CreateProductLogic) CreateProduct(in *pb.CreateProductReq) (*pb.IdResp,
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
productId := uid.Generate()
|
||||
salesPrice, _ := strconv.ParseFloat(in.SalesPrice, 64)
|
||||
productName := strings.TrimSpace(in.ProductName)
|
||||
if productName == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "产品名称不能为空")
|
||||
}
|
||||
color := normalizeProductColor(in.Color)
|
||||
colorNo, err := resolveColorNo(l.ctx, l.svcCtx.ProductModel, tenantId, productName, in.ColorNo)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
productCode := strings.ToUpper(strings.TrimSpace(in.ProductCode))
|
||||
if productCode == "" {
|
||||
productCode = generateProductCode(productName, colorNo)
|
||||
}
|
||||
if err := ensureProductCodeAvailable(l.ctx, l.svcCtx.ProductModel, tenantId, productCode); err != nil {
|
||||
return nil, status.Error(codes.AlreadyExists, err.Error())
|
||||
}
|
||||
if err := validateProductBatchInput(in.InitialBatch); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
|
||||
product := &model.InvProduct{
|
||||
ProductId: productId,
|
||||
TenantId: tenantId,
|
||||
ProductName: in.ProductName,
|
||||
ProductName: productName,
|
||||
ImageUrl: in.ImageUrl,
|
||||
Spec: in.Spec,
|
||||
Color: in.Color,
|
||||
Color: color,
|
||||
ColorNo: colorNo,
|
||||
ProductCode: productCode,
|
||||
SalesPrice: salesPrice,
|
||||
Remark: sql.NullString{String: in.Remark, Valid: in.Remark != ""},
|
||||
Status: 1,
|
||||
}
|
||||
|
||||
_, err := l.svcCtx.ProductModel.Insert(l.ctx, product)
|
||||
_, err = l.svcCtx.ProductModel.Insert(l.ctx, product)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
firstBatchNo := productCode + "-B001"
|
||||
if batchNo := strings.TrimSpace(in.InitialBatch.GetBatchNo()); batchNo != "" {
|
||||
firstBatchNo = batchNo
|
||||
}
|
||||
|
||||
firstBatch := &model.InvProductBatch{
|
||||
BatchId: uid.Generate(),
|
||||
TenantId: tenantId,
|
||||
ProductId: productId,
|
||||
BatchNo: firstBatchNo,
|
||||
Status: 1,
|
||||
}
|
||||
if err := batchInputToModelFields(in.InitialBatch, firstBatch); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
if _, err := l.svcCtx.ProductBatchModel.Insert(l.ctx, firstBatch); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
var totalLengthM float64
|
||||
var totalBolts int
|
||||
|
||||
@ -59,12 +101,21 @@ func (l *CreateProductLogic) CreateProduct(in *pb.CreateProductReq) (*pb.IdResp,
|
||||
pans := make([]*model.InvProductPan, 0, len(in.Pans))
|
||||
for i, panInput := range in.Pans {
|
||||
panId := uid.Generate()
|
||||
batchId := panInput.BatchId
|
||||
if batchId == "" {
|
||||
batchId = firstBatch.BatchId
|
||||
}
|
||||
if _, err := l.svcCtx.ProductBatchModel.FindOneByProductAndBatchId(l.ctx, tenantId, productId, batchId); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "批次不存在或不属于该产品")
|
||||
}
|
||||
pans = append(pans, &model.InvProductPan{
|
||||
PanId: panId,
|
||||
ProductId: productId,
|
||||
BatchId: batchId,
|
||||
Name: panInput.Name,
|
||||
Position: panInput.Position,
|
||||
SortOrder: int64(i + 1),
|
||||
TenantId: tenantId,
|
||||
})
|
||||
|
||||
if len(panInput.BoltLengths) > 0 {
|
||||
@ -76,23 +127,25 @@ func (l *CreateProductLogic) CreateProduct(in *pb.CreateProductReq) (*pb.IdResp,
|
||||
BoltId: uid.Generate(),
|
||||
PanId: panId,
|
||||
LengthM: lengthM,
|
||||
Color: color,
|
||||
SortOrder: int64(j + 1),
|
||||
TenantId: tenantId,
|
||||
})
|
||||
}
|
||||
totalBolts += len(panInput.BoltLengths)
|
||||
if err := l.svcCtx.BoltModel.BulkReplace(l.ctx, panId, "", bolts); err != nil {
|
||||
if err := l.svcCtx.BoltModel.BulkReplace(l.ctx, panId, tenantId, bolts); err != nil {
|
||||
l.Errorf("create bolts for pan %s failed: %v", panId, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := l.svcCtx.PanModel.BulkReplace(l.ctx, productId, "", pans); err != nil {
|
||||
if err := l.svcCtx.PanModel.BulkReplace(l.ctx, productId, tenantId, pans); err != nil {
|
||||
l.Errorf("create pans for product %s failed: %v", productId, err)
|
||||
}
|
||||
}
|
||||
|
||||
metrics.ProductCreatedTotal.WithLabelValues(tenantId, in.ProductName, in.Spec, in.Color).Inc()
|
||||
metrics.ProductCreatedTotal.WithLabelValues(tenantId, productName, in.Spec, color).Inc()
|
||||
if totalLengthM > 0 {
|
||||
metrics.InboundLengthMetersTotal.WithLabelValues(tenantId, in.ProductName, in.Color, "create").Add(totalLengthM)
|
||||
metrics.InboundLengthMetersTotal.WithLabelValues(tenantId, productName, color, "create").Add(totalLengthM)
|
||||
}
|
||||
if totalBolts > 0 {
|
||||
metrics.InboundBoltsTotal.WithLabelValues(tenantId, "create").Add(float64(totalBolts))
|
||||
|
||||
68
rpc/inventory/internal/logic/createyarnlogic.go
Normal file
68
rpc/inventory/internal/logic/createyarnlogic.go
Normal file
@ -0,0 +1,68 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/pkg/uid"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type CreateYarnLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewCreateYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *CreateYarnLogic {
|
||||
return &CreateYarnLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *CreateYarnLogic) CreateYarn(in *pb.CreateYarnReq) (*pb.IdResp, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
yarnName := strings.TrimSpace(in.YarnName)
|
||||
if yarnName == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "纱线名称不能为空")
|
||||
}
|
||||
if strings.TrimSpace(in.SupplierId) == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "供应商不能为空")
|
||||
}
|
||||
supplier, err := l.svcCtx.SupplierModel.FindOneBySupplierId(l.ctx, in.SupplierId)
|
||||
if err != nil || supplier.TenantId != tenantId {
|
||||
return nil, status.Error(codes.InvalidArgument, "供应商不存在")
|
||||
}
|
||||
|
||||
color := normalizeYarnColor(in.Color)
|
||||
if _, err := l.svcCtx.YarnModel.FindOneByUnique(l.ctx, tenantId, yarnName, color, in.SupplierId); err == nil {
|
||||
return nil, status.Error(codes.AlreadyExists, "纱线已存在")
|
||||
} else if !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
yarnId := uid.Generate()
|
||||
yarn := &model.InvYarn{
|
||||
YarnId: yarnId,
|
||||
TenantId: tenantId,
|
||||
YarnName: yarnName,
|
||||
Color: color,
|
||||
WeightGM: parseDecimalString(in.WeightGM),
|
||||
SupplierId: in.SupplierId,
|
||||
DyeFactory: normalizeDyeFactory(in.DyeFactory),
|
||||
ImageUrl: in.ImageUrl,
|
||||
Remark: sql.NullString{String: in.Remark, Valid: in.Remark != ""},
|
||||
Status: 1,
|
||||
}
|
||||
if _, err := l.svcCtx.YarnModel.Insert(l.ctx, yarn); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return &pb.IdResp{Id: yarnId}, nil
|
||||
}
|
||||
46
rpc/inventory/internal/logic/deleteproductbatchlogic.go
Normal file
46
rpc/inventory/internal/logic/deleteproductbatchlogic.go
Normal file
@ -0,0 +1,46 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type DeleteProductBatchLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewDeleteProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteProductBatchLogic {
|
||||
return &DeleteProductBatchLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *DeleteProductBatchLogic) DeleteProductBatch(in *pb.DeleteProductBatchReq) (*pb.Empty, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
batch, err := l.svcCtx.ProductBatchModel.FindOneByProductAndBatchId(l.ctx, tenantId, in.ProductId, in.BatchId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.NotFound, "批次不存在")
|
||||
}
|
||||
if strings.HasSuffix(batch.BatchNo, "-B001") {
|
||||
return nil, status.Error(codes.FailedPrecondition, "B001 批次不可删除")
|
||||
}
|
||||
count, err := l.svcCtx.PanModel.CountByBatchId(l.ctx, in.BatchId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
if count > 0 {
|
||||
return nil, status.Error(codes.FailedPrecondition, "批次已有库存引用,不能删除")
|
||||
}
|
||||
if err := l.svcCtx.ProductBatchModel.SoftDeleteByBatchId(l.ctx, in.BatchId); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return &pb.Empty{}, nil
|
||||
}
|
||||
35
rpc/inventory/internal/logic/deleteyarnlogic.go
Normal file
35
rpc/inventory/internal/logic/deleteyarnlogic.go
Normal file
@ -0,0 +1,35 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type DeleteYarnLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewDeleteYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *DeleteYarnLogic {
|
||||
return &DeleteYarnLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *DeleteYarnLogic) DeleteYarn(in *pb.DeleteYarnReq) (*pb.Empty, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
yarn, err := l.svcCtx.YarnModel.FindOneByYarnId(l.ctx, in.YarnId)
|
||||
if err != nil || yarn.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "纱线不存在")
|
||||
}
|
||||
if err := l.svcCtx.YarnModel.SoftDeleteByYarnId(l.ctx, in.YarnId); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return &pb.Empty{}, nil
|
||||
}
|
||||
@ -2,7 +2,6 @@ package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
@ -32,16 +31,5 @@ func (l *GetProductLogic) GetProduct(in *pb.GetProductReq) (*pb.ProductInfo, err
|
||||
return nil, status.Error(codes.NotFound, "product not found")
|
||||
}
|
||||
|
||||
return &pb.ProductInfo{
|
||||
ProductId: product.ProductId,
|
||||
ProductName: product.ProductName,
|
||||
ImageUrl: product.ImageUrl,
|
||||
Spec: product.Spec,
|
||||
Color: product.Color,
|
||||
SalesPrice: fmt.Sprintf("%.2f", product.SalesPrice),
|
||||
Remark: product.Remark.String,
|
||||
Status: product.Status,
|
||||
CreatedAt: product.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: product.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
}, nil
|
||||
return productToPb(product), nil
|
||||
}
|
||||
|
||||
@ -36,6 +36,14 @@ func (l *GetProductTreeLogic) GetProductTree(in *pb.GetProductReq) (*pb.ProductT
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
batches, err := l.svcCtx.ProductBatchModel.FindByProductId(l.ctx, product.TenantId, in.ProductId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
batchNoById := make(map[string]string, len(batches))
|
||||
for _, b := range batches {
|
||||
batchNoById[b.BatchId] = b.BatchNo
|
||||
}
|
||||
|
||||
var totalBoltCount int64
|
||||
var totalLengthM float64
|
||||
@ -65,6 +73,8 @@ func (l *GetProductTreeLogic) GetProductTree(in *pb.GetProductReq) (*pb.ProductT
|
||||
pbPans = append(pbPans, &pb.PanInfo{
|
||||
PanId: pan.PanId,
|
||||
ProductId: pan.ProductId,
|
||||
BatchId: pan.BatchId,
|
||||
BatchNo: batchNoById[pan.BatchId],
|
||||
Name: pan.Name,
|
||||
Position: pan.Position,
|
||||
SortOrder: pan.SortOrder,
|
||||
@ -75,18 +85,7 @@ func (l *GetProductTreeLogic) GetProductTree(in *pb.GetProductReq) (*pb.ProductT
|
||||
}
|
||||
|
||||
return &pb.ProductTreeResp{
|
||||
Product: &pb.ProductInfo{
|
||||
ProductId: product.ProductId,
|
||||
ProductName: product.ProductName,
|
||||
ImageUrl: product.ImageUrl,
|
||||
Spec: product.Spec,
|
||||
Color: product.Color,
|
||||
SalesPrice: fmt.Sprintf("%.2f", product.SalesPrice),
|
||||
Remark: product.Remark.String,
|
||||
Status: product.Status,
|
||||
CreatedAt: product.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: product.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
},
|
||||
Product: productToPb(product),
|
||||
Pans: pbPans,
|
||||
TotalPanCount: int64(len(pans)),
|
||||
TotalBoltCount: totalBoltCount,
|
||||
|
||||
36
rpc/inventory/internal/logic/getyarnlogic.go
Normal file
36
rpc/inventory/internal/logic/getyarnlogic.go
Normal file
@ -0,0 +1,36 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type GetYarnLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewGetYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *GetYarnLogic {
|
||||
return &GetYarnLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *GetYarnLogic) GetYarn(in *pb.GetYarnReq) (*pb.YarnInfo, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
yarn, err := l.svcCtx.YarnModel.FindOneByYarnId(l.ctx, in.YarnId)
|
||||
if err != nil || yarn.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "纱线不存在")
|
||||
}
|
||||
supplierName := ""
|
||||
if supplier, err := l.svcCtx.SupplierModel.FindOneBySupplierId(l.ctx, yarn.SupplierId); err == nil {
|
||||
supplierName = supplier.SupplierName
|
||||
}
|
||||
return yarnToPb(yarn, supplierName), nil
|
||||
}
|
||||
@ -3,7 +3,7 @@ package logic
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strconv"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
@ -33,33 +33,20 @@ func NewImportProductsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *Im
|
||||
}
|
||||
|
||||
func (l *ImportProductsLogic) ImportProducts(in *pb.ImportProductReq) (*pb.ImportProductResp, error) {
|
||||
products := make([]*model.InvProduct, 0, len(in.Products))
|
||||
totalCount := int64(len(in.Products))
|
||||
var successCount int64
|
||||
errs := make([]string, 0)
|
||||
creator := NewCreateProductLogic(l.ctx, l.svcCtx)
|
||||
|
||||
for _, p := range in.Products {
|
||||
salesPrice, _ := strconv.ParseFloat(p.SalesPrice, 64)
|
||||
|
||||
products = append(products, &model.InvProduct{
|
||||
ProductId: uid.Generate(),
|
||||
ProductName: p.ProductName,
|
||||
ImageUrl: p.ImageUrl,
|
||||
Spec: p.Spec,
|
||||
Color: p.Color,
|
||||
SalesPrice: salesPrice,
|
||||
Remark: sql.NullString{String: p.Remark, Valid: p.Remark != ""},
|
||||
Status: 1,
|
||||
})
|
||||
for i, p := range in.Products {
|
||||
if _, err := creator.CreateProduct(p); err != nil {
|
||||
errs = append(errs, fmt.Sprintf("第%d行: %v", i+1, err))
|
||||
continue
|
||||
}
|
||||
|
||||
totalCount := int64(len(products))
|
||||
affected, err := l.svcCtx.ProductModel.BulkInsert(l.ctx, products)
|
||||
|
||||
var errorMsg string
|
||||
successCount := affected
|
||||
failCount := totalCount - affected
|
||||
if err != nil {
|
||||
errorMsg = err.Error()
|
||||
l.Errorf("bulk insert partial failure: %v", err)
|
||||
successCount++
|
||||
}
|
||||
failCount := totalCount - successCount
|
||||
errorMsg := strings.Join(errs, "; ")
|
||||
|
||||
importId := uid.Generate()
|
||||
importLog := &model.InvStockImportLog{
|
||||
@ -74,7 +61,7 @@ func (l *ImportProductsLogic) ImportProducts(in *pb.ImportProductReq) (*pb.Impor
|
||||
|
||||
if _, logErr := l.svcCtx.ImportLogModel.Insert(l.ctx, importLog); logErr != nil {
|
||||
l.Errorf("save import log failed: %v", logErr)
|
||||
if err != nil {
|
||||
if errorMsg != "" {
|
||||
return nil, status.Error(codes.Internal, strings.Join([]string{errorMsg, logErr.Error()}, "; "))
|
||||
}
|
||||
return nil, status.Error(codes.Internal, logErr.Error())
|
||||
@ -87,9 +74,6 @@ func (l *ImportProductsLogic) ImportProducts(in *pb.ImportProductReq) (*pb.Impor
|
||||
metrics.ImportProductsTotal.WithLabelValues(tenantId, "fail").Add(float64(failCount))
|
||||
}
|
||||
metrics.ImportBatchSize.WithLabelValues(tenantId).Observe(float64(totalCount))
|
||||
for _, p := range in.Products {
|
||||
metrics.ProductCreatedTotal.WithLabelValues(tenantId, p.ProductName, p.Spec, p.Color).Inc()
|
||||
}
|
||||
|
||||
return &pb.ImportProductResp{
|
||||
TotalCount: totalCount,
|
||||
|
||||
@ -40,10 +40,13 @@ func (l *ListColorDetailLogic) ListColorDetail(in *pb.ListColorDetailReq) (*pb.L
|
||||
ProductId: d.ProductId,
|
||||
ProductName: d.ProductName,
|
||||
Color: d.Color,
|
||||
ColorNo: d.ColorNo,
|
||||
ProductCode: d.ProductCode,
|
||||
Spec: d.Spec,
|
||||
ImageUrl: d.ImageUrl,
|
||||
PanCount: d.PanCount,
|
||||
BoltCount: d.BoltCount,
|
||||
BatchCount: d.BatchCount,
|
||||
TotalLengthM: fmt.Sprintf("%.2f", d.TotalLengthM),
|
||||
Locations: d.Locations,
|
||||
SalesPrice: fmt.Sprintf("%.2f", d.SalesPrice),
|
||||
|
||||
@ -27,10 +27,22 @@ func NewListPansLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListPans
|
||||
}
|
||||
|
||||
func (l *ListPansLogic) ListPans(in *pb.ListPanReq) (*pb.ListPanResp, error) {
|
||||
product, err := l.svcCtx.ProductModel.FindOneByProductId(l.ctx, in.ProductId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.NotFound, "product not found")
|
||||
}
|
||||
pans, err := l.svcCtx.PanModel.FindByProductId(l.ctx, in.ProductId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
batches, err := l.svcCtx.ProductBatchModel.FindByProductId(l.ctx, product.TenantId, in.ProductId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
batchNoById := make(map[string]string, len(batches))
|
||||
for _, b := range batches {
|
||||
batchNoById[b.BatchId] = b.BatchNo
|
||||
}
|
||||
|
||||
pbPans := make([]*pb.PanInfo, 0, len(pans))
|
||||
for _, pan := range pans {
|
||||
@ -52,6 +64,8 @@ func (l *ListPansLogic) ListPans(in *pb.ListPanReq) (*pb.ListPanResp, error) {
|
||||
pbPans = append(pbPans, &pb.PanInfo{
|
||||
PanId: pan.PanId,
|
||||
ProductId: pan.ProductId,
|
||||
BatchId: pan.BatchId,
|
||||
BatchNo: batchNoById[pan.BatchId],
|
||||
Name: pan.Name,
|
||||
Position: pan.Position,
|
||||
SortOrder: pan.SortOrder,
|
||||
|
||||
@ -39,6 +39,8 @@ func (l *ListPanViewLogic) ListPanView(in *pb.ListPanViewReq) (*pb.ListPanViewRe
|
||||
pbList = append(pbList, &pb.PanListItem{
|
||||
PanId: item.PanId,
|
||||
ProductId: item.ProductId,
|
||||
BatchId: item.BatchId,
|
||||
BatchNo: item.BatchNo,
|
||||
Name: item.Name,
|
||||
Position: item.Position,
|
||||
ProductName: item.ProductName,
|
||||
|
||||
40
rpc/inventory/internal/logic/listproductbatchlogic.go
Normal file
40
rpc/inventory/internal/logic/listproductbatchlogic.go
Normal file
@ -0,0 +1,40 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type ListProductBatchLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewListProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListProductBatchLogic {
|
||||
return &ListProductBatchLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *ListProductBatchLogic) ListProductBatch(in *pb.ListProductBatchReq) (*pb.ListProductBatchResp, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
product, err := l.svcCtx.ProductModel.FindOneByProductId(l.ctx, in.ProductId)
|
||||
if err != nil || product.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "product not found")
|
||||
}
|
||||
list, err := l.svcCtx.ProductBatchModel.FindByProductId(l.ctx, tenantId, in.ProductId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
pbList := make([]*pb.ProductBatchInfo, 0, len(list))
|
||||
for _, b := range list {
|
||||
pbList = append(pbList, batchToPb(b))
|
||||
}
|
||||
return &pb.ListProductBatchResp{List: pbList}, nil
|
||||
}
|
||||
@ -2,7 +2,6 @@ package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
@ -29,25 +28,14 @@ func NewListProductLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListP
|
||||
|
||||
func (l *ListProductLogic) ListProduct(in *pb.ListProductReq) (*pb.ListProductResp, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
list, total, err := l.svcCtx.ProductModel.FindList(l.ctx, tenantId, in.Page, in.PageSize, in.ProductName, in.Spec, in.Color, in.Status)
|
||||
list, total, err := l.svcCtx.ProductModel.FindList(l.ctx, tenantId, in.Page, in.PageSize, in.ProductName, in.Spec, in.Color, in.ColorNo, in.ProductCode, in.Status)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
pbList := make([]*pb.ProductInfo, 0, len(list))
|
||||
for _, p := range list {
|
||||
pbList = append(pbList, &pb.ProductInfo{
|
||||
ProductId: p.ProductId,
|
||||
ProductName: p.ProductName,
|
||||
ImageUrl: p.ImageUrl,
|
||||
Spec: p.Spec,
|
||||
Color: p.Color,
|
||||
SalesPrice: fmt.Sprintf("%.2f", p.SalesPrice),
|
||||
Remark: p.Remark.String,
|
||||
Status: p.Status,
|
||||
CreatedAt: p.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: p.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
})
|
||||
pbList = append(pbList, productToPb(p))
|
||||
}
|
||||
|
||||
return &pb.ListProductResp{
|
||||
|
||||
42
rpc/inventory/internal/logic/listyarnlogic.go
Normal file
42
rpc/inventory/internal/logic/listyarnlogic.go
Normal file
@ -0,0 +1,42 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type ListYarnLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewListYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ListYarnLogic {
|
||||
return &ListYarnLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *ListYarnLogic) ListYarn(in *pb.ListYarnReq) (*pb.ListYarnResp, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
list, total, err := l.svcCtx.YarnModel.FindList(l.ctx, tenantId, in.Page, in.PageSize, in.YarnName, in.SupplierId, in.Status)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
pbList := make([]*pb.YarnInfo, 0, len(list))
|
||||
for _, y := range list {
|
||||
supplierName := ""
|
||||
if supplier, err := l.svcCtx.SupplierModel.FindOneBySupplierId(l.ctx, y.SupplierId); err == nil {
|
||||
supplierName = supplier.SupplierName
|
||||
}
|
||||
pbList = append(pbList, yarnToPb(y, supplierName))
|
||||
}
|
||||
|
||||
return &pb.ListYarnResp{Total: total, List: pbList}, nil
|
||||
}
|
||||
101
rpc/inventory/internal/logic/productcode.go
Normal file
101
rpc/inventory/internal/logic/productcode.go
Normal file
@ -0,0 +1,101 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
|
||||
"github.com/mozillazg/go-pinyin"
|
||||
)
|
||||
|
||||
const (
|
||||
defaultProductColor = "本色"
|
||||
defaultYarnColor = "原纱本色"
|
||||
defaultDyeFactory = "无"
|
||||
defaultProcess = "无"
|
||||
)
|
||||
|
||||
func normalizeColorNo(colorNo string) string {
|
||||
return strings.ToUpper(strings.TrimSpace(colorNo))
|
||||
}
|
||||
|
||||
func normalizeProductColor(color string) string {
|
||||
color = strings.TrimSpace(color)
|
||||
if color == "" {
|
||||
return defaultProductColor
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
||||
func normalizeYarnColor(color string) string {
|
||||
color = strings.TrimSpace(color)
|
||||
if color == "" {
|
||||
return defaultYarnColor
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
||||
func normalizeDyeFactory(factory string) string {
|
||||
factory = strings.TrimSpace(factory)
|
||||
if factory == "" {
|
||||
return defaultDyeFactory
|
||||
}
|
||||
return factory
|
||||
}
|
||||
|
||||
func productNameCodePrefix(productName string) string {
|
||||
args := pinyin.NewArgs()
|
||||
args.Style = pinyin.Normal
|
||||
var b strings.Builder
|
||||
|
||||
for _, r := range productName {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z':
|
||||
b.WriteRune(unicode.ToUpper(r))
|
||||
case r >= 'A' && r <= 'Z':
|
||||
b.WriteRune(r)
|
||||
case r >= '0' && r <= '9':
|
||||
b.WriteRune(r)
|
||||
case unicode.Is(unicode.Han, r):
|
||||
py := pinyin.Pinyin(string(r), args)
|
||||
if len(py) > 0 && len(py[0]) > 0 && py[0][0] != "" {
|
||||
first := []rune(py[0][0])
|
||||
if len(first) > 0 {
|
||||
b.WriteRune(unicode.ToUpper(first[0]))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code := b.String()
|
||||
if code == "" {
|
||||
return "P"
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
func generateProductCode(productName, colorNo string) string {
|
||||
return productNameCodePrefix(productName) + normalizeColorNo(colorNo)
|
||||
}
|
||||
|
||||
func resolveColorNo(ctx context.Context, productModel model.InvProductModel, tenantId, productName, colorNo string) (string, error) {
|
||||
colorNo = normalizeColorNo(colorNo)
|
||||
if colorNo != "" {
|
||||
return colorNo, nil
|
||||
}
|
||||
return productModel.NextColorNo(ctx, tenantId, productName)
|
||||
}
|
||||
|
||||
func ensureProductCodeAvailable(ctx context.Context, productModel model.InvProductModel, tenantId, productCode string) error {
|
||||
_, err := productModel.FindOneByTenantProductCode(ctx, tenantId, productCode)
|
||||
if err == nil {
|
||||
return errors.New("产品码已存在")
|
||||
}
|
||||
if errors.Is(err, model.ErrNotFound) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
@ -32,6 +32,14 @@ func NewSaveBoltsLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SaveBol
|
||||
|
||||
func (l *SaveBoltsLogic) SaveBolts(in *pb.SaveBoltsReq) (*pb.Empty, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
pan, err := l.svcCtx.PanModel.FindOneByPanId(l.ctx, in.PanId)
|
||||
if err != nil || pan.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "pan not found")
|
||||
}
|
||||
product, err := l.svcCtx.ProductModel.FindOneByProductId(l.ctx, pan.ProductId)
|
||||
if err != nil || product.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "product not found")
|
||||
}
|
||||
|
||||
var totalLengthM float64
|
||||
bolts := make([]*model.InvProductBolt, 0, len(in.Lengths))
|
||||
@ -42,7 +50,9 @@ func (l *SaveBoltsLogic) SaveBolts(in *pb.SaveBoltsReq) (*pb.Empty, error) {
|
||||
BoltId: uid.Generate(),
|
||||
PanId: in.PanId,
|
||||
LengthM: lengthM,
|
||||
Color: product.Color,
|
||||
SortOrder: int64(i + 1),
|
||||
TenantId: tenantId,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -2,6 +2,7 @@ package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strconv"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
@ -33,19 +34,53 @@ func NewSavePansLogic(ctx context.Context, svcCtx *svc.ServiceContext) *SavePans
|
||||
func (l *SavePansLogic) SavePans(in *pb.SavePansReq) (*pb.Empty, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
|
||||
if err := l.svcCtx.BoltModel.DeleteByProductId(l.ctx, in.ProductId); err != nil {
|
||||
product, err := l.svcCtx.ProductModel.FindOneByProductId(l.ctx, in.ProductId)
|
||||
if err != nil || product.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "product not found")
|
||||
}
|
||||
|
||||
defaultBatch, err := l.svcCtx.ProductBatchModel.FindLatestByProductId(l.ctx, tenantId, in.ProductId)
|
||||
if err != nil {
|
||||
if !errors.Is(err, model.ErrNotFound) {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
defaultBatch = &model.InvProductBatch{
|
||||
BatchId: uid.Generate(),
|
||||
TenantId: tenantId,
|
||||
ProductId: in.ProductId,
|
||||
BatchNo: product.ProductCode + "-B001",
|
||||
Status: 1,
|
||||
}
|
||||
if err := batchInputToModelFields(nil, defaultBatch); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
if _, err := l.svcCtx.ProductBatchModel.Insert(l.ctx, defaultBatch); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
var totalLengthM float64
|
||||
var totalBolts int
|
||||
type panBoltSet struct {
|
||||
panId string
|
||||
bolts []*model.InvProductBolt
|
||||
}
|
||||
boltsByPan := make([]panBoltSet, 0, len(in.Pans))
|
||||
|
||||
pans := make([]*model.InvProductPan, 0, len(in.Pans))
|
||||
for i, panInput := range in.Pans {
|
||||
panId := uid.Generate()
|
||||
batchId := panInput.BatchId
|
||||
if batchId == "" {
|
||||
batchId = defaultBatch.BatchId
|
||||
}
|
||||
if _, err := l.svcCtx.ProductBatchModel.FindOneByProductAndBatchId(l.ctx, tenantId, in.ProductId, batchId); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, "批次不存在或不属于该产品")
|
||||
}
|
||||
pans = append(pans, &model.InvProductPan{
|
||||
PanId: panId,
|
||||
ProductId: in.ProductId,
|
||||
BatchId: batchId,
|
||||
Name: panInput.Name,
|
||||
Position: panInput.Position,
|
||||
SortOrder: int64(i + 1),
|
||||
@ -61,20 +96,30 @@ func (l *SavePansLogic) SavePans(in *pb.SavePansReq) (*pb.Empty, error) {
|
||||
BoltId: uid.Generate(),
|
||||
PanId: panId,
|
||||
LengthM: lengthM,
|
||||
Color: product.Color,
|
||||
SortOrder: int64(j + 1),
|
||||
TenantId: tenantId,
|
||||
})
|
||||
}
|
||||
totalBolts += len(panInput.BoltLengths)
|
||||
if err := l.svcCtx.BoltModel.BulkReplace(l.ctx, panId, tenantId, bolts); err != nil {
|
||||
boltsByPan = append(boltsByPan, panBoltSet{panId: panId, bolts: bolts})
|
||||
}
|
||||
}
|
||||
|
||||
if err := l.svcCtx.BoltModel.DeleteByProductId(l.ctx, in.ProductId); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := l.svcCtx.PanModel.BulkReplace(l.ctx, in.ProductId, tenantId, pans); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
|
||||
for _, item := range boltsByPan {
|
||||
if err := l.svcCtx.BoltModel.BulkReplace(l.ctx, item.panId, tenantId, item.bolts); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
metrics.PansSavedTotal.WithLabelValues(tenantId, in.ProductId).Inc()
|
||||
if totalLengthM > 0 {
|
||||
productName := l.resolveProductName(in.ProductId)
|
||||
|
||||
43
rpc/inventory/internal/logic/updateproductbatchlogic.go
Normal file
43
rpc/inventory/internal/logic/updateproductbatchlogic.go
Normal file
@ -0,0 +1,43 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type UpdateProductBatchLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewUpdateProductBatchLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateProductBatchLogic {
|
||||
return &UpdateProductBatchLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *UpdateProductBatchLogic) UpdateProductBatch(in *pb.UpdateProductBatchReq) (*pb.Empty, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
batch, err := l.svcCtx.ProductBatchModel.FindOneByProductAndBatchId(l.ctx, tenantId, in.ProductId, in.BatchId)
|
||||
if err != nil {
|
||||
return nil, status.Error(codes.NotFound, "批次不存在")
|
||||
}
|
||||
if err := batchInputToModelFields(in.Batch, batch); err != nil {
|
||||
return nil, status.Error(codes.InvalidArgument, err.Error())
|
||||
}
|
||||
if in.Status == 0 {
|
||||
batch.Status = 0
|
||||
} else {
|
||||
batch.Status = 1
|
||||
}
|
||||
if err := l.svcCtx.ProductBatchModel.Update(l.ctx, batch); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return &pb.Empty{}, nil
|
||||
}
|
||||
@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
@ -35,10 +36,18 @@ func (l *UpdateProductLogic) UpdateProduct(in *pb.UpdateProductReq) (*pb.Empty,
|
||||
|
||||
salesPrice, _ := strconv.ParseFloat(in.SalesPrice, 64)
|
||||
|
||||
product.ProductName = in.ProductName
|
||||
productName := strings.TrimSpace(in.ProductName)
|
||||
if productName == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "产品名称不能为空")
|
||||
}
|
||||
|
||||
product.ProductName = productName
|
||||
product.ImageUrl = in.ImageUrl
|
||||
product.Spec = in.Spec
|
||||
product.Color = in.Color
|
||||
product.Color = normalizeProductColor(in.Color)
|
||||
if strings.TrimSpace(in.ColorNo) != "" {
|
||||
product.ColorNo = normalizeColorNo(in.ColorNo)
|
||||
}
|
||||
product.SalesPrice = salesPrice
|
||||
product.Remark = sql.NullString{String: in.Remark, Valid: in.Remark != ""}
|
||||
|
||||
|
||||
62
rpc/inventory/internal/logic/updateyarnlogic.go
Normal file
62
rpc/inventory/internal/logic/updateyarnlogic.go
Normal file
@ -0,0 +1,62 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"strings"
|
||||
|
||||
"muyu-apiserver/pkg/tenantctx"
|
||||
"muyu-apiserver/rpc/inventory/internal/svc"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
type UpdateYarnLogic struct {
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
logx.Logger
|
||||
}
|
||||
|
||||
func NewUpdateYarnLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UpdateYarnLogic {
|
||||
return &UpdateYarnLogic{ctx: ctx, svcCtx: svcCtx, Logger: logx.WithContext(ctx)}
|
||||
}
|
||||
|
||||
func (l *UpdateYarnLogic) UpdateYarn(in *pb.UpdateYarnReq) (*pb.Empty, error) {
|
||||
tenantId := tenantctx.ExtractTenantId(l.ctx)
|
||||
yarn, err := l.svcCtx.YarnModel.FindOneByYarnId(l.ctx, in.YarnId)
|
||||
if err != nil || yarn.TenantId != tenantId {
|
||||
return nil, status.Error(codes.NotFound, "纱线不存在")
|
||||
}
|
||||
yarnName := strings.TrimSpace(in.YarnName)
|
||||
if yarnName == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "纱线名称不能为空")
|
||||
}
|
||||
if strings.TrimSpace(in.SupplierId) == "" {
|
||||
return nil, status.Error(codes.InvalidArgument, "供应商不能为空")
|
||||
}
|
||||
supplier, err := l.svcCtx.SupplierModel.FindOneBySupplierId(l.ctx, in.SupplierId)
|
||||
if err != nil || supplier.TenantId != tenantId {
|
||||
return nil, status.Error(codes.InvalidArgument, "供应商不存在")
|
||||
}
|
||||
|
||||
yarn.YarnName = yarnName
|
||||
yarn.Color = normalizeYarnColor(in.Color)
|
||||
yarn.WeightGM = parseDecimalString(in.WeightGM)
|
||||
yarn.SupplierId = in.SupplierId
|
||||
yarn.DyeFactory = normalizeDyeFactory(in.DyeFactory)
|
||||
yarn.ImageUrl = in.ImageUrl
|
||||
yarn.Remark = sql.NullString{String: in.Remark, Valid: in.Remark != ""}
|
||||
if in.Status == 0 {
|
||||
yarn.Status = 0
|
||||
} else {
|
||||
yarn.Status = 1
|
||||
}
|
||||
|
||||
if err := l.svcCtx.YarnModel.Update(l.ctx, yarn); err != nil {
|
||||
return nil, status.Error(codes.Internal, err.Error())
|
||||
}
|
||||
return &pb.Empty{}, nil
|
||||
}
|
||||
25
rpc/inventory/internal/logic/yarn_helpers.go
Normal file
25
rpc/inventory/internal/logic/yarn_helpers.go
Normal file
@ -0,0 +1,25 @@
|
||||
package logic
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"muyu-apiserver/model"
|
||||
"muyu-apiserver/rpc/inventory/pb"
|
||||
)
|
||||
|
||||
func yarnToPb(yarn *model.InvYarn, supplierName string) *pb.YarnInfo {
|
||||
return &pb.YarnInfo{
|
||||
YarnId: yarn.YarnId,
|
||||
YarnName: yarn.YarnName,
|
||||
Color: yarn.Color,
|
||||
WeightGM: fmt.Sprintf("%.4f", yarn.WeightGM),
|
||||
SupplierId: yarn.SupplierId,
|
||||
SupplierName: supplierName,
|
||||
DyeFactory: yarn.DyeFactory,
|
||||
ImageUrl: yarn.ImageUrl,
|
||||
Remark: yarn.Remark.String,
|
||||
Status: yarn.Status,
|
||||
CreatedAt: yarn.CreatedAt.Format("2006-01-02 15:04:05"),
|
||||
UpdatedAt: yarn.UpdatedAt.Format("2006-01-02 15:04:05"),
|
||||
}
|
||||
}
|
||||
@ -67,6 +67,53 @@ func (s *InventoryServiceServer) ListColorDetail(ctx context.Context, in *pb.Lis
|
||||
return l.ListColorDetail(in)
|
||||
}
|
||||
|
||||
// Yarn CRUD
|
||||
func (s *InventoryServiceServer) CreateYarn(ctx context.Context, in *pb.CreateYarnReq) (*pb.IdResp, error) {
|
||||
l := logic.NewCreateYarnLogic(ctx, s.svcCtx)
|
||||
return l.CreateYarn(in)
|
||||
}
|
||||
|
||||
func (s *InventoryServiceServer) UpdateYarn(ctx context.Context, in *pb.UpdateYarnReq) (*pb.Empty, error) {
|
||||
l := logic.NewUpdateYarnLogic(ctx, s.svcCtx)
|
||||
return l.UpdateYarn(in)
|
||||
}
|
||||
|
||||
func (s *InventoryServiceServer) DeleteYarn(ctx context.Context, in *pb.DeleteYarnReq) (*pb.Empty, error) {
|
||||
l := logic.NewDeleteYarnLogic(ctx, s.svcCtx)
|
||||
return l.DeleteYarn(in)
|
||||
}
|
||||
|
||||
func (s *InventoryServiceServer) GetYarn(ctx context.Context, in *pb.GetYarnReq) (*pb.YarnInfo, error) {
|
||||
l := logic.NewGetYarnLogic(ctx, s.svcCtx)
|
||||
return l.GetYarn(in)
|
||||
}
|
||||
|
||||
func (s *InventoryServiceServer) ListYarn(ctx context.Context, in *pb.ListYarnReq) (*pb.ListYarnResp, error) {
|
||||
l := logic.NewListYarnLogic(ctx, s.svcCtx)
|
||||
return l.ListYarn(in)
|
||||
}
|
||||
|
||||
// Product Batch CRUD
|
||||
func (s *InventoryServiceServer) CreateProductBatch(ctx context.Context, in *pb.CreateProductBatchReq) (*pb.IdResp, error) {
|
||||
l := logic.NewCreateProductBatchLogic(ctx, s.svcCtx)
|
||||
return l.CreateProductBatch(in)
|
||||
}
|
||||
|
||||
func (s *InventoryServiceServer) UpdateProductBatch(ctx context.Context, in *pb.UpdateProductBatchReq) (*pb.Empty, error) {
|
||||
l := logic.NewUpdateProductBatchLogic(ctx, s.svcCtx)
|
||||
return l.UpdateProductBatch(in)
|
||||
}
|
||||
|
||||
func (s *InventoryServiceServer) DeleteProductBatch(ctx context.Context, in *pb.DeleteProductBatchReq) (*pb.Empty, error) {
|
||||
l := logic.NewDeleteProductBatchLogic(ctx, s.svcCtx)
|
||||
return l.DeleteProductBatch(in)
|
||||
}
|
||||
|
||||
func (s *InventoryServiceServer) ListProductBatch(ctx context.Context, in *pb.ListProductBatchReq) (*pb.ListProductBatchResp, error) {
|
||||
l := logic.NewListProductBatchLogic(ctx, s.svcCtx)
|
||||
return l.ListProductBatch(in)
|
||||
}
|
||||
|
||||
// Pan / Bolt list views
|
||||
func (s *InventoryServiceServer) ListPanView(ctx context.Context, in *pb.ListPanViewReq) (*pb.ListPanViewResp, error) {
|
||||
l := logic.NewListPanViewLogic(ctx, s.svcCtx)
|
||||
|
||||
@ -10,8 +10,11 @@ import (
|
||||
type ServiceContext struct {
|
||||
Config config.Config
|
||||
ProductModel model.InvProductModel
|
||||
YarnModel model.InvYarnModel
|
||||
ProductBatchModel model.InvProductBatchModel
|
||||
PanModel model.InvProductPanModel
|
||||
BoltModel model.InvProductBoltModel
|
||||
SupplierModel model.PurSupplierModel
|
||||
ImportLogModel model.InvStockImportLogModel
|
||||
StockCheckModel model.InvStockCheckModel
|
||||
CheckDetailModel model.InvStockCheckDetailModel
|
||||
@ -24,8 +27,11 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
||||
return &ServiceContext{
|
||||
Config: c,
|
||||
ProductModel: model.NewInvProductModel(conn, c.Cache),
|
||||
YarnModel: model.NewInvYarnModel(conn),
|
||||
ProductBatchModel: model.NewInvProductBatchModel(conn),
|
||||
PanModel: model.NewInvProductPanModel(conn),
|
||||
BoltModel: model.NewInvProductBoltModel(conn),
|
||||
SupplierModel: model.NewPurSupplierModel(conn, c.Cache),
|
||||
ImportLogModel: model.NewInvStockImportLogModel(conn, c.Cache),
|
||||
StockCheckModel: model.NewInvStockCheckModel(conn, c.Cache),
|
||||
CheckDetailModel: model.NewInvStockCheckDetailModel(conn, c.Cache),
|
||||
|
||||
@ -17,6 +17,8 @@ message ProductInfo {
|
||||
int64 status = 8;
|
||||
string created_at = 9;
|
||||
string updated_at = 10;
|
||||
string color_no = 11;
|
||||
string product_code = 12;
|
||||
}
|
||||
|
||||
message CreateProductReq {
|
||||
@ -27,6 +29,9 @@ message CreateProductReq {
|
||||
string sales_price = 5;
|
||||
string remark = 6;
|
||||
repeated PanInput pans = 7;
|
||||
string color_no = 8;
|
||||
ProductBatchInput initial_batch = 9;
|
||||
string product_code = 10;
|
||||
}
|
||||
|
||||
message UpdateProductReq {
|
||||
@ -37,6 +42,7 @@ message UpdateProductReq {
|
||||
string color = 5;
|
||||
string sales_price = 6;
|
||||
string remark = 7;
|
||||
string color_no = 8;
|
||||
}
|
||||
|
||||
message DeleteProductReq {
|
||||
@ -54,6 +60,8 @@ message ListProductReq {
|
||||
string spec = 4;
|
||||
string color = 5;
|
||||
int64 status = 6;
|
||||
string color_no = 7;
|
||||
string product_code = 8;
|
||||
}
|
||||
|
||||
message ListProductResp {
|
||||
@ -72,12 +80,15 @@ message PanInfo {
|
||||
repeated BoltInfo bolts = 6;
|
||||
int64 bolt_count = 7;
|
||||
string total_length = 8;
|
||||
string batch_id = 9;
|
||||
string batch_no = 10;
|
||||
}
|
||||
|
||||
message PanInput {
|
||||
string name = 1;
|
||||
string position = 2;
|
||||
repeated string bolt_lengths = 3;
|
||||
string batch_id = 4;
|
||||
}
|
||||
|
||||
message ListPanReq {
|
||||
@ -157,6 +168,9 @@ message ColorDetailItem {
|
||||
string total_length_m = 8;
|
||||
string locations = 9;
|
||||
string sales_price = 10;
|
||||
string color_no = 11;
|
||||
string product_code = 12;
|
||||
int64 batch_count = 13;
|
||||
}
|
||||
|
||||
message ListColorDetailReq {
|
||||
@ -178,6 +192,8 @@ message PanListItem {
|
||||
string colors = 6; // aggregated from bolt.color, no spec
|
||||
int64 bolt_count = 7;
|
||||
string total_length_m = 8;
|
||||
string batch_id = 9;
|
||||
string batch_no = 10;
|
||||
}
|
||||
|
||||
message ListPanViewReq {
|
||||
@ -242,6 +258,116 @@ message ListImportLogResp {
|
||||
repeated ImportLogInfo list = 2;
|
||||
}
|
||||
|
||||
// ==================== Yarn ====================
|
||||
|
||||
message YarnInfo {
|
||||
string yarn_id = 1;
|
||||
string yarn_name = 2;
|
||||
string color = 3;
|
||||
string weight_g_m = 4;
|
||||
string supplier_id = 5;
|
||||
string supplier_name = 6;
|
||||
string dye_factory = 7;
|
||||
string image_url = 8;
|
||||
string remark = 9;
|
||||
int64 status = 10;
|
||||
string created_at = 11;
|
||||
string updated_at = 12;
|
||||
}
|
||||
|
||||
message CreateYarnReq {
|
||||
string yarn_name = 1;
|
||||
string color = 2;
|
||||
string weight_g_m = 3;
|
||||
string supplier_id = 4;
|
||||
string dye_factory = 5;
|
||||
string image_url = 6;
|
||||
string remark = 7;
|
||||
}
|
||||
|
||||
message UpdateYarnReq {
|
||||
string yarn_id = 1;
|
||||
string yarn_name = 2;
|
||||
string color = 3;
|
||||
string weight_g_m = 4;
|
||||
string supplier_id = 5;
|
||||
string dye_factory = 6;
|
||||
string image_url = 7;
|
||||
string remark = 8;
|
||||
int64 status = 9;
|
||||
}
|
||||
|
||||
message DeleteYarnReq {
|
||||
string yarn_id = 1;
|
||||
}
|
||||
|
||||
message GetYarnReq {
|
||||
string yarn_id = 1;
|
||||
}
|
||||
|
||||
message ListYarnReq {
|
||||
int64 page = 1;
|
||||
int64 page_size = 2;
|
||||
string yarn_name = 3;
|
||||
string supplier_id = 4;
|
||||
int64 status = 5;
|
||||
}
|
||||
|
||||
message ListYarnResp {
|
||||
int64 total = 1;
|
||||
repeated YarnInfo list = 2;
|
||||
}
|
||||
|
||||
// ==================== Product Batch ====================
|
||||
|
||||
message ProductBatchInput {
|
||||
string yarn_ratio = 1;
|
||||
string warp_weight_g_m = 2;
|
||||
string weft_weight_g_m = 3;
|
||||
string production_process = 4;
|
||||
string remark = 5;
|
||||
string batch_no = 6;
|
||||
}
|
||||
|
||||
message ProductBatchInfo {
|
||||
string batch_id = 1;
|
||||
string product_id = 2;
|
||||
string batch_no = 3;
|
||||
string yarn_ratio = 4;
|
||||
string warp_weight_g_m = 5;
|
||||
string weft_weight_g_m = 6;
|
||||
string production_process = 7;
|
||||
string remark = 8;
|
||||
int64 status = 9;
|
||||
string created_at = 10;
|
||||
string updated_at = 11;
|
||||
}
|
||||
|
||||
message CreateProductBatchReq {
|
||||
string product_id = 1;
|
||||
ProductBatchInput batch = 2;
|
||||
}
|
||||
|
||||
message UpdateProductBatchReq {
|
||||
string product_id = 1;
|
||||
string batch_id = 2;
|
||||
ProductBatchInput batch = 3;
|
||||
int64 status = 4;
|
||||
}
|
||||
|
||||
message DeleteProductBatchReq {
|
||||
string product_id = 1;
|
||||
string batch_id = 2;
|
||||
}
|
||||
|
||||
message ListProductBatchReq {
|
||||
string product_id = 1;
|
||||
}
|
||||
|
||||
message ListProductBatchResp {
|
||||
repeated ProductBatchInfo list = 1;
|
||||
}
|
||||
|
||||
message ImportLogInfo {
|
||||
string import_id = 1;
|
||||
string file_name = 2;
|
||||
@ -440,6 +566,19 @@ service InventoryService {
|
||||
// Color Detail (panel 2)
|
||||
rpc ListColorDetail(ListColorDetailReq) returns (ListColorDetailResp);
|
||||
|
||||
// Yarn CRUD
|
||||
rpc CreateYarn(CreateYarnReq) returns (IdResp);
|
||||
rpc UpdateYarn(UpdateYarnReq) returns (Empty);
|
||||
rpc DeleteYarn(DeleteYarnReq) returns (Empty);
|
||||
rpc GetYarn(GetYarnReq) returns (YarnInfo);
|
||||
rpc ListYarn(ListYarnReq) returns (ListYarnResp);
|
||||
|
||||
// Product Batch CRUD
|
||||
rpc CreateProductBatch(CreateProductBatchReq) returns (IdResp);
|
||||
rpc UpdateProductBatch(UpdateProductBatchReq) returns (Empty);
|
||||
rpc DeleteProductBatch(DeleteProductBatchReq) returns (Empty);
|
||||
rpc ListProductBatch(ListProductBatchReq) returns (ListProductBatchResp);
|
||||
|
||||
// Pan / Bolt list views (dimension panels)
|
||||
rpc ListPanView(ListPanViewReq) returns (ListPanViewResp);
|
||||
rpc ListBoltView(ListBoltViewReq) returns (ListBoltViewResp);
|
||||
|
||||
@ -20,13 +20,18 @@ type (
|
||||
ColorDetailItem = pb.ColorDetailItem
|
||||
ConfirmStockCheckReq = pb.ConfirmStockCheckReq
|
||||
CreateProductReq = pb.CreateProductReq
|
||||
CreateProductBatchReq = pb.CreateProductBatchReq
|
||||
CreateStockAdjustReq = pb.CreateStockAdjustReq
|
||||
CreateStockCheckReq = pb.CreateStockCheckReq
|
||||
CreateYarnReq = pb.CreateYarnReq
|
||||
DeleteProductBatchReq = pb.DeleteProductBatchReq
|
||||
DeleteProductReq = pb.DeleteProductReq
|
||||
DeleteYarnReq = pb.DeleteYarnReq
|
||||
Empty = pb.Empty
|
||||
GetProductReq = pb.GetProductReq
|
||||
GetStockAdjustReq = pb.GetStockAdjustReq
|
||||
GetStockCheckReq = pb.GetStockCheckReq
|
||||
GetYarnReq = pb.GetYarnReq
|
||||
IdResp = pb.IdResp
|
||||
ImportLogInfo = pb.ImportLogInfo
|
||||
ImportProductReq = pb.ImportProductReq
|
||||
@ -44,6 +49,8 @@ type (
|
||||
ListImportLogResp = pb.ListImportLogResp
|
||||
ListPanReq = pb.ListPanReq
|
||||
ListPanResp = pb.ListPanResp
|
||||
ListProductBatchReq = pb.ListProductBatchReq
|
||||
ListProductBatchResp = pb.ListProductBatchResp
|
||||
ListProductReq = pb.ListProductReq
|
||||
ListProductResp = pb.ListProductResp
|
||||
ListProductSummaryReq = pb.ListProductSummaryReq
|
||||
@ -52,8 +59,12 @@ type (
|
||||
ListStockAdjustResp = pb.ListStockAdjustResp
|
||||
ListStockCheckReq = pb.ListStockCheckReq
|
||||
ListStockCheckResp = pb.ListStockCheckResp
|
||||
ListYarnReq = pb.ListYarnReq
|
||||
ListYarnResp = pb.ListYarnResp
|
||||
PanInfo = pb.PanInfo
|
||||
PanInput = pb.PanInput
|
||||
ProductBatchInfo = pb.ProductBatchInfo
|
||||
ProductBatchInput = pb.ProductBatchInput
|
||||
ProductInfo = pb.ProductInfo
|
||||
ProductSummaryItem = pb.ProductSummaryItem
|
||||
ProductTreeResp = pb.ProductTreeResp
|
||||
@ -70,8 +81,11 @@ type (
|
||||
StockGroupResp = pb.StockGroupResp
|
||||
StockSummaryReq = pb.StockSummaryReq
|
||||
StockSummaryResp = pb.StockSummaryResp
|
||||
UpdateProductBatchReq = pb.UpdateProductBatchReq
|
||||
UpdateProductReq = pb.UpdateProductReq
|
||||
UpdateStockCheckReq = pb.UpdateStockCheckReq
|
||||
UpdateYarnReq = pb.UpdateYarnReq
|
||||
YarnInfo = pb.YarnInfo
|
||||
|
||||
InventoryService interface {
|
||||
// Product CRUD
|
||||
@ -86,6 +100,17 @@ type (
|
||||
ListProductSummary(ctx context.Context, in *ListProductSummaryReq, opts ...grpc.CallOption) (*ListProductSummaryResp, error)
|
||||
// Color Detail (panel 2)
|
||||
ListColorDetail(ctx context.Context, in *ListColorDetailReq, opts ...grpc.CallOption) (*ListColorDetailResp, error)
|
||||
// Yarn CRUD
|
||||
CreateYarn(ctx context.Context, in *CreateYarnReq, opts ...grpc.CallOption) (*IdResp, error)
|
||||
UpdateYarn(ctx context.Context, in *UpdateYarnReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
DeleteYarn(ctx context.Context, in *DeleteYarnReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
GetYarn(ctx context.Context, in *GetYarnReq, opts ...grpc.CallOption) (*YarnInfo, error)
|
||||
ListYarn(ctx context.Context, in *ListYarnReq, opts ...grpc.CallOption) (*ListYarnResp, error)
|
||||
// Product Batch CRUD
|
||||
CreateProductBatch(ctx context.Context, in *CreateProductBatchReq, opts ...grpc.CallOption) (*IdResp, error)
|
||||
UpdateProductBatch(ctx context.Context, in *UpdateProductBatchReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
DeleteProductBatch(ctx context.Context, in *DeleteProductBatchReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
ListProductBatch(ctx context.Context, in *ListProductBatchReq, opts ...grpc.CallOption) (*ListProductBatchResp, error)
|
||||
// Pan / Bolt list views
|
||||
ListPanView(ctx context.Context, in *ListPanViewReq, opts ...grpc.CallOption) (*ListPanViewResp, error)
|
||||
ListBoltView(ctx context.Context, in *ListBoltViewReq, opts ...grpc.CallOption) (*ListBoltViewResp, error)
|
||||
@ -166,6 +191,51 @@ func (m *defaultInventoryService) ListColorDetail(ctx context.Context, in *ListC
|
||||
return client.ListColorDetail(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) CreateYarn(ctx context.Context, in *CreateYarnReq, opts ...grpc.CallOption) (*IdResp, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.CreateYarn(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) UpdateYarn(ctx context.Context, in *UpdateYarnReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.UpdateYarn(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) DeleteYarn(ctx context.Context, in *DeleteYarnReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.DeleteYarn(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) GetYarn(ctx context.Context, in *GetYarnReq, opts ...grpc.CallOption) (*YarnInfo, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.GetYarn(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) ListYarn(ctx context.Context, in *ListYarnReq, opts ...grpc.CallOption) (*ListYarnResp, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.ListYarn(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) CreateProductBatch(ctx context.Context, in *CreateProductBatchReq, opts ...grpc.CallOption) (*IdResp, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.CreateProductBatch(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) UpdateProductBatch(ctx context.Context, in *UpdateProductBatchReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.UpdateProductBatch(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) DeleteProductBatch(ctx context.Context, in *DeleteProductBatchReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.DeleteProductBatch(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) ListProductBatch(ctx context.Context, in *ListProductBatchReq, opts ...grpc.CallOption) (*ListProductBatchResp, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.ListProductBatch(ctx, in, opts...)
|
||||
}
|
||||
|
||||
func (m *defaultInventoryService) ListPanView(ctx context.Context, in *ListPanViewReq, opts ...grpc.CallOption) (*ListPanViewResp, error) {
|
||||
client := pb.NewInventoryServiceClient(m.cli.Conn())
|
||||
return client.ListPanView(ctx, in, opts...)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,7 +1,7 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.5.1
|
||||
// - protoc v5.29.3
|
||||
// - protoc-gen-go-grpc v1.6.2
|
||||
// - protoc v7.34.1
|
||||
// source: inventory.proto
|
||||
|
||||
package pb
|
||||
@ -27,6 +27,15 @@ const (
|
||||
InventoryService_GetProductTree_FullMethodName = "/inventory.InventoryService/GetProductTree"
|
||||
InventoryService_ListProductSummary_FullMethodName = "/inventory.InventoryService/ListProductSummary"
|
||||
InventoryService_ListColorDetail_FullMethodName = "/inventory.InventoryService/ListColorDetail"
|
||||
InventoryService_CreateYarn_FullMethodName = "/inventory.InventoryService/CreateYarn"
|
||||
InventoryService_UpdateYarn_FullMethodName = "/inventory.InventoryService/UpdateYarn"
|
||||
InventoryService_DeleteYarn_FullMethodName = "/inventory.InventoryService/DeleteYarn"
|
||||
InventoryService_GetYarn_FullMethodName = "/inventory.InventoryService/GetYarn"
|
||||
InventoryService_ListYarn_FullMethodName = "/inventory.InventoryService/ListYarn"
|
||||
InventoryService_CreateProductBatch_FullMethodName = "/inventory.InventoryService/CreateProductBatch"
|
||||
InventoryService_UpdateProductBatch_FullMethodName = "/inventory.InventoryService/UpdateProductBatch"
|
||||
InventoryService_DeleteProductBatch_FullMethodName = "/inventory.InventoryService/DeleteProductBatch"
|
||||
InventoryService_ListProductBatch_FullMethodName = "/inventory.InventoryService/ListProductBatch"
|
||||
InventoryService_ListPanView_FullMethodName = "/inventory.InventoryService/ListPanView"
|
||||
InventoryService_ListBoltView_FullMethodName = "/inventory.InventoryService/ListBoltView"
|
||||
InventoryService_ListPans_FullMethodName = "/inventory.InventoryService/ListPans"
|
||||
@ -64,6 +73,17 @@ type InventoryServiceClient interface {
|
||||
ListProductSummary(ctx context.Context, in *ListProductSummaryReq, opts ...grpc.CallOption) (*ListProductSummaryResp, error)
|
||||
// Color Detail (panel 2)
|
||||
ListColorDetail(ctx context.Context, in *ListColorDetailReq, opts ...grpc.CallOption) (*ListColorDetailResp, error)
|
||||
// Yarn CRUD
|
||||
CreateYarn(ctx context.Context, in *CreateYarnReq, opts ...grpc.CallOption) (*IdResp, error)
|
||||
UpdateYarn(ctx context.Context, in *UpdateYarnReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
DeleteYarn(ctx context.Context, in *DeleteYarnReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
GetYarn(ctx context.Context, in *GetYarnReq, opts ...grpc.CallOption) (*YarnInfo, error)
|
||||
ListYarn(ctx context.Context, in *ListYarnReq, opts ...grpc.CallOption) (*ListYarnResp, error)
|
||||
// Product Batch CRUD
|
||||
CreateProductBatch(ctx context.Context, in *CreateProductBatchReq, opts ...grpc.CallOption) (*IdResp, error)
|
||||
UpdateProductBatch(ctx context.Context, in *UpdateProductBatchReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
DeleteProductBatch(ctx context.Context, in *DeleteProductBatchReq, opts ...grpc.CallOption) (*Empty, error)
|
||||
ListProductBatch(ctx context.Context, in *ListProductBatchReq, opts ...grpc.CallOption) (*ListProductBatchResp, error)
|
||||
// Pan / Bolt list views (dimension panels)
|
||||
ListPanView(ctx context.Context, in *ListPanViewReq, opts ...grpc.CallOption) (*ListPanViewResp, error)
|
||||
ListBoltView(ctx context.Context, in *ListBoltViewReq, opts ...grpc.CallOption) (*ListBoltViewResp, error)
|
||||
@ -180,6 +200,96 @@ func (c *inventoryServiceClient) ListColorDetail(ctx context.Context, in *ListCo
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) CreateYarn(ctx context.Context, in *CreateYarnReq, opts ...grpc.CallOption) (*IdResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(IdResp)
|
||||
err := c.cc.Invoke(ctx, InventoryService_CreateYarn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) UpdateYarn(ctx context.Context, in *UpdateYarnReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, InventoryService_UpdateYarn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) DeleteYarn(ctx context.Context, in *DeleteYarnReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, InventoryService_DeleteYarn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) GetYarn(ctx context.Context, in *GetYarnReq, opts ...grpc.CallOption) (*YarnInfo, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(YarnInfo)
|
||||
err := c.cc.Invoke(ctx, InventoryService_GetYarn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) ListYarn(ctx context.Context, in *ListYarnReq, opts ...grpc.CallOption) (*ListYarnResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListYarnResp)
|
||||
err := c.cc.Invoke(ctx, InventoryService_ListYarn_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) CreateProductBatch(ctx context.Context, in *CreateProductBatchReq, opts ...grpc.CallOption) (*IdResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(IdResp)
|
||||
err := c.cc.Invoke(ctx, InventoryService_CreateProductBatch_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) UpdateProductBatch(ctx context.Context, in *UpdateProductBatchReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, InventoryService_UpdateProductBatch_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) DeleteProductBatch(ctx context.Context, in *DeleteProductBatchReq, opts ...grpc.CallOption) (*Empty, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, InventoryService_DeleteProductBatch_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) ListProductBatch(ctx context.Context, in *ListProductBatchReq, opts ...grpc.CallOption) (*ListProductBatchResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListProductBatchResp)
|
||||
err := c.cc.Invoke(ctx, InventoryService_ListProductBatch_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *inventoryServiceClient) ListPanView(ctx context.Context, in *ListPanViewReq, opts ...grpc.CallOption) (*ListPanViewResp, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(ListPanViewResp)
|
||||
@ -386,6 +496,17 @@ type InventoryServiceServer interface {
|
||||
ListProductSummary(context.Context, *ListProductSummaryReq) (*ListProductSummaryResp, error)
|
||||
// Color Detail (panel 2)
|
||||
ListColorDetail(context.Context, *ListColorDetailReq) (*ListColorDetailResp, error)
|
||||
// Yarn CRUD
|
||||
CreateYarn(context.Context, *CreateYarnReq) (*IdResp, error)
|
||||
UpdateYarn(context.Context, *UpdateYarnReq) (*Empty, error)
|
||||
DeleteYarn(context.Context, *DeleteYarnReq) (*Empty, error)
|
||||
GetYarn(context.Context, *GetYarnReq) (*YarnInfo, error)
|
||||
ListYarn(context.Context, *ListYarnReq) (*ListYarnResp, error)
|
||||
// Product Batch CRUD
|
||||
CreateProductBatch(context.Context, *CreateProductBatchReq) (*IdResp, error)
|
||||
UpdateProductBatch(context.Context, *UpdateProductBatchReq) (*Empty, error)
|
||||
DeleteProductBatch(context.Context, *DeleteProductBatchReq) (*Empty, error)
|
||||
ListProductBatch(context.Context, *ListProductBatchReq) (*ListProductBatchResp, error)
|
||||
// Pan / Bolt list views (dimension panels)
|
||||
ListPanView(context.Context, *ListPanViewReq) (*ListPanViewResp, error)
|
||||
ListBoltView(context.Context, *ListBoltViewReq) (*ListBoltViewResp, error)
|
||||
@ -423,85 +544,112 @@ type InventoryServiceServer interface {
|
||||
type UnimplementedInventoryServiceServer struct{}
|
||||
|
||||
func (UnimplementedInventoryServiceServer) CreateProduct(context.Context, *CreateProductReq) (*IdResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateProduct not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateProduct not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) UpdateProduct(context.Context, *UpdateProductReq) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateProduct not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateProduct not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) DeleteProduct(context.Context, *DeleteProductReq) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method DeleteProduct not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method DeleteProduct not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) GetProduct(context.Context, *GetProductReq) (*ProductInfo, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetProduct not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method GetProduct not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListProduct(context.Context, *ListProductReq) (*ListProductResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListProduct not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListProduct not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) GetProductTree(context.Context, *GetProductReq) (*ProductTreeResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetProductTree not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method GetProductTree not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListProductSummary(context.Context, *ListProductSummaryReq) (*ListProductSummaryResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListProductSummary not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListProductSummary not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListColorDetail(context.Context, *ListColorDetailReq) (*ListColorDetailResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListColorDetail not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListColorDetail not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) CreateYarn(context.Context, *CreateYarnReq) (*IdResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateYarn not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) UpdateYarn(context.Context, *UpdateYarnReq) (*Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateYarn not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) DeleteYarn(context.Context, *DeleteYarnReq) (*Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DeleteYarn not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) GetYarn(context.Context, *GetYarnReq) (*YarnInfo, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetYarn not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListYarn(context.Context, *ListYarnReq) (*ListYarnResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListYarn not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) CreateProductBatch(context.Context, *CreateProductBatchReq) (*IdResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateProductBatch not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) UpdateProductBatch(context.Context, *UpdateProductBatchReq) (*Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateProductBatch not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) DeleteProductBatch(context.Context, *DeleteProductBatchReq) (*Empty, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DeleteProductBatch not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListProductBatch(context.Context, *ListProductBatchReq) (*ListProductBatchResp, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method ListProductBatch not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListPanView(context.Context, *ListPanViewReq) (*ListPanViewResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListPanView not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListPanView not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListBoltView(context.Context, *ListBoltViewReq) (*ListBoltViewResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListBoltView not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListBoltView not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListPans(context.Context, *ListPanReq) (*ListPanResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListPans not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListPans not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) SavePans(context.Context, *SavePansReq) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SavePans not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method SavePans not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListBolts(context.Context, *ListBoltReq) (*ListBoltResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListBolts not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListBolts not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) SaveBolts(context.Context, *SaveBoltsReq) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method SaveBolts not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method SaveBolts not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ImportProducts(context.Context, *ImportProductReq) (*ImportProductResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ImportProducts not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ImportProducts not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListImportLog(context.Context, *ListImportLogReq) (*ListImportLogResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListImportLog not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListImportLog not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) GetStockSummary(context.Context, *StockSummaryReq) (*StockSummaryResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStockSummary not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStockSummary not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) GetStockGroup(context.Context, *StockGroupReq) (*StockGroupResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStockGroup not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStockGroup not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) CreateStockCheck(context.Context, *CreateStockCheckReq) (*IdResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateStockCheck not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateStockCheck not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) UpdateStockCheck(context.Context, *UpdateStockCheckReq) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method UpdateStockCheck not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateStockCheck not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ConfirmStockCheck(context.Context, *ConfirmStockCheckReq) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ConfirmStockCheck not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ConfirmStockCheck not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) GetStockCheck(context.Context, *GetStockCheckReq) (*StockCheckInfo, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStockCheck not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStockCheck not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListStockCheck(context.Context, *ListStockCheckReq) (*ListStockCheckResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListStockCheck not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListStockCheck not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) CreateStockAdjust(context.Context, *CreateStockAdjustReq) (*IdResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method CreateStockAdjust not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method CreateStockAdjust not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ApproveStockAdjust(context.Context, *ApproveStockAdjustReq) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ApproveStockAdjust not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ApproveStockAdjust not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) GetStockAdjust(context.Context, *GetStockAdjustReq) (*StockAdjustInfo, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method GetStockAdjust not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method GetStockAdjust not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) ListStockAdjust(context.Context, *ListStockAdjustReq) (*ListStockAdjustResp, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method ListStockAdjust not implemented")
|
||||
return nil, status.Error(codes.Unimplemented, "method ListStockAdjust not implemented")
|
||||
}
|
||||
func (UnimplementedInventoryServiceServer) mustEmbedUnimplementedInventoryServiceServer() {}
|
||||
func (UnimplementedInventoryServiceServer) testEmbeddedByValue() {}
|
||||
@ -514,7 +662,7 @@ type UnsafeInventoryServiceServer interface {
|
||||
}
|
||||
|
||||
func RegisterInventoryServiceServer(s grpc.ServiceRegistrar, srv InventoryServiceServer) {
|
||||
// If the following call pancis, it indicates UnimplementedInventoryServiceServer was
|
||||
// If the following call panics, it indicates UnimplementedInventoryServiceServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
@ -668,6 +816,168 @@ func _InventoryService_ListColorDetail_Handler(srv interface{}, ctx context.Cont
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_CreateYarn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateYarnReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).CreateYarn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_CreateYarn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).CreateYarn(ctx, req.(*CreateYarnReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_UpdateYarn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateYarnReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).UpdateYarn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_UpdateYarn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).UpdateYarn(ctx, req.(*UpdateYarnReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_DeleteYarn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteYarnReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).DeleteYarn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_DeleteYarn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).DeleteYarn(ctx, req.(*DeleteYarnReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_GetYarn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetYarnReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).GetYarn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_GetYarn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).GetYarn(ctx, req.(*GetYarnReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_ListYarn_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListYarnReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).ListYarn(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_ListYarn_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).ListYarn(ctx, req.(*ListYarnReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_CreateProductBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(CreateProductBatchReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).CreateProductBatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_CreateProductBatch_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).CreateProductBatch(ctx, req.(*CreateProductBatchReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_UpdateProductBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateProductBatchReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).UpdateProductBatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_UpdateProductBatch_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).UpdateProductBatch(ctx, req.(*UpdateProductBatchReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_DeleteProductBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeleteProductBatchReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).DeleteProductBatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_DeleteProductBatch_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).DeleteProductBatch(ctx, req.(*DeleteProductBatchReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_ListProductBatch_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListProductBatchReq)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(InventoryServiceServer).ListProductBatch(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: InventoryService_ListProductBatch_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(InventoryServiceServer).ListProductBatch(ctx, req.(*ListProductBatchReq))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _InventoryService_ListPanView_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(ListPanViewReq)
|
||||
if err := dec(in); err != nil {
|
||||
@ -1049,6 +1359,42 @@ var InventoryService_ServiceDesc = grpc.ServiceDesc{
|
||||
MethodName: "ListColorDetail",
|
||||
Handler: _InventoryService_ListColorDetail_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateYarn",
|
||||
Handler: _InventoryService_CreateYarn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateYarn",
|
||||
Handler: _InventoryService_UpdateYarn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteYarn",
|
||||
Handler: _InventoryService_DeleteYarn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetYarn",
|
||||
Handler: _InventoryService_GetYarn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListYarn",
|
||||
Handler: _InventoryService_ListYarn_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "CreateProductBatch",
|
||||
Handler: _InventoryService_CreateProductBatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateProductBatch",
|
||||
Handler: _InventoryService_UpdateProductBatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeleteProductBatch",
|
||||
Handler: _InventoryService_DeleteProductBatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListProductBatch",
|
||||
Handler: _InventoryService_ListProductBatch_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "ListPanView",
|
||||
Handler: _InventoryService_ListPanView_Handler,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user