实践案例|业务批量化工作:实现复制、编辑、审批一体化流程

在快节奏的商业世界中,合同批量续签的效率至关重要。本文将向您展示如何通过创新的批量复制、编辑和审批流程,将繁琐的合同管理工作转变为高效、自动化的解决方案,让企业在合同管理上更加得心应手。
作者在案例基础上,也解析了相关技术知识,以帮助读者更好地理解实现思路,一起看看吧~
业务背景
某公司在进行项目管理时,常需与合作伙伴签订多份合同。当这些合同到期时,为了保持业务的连续性,公司希望能够实现自动续签功能。在续签过程中,公司需要统一修改每份合同的生效日期和截止日期,以确保新合同的时效性与旧合同相衔接。
同时,为了保障合同内容的一致性和完整性,其他合同信息在续签时将不允许修改。
此外,为了提升业务处理效率,公司还希望能够对批量续签的合同进行批量审批,以减少人工操作,加快审批流程,从而加速业务进行速度。以商超与供应商之间的合作为例,当面临多批次、多类型的供应合同到期时,公司可以根据旧有协议,对同一供应商的所有供应货物进行批量续签,并通过批量审批快速完成合同的更新和确认。
解决方案
在合同管理过程中,续签通常涉及到单据的复制、编辑和审批三个关键环节。尽管单个环节的复制、编辑和审批功能在现有的系统中都能得到较好的支持,但当需要实现这三个环节的批量操作时,尤其是要将它们在一个流程中统一,苍穹系统目前未提供标准化的解决方案。
现有的单个环节解决方案,在整合使用时存在两个问题:
1. 这些方案之间的匹配度不高,可能无法覆盖所有必要的业务场景,流程存在缺失环节;
2. 这种方式无法直观地展示三个环节统一后的业务逻辑,使得操作变得复杂且不够直观。
为了解决这些问题,我们决定重新设计并整合这三个环节,将它们统一到一个新的“批量续签单据”中。
通过这个新的单据,我们可以对同一合同类型的多份合同进行批量续签的编辑和审批操作。这样做的好处是,所有批量续签的合同都将有一个统一的入口进行展示和编辑,领导在审批过程中也能直观地看到每个待审批的单据,并可以根据需要单独控制每个合同的审批状态。这种整合方式不仅提高了操作的便捷性和效率,也使整个续签过程更加清晰和可控。
方案效果图展示如下:

本文所涉及的知识点包括父子页面的交互、前后台数据的交互、DynamicObject的序列化和反序列化等,请参考以下社区文档:
父子页面交互、前后台交互的知识点总结
https://vip.kingdee.com/link/s/l49m4
1. 绘制批量审批单据

