物料备注放宽到 500 字,补回被截断的两条备注

- scripts/init.sql:materials.remark 由 VARCHAR(200) 改为 VARCHAR(500);并从
  源 SQLite 库补回武汉 B010059、B010076 被截断的完整备注(222 字,此前只落库
  前 200 字,结尾断在 &upStre)
- 后端:maxMaterialRemarkLength 与错误文案改为 500,Excel 模板填写说明同步
- 前端:物料表单备注输入框 maxlength 与提交前校验改为 500
- 文档:api.md、数据库表结构与关系说明、后端结构说明同步;并补充说明默认用户
  由后端启动时创建,重建数据库后重启后端即可恢复
This commit is contained in:
文睿 张 2026-10-14 17:04:00 +08:00
parent 18df4f48e4
commit 062abcb3d0
18 changed files with 22 additions and 16 deletions

View File

@ -78,6 +78,10 @@ ipconfig
| admin | admin123 | 管理员 | | admin | admin123 | 管理员 |
| user | user123 | 普通用户 | | user | user123 | 普通用户 |
这两个账号**不是 `scripts/init.sql` 建的**——该脚本只创建 `users` 表,不插入任何用户数据。账号由后端启动时调用 `repository.CreateDefaultUsers()`(见 `cmd/server/main.go`)幂等创建:`admin` 已存在就跳过,不存在才新建 `admin`/`user` 并绑定角色。
因此**重建数据库后,重启后端即可恢复默认账号**,不需要手工执行建用户的 SQL。本地 3306 验证库(用 `scripts/init.sql` 重建)走的也是同一条路径。反过来,如果改过 `admin` 密码或另建过账号,这些不会随重建回来。
## 常用命令 ## 常用命令
```bash ```bash

View File

