物料相关记录
• 背景
在金蝶星瀚物料列表中,标准列“物料分组”默认显示的是“物料基本分类标准”下的分组值。业务上希望在物料列表中显示另一个分类标
准“存货类别”下的分组,例如“烟、酒、茶”。
经过分析,不建议直接修改标准“物料分组”的显示逻辑,因为该字段有标准语义,直接改动可能影响查询、报表、权限规则和后续升
级。因此采用新增字段的方式实现。
实现思路
在物料主数据 bd_material 上新增一个自定义字段:
cyjt_invcategory / fk_cyjt_invcategory
用于保存“存货类别”分类标准对应的分类名称。
物料保存时,通过插件读取物料“分类标准”分录,找到分类标准为“存货类别”的那一行,将对应分组名称写入该字段。然后在物料列表
配置中增加该字段,即可显示“存货类别”。
插件实现
插件放在星瀚工程的操作插件包下:
cyjt.cy001.devcy.devcy01.plugin.operate
插件类:
MaterialInvCategorySavePlugin
核心逻辑:
物料保存时
-> 读取 entry_groupstandard 分类标准分录
-> 找到 standardid 对应编码为 002 或名称为“存货类别”的分录
-> 取 groupid 对应分组的 name
-> 写入物料主表字段 cyjt_invcategory
这样新保存或修改的物料都会自动同步“存货类别”。
列表显示配置
编辑界面新增字段后,列表不会自动显示,还需要在开发平台中配置列表字段:
物料 bd_material 扩展
-> 新增字段 cyjt_invcategory
-> 设置字段入库、可见、允许列表显示
-> 列表字段中加入“存货类别”
-> 发布元数据
-> 清缓存或重新登录
如果前台列表仍未显示,需要在列表个性化或列设置中勾选该字段。
历史数据处理
插件只对后续保存的物料生效,历史数据需要通过 SQL 批量补齐。
实际确认到相关物理表如下:
T_BD_MATERIAL -- 物料主表
t_bd_materialgroupdetail -- 物料分类标准分录表
t_bd_materialgroup -- 物料分组
t_bd_mtgroupstandard -- 分类标准表
主键均为:
fid
其中:
T_BD_MATERIAL.fk_cyjt_invcategory
为新增字段对应的数据库列。
分类标准“存货类别”的编码为:
002
UPDATE T_BD_MATERIAL m SET fk_cyjt_invcategory = g.fname FROM t_bd_materialgroupdetail e INNER JOIN t_bd_mtgroupstandard gs ON gs.fid = e.fstandardid INNER JOIN T_bd_materialgroup g ON g.fid = e.fgroupid WHERE e.fmaterialid = m.fid AND gs.fnumber = '002' AND (m.fk_cyjt_invcategory IS NULL OR trim(m.fk_cyjt_invcategory) = '') select * from T_BD_MATERIAL -- 物料主表 select * from t_bd_materialgroupdetail -- 物料分类标准分录表 select * from t_bd_materialgroup -- 物料分组 select * from t_bd_mtgroupstandard -- 分类标准表历史数据预览 SQL
SELECT
m.fid,
m.fnumber,
m.fname,
m.fk_cyjt_invcategory AS 当前存货类别,
gs.fnumber AS 分类标准编码,
gs.fname AS 分类标准名称,
g.fnumber AS 分组编码,
g.fname AS 分组名称
FROM T_BD_MATERIAL m
INNER JOIN t_bd_materialgroupdetail e
ON e.fmaterialid = m.fid
INNER JOIN t_bd_mtgroupstandard gs
ON gs.fid = e.fstandardid
INNER JOIN T_bd_materialgroup g
ON g.fid = e.fgroupid
WHERE gs.fnumber = '002';
历史数据更新 SQL
UPDATE T_BD_MATERIAL m
SET fk_cyjt_invcategory = g.fname
FROM t_bd_materialgroupdetail e
INNER JOIN t_bd_mtgroupstandard gs
ON gs.fid = e.fstandardid
INNER JOIN T_bd_materialgroup g
ON g.fid = e.fgroupid
WHERE e.fmaterialid = m.fid
AND gs.fnumber = '002'
AND g.fname IS NOT NULL
AND trim(g.fname) <> ''
AND (
m.fk_cyjt_invcategory IS NULL
OR trim(m.fk_cyjt_invcategory) = ''
);
遇到的问题
一开始使用了 SQL Server 风格写法:
UPDATE m
SET ...
FROM ...
在 PostgreSQL 中报错:
relation "m" does not exist
原因是 PostgreSQL 不能直接 UPDATE m,需要写真实表名:
UPDATE T_BD_MATERIAL m
之后又遇到:
relation "bd_material" does not exist
原因是 bd_material 是星瀚实体标识,不是数据库物理表名。实际物理表是:
T_BD_MATERIAL
最后还发现字段看起来为空,但加上空值条件后查不到数据。排查发现:
fk_cyjt_invcategory 字符长度 = 1
原始值 = ' '
也就是说字段里存的是一个空格,不是 NULL,也不是空字符串。因此条件需要从:
m.fk_cyjt_invcategory IS NULL OR m.fk_cyjt_invcategory = ''
改为:
m.fk_cyjt_invcategory IS NULL OR trim(m.fk_cyjt_invcategory) = ''
总结
本次实现没有改动标准“物料分组”字段,而是通过新增物料字段 cyjt_invcategory 来冗余保存“存货类别”分类名称。新数据通过保存
插件自动同步,历史数据通过 SQL 批量补齐。该方案对标准逻辑影响小,列表展示清晰,也便于后续过滤、导出和报表使用。
package cyjt.cy001.devcy.devcy01.plugin.operate; import kd.bos.dataentity.entity.DynamicObject; import kd.bos.dataentity.entity.DynamicObjectCollection; import kd.bos.entity.plugin.AbstractOperationServicePlugIn; import kd.bos.entity.plugin.args.BeforeOperationArgs; import kd.bos.logging.Log; import kd.bos.logging.LogFactory; /** * 物料保存插件:将“存货类别”分类标准分录同步到物料主表字段。 */ public class MaterialInvCategorySavePlugin extends AbstractOperationServicePlugIn { private static final Log log = LogFactory.getLog(MaterialInvCategorySavePlugin.class); private static final String ENTRY_GROUP_STANDARD = "entry_groupstandard"; private static final String FIELD_STANDARD = "standardid"; private static final String FIELD_GROUP = "groupid"; /** * 物料主表上的自定义字段标识,需要和开发平台设计器中创建的字段标识保持一致。 */ private static final String FIELD_INV_CATEGORY = "cyjt_invcategory"; /** * “存货类别”分类标准编码,当前按截图中的编码 002 处理。 */ private static final String INV_CATEGORY_STANDARD_NUMBER = "002"; private static final String INV_CATEGORY_STANDARD_NAME = "存货类别"; @Override public void beforeExecuteOperationTransaction(BeforeOperationArgs e) { super.beforeExecuteOperationTransaction(e); DynamicObject[] dataEntities = e.getDataEntities(); if (dataEntities == null || dataEntities.length == 0) { return; } for (DynamicObject material : dataEntities) { try { material.set(FIELD_INV_CATEGORY, getInvCategoryName(material)); } catch (Exception ex) { log.error("同步物料存货类别失败,物料ID:" + material.getLong("id"), ex); } } } private String getInvCategoryName(DynamicObject material) { DynamicObjectCollection entries = material.getDynamicObjectCollection(ENTRY_GROUP_STANDARD); if (entries == null || entries.isEmpty()) { return null; } for (DynamicObject entry : entries) { DynamicObject standard = entry.getDynamicObject(FIELD_STANDARD); if (!isInvCategoryStandard(standard)) { continue; } DynamicObject group = entry.getDynamicObject(FIELD_GROUP); return group == null ? null : group.getString("name"); } return null; } private boolean isInvCategoryStandard(DynamicObject standard) { if (standard == null) { return false; } String number = standard.getString("number"); String name = standard.getString("name"); return INV_CATEGORY_STANDARD_NUMBER.equals(number) || INV_CATEGORY_STANDARD_NAME.equals(name); } }