为了提升批量续签合同的操作效率和用户体验,我们将整个流程划分为三个主要部分:
数据编辑面板用于批量编辑信息;
分录面板用于记录续签合同与原单据的关联关系;
页签展示面板详细展示续签合同的各项信息。
业务呈现方式如下:
(1)操作员首先进入合同列表页面,从中勾选出需要进行批量续签的合同。随后,携带这些选定的合同信息,操作员将进入批量续签的单据页面。在此页面,操作员将原合同的关键信息填写到单据体中,确保信息的准确性。
(2)接下来,操作员检查并设置续签合同的相关信息,这些信息将展示在页签上。请注意,由于系统限制,续签合同的数量不得超过页签的上限数量,即最多10份。
(3)完成合同信息的设置后,操作员可以利用上方的批量编辑面板,对需要统一修改的字段进行批量编辑。填写完毕后,通过保存操作,这些更改将自动应用到所有选定的合同单据中。
(4)最后,操作员确认所有信息无误,即可退出批量续签页面,完成整个批量续签流程。
整个流程如下:
进入合同列表页面 → 勾选需续签的合同 → 进入批量续签页面 → 填写原合同关键信息 → 检查并设置续签合同信息 → 使用批量编辑面板进行编辑 → 保存并应用更改 → 确认并退出。
2. 批量单据复制实操
在处理单据的批量续签时,苍穹系统提供了“copy”操作代码来简化复制流程。尽管“copy”操作能够自动处理新单据的页签显示、原单数据的携带以及编码规则的获取等步骤,但在本次的特定需求中,我们需要将批量复制的单据展示在特定的页签上。由于“copy”操作背后的逻辑在常规跟踪过程中并不直接可见,我们需要采取一种不同的方法来实现这一需求。
我们考虑了两个方案:
方案一:我们计划直接在页签上打开并展示指定数量的单据,通过程序逻辑将数据赋值到相应的页面元素上。这种方案能够确保客户在数据保存之前就能直观地看到并确认每张续签单据的信息。
方案二:先将所有批量续签的数据保存到数据库中,并做好相应的标记,然后再从数据库中加载这些数据并展示在页签上。虽然这种方法在技术上相对简单,但它有一个显著的缺点:在数据保存到数据库之前,客户无法直接看到和确认这些单据,这可能会遗漏一些必要的配置或要修改的内容。
鉴于上述考虑,我们选择了方案一。然而,这也带来了一个挑战:如何将携带有特定数据的单据准确地展示在指定的页签上。
为了解决这个问题,我们将采用一种循环加载的策略。具体来说,我们会根据待续签的单据数量,动态地创建并加载相应数量的页签,然后通过编程逻辑将每份单据的数据赋值到对应的页签上。最后,我们会通过激活第一个页签的pageId,使其成为当前默认展示的页签。这样,客户就能在一个统一的界面中,直观地查看、编辑并确认所有的续签单据。
private String showBillInTab(int i){
String billFormId = (String)getModel().getValue("isv_bill_type");
Bil1ShowParameter parameter = new BillShowParameter();
parameter.setFormId(billFormId);
parameter.setParentFormId(getView().getPageId());
parameter.setStatus(Operationstatus.ADDNEW);
parameter.geOpenstyle().setShowType(ShowType.NewTabPage);
parameter.geOpenstyle().setTargetKey("isv_tabap");
parameter.setCustomParam("billFormId", billFormId);
parameter.setBillTypeId(billFormId);
// 为了对应续签前的原单据数据,我们可以将对应续签单据体的行号传进来
parameter.getCustomParams().put("index", i);
parameter.addCustPlugin("isv.xxx.Batchshowplugin");
parameter.setHasRight(true);
getview().showForm(parameter);
return parameter.gePageld();
}
private void activeDefaultTab(String pageId) {
Tab tab = getcontrol("isv_tabap");
tab.activeTab(pageld);
}通过这样的方式展示,我们最终能得到指定数量的、新的、携带默认值而不是复制得到的原单据数据的单据,相关的批量逻辑控制如不允许编辑指定相关字段等,可以通过注册动态插件来完成。所以我们接下来的重点工作是如何分别把原单数据携带到我们刚刚创建的单据中。
查看指定的数据一般是通过“parameter.setPkId()”来完成,但是我们未落库的数据查看,就需要借助特殊的方法去完成了。
对于已加载的页面,可以通过“model.push(dynamicObj)”来完成数据的加载,但由于苍穹在页签加载过程中,未被激活过的页签是不会实际加载页面的,也就无法完成数据的刷新,所以我们需要进一步进行逻辑处理。
我们分为两步走,当页签页面被激活后,我们通过上述方案刷新数据,如果没有激活我们可以缓存下来,直到页面激活进入真正的生命周期我们加载数据,于是有了以下方案:
批量续签的插件
public void afterCreateNewData(EventObject e){
//准备数据
Dynanicobject[] originData = BusinessDataServiceHelper.load();
for(Dynanicobject dataEntity : originData) {
if (getView().getView(pageld).getModel().isDataLoaded()){
// 页面已完成加载刷新数据,对应tab已经active的页签
getView().getView(papeld).getModel().push(dataEntity);
} else {
//页面未完成加载记录到缓存
getPageCache().put(pageId, DataEntityserializer.serializerToString(dataEntity, option));
}
}
}
// 动志注册的子页面的插件 isv.xxx.BatchShowPlugin
public void createNewData(BizDataEventArgs e) {
super.createNewData();
String data = getView().getParentView().getPageCache().get(getView().getPageId());
// 如果准备了数实则使用运存中的数据
if (!StringUtils.isEmpty(data)) {
DataEntityDeserializerOption option = new DataEntityDeserializerOption();
DynamicObject saveData = (DymamiOobject)DataEntitySerializer.deSerializerFromString(data, entityType, option);
BusinessDataServiceHelper.loadRefence(new object[]{saveData}, entityType);
e.setDataEntity(saveData):
}
}这样,我们就完成了数据的后台创建,并能推送到前台显示。
3. 批量编辑与数据加载
有了合适的数据及界面展示,剩下的工作就是批量编辑。如果有N个字段需要编辑,我们在设计器上画上N个对应类型的字段,在“保存”按钮点击时,就将这N个字段的值反写回去。
@Override
publ1c void beforeDoOperation(BeforeDoOperationEventArgs args) {
switch(opkey){
case "save":
if (getView().getView(papeld).getModel().isDataLoaded()) {
// 页面已完成加载直接执行页面的保存操作
getView().getView(pageId).getModel().setValue("xxx",getModule,getValue("xxx"));
// 前端页面激活保存
OperationResult save = getView(pageId).invokeOperation("save");
getView().sendFormAction(getView().getView(pageId));
} else {
// 页面未完成加载执行后台保存操作,并根据需要是否落库
DataEntityDeserializerOption option = new DataEntityDeserializerOption();
String cache = getPageCache().get(pageId);
DynamicObject saveData = DataEntitySerializer.deSerializerFromString(cache, entityType, option);
// 设置批量编辑的字段值
saveData.set("xxx", getModule().getVaule("xxx"));
// 数据库直接保存
saveByDataBase(pageId, saveData);
// 无前端状态保存
// saveByBackground(pageId, saveData);
}
break;
default:
break;
}
}
private boolean saveByDataBase(String pageId, DynamicObject saveData){
CodeRuleInfo codeRule = CodeRuleServiceHelper.getCodeRule(saveData.getDataEntityType().getName(), saveData, String.valueOf(RequestContext.get().getOrgId()));
String number = CodeRuleServiceHelper.getNumber(codeRule, saveData);
saveData.set(BILLNO, number);
// 记得保存完成之后将修改过的值存入缓存,避免未激活的页面未获取到最新的修改
cache = DataEntitySerializer.serializerToString(saveData, new DataEntitySerializerOption());
getPageCache().put(pageId, cache);
}
private boolean saveByBackground(String pageId, DynamicObject saveData){
// 方案一
String cache = DataEntitySerializer.serializerToString(saveData, new DataEntitySerializerOption());
getPageCache().put(pageId, cache);
IFormView billViews = (IBillView) SessionManager.getCurrent().getView(pageId);
billView.getModel().beginInit();
billView.getModel().createNewData(saveData);
billView.getModel().setCacheExpireAfter(true);
billView.getModel().endInit()
billView.updateView();
billView.invokeOperatio实践案例|业务批量化工作:实现复制、编辑、审批一体化流程
声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。如若本站内容侵犯了原著者的合法权益,可联系本站删除。