@ -205,7 +205,7 @@
} }
``` ```
`material_image`、`spec`、`manufacturer_model`、`source`、`remark`、`supplier_id`、`manufacturer_id`、`category_id`、`base_unit_id`、`unit` 为空时**不会出现在响应里**(后端 `omitempty`),前端取值时需要做空值兜底。`source` 可为空,非空时仅为「外购」或「外协」;`remark` 可为空且最多 200 字。`manufacturer_model` 是制造商为产品指定的型号或订货识别型号;`spec` 表示物料规格,`manufacturer_id` 表示制造商伙伴关联。`supplier_name` 和 `manufacturer_name` 是伙伴显示名称。 `material_image`、`spec`、`manufacturer_model`、`source`、`remark`、`supplier_id`、`manufacturer_id`、`category_id`、`base_unit_id`、`unit` 为空时**不会出现在响应里**(后端 `omitempty`),前端取值时需要做空值兜底。`source` 可为空,非空时仅为「外购」或「外协」;`remark` 可为空且最多 500 字。`manufacturer_model` 是制造商为产品指定的型号或订货识别型号;`spec` 表示物料规格,`manufacturer_id` 表示制造商伙伴关联。`supplier_name` 和 `manufacturer_name` 是伙伴显示名称。
**状态码:** **状态码:**
@ -265,7 +265,7 @@
| spec | string | 否 | 规格 | | spec | string | 否 | 规格 |
| manufacturer_model| string | 否 | 制造商为产品指定的型号或订货识别型号 | | manufacturer_model| string | 否 | 制造商为产品指定的型号或订货识别型号 |
| source | string | 否 | 来源,可留空;非空时填写「外购」或「外协」 | | source | string | 否 | 来源,可留空;非空时填写「外购」或「外协」 |
| remark | string | 否 | 备注,可留空,最多 200 字 | | remark | string | 否 | 备注,可留空,最多 500 字 |
| base_unit_id | integer | 否 | 基本计量单位 ID | | base_unit_id | integer | 否 | 基本计量单位 ID |
| unit | string | 否 | 单位编码(冗余展示字段) | | unit | string | 否 | 单位编码(冗余展示字段) |
| material_image | string | 否 | 图片地址 | | material_image | string | 否 | 图片地址 |
@ -329,7 +329,7 @@
| spec | string | 否 | 规格 | | spec | string | 否 | 规格 |
| manufacturer_model| string | 否 | 制造商为产品指定的型号或订货识别型号 | | manufacturer_model| string | 否 | 制造商为产品指定的型号或订货识别型号 |
| source | string | 否 | 来源,可留空;传空字符串清空;非空时填写「外购」或「外协」 | | source | string | 否 | 来源,可留空;传空字符串清空;非空时填写「外购」或「外协」 |
| remark | string | 否 | 备注,可留空;传空字符串清空;最多 200 字 | | remark | string | 否 | 备注,可留空;传空字符串清空;最多 500 字 |
| base_unit_id | integer | 否 | 基本计量单位 ID | | base_unit_id | integer | 否 | 基本计量单位 ID |
| unit | string | 否 | 单位编码 | | unit | string | 否 | 单位编码 |
| material_image | string | 否 | 图片地址 | | material_image | string | 否 | 图片地址 |
@ -435,7 +435,7 @@
- 列顺序可以随意调整;表头允许带首尾空格和末尾的 `*` 必填标记;不认识的列忽略; - 列顺序可以随意调整;表头允许带首尾空格和末尾的 `*` 必填标记;不认识的列忽略;
- 必填列 `物料名称`、`大类`、`小类` 缺任何一列,判为模板不合法并返回「模板格式不正确,请重新下载最新模板」; - 必填列 `物料名称`、`大类`、`小类` 缺任何一列,判为模板不合法并返回「模板格式不正确,请重新下载最新模板」;
- 选填列 `规格`、`单位`、`供应商`、`制造商`、`来源`、`备注` 可以整列删掉,取值按空处理;来源只能为「外购」或「外协」,备注最多 200 字。 - 选填列 `规格`、`单位`、`供应商`、`制造商`、`来源`、`备注` 可以整列删掉,取值按空处理;来源只能为「外购」或「外协」,备注最多 500 字。
分类**按名称**填写;名称之外也接受**编码**和界面上的「编码 名称」写法(界面一律显示成 `A 机械结构类`,用户整段照抄进模板是常事)。后端按「大类 + 小类」在本公司内组合匹配(小类名在一个公司内会重名,例如多个大类下都有「其他」,所以必须两级组合)。名称、编码、「编码 名称」三种写法同名或同码时以**名称**优先。单位按名称匹配,同时兼容按编码匹配(如 `kg` 与 `千克`)。 分类**按名称**填写;名称之外也接受**编码**和界面上的「编码 名称」写法(界面一律显示成 `A 机械结构类`,用户整段照抄进模板是常事)。后端按「大类 + 小类」在本公司内组合匹配(小类名在一个公司内会重名,例如多个大类下都有「其他」,所以必须两级组合)。名称、编码、「编码 名称」三种写法同名或同码时以**名称**优先。单位按名称匹配,同时兼容按编码匹配(如 `kg` 与 `千克`)。

View File

@ -256,7 +256,7 @@
## 13. 物料来源与备注(2026-10-14) ## 13. 物料来源与备注(2026-10-14)
`materials.source` 为可空来源字段,允许值为「外购」或「外协」;`materials.remark` 为可空备注,最多 200 字。旧记录升级后两列均保持 `NULL`,新建和批量导入也允许留空。已有库使用 `scripts/upgrades/2026-10-14-material-source-remark.sql` 升级,空库使用 `scripts/init.sql`。 `materials.source` 为可空来源字段,允许值为「外购」或「外协」;`materials.remark` 为可空备注,最多 500 字。旧记录升级后两列均保持 `NULL`,新建和批量导入也允许留空。已有库使用 `scripts/upgrades/2026-10-14-material-source-remark.sql` 升级,空库使用 `scripts/init.sql`。
## 14. 物料日志操作人用户 ID(2026-10-14) ## 14. 物料日志操作人用户 ID(2026-10-14)

