物料相关记录 • 背景在金蝶星瀚物料列表中标准列“物料分组”默认显示的是“物料基本分类标准”下的分组值。业务上希望在物料列表中显示另一个分类标准“存货类别”下的分组例如“烟、酒、茶”。经过分析不建议直接修改标准“物料分组”的显示逻辑因为该字段有标准语义直接改动可能影响查询、报表、权限规则和后续升级。因此采用新增字段的方式实现。实现思路在物料主数据 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为新增字段对应的数据库列。分类标准“存货类别”的编码为002UPDATE 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 -- 分类标准表历史数据预览 SQLSELECTm.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 mINNER JOIN t_bd_materialgroupdetail eON e.fmaterialid m.fidINNER JOIN t_bd_mtgroupstandard gsON gs.fid e.fstandardidINNER JOIN T_bd_materialgroup gON g.fid e.fgroupidWHERE gs.fnumber 002;历史数据更新 SQLUPDATE T_BD_MATERIAL mSET fk_cyjt_invcategory g.fnameFROM t_bd_materialgroupdetail eINNER JOIN t_bd_mtgroupstandard gsON gs.fid e.fstandardidINNER JOIN T_bd_materialgroup gON g.fid e.fgroupidWHERE e.fmaterialid m.fidAND gs.fnumber 002AND g.fname IS NOT NULLAND trim(g.fname) AND (m.fk_cyjt_invcategory IS NULLOR trim(m.fk_cyjt_invcategory) );遇到的问题一开始使用了 SQL Server 风格写法UPDATE mSET ...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); } }