跨级反写问题场景: 使用插件分配反写量

栏目:云苍穹知识作者:金蝶来源:金蝶云社区发布:2024-09-23浏览:1

跨级反写问题场景: 使用插件分配反写量

# **1 业务背景**
使用跨级反写时, 如果单据下推时存在合并下推情况, 反写引擎无法识别反写分配量, 此时需要开发插件自行处理分配方式.
## 反写量的两种计算逻辑 > 为什么需要插件做分配? 回答这个问题前我们先了解下反写引擎的计算逻辑 标准反写引擎计算反写量有一个准则 "基于直接上游的下推携带量来计算".
针对这个准则我们可以给出两种不同分配反写量逻辑的场景: * 依次分配: 由于上下游的分录行一一对应, 所以反写量的分配也遵循一一对应原则: ![依次下推-4480909.webp](/download/010097dd0ab0cbc149c995661302c03e3032.webp)
* 逐级分配: 下游对应多个上游分录行, 反写量分配时优先使用"下推携带量", 多余的部分分配给最后一个反写行. ![合并下推.webp](/download/01007eb55e023e7f46779fc57996e6e27504.webp) ​ (上图描述增加目标单金额的场景, 反之如果修改后的目标单金额与下推金额100相比更少, 则缺少的部分也从最后一行扣减. 原则是尽量先满足顺序在前的反写行的下推量)
## 跨级反写的反写量计算 理解完标准反写引擎计算反写量的逻辑, 我们再来看跨级反写中如何支持反写量计算: * 场景一 逐级下推时不合并: ![跨级依次下推.webp](/download/01005c3ab917e7a7478eb2ecaa94146c4cd3.webp) ​ (可以看出虽然是跨级反写, 但反写量的计算方式与非跨级反写一致, 因为"采购入库单"与"采购申请单"的分录关系依旧是一一对应, 反写量依次分配即可.)
* 场景二 逐级下推时, 最后一次下推合并: ![跨级合并下推.webp](/download/01000f475e91c52743519c16f8333bd88390.webp) ​ (此场景存在合并下推, "采购入库单"根据直接上游"采购订单"的携带量, 使用**逐级分配模式**计算跨级反写"采购申请单"的反写量)
* 场景三 逐级下推时, 第一次下推做合并(× 无法计算): ![跨级合并下推-2.webp](/download/010038a087e438224b389b515b5358ec3daf.webp)

## 首次合并, 为何无法计算反写量
回到我们反写量计算的准则: 基于直接上游的下推携带量来计算.
此场景下 "采购入库单" 的直接上游 "采购订单" 仅携带一条分录值, 而 "采购入库单" 跨级反写 "采购申请单" 属于一条分录反写三条分录, **但仅有一条分录携带量的信息**, 所以依次分配和逐级分配的方式都无法使用.
场景二 "逐级下推时, 最后一次下推合并" 与场景三 "逐级下推时, 第一次下推做合并" 很相似, 但场景二是可以做逐级分配的, 因为其直接上游 "采购订单" 的下推存在三条分录携带量信息.