View File

@ -13,6 +13,8 @@
| `scripts/` | `init.sql`、增量升级 SQL 与校验脚本 | | `scripts/` | `init.sql`、增量升级 SQL 与校验脚本 |
| `docs/` | 项目文档 | | `docs/` | 项目文档 |
上表提到的「建默认用户」,指 `main.go` 启动时调用 `repository.CreateDefaultUsers()`:幂等创建 `admin`/`user`(`admin` 已存在就跳过),并绑定 `super_admin` / `warehouse_admin` 角色。这两个账号**不在 `scripts/init.sql` 里**——该脚本只建 `users` 表、不插入任何用户数据——因此数据库重建后**重启后端即可恢复默认账号**,不必手工执行建用户的 SQL;反过来,改过的密码或另建的账号不会随重建回来。
## 整体 ## 整体
`cmd/server/main.go` 是入口(注册路由、连库、校验 schema),`internal/` 只是内核。一次请求的链路是: `cmd/server/main.go` 是入口(注册路由、连库、校验 schema),`internal/` 只是内核。一次请求的链路是:

View File

@ -125,7 +125,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="备注"> <el-form-item label="备注">
<el-input v-model="form.remark" type="textarea" maxlength="200" show-word-limit placeholder="选填,最多200字" /> <el-input v-model="form.remark" type="textarea" maxlength="500" show-word-limit placeholder="选填,最多500字" />
</el-form-item> </el-form-item>
<el-form-item label="大类类型" required> <el-form-item label="大类类型" required>
<el-select v-model="form.major_category_id" style="width: 100%" @change="syncMajorCategory"> <el-select v-model="form.major_category_id" style="width: 100%" @change="syncMajorCategory">
@ -595,8 +595,8 @@ async function handleSave() {
ElMessage.warning('物料规格过长,请小于200字') ElMessage.warning('物料规格过长,请小于200字')
return return
} }
if (Array.from(form.remark || '').length > 200) { if (Array.from(form.remark || '').length > 500) {
ElMessage.warning('备注不能超过200字') ElMessage.warning('备注不能超过500字')
return return
} }
saveLoading.value = true saveLoading.value = true

View File

