NC65使用UAP客开主子单据以及NC65单据相关的开发技术(超级详细附带图和源码)
本篇主要讲述使用UAP开发工具客开主子单据包括:创建项目发布、创建发布元数据、生成主子单据、以及相关报错和打补丁。在单据相关技术主要包括:增加空按钮、增加导入导出按钮、复制单据相关修改、系统数据获取、列表多选、表体行的配置、编辑前后事件、参照弹框相关操作以及在客开的时候遇见的报错和解决方案
特殊说明: 如果是自建模块得话,在开发环境是可以使用的,如果打补丁到home将会出现该产品的用户数已达到产品授权数!
的问题
元数据
MDP资源管理器窗口
-
UAP-Studio中,依次点击【窗口】-【显示视图】-【其他】
-
显示视图窗口中,【MDP视图】-【MDP资源管理器】
创建元数据
-
在UAP-Studio中,依次点击【文件】-【新建】-【其他】-【UAP业务组件项目】
-
在UAP-Studio中,依次点击【选中项目右键】-【新建】-【其他】-【UAP业务组件】
-
项目的目录结构如下
-
在UAP-Studio中打开透视图步骤
- 项目目录如下:
- 创建实体组件:一种是新建一种是导入已经有的实体组件
- 拖拽实体,右键点击实体,选择【导入属性】,在弹出窗口中选择【从pdm(.xml)文件导入】,导入相应的pdm中的单个表
发布元数据
-
发布元数据
如果是导入的bmf文件,发布元数据前需要检查元素据ID、实体ID、字段ID是否和原来元数据一致,如果一致会报错(该元数据已经发布或者其他的报错信息),报错可以选择继续发布元数据(发布元数据(忽略版本)如果确定各项ID不一致那么可以尝试建议不要)
-
生成VO代码
-
生成实体对应的数据库表
选中具体的实体(比如费用主表)-右键-生成建库sql脚本并执行(选中一个他会将主子表都发布)
重点说明
以下接口必须实现以及接口路径:
-
baseapp\METADATA\pf\pfbizitf.bmf
-
uapbs\METADATA\meta\general_interface.bmf
-
baseapp\METADATA\meta\IBillInterface.bmf
特殊说明:
-
主子单据关联使用
组合线
,从主表拖拽到子表。关联字段访问策略选择nc.md.model.access.BodyOfAggVOAccessor
-
实体实现接口使用
实现线
,从实现线从实体拖拽到接口,并且在业务接口属性映射
配置接口字段和实体字段的关联
前台配置模块
-
功能注册
-
菜单注册
-
业务模块初始化(如果在已有的模块客开单据则不需要这一步)
需要在admin账号下进行操作,其他账号找不到该节点,如果该模块下没有功能就算模块初始化了也不会看到对应的模块
后台生成代码及脚本
-
在UAP-Studio中,依次点击【选中项目右键】-【新建】-【其他】-【主子表单据节点】
-
设置功能功能、菜单、单据信息
-
选择客开的单据所需要的动作
-
自定义查询模板和打印模板名称
-
生成的资源以及资源的路径(重要)
后续补充
分配权限及效果展示
分配权限
首先在用户权限分配节点获取对应人员的角色信息,然后在职责-集团给对应角色分配对应节点权限
效果展示
-
节点展示
-
单据展示
出补丁
发布元数据
使用开发工具发布元数据(将bmf文件拷贝到对应目录,未知原因发布不成功)
-
配置数据源信息
-
发布数据源,以及实体对应的数据表
-
查看元数据是否发布成功
能看到模块无法在元数据管理找到对应的元数据(疑问)
-
查看实体对应的数据库表是否创建成功
直接对应数据库查询对应表名称
单据所需要的脚本
-
脚本位置
-
执行脚本insert_All.sql
所有的脚本都在insert_All.sql中,如果需要执行某一个脚本(比如注册功能)可以执行insert_FunctionRegister.sql脚本
如果需要放在已有得菜单下,记得先在菜单和功能里注册
打补丁到home
-
打补丁moudel
-
打补丁到resources
相关报错与问题(重点)
相关报错
-
元数据实体对应的数据库表没有创建: 参照发布元数据第三步,生成数据库表
-
元数据名称和字段名称不一致导致报错: 更改为一致,删除原有的数据库表,重新生成数据库表并且重新发布元数据(如果继续报错从发布到生成单据从头走一遍)
-
复制单据违反唯一约束条件 (NC20240810.PK_CSDJ_TEMPSALEH): 需要增加复制处理器
-
保存成功但是查询不到数据: 检查在增加处理器中是否给单据日期字段赋初始值
package nc.ui.csdj.dyts.ace.handler; import nc.ui.pubapp.uif2app.event.IAppEventHandler; import nc.ui.pubapp.uif2app.event.billform.AddEvent; import nc.vo.pub.pf.BillStatusEnum; import nc.vo.pubapp.AppContext; import nc.ui.pub.bill.BillCardPanel; public class AceAddHandler implements IAppEventHandler<AddEvent> { @Override public void handleAppEvent(AddEvent e) { String pk_group = e.getContext().getPk_group(); String pk_org = e.getContext().getPk_org(); BillCardPanel panel = e.getBillForm().getBillCardPanel(); // 设置主组织默认值 panel.setHeadItem("pk_group", pk_group); panel.setHeadItem("pk_org", pk_org); // 设置单据状态、单据业务日期默认值 panel.setHeadItem("vbillstatus", BillStatusEnum.FREE.value()); //设置单据日期 panel.setHeadItem("dbilldate", AppContext.getInstance().getBusiDate()); } }
-
**删除时或者点击其他按钮报错:**实体[xxx]没有实现制定的业务接口:nc.itf.uap.pf.metadata.IHeadBodyQueryItf或者是 nc.uap.pf.metadata.HeadBodyQueryImpl
-
找到对应的bmf文件,主实体实现businInterface接口
-
没有元数据文件(或者第一种方法不生效),我们可以选择直接执行sql语句
5205ef20-5eae-4c75-bad8-16639152e622
固定值-该值为单据主子VO查询的IDinsert into md_bizitfmap (BIZINTERFACEID, BIZITFIMPCLASSNAME, CLASSATTRID, CLASSATTRPATH, CLASSID, DR, INDUSTRY, INTATTRID, TS, VERSIONTYPE) values ('5205ef20-5eae-4c75-bad8-16639152e622', '', '', '', '主实体ID', 0, '', '5205ef20-5eae-4c75-bad8-16639152e622', to_char(sysdate, 'yyyy-MM-dd HH24:mi:ss'), null);
-
-
单据号规则报错:
解决方法:
1、用集团管理员登录是否有该单据的编码规则
2、向导生成的脚本可能丢失,手动执行一下
-
Error to init aggVOStyle
解决方法:
元数据的访问策略有问题
-
流程平台缓存中不存在该单据或交易类型
解决方法:
1、执行一下动作脚本2、查下数据库是否有值
select * from bd_billtype where pk_billtypecode =‘H2H1’ -
保存单据信息报错
保存单据信息时报is not found in jndi解决方法:
1、 清除缓存、重启客户端
2、 缺少module.xml,对应项目META-INF下有个module.xml文件,复制到nchome重启客户端
-
单据已删除请刷新页面
用元数据生成的档案能新增保存,但是修改、提交等按钮报以上错误原因:
1、 元数据的业务接口映射有问题,没有锁主键而是锁了别的字段
2、 修改了元数据的主键名称
3、 与ts时间戳有关系
-
没有找到设置得单据模板信息
解决方法
1、 功能节点默认模板配置单据模板bt,查询模板qt2、 配置功能节点发现还是报错,说明数据库有冗余数据
select * from pub_systemplate where funnode like ‘%功能节点编号%’
删除查询出来的数据
-
点击查询得时候报位置错误
解决方法
单据查询模板有问题,可能某个字段做了修改或者丢失 -
This Attribute is not in the Ref
解决方法
字段前不能加别名
客开问题
-
发布元数据所在表
md_component,md_class,md_property,md_table,md_column -
功能注册和菜单注册所在表
dap_dapsystem sm_funcregister -
自定参照得bmf所在路径
modules\uapbd\METADATA\metadata\udi
NC65单据相关技术点
增加一个空按钮
引用<ref bean="separatorAction" />
增加导入导出
配置按钮XML
以下为客开单据增加导入导出按钮
-
导入导出按钮xml
<?xml version="1.0" encoding="gbk"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 导入导出按钮 --> <!-- 导入导出 --> <bean id="importExportMenu" class="nc.funcnode.ui.action.MenuAction"> <property name="code"> <value>importExport</value> </property> <property name="name" value="导入导出" /> <property name="actions"> <list> <ref bean="ImportData" /> <!-- 导入Excel --> <ref bean="ExportData" /> <!-- 导出Excel --> <ref bean="ExportTemplate" /> <!-- 导出模板 --> </list> </property> </bean> <!-- 导入Excel --> <!-- 具体的导出规则下面做出解释 --> <bean id="ImportData" class="nc.ui.uif2.excelimport.ImportAction"> <property name="model" ref="bmModel" /> <property name="importableEditor" ref="ImportExportEditor" /> <property name="btnName" value="导入临时单Excel" /> </bean> <!-- 导出模板到Excel --> <bean id="ExportTemplate" class="nc.ui.uif2.excelimport.ExportExcelTemplateAction"> <property name="model" ref="bmModel" /> <property name="importableEditor" ref="ImportExportEditor" /> <property name="btnName" value="导出模板" /> </bean> <!-- 导入编辑器主要用于导入的时候-执行新增-保存-取消 --> <bean id="ImportExportEditor" class="nc.ui.uif2.excelimport.DefaultUIF2ImportableEditor"> <property name="billcardPanelEditor" ref="billForm" /> <!-- ref中的值为新增动作id --> <property name="addAction" ref="addAction" /> <!-- ref中的值为取消动作id --> <property name="cancelAction" ref="cancelAction" /> <!-- ref中的值为保存动作id --> <property name="saveAction" ref="saveScriptAction" /> <property name="appModel" ref="bmModel" /> </bean> <!-- 导出数据到Excel --> <bean id="ExportData" class="nc.ui.uif2.excelimport.ExportAction"> <property name="model" ref="bmModel" /> <property name="importableEditor" ref="ImportExportEditor" /> <property name="btnName" value="导出临时单到Excle" /> </bean> </beans>
-
引入导出导出xml到主xml,并且将导出导出按钮加入列表
<import resource="classpath:/nc/ui/csdj/dyts/ace/view/dyts_excelImport.xml" />
引入位置
加入列表
-
设置列表视图可以对选(实现多行导出Excel)
<property name="multiSelectionEnable" value="true" />
Excel导入规则
当以列中的数字设置主子关系,当数字相同时,他为一张单据如图:
如果想要同时导入多张单据,那么像上图,只要保证主子单据中的数字一致就欧克了
效果图
设置导出的时候需要导出什么字段
复制单据
增加复制处理器
package nc.ui.csdj.dyts.action;
import nc.ui.pubapp.uif2app.actions.intf.ICopyActionProcessor;
import nc.vo.pub.BusinessException;
import nc.vo.pub.CircularlyAccessibleValueObject;
import nc.vo.csdj.dyts.AggTempsaleh;
import nc.vo.uif2.LoginContext;
public class CopyActionProcessor implements
ICopyActionProcessor<AggTempsaleh> {
@Override
public void processVOAfterCopy(AggTempsaleh paramT,
LoginContext paramLoginContext) {
paramT.getParentVO().setPrimaryKey(null);
paramT.getParentVO().setVbillcode(null);
paramT.getParentVO().setTs(null);
String[] codes =paramT.getTableCodes();
if (codes != null && codes.length>0) {
for (int i = 0; i < codes.length; i++) {
String tableCode = codes[i];
CircularlyAccessibleValueObject[] childVOs = paramT.getTableVO(tableCode);
for (CircularlyAccessibleValueObject childVO : childVOs) {
try {
childVO.setPrimaryKey(null);
childVO.setAttributeValue("ts",null);
} catch (BusinessException e) {
e.printStackTrace();
}
}
}
}
}
}
修改单据配置文件
<property name="copyActionProcessor"><bean class="nc.ui.csdj.dyts.action.CopyActionProcessor" /></property>
表体行配置
主要配置表体得增行、删行、复制行、粘贴行等
<bean class="nc.ui.pubapp.uif2app.actions.BodyAddLineAction" />
<bean class="nc.ui.pubapp.uif2app.actions.BodyInsertLineAction" />
<bean class="nc.ui.pubapp.uif2app.actions.BodyDelLineAction" />
<bean class="nc.ui.pubapp.uif2app.actions.BodyCopyLineAction" />
<bean class="nc.ui.pubapp.uif2app.actions.BodyPasteLineAction" />
修改配置文件
系统数据获取
生成单据号
前置任务:配置单据号规则
//两种都可以
new HYPubBO().getBillNo(单据类型, 组织, null, null);
new BillcodeGenerater ().getBillCode (单据类型, 组织,null,null);
生成主键
KeyGenerator.getInstance().getKey(20);
new SequenceGenerator().generate(pk_corp,num);
系统变量
InvocationInfoProxy
WorkbenchEnvironment
ClientEnvironment
设置列表多选
无多选框
<property name="multiSelectionEnable" value="true" />
位置:
有多选框
<property name="multiSelectionMode" value="1" />
<property name="multiSelectionEnable" value="true" />
位置:
特殊情况(非客开单据)
getBillListPanel().setMultiSelect(true);
获取多选数据
其中m_billVo,、m_billHeadVo、m_billBodyVo为聚合、主、子VO的全路径名
AggregatedValueObject selectedBillVOs[] = getbillListPanel()
.getMultiSelectedVOs(m_billVo, m_billHeadVo,
m_billBodyVo);
其中BillManageModel为当前单据的Model
Object[] obj = (Object[]) ((BillManageModel) getModel()).getSelectedOperaDatas();
设置表头表体编辑前后事件
表头编辑前后事件
-
表头表尾编辑前事件
配置文件<!-- 表头表尾字段编辑前事件 --> <bean class="nc.ui.pubapp.uif2app.event.EventHandlerGroup"> <property name="event" value="nc.ui.pubapp.uif2app.event.card.CardHeadTailBeforeEditEvent" /> <property name="handler"> <bean class="nc.ui.csdj.dyts.ace.handler.AceHeadTailBeforeEditHandler" /> </property> </bean>
代码
package nc.ui.csdj.dyts.ace.handler; import nc.ui.bd.ref.AbstractRefGridTreeModel; import nc.ui.pub.beans.UIRefPane; import nc.ui.pub.bill.BillCardPanel; import nc.ui.pubapp.uif2app.event.IAppEventHandler; import nc.ui.pubapp.uif2app.event.card.CardHeadTailBeforeEditEvent; /** * 单据表头表尾字段编辑前事件 * @author Administrator * */ public class AceHeadTailBeforeEditHandler implements IAppEventHandler<CardHeadTailBeforeEditEvent> { @Override public void handleAppEvent(CardHeadTailBeforeEditEvent e) { // TODO 自动生成的方法存根 e.setReturnValue(true); BillCardPanel cpanel = e.getBillCardPanel(); String key = e.getKey(); if("pk_cust".equals(key)){//根据客户分类过滤客户 String pk_custclass = (String)cpanel.getHeadItem("pk_custclass").getValueObject(); //获取参照Model AbstractRefGridTreeModel refModel = (AbstractRefGridTreeModel)((UIRefPane)cpanel.getHeadItem(key).getComponent()).getRefModel(); //若客户分类不为空,过滤 if(null != pk_custclass && !"".equals(pk_custclass.trim())){ refModel.addWherePart(" and pk_supclass = '"+pk_custclass+"' "); refModel.setClassWherePart(" pk_custclass = '"+pk_custclass+"' "); }else{ refModel.addWherePart(" and 1 = 1 ");//重设参照过滤条件 refModel.setClassWherePart(" 1 = 1 "); } } } }
-
表头表尾编辑后事件
配置文件<!-- 表头表尾字段编辑后事件 --> <bean class="nc.ui.pubapp.uif2app.event.EventHandlerGroup"> <property name="event" value="nc.ui.pubapp.uif2app.event.card.CardHeadTailAfterEditEvent" /> <property name="handler"> <bean class="nc.ui.csdj.dyts.ace.handler.AceHeadTailAfterEditHandler" /> </property> </bean>
代码
package nc.ui.csdj.dyts.ace.handler; import nc.ui.pub.beans.UIRefPane; import nc.ui.pub.bill.BillCardPanel; import nc.ui.pubapp.uif2app.event.IAppEventHandler; import nc.ui.pubapp.uif2app.event.card.CardHeadTailAfterEditEvent; /** * 单据表头表尾字段编辑后事件 * @author Administrator * */ public class AceHeadTailAfterEditHandler implements IAppEventHandler<CardHeadTailAfterEditEvent> { @Override public void handleAppEvent(CardHeadTailAfterEditEvent e) { // TODO 自动生成的方法存根 BillCardPanel cpanel = e.getBillCardPanel(); String key = e.getKey(); if("pk_custclass".equals(key)){//客户分类变更时清空客户 cpanel.setHeadItem("pk_cust", null); }else if("pk_cust".equals(key)){ String pk_custclass = (String)cpanel.getHeadItem("pk_custclass").getValueObject(); if(null == pk_custclass || "".equals(pk_custclass)){ //获得关联的客户分类主键 pk_custclass = (String)((UIRefPane)cpanel.getHeadItem(key).getComponent()).getRefValue("pk_supclass"); cpanel.setHeadItem("pk_custclass", pk_custclass); //停止编辑,使得焦点依旧在客户字段时再点击参照按钮时依然能够触发beforeEdit事件 cpanel.stopEditing(); } } } }
表体编辑前后事件
-
表体编辑前事件
配置文件<!-- 表体字段编辑前事件 --> <bean class="nc.ui.pubapp.uif2app.event.EventHandlerGroup"> <property name="event" value="nc.ui.pubapp.uif2app.event.card.CardBodyBeforeEditEvent" /> <property name="handler"> <bean class="nc.ui.csdj.dyts.ace.handler.AceBodyBeforeEditHandler" /> </property> </bean>
代码
package nc.ui.csdj.dyts.ace.handler; import nc.ui.bd.ref.AbstractRefGridTreeModel; import nc.ui.pub.beans.UIRefPane; import nc.ui.pub.bill.BillCardPanel; import nc.ui.pubapp.uif2app.event.IAppEventHandler; import nc.ui.pubapp.uif2app.event.card.CardHeadTailBeforeEditEvent; /** * 单据表体字段编辑前事件 * @author Administrator * */ public class AceHeadTailBeforeEditHandler implements IAppEventHandler<CardHeadTailBeforeEditEvent> { @Override public void handleAppEvent(CardHeadTailBeforeEditEvent e) { // TODO 自动生成的方法存根 e.setReturnValue(true); BillCardPanel cpanel = e.getBillCardPanel(); String key = e.getKey(); if("pk_cust".equals(key)){//根据客户分类过滤客户 String pk_custclass = (String)cpanel.getHeadItem("pk_custclass").getValueObject(); //获取参照Model AbstractRefGridTreeModel refModel = (AbstractRefGridTreeModel)((UIRefPane)cpanel.getHeadItem(key).getComponent()).getRefModel(); //若客户分类不为空,过滤 if(null != pk_custclass && !"".equals(pk_custclass.trim())){ refModel.addWherePart(" and pk_supclass = '"+pk_custclass+"' "); refModel.setClassWherePart(" pk_custclass = '"+pk_custclass+"' "); }else{ refModel.addWherePart(" and 1 = 1 ");//重设参照过滤条件 refModel.setClassWherePart(" 1 = 1 "); } } } }
-
表体编辑后事件
配置文件<!-- 表体字段编辑后事件 --> <bean class="nc.ui.pubapp.uif2app.event.EventHandlerGroup"> <property name="event" value="nc.ui.pubapp.uif2app.event.card.CardBodyAfterEditEvent" /> <property name="handler"> <bean class="nc.ui.csdj.dyts.ace.handler.AceBodyAfterEditHandler" /> </property> </bean>
代码
package nc.ui.csdj.dyts.ace.handler; import nc.ui.pubapp.uif2app.event.IAppEventHandler; import nc.ui.pubapp.uif2app.event.card.CardBodyAfterEditEvent; /** * 单据表体字段编辑后事件 * @author Administrator * */ public class AceBodyAfterEditHandler implements IAppEventHandler<CardBodyAfterEditEvent>{ @Override public void handleAppEvent(CardBodyAfterEditEvent e) { } }
注意事项
- e.setReturnValue(true)该行代码为必须代码,如果返回false将不能编辑,该代码主要用于编辑前事件
- 放置位置
参照框设置多选和筛选条件
弹窗增加参照框
select * from bd_refinfo,参照信息表bd_refinfo中name为参照名称同时也是下列中日历
的取值
UIRefPane refDate = new UIRefPane();
refDate.setRefNodeName("日历");
获取单据参照框
-
获取表头参照框
//参照面板 UIRefPane headRefPane =(UIRefPane)billCardPanel.getHeadItem("key").getComponent(); //参照模型 AbstractRefModel refHeadModel = headRefPane.getRefModel();
-
获取表体参照框
UIRefPane bodyRefPane =(UIRefPane)billCardPanel.getBodyItem("vbillno").getComponent(); AbstractRefModel refBodyModel = bodyRefPane.getRefModel();
设置多选和过滤条件
设置多选保存的时候还是保存第一个主键,而且显示的时候还是显示一个(未解决)
-
列表参照
-
树型参照
-
树表型参照
参照基类:
三种常用类型的参照,Model提供了三个抽象类:
- 列表参照: AbstractRefModel
- 树型参照: AbstractRefTreeModel
- 树表型参照: AbstractRefGridTreeModel
参照类代码示例:
不同类型的参照对应的参照类须继承上述的相应基类
- 树形参照:参考nc.ui.train.pub.ref.CustClassRefModel
- 树表型参照:参考nc.ui.train.pub.ref.CustRefModel
BillItem headItem = billCardPanel.getHeadItem("selfee");
UIRefPane ref = (UIRefPane) headItem.getComponent();
ref.setMultiSelectedEnabled(true);//设置可以多选
ref.setNotLeafSelectedEnabled(false);//不允许选择非末级
//设置动态参照类
ref.setRefModel(参照类.class);
//设置既可以输入值又可以参照得值
ref.setAutoCheck(false);
String value ="11000A2342,11000B2345";
//设置多选值
ref.setPKs(value.split(","));
//以下为设置过滤条件的配置
AbstractRefModel refModel = (AbstractRefModel)ref.getRefModel();
//设置国家
ref.setPk_country("0001Z010000000079UJJ");
//设置集团
ref.setPk_group(AppContext.getInstance().getPkGroup());
//设置用户
ref.setPk_user(AppContext.getInstance().getPkUser());
//设置过滤条件
refModel.addWherePart(" and pk_supclass = '"+pk_custclass+"' ");
//设置只显示一级
//refModel.addWherePart(" and length(pid)-1");
//有可能是关联相关的设置例如:选择县参照,该值为省的pk(未验证)
refModel.setClassWherePart(" pk_custclass = '"+pk_custclass+"' ");
ustRefModel
BillItem headItem = billCardPanel.getHeadItem("selfee");
UIRefPane ref = (UIRefPane) headItem.getComponent();
ref.setMultiSelectedEnabled(true);//设置可以多选
ref.setNotLeafSelectedEnabled(false);//不允许选择非末级
//设置动态参照类
ref.setRefModel(参照类.class);
//设置既可以输入值又可以参照得值
ref.setAutoCheck(false);
String value ="11000A2342,11000B2345";
//设置多选值
ref.setPKs(value.split(","));
//以下为设置过滤条件的配置
AbstractRefModel refModel = (AbstractRefModel)ref.getRefModel();
//设置国家
ref.setPk_country("0001Z010000000079UJJ");
//设置集团
ref.setPk_group(AppContext.getInstance().getPkGroup());
//设置用户
ref.setPk_user(AppContext.getInstance().getPkUser());
//设置过滤条件
refModel.addWherePart(" and pk_supclass = '"+pk_custclass+"' ");
//设置只显示一级
//refModel.addWherePart(" and length(pid)-1");
//有可能是关联相关的设置例如:选择县参照,该值为省的pk(未验证)
refModel.setClassWherePart(" pk_custclass = '"+pk_custclass+"' ");