# 2 解决方案 针对此跨级反写下, 合并下推无法计算反写量的场景, 会抛出提示 "**合并下推后跨级反写,必须自行开发插件分配反写量,且分配总额与计算额相等**"
标准反写引擎提供插件时机, 用于干预此场景下反写量的计算, 业务同事需要根据自身业务特点实现插件代码开发.
## afterCalcWriteValue事件 - 事件触发时机 - 基于下游单据当前行,反写值计算完毕后,触发此事件。 - 用于修正反写量,调整对各源单行的分配量。 - 示例代码 ```java /** * @Description 反写插件示例: 跨级反写下反写量分配 * @Author tulane **/ public class CalcWriteValueSample extends AbstractWriteBackPlugIn { @Override public void afterCalcWriteValue(AfterCalcWriteValueEventArgs e) { // 第一步: 判定是否为一行反写多行场景, 如果不是则无需处理 if(e.getSrcRowVal() == null || e.getSrcRowVal().size() <= 1){ return; } // 第二步: 获取总反写量 BigDecimal allVal = e.getVal(); // 第三步: 获取需要反写的源单行对象: RowId Map<BFRowId, BigDecimal> srcRowVal = e.getSrcRowVal(); /** * 拆解内部信息: * rowId: 将要反写的源单行对象, 我们通过对象内部属性可以追溯到具体的源单对象 * - mainTableId: 如源单行是采购申请单的物料明细的一行分录, 则 mainTableId 指向采购申请单表 * - tableId: 如源单行是采购申请单的物料明细的一行分录, 则 tableId 指向物料明细表 * (如果源单行就是采购申请单的单头信息, 则指向采购申请表) * - billId: 单据内码 * - entryId: 单据体内码 (如果源单行就是采购申请单的单头信息, 则是单据内码) * */ for (Map.Entry<BFRowId, BigDecimal> entry : srcRowVal.entrySet()) { BFRowId rowId = entry.getKey(); Long mainTableId = rowId.getMainTableId(); Long tableId = rowId.getTableId(); Long billId = rowId.getBillId(); Long entryId = rowId.getEntryId(); // 第四步: 计算反写量 (涉及计算每个源单行反写量的算法, 请根据业务实际情况实现, 示例为"平均分配") BigDecimal valForSingle = calcWriteValueForSingle(srcRowVal, rowId, allVal); // 第五步: 赋值反写量 srcRowVal.put(rowId, valForSingle); } } // 反写量计算: 平均分配 private BigDecimal calcWriteValueForSingle(Map<BFRowId, BigDecimal> srcRowVal, BFRowId rowId, BigDecimal allVal) { return allVal.divide(new BigDecimal(srcRowVal.size()), BigDecimal.ROUND_DOWN); } } ``` - 事件参数 ```java public class AfterCalcWriteValueEventArgs extends WriteBackEventArgs { private EntityType entity; private DynamicObject activeRow; private WriteBackRuleElement rule; private WriteBackFormula ruleItem; private BigDecimal val; private Object cVal; private Map<BFRowId, BigDecimal> srcRowVal = new HashMap<>(); /** * 构造函数 * @param entity 关联主实体 * @param activeRow 关联主实体当前行 * @param rule 反写规则 * @param ruleItem 反写公式 * @param val 根据反写公式,算出的反写量 * @param cVal 覆盖反写模式,根据反写公式算出的覆盖值 */ public AfterCalcWriteValueEventArgs(EntityType entity, DynamicObject activeRow, WriteBackRuleElement rule, WriteBackFormula ruleItem, BigDecimal val, Object cVal) { this.entity = entity; this.activeRow = activeRow; this.rule = rule; this.ruleItem = ruleItem; this.val = val; this.cVal = cVal; } /** * 关联主实体 * @return */ @KSMethod public EntityType getEntity() { return entity; } /** * 关联主实体当前行 * @return */ @KSMethod public DynamicObject getActiveRow() { return activeRow; } /** * 反写规则 * @return */ @KSMethod public WriteBackRuleElement getRule() { return rule; } /** * 反写公式 */ @KSMethod public WriteBackFormula getRuleItem() { return ruleItem; } /** * 根据反写公式,算出的反写量 * @return */ @KSMethod public BigDecimal getVal() { return val; } /** * 覆盖反写模式,根据反写公式算出的覆盖值 */ @KSMethod public Object getCVal() { return cVal; } /** * 搜索出的源单行,以及其分配的反写量: * 如果有多个源单行,默认每行的反写量为0;需要插件自行给各源单行分配反写量; * 如果插件不处理,则不反写 * @return */ @KSMethod public Map<BFRowId, BigDecimal> getSrcRowVal() { return srcRowVal; } } ```

# 3 注意事项 本文档中**逐级分配**的模式是建立在下游单关联关系表已配置控制字段的基础上, 具体配置及说明文档见链接: [文章-单据关联配置中的反写字段使用方法](https://vip.kingdee.com/article/416626821090570752?productLineId=29&isKnowledge=2)

跨级反写问题场景: 使用插件分配反写量

# **1 业务背景** 使用跨级反写时, 如果单据下推时存在合并下推情况, 反写引擎无法识别反写分配量, 此时需要开发插件自行处理分配方...
点击下载文档
确认删除?
回到顶部
客服QQ
  • 客服QQ点击这里给我发消息