@ -16,14 +16,14 @@ var ErrInvalidMaterialCategory = errors.New("请选择有效的小类类型")
var ErrMaterialNameTooLong = errors.New("物料名称过长,请小于200字") var ErrMaterialNameTooLong = errors.New("物料名称过长,请小于200字")
var ErrMaterialSpecTooLong = errors.New("物料规格过长,请小于200字") var ErrMaterialSpecTooLong = errors.New("物料规格过长,请小于200字")
var ErrInvalidMaterialSource = errors.New("物料来源只能填写外购或外协") var ErrInvalidMaterialSource = errors.New("物料来源只能填写外购或外协")
var ErrMaterialRemarkTooLong = errors.New("物料备注不能超过200字") var ErrMaterialRemarkTooLong = errors.New("物料备注不能超过500字")
var ErrInvalidMaterialSupplier = errors.New("请选择本公司的有效供应商") var ErrInvalidMaterialSupplier = errors.New("请选择本公司的有效供应商")
var ErrInvalidMaterialManufacturer = errors.New("请选择本公司的有效制造商") var ErrInvalidMaterialManufacturer = errors.New("请选择本公司的有效制造商")
var ErrMaterialDuplicate = errors.New("发现名称、规格、单位、供应商和制造商均相同的物料,请先检查重复项") var ErrMaterialDuplicate = errors.New("发现名称、规格、单位、供应商和制造商均相同的物料,请先检查重复项")
const maxMaterialNameLength = 200 const maxMaterialNameLength = 200
const maxMaterialSpecLength = 200 const maxMaterialSpecLength = 200
const maxMaterialRemarkLength = 200 const maxMaterialRemarkLength = 500
func validMaterialSource(source string) bool { func validMaterialSource(source string) bool {
return source == "" || source == "外购" || source == "外协" return source == "" || source == "外购" || source == "外协"

View File

@ -187,7 +187,7 @@ func (s *MaterialImportService) validateRows(organizationID int64, rows []model.
reasons = append(reasons, "来源只能填写外购或外协") reasons = append(reasons, "来源只能填写外购或外协")
} }
if utf8.RuneCountInString(remark) > maxMaterialRemarkLength { if utf8.RuneCountInString(remark) > maxMaterialRemarkLength {
reasons = append(reasons, "备注超过 200 字") reasons = append(reasons, "备注超过 500 字")
} }
majorName := normalizeWhitespace(row.MajorName) majorName := normalizeWhitespace(row.MajorName)

View File

@ -54,7 +54,7 @@ func BuildMaterialImportTemplate() ([]byte, error) {
{"供应商", "选填", "示例供应商或供应商编码", "填写当前组织已有供应商的名称或编码;留空表示不指定。"}, {"供应商", "选填", "示例供应商或供应商编码", "填写当前组织已有供应商的名称或编码;留空表示不指定。"},
{"制造商", "选填", "示例制造商或制造商编码", "填写当前组织已有制造商的名称或编码;留空表示不指定。"}, {"制造商", "选填", "示例制造商或制造商编码", "填写当前组织已有制造商的名称或编码;留空表示不指定。"},
{"来源", "选填", "外购 / 外协", "只能填写外购或外协;可留空。"}, {"来源", "选填", "外购 / 外协", "只能填写外购或外协;可留空。"},
{"备注", "选填", "物料用途说明", "可留空,最多 200 字。"}, {"备注", "选填", "物料用途说明", "可留空,最多 500 字。"},
{"大类", "必填", "机械结构类", "填写当前组织已启用的大类名称、编码或「编码 名称」。"}, {"大类", "必填", "机械结构类", "填写当前组织已启用的大类名称、编码或「编码 名称」。"},
{"小类", "必填", "标准件", "填写所选大类下的小类名称、编码或「编码 名称」;小类须配置启用的编码规则。"}, {"小类", "必填", "标准件", "填写所选大类下的小类名称、编码或「编码 名称」;小类须配置启用的编码规则。"},
} }
@ -70,7 +70,7 @@ func BuildMaterialImportTemplate() ([]byte, error) {
25: "重复检查:名称、规格、单位、供应商和制造商全部相同才判为重复;同一文件内也会检查。", 25: "重复检查:名称、规格、单位、供应商和制造商全部相同才判为重复;同一文件内也会检查。",
26: "编码与库存:物料编码由系统生成,无需填写;仅新增物料,不增加库存,也不更新已有物料。", 26: "编码与库存:物料编码由系统生成,无需填写;仅新增物料,不增加库存,也不更新已有物料。",
27: "默认单位:单位留空时使用「个」;当前组织未启用「个」时,请填写其他有效单位。", 27: "默认单位:单位留空时使用「个」;当前组织未启用「个」时,请填写其他有效单位。",
28: "来源和备注均可留空;来源仅支持外购或外协,备注最多 200 字。", 28: "来源和备注均可留空;来源仅支持外购或外协,备注最多 500 字。",
34: "示例只展示写法,请替换为当前组织实际启用的数据;示例不在数据页,不会自动导入。", 34: "示例只展示写法,请替换为当前组织实际启用的数据;示例不在数据页,不会自动导入。",
} }
for row, text := range sections { for row, text := range sections {

View File

@ -16,7 +16,7 @@ CREATE TABLE revoked_tokens (jti VARCHAR(64) PRIMARY KEY, user_id BIGINT NOT NUL
CREATE TABLE material_types (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_types_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE material_types (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_types_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE material_categories (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(1000) NOT NULL DEFAULT '', parent_id BIGINT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_categories_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(parent_id) REFERENCES material_categories(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE material_categories (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, description VARCHAR(1000) NOT NULL DEFAULT '', parent_id BIGINT NULL, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_material_categories_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(parent_id) REFERENCES material_categories(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE units (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, precision_scale TINYINT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_units_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE units (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(100) NOT NULL, precision_scale TINYINT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_units_org_code(organization_id,code), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE materials (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_code VARCHAR(50) NOT NULL, name VARCHAR(200) NOT NULL, material_image VARCHAR(500), spec VARCHAR(200), manufacturer_model VARCHAR(200), source VARCHAR(20) NULL COMMENT '物料来源:外购或外协', remark VARCHAR(200) NULL COMMENT '物料备注,最多200字', supplier_id BIGINT NULL COMMENT 'Default supplier organization_partner ID', manufacturer_id BIGINT NULL COMMENT 'Manufacturer organization_partner ID', material_type_id BIGINT, category_id BIGINT NOT NULL, base_unit_id BIGINT, unit VARCHAR(20), status VARCHAR(20) NOT NULL DEFAULT 'active', version INT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_materials_org_code(organization_id,material_code), KEY idx_materials_category(category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(material_type_id) REFERENCES material_types(id) ON DELETE RESTRICT, FOREIGN KEY(category_id) REFERENCES material_categories(id) ON DELETE RESTRICT, FOREIGN KEY(base_unit_id) REFERENCES units(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE materials (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_code VARCHAR(50) NOT NULL, name VARCHAR(200) NOT NULL, material_image VARCHAR(500), spec VARCHAR(200), manufacturer_model VARCHAR(200), source VARCHAR(20) NULL COMMENT '物料来源:外购或外协', remark VARCHAR(500) NULL COMMENT '物料备注,最多500字', supplier_id BIGINT NULL COMMENT 'Default supplier organization_partner ID', manufacturer_id BIGINT NULL COMMENT 'Manufacturer organization_partner ID', material_type_id BIGINT, category_id BIGINT NOT NULL, base_unit_id BIGINT, unit VARCHAR(20), status VARCHAR(20) NOT NULL DEFAULT 'active', version INT NOT NULL DEFAULT 1, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, UNIQUE KEY uk_materials_org_code(organization_id,material_code), KEY idx_materials_category(category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id) ON DELETE RESTRICT, FOREIGN KEY(material_type_id) REFERENCES material_types(id) ON DELETE RESTRICT, FOREIGN KEY(category_id) REFERENCES material_categories(id) ON DELETE RESTRICT, FOREIGN KEY(base_unit_id) REFERENCES units(id) ON DELETE RESTRICT) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE code_rules (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, category_id BIGINT NOT NULL, prefix VARCHAR(30) NOT NULL, sequence_length TINYINT NOT NULL, next_sequence BIGINT NOT NULL DEFAULT 1, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_code_rules_org_category(organization_id,category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(category_id) REFERENCES material_categories(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE code_rules (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, category_id BIGINT NOT NULL, prefix VARCHAR(30) NOT NULL, sequence_length TINYINT NOT NULL, next_sequence BIGINT NOT NULL DEFAULT 1, status VARCHAR(20) NOT NULL DEFAULT 'active', UNIQUE KEY uk_code_rules_org_category(organization_id,category_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(category_id) REFERENCES material_categories(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE material_import_logs (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, operator VARCHAR(100) NOT NULL, file_name VARCHAR(255) NOT NULL, total_rows INT NOT NULL DEFAULT 0, success_rows INT NOT NULL DEFAULT 0, failed_rows INT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL, failure_reason VARCHAR(1000) NOT NULL DEFAULT '', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_material_import_logs_org_created(organization_id,created_at), FOREIGN KEY(organization_id) REFERENCES organizations(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE material_import_logs (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, operator VARCHAR(100) NOT NULL, file_name VARCHAR(255) NOT NULL, total_rows INT NOT NULL DEFAULT 0, success_rows INT NOT NULL DEFAULT 0, failed_rows INT NOT NULL DEFAULT 0, status VARCHAR(20) NOT NULL, failure_reason VARCHAR(1000) NOT NULL DEFAULT '', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, KEY idx_material_import_logs_org_created(organization_id,created_at), FOREIGN KEY(organization_id) REFERENCES organizations(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE material_unit_conversions (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, from_unit_id BIGINT NOT NULL, to_unit_id BIGINT NOT NULL, conversion_rate DECIMAL(20,6) NOT NULL, effective_from DATE NOT NULL, effective_to DATE NULL, KEY idx_material_unit_conversions_material(organization_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(from_unit_id) REFERENCES units(id), FOREIGN KEY(to_unit_id) REFERENCES units(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; CREATE TABLE material_unit_conversions (id BIGINT AUTO_INCREMENT PRIMARY KEY, organization_id BIGINT NOT NULL, material_id BIGINT NOT NULL, from_unit_id BIGINT NOT NULL, to_unit_id BIGINT NOT NULL, conversion_rate DECIMAL(20,6) NOT NULL, effective_from DATE NOT NULL, effective_to DATE NULL, KEY idx_material_unit_conversions_material(organization_id,material_id), FOREIGN KEY(organization_id) REFERENCES organizations(id), FOREIGN KEY(material_id) REFERENCES materials(id), FOREIGN KEY(from_unit_id) REFERENCES units(id), FOREIGN KEY(to_unit_id) REFERENCES units(id)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
@ -1324,7 +1324,7 @@ LF-3070A', NULL, '单台数量:1; 物料等级:B; 存储条件:存放于室内
NULL, NULL, @c_wh_B01, @u_wh_jian, '件', NULL, NULL, @c_wh_B01, @u_wh_jian, '件',
'active', 1, '2026-09-11 07:44:59', '2026-09-11 07:44:59'), 'active', 1, '2026-09-11 07:44:59', '2026-09-11 07:44:59'),
(@org_wh, 'B010059', '触摸屏8寸', (@org_wh, 'B010059', '触摸屏8寸',
'8寸', '外购', 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStre', '8寸', '外购', 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStreamPrice=28800&xxc=shop',
NULL, NULL, @c_wh_B01, @u_wh_jian, '件', NULL, NULL, @c_wh_B01, @u_wh_jian, '件',
'active', 1, '2026-09-11 07:44:59', '2026-09-28 08:43:20'), 'active', 1, '2026-09-11 07:44:59', '2026-09-28 08:43:20'),
(@org_wh, 'F010049', '气路组件', (@org_wh, 'F010049', '气路组件',
@ -1468,7 +1468,7 @@ LF-3070A', NULL, '单台数量:1; 物料等级:B; 存储条件:存放于室内
NULL, NULL, @c_wh_F01, NULL, '', NULL, NULL, @c_wh_F01, NULL, '',
'active', 1, '2026-09-23 15:26:31', '2026-09-23 15:28:10'), 'active', 1, '2026-09-23 15:26:31', '2026-09-23 15:28:10'),
(@org_wh, 'B010076', '触摸屏8寸', (@org_wh, 'B010076', '触摸屏8寸',
'8寸', NULL, 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStre', '8寸', NULL, 'https://item.taobao.com/item.htm?from=cart&id=749116190950&mi_id=000086DKOQjDf9p_N94ymKsE2dWs-QKiH_b2Om7xRMRWkkU&skuId=5940217905947&spm=a1z0d.6639537%2F202410.item.d749116190950.34747484e3AbaM&upStreamPrice=28800&xxc=shop',
NULL, NULL, @c_wh_B01, NULL, '', NULL, NULL, @c_wh_B01, NULL, '',
'obsolete', 1, '2026-09-28 06:17:01', '2026-09-28 06:47:57'), 'obsolete', 1, '2026-09-28 06:17:01', '2026-09-28 06:47:57'),
(@org_wh, 'B010077', '香橙派开发板Pi 5', (@org_wh, 'B010077', '香橙派开发板Pi 5',