|
|
|
@ -16,6 +16,8 @@ import apelet.common.botp.service.IConvertPluginEntryService; |
|
|
|
import apelet.common.botp.service.IReverseWritingBusinessService; |
|
|
|
import apelet.common.botp.service.IReverseWritingBusinessService; |
|
|
|
import apelet.common.botp.service.IReverseWritingFormulaService; |
|
|
|
import apelet.common.botp.service.IReverseWritingFormulaService; |
|
|
|
import apelet.common.botp.service.IReverseWritingRulesService; |
|
|
|
import apelet.common.botp.service.IReverseWritingRulesService; |
|
|
|
|
|
|
|
import apelet.common.botp.service.execute.entity.ResolvedWriteBackRule; |
|
|
|
|
|
|
|
import apelet.common.botp.util.RuleSnapshotUtil; |
|
|
|
import apelet.common.botp.util.RuleValueCalculator; |
|
|
|
import apelet.common.botp.util.RuleValueCalculator; |
|
|
|
import apelet.common.core.botp.BotpReverseWriteFacade; |
|
|
|
import apelet.common.core.botp.BotpReverseWriteFacade; |
|
|
|
import apelet.common.core.botp.BotpWriteBackRequest; |
|
|
|
import apelet.common.core.botp.BotpWriteBackRequest; |
|
|
|
@ -32,8 +34,10 @@ import apelet.common.online.rule.model.RuleEvalContext; |
|
|
|
import apelet.common.orm.impl.Selector; |
|
|
|
import apelet.common.orm.impl.Selector; |
|
|
|
import apelet.common.orm.impl.SelectorItem; |
|
|
|
import apelet.common.orm.impl.SelectorItem; |
|
|
|
import com.alibaba.fastjson.JSON; |
|
|
|
import com.alibaba.fastjson.JSON; |
|
|
|
|
|
|
|
import com.alibaba.fastjson.JSONArray; |
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.alibaba.fastjson.JSONObject; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
|
|
|
|
|
|
|
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import lombok.extern.slf4j.Slf4j; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.apache.commons.lang3.StringUtils; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
import org.springframework.beans.factory.annotation.Autowired; |
|
|
|
@ -44,6 +48,7 @@ import org.springframework.transaction.support.TransactionSynchronizationManager |
|
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.math.BigDecimal; |
|
|
|
import java.util.*; |
|
|
|
import java.util.*; |
|
|
|
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 反写编排器:保存 / 审核 / 撤销 / 弃审四个入口共用的唯一实现。 |
|
|
|
* 反写编排器:保存 / 审核 / 撤销 / 弃审四个入口共用的唯一实现。 |
|
|
|
@ -149,6 +154,11 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
private void executeRule(ReverseWritingRules rule, ObjectValue targetBill, OnlFormHead targetHead, BotpWriteBackRequest request) { |
|
|
|
private void executeRule(ReverseWritingRules rule, ObjectValue targetBill, OnlFormHead targetHead, BotpWriteBackRequest request) { |
|
|
|
List<ConvertRelation> relations = locateRelations(targetHead, request); |
|
|
|
List<ConvertRelation> relations = locateRelations(targetHead, request); |
|
|
|
List<ConvertRelationEntry> entries = relations.isEmpty() ? Collections.emptyList() : loadRelationEntries(relations); |
|
|
|
List<ConvertRelationEntry> entries = relations.isEmpty() ? Collections.emptyList() : loadRelationEntries(relations); |
|
|
|
|
|
|
|
if (entries.isEmpty() && !relations.isEmpty()) { |
|
|
|
|
|
|
|
// 单表单据(无分录)不会产生分录级关联关系,退化成「单据 ↔ 单据」配对:
|
|
|
|
|
|
|
|
// 此时分行 id 就是单据 id,公式里的 表.字段 直接落在单据表上。
|
|
|
|
|
|
|
|
entries = toBillLevelPairs(relations); |
|
|
|
|
|
|
|
} |
|
|
|
if (entries.isEmpty()) { |
|
|
|
if (entries.isEmpty()) { |
|
|
|
// 关系记录缺失时回退读单据上的 srcbillid / srcbillentryid(迁移期兼容)
|
|
|
|
// 关系记录缺失时回退读单据上的 srcbillid / srcbillentryid(迁移期兼容)
|
|
|
|
entries = fallbackBySrcBillId(targetBill); |
|
|
|
entries = fallbackBySrcBillId(targetBill); |
|
|
|
@ -159,13 +169,161 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
rule.getId(), request.getTargetTableName(), request.getTargetBillId()); |
|
|
|
rule.getId(), request.getTargetTableName(), request.getTargetBillId()); |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
List<WriteBackPlugIn> plugins = loadPlugins(rule.getId()); |
|
|
|
// 回滚时优先按关联关系上记录的版本取快照执行,保证「怎么加的就怎么退」
|
|
|
|
WriteBackContext context = buildContext(rule, targetBill, request.isRollback()); |
|
|
|
ResolvedWriteBackRule resolved = resolveRule(rule, entries, request.isRollback()); |
|
|
|
|
|
|
|
List<WriteBackPlugIn> plugins = loadPlugins(resolved.getRule().getId()); |
|
|
|
|
|
|
|
WriteBackContext context = buildContext(resolved.getRule(), targetBill, request.isRollback()); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.BEFORE_RULE); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.BEFORE_RULE); |
|
|
|
execBusinessRules(rule, entries, context); |
|
|
|
execBusinessRules(resolved, entries, context); |
|
|
|
execFormulas(rule, entries, context); |
|
|
|
execFormulas(resolved, entries, context, plugins); |
|
|
|
closeSourceRows(rule, entries, context); |
|
|
|
closeSourceRows(resolved.getRule(), entries, context); |
|
|
|
registerAfterCommit(plugins, context); |
|
|
|
registerAfterCommit(plugins, context); |
|
|
|
|
|
|
|
if (!request.isRollback()) { |
|
|
|
|
|
|
|
recordRuleVersion(resolved, entries); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 解析本次执行用的规则。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <p>回滚时若关联关系上记录了版本、且与当前版本不同,就按记录版本取快照执行—— |
|
|
|
|
|
|
|
* 否则反写规则改版后,老单据的撤销会用新公式去退旧数据。</p> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param current 当前规则(库里最新版本)。 |
|
|
|
|
|
|
|
* @param entries 关联关系。 |
|
|
|
|
|
|
|
* @param rollback 是否回滚方向。 |
|
|
|
|
|
|
|
* @return 解析结果。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private ResolvedWriteBackRule resolveRule(ReverseWritingRules current, List<ConvertRelationEntry> entries, boolean rollback) { |
|
|
|
|
|
|
|
Integer recorded = firstRecordedVersion(entries); |
|
|
|
|
|
|
|
if (rollback && recorded != null && !recorded.equals(current.getRuleVersion())) { |
|
|
|
|
|
|
|
ResolvedWriteBackRule fromSnapshot = loadSnapshot(current.getId(), recorded); |
|
|
|
|
|
|
|
if (fromSnapshot != null) { |
|
|
|
|
|
|
|
log.info("反写回滚按记录版本执行。规则id={},记录版本={},当前版本={}", current.getId(), recorded, current.getRuleVersion()); |
|
|
|
|
|
|
|
return fromSnapshot; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ResolvedWriteBackRule resolved = new ResolvedWriteBackRule(); |
|
|
|
|
|
|
|
resolved.setRule(current); |
|
|
|
|
|
|
|
resolved.setVersion(current.getRuleVersion()); |
|
|
|
|
|
|
|
resolved.setFormulas(reverseWritingFormulaService.list(new LambdaQueryWrapper<ReverseWritingFormula>() |
|
|
|
|
|
|
|
.eq(ReverseWritingFormula::getEnable, 1) |
|
|
|
|
|
|
|
.eq(ReverseWritingFormula::getRulesId, current.getId()) |
|
|
|
|
|
|
|
.orderByAsc(ReverseWritingFormula::getExecOrder))); |
|
|
|
|
|
|
|
resolved.setBusinesses(reverseWritingBusinessService.list(new LambdaQueryWrapper<ReverseWritingBusiness>() |
|
|
|
|
|
|
|
.eq(ReverseWritingBusiness::getEnable, 1) |
|
|
|
|
|
|
|
.eq(ReverseWritingBusiness::getRulesId, current.getId()))); |
|
|
|
|
|
|
|
return resolved; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 按版本读反写规则快照。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param ruleId 规则 id。 |
|
|
|
|
|
|
|
* @param version 版本号。 |
|
|
|
|
|
|
|
* @return 解析结果;快照缺失或内容不完整时返回 null(调用方退回当前版本)。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private ResolvedWriteBackRule loadSnapshot(Long ruleId, Integer version) { |
|
|
|
|
|
|
|
ReverseWritingRulesVer snapshot = reverseWritingRulesService.getVersionSnapshot(ruleId, version); |
|
|
|
|
|
|
|
if (snapshot == null || StringUtils.isBlank(snapshot.getSnapshotJson())) { |
|
|
|
|
|
|
|
log.warn("反写规则快照不存在,退回当前版本执行。规则id={},版本={}", ruleId, version); |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
JSONObject json = JSON.parseObject(snapshot.getSnapshotJson()); |
|
|
|
|
|
|
|
ReverseWritingRules rule = JSON.parseObject(json.getString(RuleSnapshotUtil.SECTION_RULE), ReverseWritingRules.class); |
|
|
|
|
|
|
|
if (rule == null) { |
|
|
|
|
|
|
|
log.warn("反写规则快照内容不完整(缺少 rule 分区),退回当前版本执行。规则id={},版本={}", ruleId, version); |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ResolvedWriteBackRule resolved = new ResolvedWriteBackRule(); |
|
|
|
|
|
|
|
resolved.setRule(rule); |
|
|
|
|
|
|
|
resolved.setFormulas(parseSection(json.getJSONArray(RuleSnapshotUtil.SECTION_FORMULAS), ReverseWritingFormula.class).stream() |
|
|
|
|
|
|
|
.filter(f -> f.getEnable() == 1) |
|
|
|
|
|
|
|
.sorted(Comparator.comparing(ReverseWritingFormula::getExecOrder, Comparator.nullsLast(Comparator.naturalOrder()))) |
|
|
|
|
|
|
|
.collect(Collectors.toList())); |
|
|
|
|
|
|
|
resolved.setBusinesses(parseSection(json.getJSONArray(RuleSnapshotUtil.SECTION_BUSINESSES), ReverseWritingBusiness.class).stream() |
|
|
|
|
|
|
|
.filter(b -> b.getEnable() == 1) |
|
|
|
|
|
|
|
.collect(Collectors.toList())); |
|
|
|
|
|
|
|
resolved.setVersion(snapshot.getRuleVersion()); |
|
|
|
|
|
|
|
return resolved; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 解析快照里的子表分区。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param array 分区数组,可空。 |
|
|
|
|
|
|
|
* @param clazz 目标类型。 |
|
|
|
|
|
|
|
* @param <T> 类型。 |
|
|
|
|
|
|
|
* @return 列表,不会返回 null。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private <T> List<T> parseSection(JSONArray array, Class<T> clazz) { |
|
|
|
|
|
|
|
return array == null ? new ArrayList<>() : array.toJavaList(clazz); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 取关联关系上记录的反写规则版本。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param entries 关联关系。 |
|
|
|
|
|
|
|
* @return 版本号;没有记录时返回 null。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private Integer firstRecordedVersion(List<ConvertRelationEntry> entries) { |
|
|
|
|
|
|
|
for (ConvertRelationEntry entry : entries) { |
|
|
|
|
|
|
|
if (entry.getWritebackRuleVersion() != null) { |
|
|
|
|
|
|
|
return entry.getWritebackRuleVersion(); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return null; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 记录本次反写实际使用的规则版本(正向执行时)。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <p>按目标单一次性更新,不逐行 update。</p> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param resolved 解析结果。 |
|
|
|
|
|
|
|
* @param entries 关联关系。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private void recordRuleVersion(ResolvedWriteBackRule resolved, List<ConvertRelationEntry> entries) { |
|
|
|
|
|
|
|
if (resolved.getVersion() == null || entries.isEmpty()) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Set<String> targetIds = new LinkedHashSet<>(); |
|
|
|
|
|
|
|
for (ConvertRelationEntry entry : entries) { |
|
|
|
|
|
|
|
if (StringUtils.isNotBlank(entry.getTargetId())) { |
|
|
|
|
|
|
|
targetIds.add(entry.getTargetId()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (targetIds.isEmpty()) { |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
convertRelationEntryMapper.update(null, new LambdaUpdateWrapper<ConvertRelationEntry>() |
|
|
|
|
|
|
|
.in(ConvertRelationEntry::getTargetId, targetIds) |
|
|
|
|
|
|
|
.set(ConvertRelationEntry::getWritebackRuleVersion, resolved.getVersion())); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
|
|
* 把单据级关联关系转成「单据 ↔ 单据」的配对。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <p>单表单据没有分录,转换时不会写 convert_relation_entry。这里把单据 id 同时当作 |
|
|
|
|
|
|
|
* 「源行 id / 目标行 id」,后续按行取数的逻辑就能原样复用。</p> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param relations 单据级关联关系。 |
|
|
|
|
|
|
|
* @return 配对列表。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private List<ConvertRelationEntry> toBillLevelPairs(List<ConvertRelation> relations) { |
|
|
|
|
|
|
|
List<ConvertRelationEntry> pairs = new ArrayList<>(relations.size()); |
|
|
|
|
|
|
|
for (ConvertRelation relation : relations) { |
|
|
|
|
|
|
|
if (StringUtils.isBlank(relation.getSourceId())) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
ConvertRelationEntry pair = new ConvertRelationEntry(); |
|
|
|
|
|
|
|
pair.setSourceId(relation.getSourceId()); |
|
|
|
|
|
|
|
pair.setSourceEntryId(relation.getSourceId()); |
|
|
|
|
|
|
|
pair.setTargetId(relation.getTargetId()); |
|
|
|
|
|
|
|
pair.setTargeEntryId(relation.getTargetId()); |
|
|
|
|
|
|
|
pairs.add(pair); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return pairs; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -215,15 +373,12 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
* <p>分支表达式形如 {@code 源单表.源单表.字段 = $fun$(参数)} 或 {@code 源单表.字段 = '常量'}: |
|
|
|
* <p>分支表达式形如 {@code 源单表.源单表.字段 = $fun$(参数)} 或 {@code 源单表.字段 = '常量'}: |
|
|
|
* 等号左边是回写位置(源单),右边是取值(函数 / 字段引用 / 字面量)。</p> |
|
|
|
* 等号左边是回写位置(源单),右边是取值(函数 / 字段引用 / 字面量)。</p> |
|
|
|
* |
|
|
|
* |
|
|
|
* @param rule 反写规则。 |
|
|
|
* @param resolved 解析后的规则(含业务规则分录)。 |
|
|
|
* @param entries 关联关系。 |
|
|
|
* @param entries 关联关系。 |
|
|
|
* @param context 反写上下文。 |
|
|
|
* @param context 反写上下文。 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private void execBusinessRules(ReverseWritingRules rule, List<ConvertRelationEntry> entries, WriteBackContext context) { |
|
|
|
private void execBusinessRules(ResolvedWriteBackRule resolved, List<ConvertRelationEntry> entries, WriteBackContext context) { |
|
|
|
List<ReverseWritingBusiness> businesses = reverseWritingBusinessService.list(new LambdaQueryWrapper<ReverseWritingBusiness>() |
|
|
|
for (ReverseWritingBusiness business : resolved.getBusinesses()) { |
|
|
|
.eq(ReverseWritingBusiness::getEnable, 1) |
|
|
|
|
|
|
|
.eq(ReverseWritingBusiness::getRulesId, rule.getId())); |
|
|
|
|
|
|
|
for (ReverseWritingBusiness business : businesses) { |
|
|
|
|
|
|
|
boolean hit = business.getPreconditionsEnglish() == null |
|
|
|
boolean hit = business.getPreconditionsEnglish() == null |
|
|
|
|| conditionHit(business.getPreconditionsEnglish(), business.getPreconditionsJson(), context.getTargetBillData()); |
|
|
|
|| conditionHit(business.getPreconditionsEnglish(), business.getPreconditionsJson(), context.getTargetBillData()); |
|
|
|
String expression = hit ? business.getEstablishedEnglish() : business.getNotEstablishedEnglish(); |
|
|
|
String expression = hit ? business.getEstablishedEnglish() : business.getNotEstablishedEnglish(); |
|
|
|
@ -259,29 +414,34 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
if (value == null) { |
|
|
|
if (value == null) { |
|
|
|
return; |
|
|
|
return; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// 回写位置可能是源单【表头】或【分录】:表头写源单 id,分录写源分录行 id。
|
|
|
|
|
|
|
|
// 原先一律用 sourceEntryId,目标是表头时会按分录行 id 去更新表头行,写错行或压根更新不到。
|
|
|
|
|
|
|
|
String sourceHeaderTable = resolveTableName(String.valueOf(context.getRule().getSourceDocuments())); |
|
|
|
|
|
|
|
boolean headerTarget = sourceHeaderTable != null && sourceHeaderTable.equals(targetRef[0]); |
|
|
|
for (ConvertRelationEntry entry : entries) { |
|
|
|
for (ConvertRelationEntry entry : entries) { |
|
|
|
writeSourceRow(targetRef[0], entry.getSourceEntryId(), targetRef[1], value); |
|
|
|
String rowId = headerTarget ? entry.getSourceId() : entry.getSourceEntryId(); |
|
|
|
|
|
|
|
if (StringUtils.isBlank(rowId)) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
writeSourceRow(targetRef[0], rowId, targetRef[1], value); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* 逐条执行反写公式,按 exec_order 升序。 |
|
|
|
* 逐条执行反写公式,按 exec_order 升序。 |
|
|
|
* |
|
|
|
* |
|
|
|
* @param rule 反写规则。 |
|
|
|
* @param resolved 解析后的规则(含公式分录)。 |
|
|
|
* @param entries 关联关系。 |
|
|
|
* @param entries 关联关系。 |
|
|
|
* @param context 反写上下文。 |
|
|
|
* @param context 反写上下文。 |
|
|
|
|
|
|
|
* @param plugins 已加载的插件实例(复用,避免每条公式都查一次库)。 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private void execFormulas(ReverseWritingRules rule, List<ConvertRelationEntry> entries, WriteBackContext context) { |
|
|
|
private void execFormulas(ResolvedWriteBackRule resolved, List<ConvertRelationEntry> entries, WriteBackContext context, List<WriteBackPlugIn> plugins) { |
|
|
|
List<ReverseWritingFormula> formulas = reverseWritingFormulaService.list(new LambdaQueryWrapper<ReverseWritingFormula>() |
|
|
|
for (ReverseWritingFormula formula : resolved.getFormulas()) { |
|
|
|
.eq(ReverseWritingFormula::getEnable, 1) |
|
|
|
|
|
|
|
.eq(ReverseWritingFormula::getRulesId, rule.getId()) |
|
|
|
|
|
|
|
.orderByAsc(ReverseWritingFormula::getExecOrder)); |
|
|
|
|
|
|
|
for (ReverseWritingFormula formula : formulas) { |
|
|
|
|
|
|
|
context.setFormula(formula); |
|
|
|
context.setFormula(formula); |
|
|
|
if (!conditionHit(formula.getConditionEnglish(), formula.getConditionJson(), context.getTargetBillData())) { |
|
|
|
if (!conditionHit(formula.getConditionEnglish(), formula.getConditionJson(), context.getTargetBillData())) { |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
} |
|
|
|
} |
|
|
|
applyFormula(formula, entries, context); |
|
|
|
applyFormula(formula, entries, context, plugins); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
@ -295,8 +455,9 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
* @param formula 反写公式。 |
|
|
|
* @param formula 反写公式。 |
|
|
|
* @param entries 关联关系。 |
|
|
|
* @param entries 关联关系。 |
|
|
|
* @param context 反写上下文。 |
|
|
|
* @param context 反写上下文。 |
|
|
|
|
|
|
|
* @param plugins 已加载的插件实例。 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private void applyFormula(ReverseWritingFormula formula, List<ConvertRelationEntry> entries, WriteBackContext context) { |
|
|
|
private void applyFormula(ReverseWritingFormula formula, List<ConvertRelationEntry> entries, WriteBackContext context, List<WriteBackPlugIn> plugins) { |
|
|
|
ReverseWriteModeEnum mode = ReverseWriteModeEnum.ofNullable(formula.getMode()); |
|
|
|
ReverseWriteModeEnum mode = ReverseWriteModeEnum.ofNullable(formula.getMode()); |
|
|
|
String[] targetRef = splitRef(formula.getFormulaEnglish()); |
|
|
|
String[] targetRef = splitRef(formula.getFormulaEnglish()); |
|
|
|
String[] sourceRef = splitRef(formula.getSourceSingleField()); |
|
|
|
String[] sourceRef = splitRef(formula.getSourceSingleField()); |
|
|
|
@ -304,7 +465,6 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
throw new BotpMappingException("反写公式配置不完整,公式=" + formula.getFormulaEnglish() |
|
|
|
throw new BotpMappingException("反写公式配置不完整,公式=" + formula.getFormulaEnglish() |
|
|
|
+ ",源单字段=" + formula.getSourceSingleField()); |
|
|
|
+ ",源单字段=" + formula.getSourceSingleField()); |
|
|
|
} |
|
|
|
} |
|
|
|
List<WriteBackPlugIn> plugins = loadPlugins(context.getRule().getId()); |
|
|
|
|
|
|
|
int scale = resolveScale(sourceRef[0], sourceRef[1]); |
|
|
|
int scale = resolveScale(sourceRef[0], sourceRef[1]); |
|
|
|
for (ConvertRelationEntry entry : entries) { |
|
|
|
for (ConvertRelationEntry entry : entries) { |
|
|
|
ObjectValue targetRow = queryRow(targetRef[0], entry.getTargeEntryId()); |
|
|
|
ObjectValue targetRow = queryRow(targetRef[0], entry.getTargeEntryId()); |
|
|
|
@ -328,7 +488,7 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
context.setCommitted(false); |
|
|
|
context.setCommitted(false); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.AFTER_CALC); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.AFTER_CALC); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.BEFORE_EXCESS); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.BEFORE_EXCESS); |
|
|
|
context.setExcess(excessHit(context)); |
|
|
|
context.setExcess(excessHit(context, sourceRow, sourceRef[1])); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.AFTER_EXCESS); |
|
|
|
invoke(plugins, context, WriteBackHookEnum.AFTER_EXCESS); |
|
|
|
if (Boolean.TRUE.equals(context.getExcess()) && ExcessActionEnum.ofNullable(context.getRule().getExcessAction()) == ExcessActionEnum.REJECT) { |
|
|
|
if (Boolean.TRUE.equals(context.getExcess()) && ExcessActionEnum.ofNullable(context.getRule().getExcessAction()) == ExcessActionEnum.REJECT) { |
|
|
|
throw new BotpMappingException("反写超额,操作已取消。规则=" + context.getRule().getName() |
|
|
|
throw new BotpMappingException("反写超额,操作已取消。规则=" + context.getRule().getName() |
|
|
|
@ -349,12 +509,28 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
* @param context 反写上下文(已填 newValue)。 |
|
|
|
* @param context 反写上下文(已填 newValue)。 |
|
|
|
* @return 超额返回 true;未配置检查公式时返回 false。 |
|
|
|
* @return 超额返回 true;未配置检查公式时返回 false。 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private boolean excessHit(WriteBackContext context) { |
|
|
|
/** |
|
|
|
|
|
|
|
* 超额检查:对「本次写入后的值」求值,为真即判定超额。 |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <p>求值上下文是<b>源分录行</b>,并且在求值前把算出的新值放进去——这样条件里 |
|
|
|
|
|
|
|
* 「已下推数量 + 本次数量 > 申请数量」这类引用才能拿到写入后的结果。 |
|
|
|
|
|
|
|
* 原先拿目标单求值,源单字段一个都取不到,条件永远不成立。</p> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* <p>条件里的字段引用请写成 {@code 分录表.分录表.字段} 形式,与反写公式的 |
|
|
|
|
|
|
|
* {@code sourceSingleField} 保持一致。</p> |
|
|
|
|
|
|
|
* |
|
|
|
|
|
|
|
* @param context 反写上下文(已填 newValue)。 |
|
|
|
|
|
|
|
* @param sourceRow 源分录行。 |
|
|
|
|
|
|
|
* @param sourceField 本次写入的源单字段名。 |
|
|
|
|
|
|
|
* @return 超额返回 true;未配置检查公式时返回 false。 |
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
private boolean excessHit(WriteBackContext context, ObjectValue sourceRow, String sourceField) { |
|
|
|
String expression = context.getRule().getExcessCheckJson(); |
|
|
|
String expression = context.getRule().getExcessCheckJson(); |
|
|
|
if (StringUtils.isBlank(expression)) { |
|
|
|
if (StringUtils.isBlank(expression) || sourceRow == null) { |
|
|
|
return false; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
return conditionHit(expression, null, context.getTargetBillData()); |
|
|
|
sourceRow.put(sourceField, context.getNewValue()); |
|
|
|
|
|
|
|
return conditionHit(expression, null, sourceRow); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
@ -451,6 +627,11 @@ public class WriteBackOrchestrator implements BotpReverseWriteFacade { |
|
|
|
* @param beforeValue 反写前的原值。 |
|
|
|
* @param beforeValue 反写前的原值。 |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
private void recordBeforeValue(ConvertRelationEntry entry, BigDecimal beforeValue) { |
|
|
|
private void recordBeforeValue(ConvertRelationEntry entry, BigDecimal beforeValue) { |
|
|
|
|
|
|
|
if (entry.getId() == null) { |
|
|
|
|
|
|
|
// 单据级配对(单表单据)没有真实的 convert_relation_entry 行,无处记录原值
|
|
|
|
|
|
|
|
log.warn("单表单据没有分录级关联记录,覆盖方式的原值未记录,撤销时无法恢复。源单id={}", entry.getSourceId()); |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
ConvertRelationEntry update = new ConvertRelationEntry(); |
|
|
|
ConvertRelationEntry update = new ConvertRelationEntry(); |
|
|
|
update.setId(entry.getId()); |
|
|
|
update.setId(entry.getId()); |
|
|
|
update.setBeforeValue(beforeValue); |
|
|
|
update.setBeforeValue(beforeValue); |
|
|
|
|