紧接着上篇文章,我们将继续讲解体检管理案例操作。
体检单
一、业务对象
创建体检单业务对象,以下是业务对象下的实体为主子孙结构
1.体检单
2. 体检项目_体检单子表
二、页面建模
1、根据业务对象创建一主多子结构单据命名为体检单
2、设置主表字段不允许修改
组织、客户、累计出库年月、单据状态
3、设置子表所有字段不允许修改 体检项目、物料、二维码编号、是否检测、检测结果、检测人
4、删除子表的新增、删行按钮
5、设置子表表格的编辑方式为侧滑弹框
6、 体检单列表页面删除新增按钮
7 、配置体检按审批流
8、配置业务流
9、体检预约下推体检单
防止重复下推: isNull(getValue("AT1772AFF21C18000A.AT1772AFF21C18000A.examination_bill","id","source_id",id))
错误提示语:下游数据已存在
触发设置:手工触发
生单方式:默认新增单据
转换规则配置如下图
10、体检单下推销售订单目标交易类型:体检
过滤设置增加防止重复下推:isNull(getValue("voucher.order.Order","id","srcBill",id))&&verifystate==2
错误提示语:下游单据已存在或单据未审核
触发设置:手工触发
生单默认: 默认新增单据,勾选转单画面
转换规则配置如下图:
使用到的公式:
税目税率id : getValue("bd.taxrate.TaxRateVO","id","code","VAT0")
本币pk :getValue("bd.currencytenant.CurrencyTenantVO","id","code","CNY")
汇率类型id: getValue("bd.exchangeRate.ExchangeRateTypeVO","id","code","01")
单据日期: dateFormat(sysdate(),"yyyy-MM-dd")
11、体检单列表页面增加月底汇总出库按钮,点击汇总消耗品生成其他出库单(1).创建业务对象月底汇总
(2)、根据业务对象创建一个卡片页面,删除列表页面
(3)、将卡片页面设置为模态框,具体操作省略
(4)、保存按钮取消保存规则,然后增加保存函数
函数中调用了其他出库单接口,具体调用接口API脚本查看
viewModel.get('btnSave') && viewModel.get('btnSave').on('click', function (data) {
// 确定--单击
let date = viewModel.get("date").getValue();
let org = viewModel.get("org").getValue();
let warehouse = viewModel.get("warehouse").getValue();
let use_version = viewModel.get("use_version").getValue();
cb.rest.invokeFunction("AT1772AFF21C18000A.api.monthUsePro", {"date":date,"org":org,"warehouse":warehouse,"use_version":use_version},
function(err, res) {
if(err!=null){
cb.utils.alert(err);
return ;
}else{
cb.utils.alert("发起请求成功,请到其他出库节点查看");
viewModel.communication({type:'modal',payload:{data:false}});
}
})
});
(5)、调用出库单API脚本
获取日期函数
let AbstractTrigger = require('AbstractTrigger');
class MyTrigger extends AbstractTrigger {
execute(context,param){
Date.prototype.format = function(fmt)
{
var o = {
"M+" : this.getMonth()+1, //月份
"d+" : this.getDate(), //日
"h+" : this.getHours()%12 == 0 ? 12 : this.getHours()%12, //小时
"H+" : this.getHours(), //小时
"m+" : this.getMinutes(), //分
"s+" : this.getSeconds(), //秒
"q+" : Math.floor((this.getMonth()+3)/3), //季度
"S" : this.getMilliseconds() //毫秒
};
if(/(y+)/.test(fmt))
fmt=fmt.replace(RegExp.$1, (this.getFullYear()+"").substr(4 - RegExp.$1.length));
for(var k in o)
if(new RegExp("("+ k +")").test(fmt))
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length==1) ? (o[k]) : (("00"+ o[k]).substr((""+ o[k]).length)));
return fmt;
}
let date = new Date().format(context);
return {date};
}
}
exports({"entryPoint":MyTrigger});
汇总统计耗材
let AbstractAPIHandler = require('AbstractAPIHandler');
class MyAPIHandler extends AbstractAPIHandler {
execute(request){
let date = request.date; //获取日期
let org = request.org;//组织
let warehouse = request.warehouse; //仓库
let use_version = request.use_version //版本
// 因为两张表没有直接的关联关系,所以不能联查
let sql = "select project as projectId,count(id) as num from AT1772AFF21C18000A.AT1772AFF21C18000A.examination_project "+
" where yearmonth = '"+date+"' group by project";
let projectNum = ObjectStore.queryByYonQL(sql);
if(projectNum.length==0){
throw new Error("为查找到数据");
}
//筛选出体检项
var sqlConditionParamsStr = "";
var projectMap = {}; //存储每个耗材使用了几份
projectNum.forEach((project)=>{
let kitId = "'"+project.projectId+"',";
sqlConditionParamsStr+=kitId;
projectMap[project.projectId]=project.num;
})
sqlConditionParamsStr = "("+substring(sqlConditionParamsStr, 0, sqlConditionParamsStr.length-1)+")";
//根据查询条件查出耗材
let consumptionSql = "select product,unit as stockUnitId,num,project_id from AT1772AFF21C18000A.AT1772AFF21C18000A.consumption "+
" where project_id in "+sqlConditionParamsStr;
let consumptionData = ObjectStore.queryByYonQL(consumptionSql);
//循环重新赋值计算,将耗材数量统计
consumptionData.forEach((data)=>{
let totalConsumptionNum = projectMap[data.project_id]*data.num;
data.qty = totalConsumptionNum;
data.subQty = totalConsumptionNum;
data._status = "Insert";
data.othOutRecordsDefineCharacter = {"item02":data.project_id}
})
//拼装请求参数
let othoutrecordParams = {};
othoutrecordParams["org"] = org;
othoutrecordParams["accountOrg"] = org;
othoutrecordParams["bustype"] = "A10001";
othoutrecordParams["_status"] = "Insert";
othoutrecordParams["warehouse"] = warehouse;//仓库
othoutrecordParams["othOutRecords"] = consumptionData;
othoutrecordParams["resubmitCheckKey"] = MD5Encode(JSON.stringify(othoutrecordParams)+use_version);
let othoutrecordFun = extrequire("AT1772AFF21C18000A.backend.othoutrecord");
othoutrecordFun.execute(othoutrecordParams);
return {};
}
}
exports({"entryPoint":MyAPIHandler});
其他出库单保存接口
let AbstractTrigger = require('AbstractTrigger');
class MyTrigger extends AbstractTrigger {
execute(context,param){
//取得日期格式
let date = extrequire("AT1772AFF21C18000A.backend.getDate");
let dateFormat = date.execute("yyyy-MM-dd");
context.vouchdate = dateFormat.date; //给单据日期赋值
//调用接口
let apiAction = "othoutrecord";
let configFun = extrequire("AT1772AFF21C18000A.backend.config");
let configRes = configFun.execute(null);
let reqType = "POST";
let body = {"data":context};
let apiResponse;
try{
apiResponse = openLinker(reqType, configRes.apiurl[apiAction], configRes.appcode, JSON.stringify(body));
let apiResponseData = JSON.parse(apiResponse);
if(apiResponseData.code!=200){
throw new Error("日志处理");
}
}catch(e){
//处理日志,调用接口是避免回滚
//取得日期格式
let dateLog = extrequire("AT1772AFF21C18000A.backend.getDate");
let dateFormatLog = dateLog.execute("yyyy-MM-dd HH:mm:ss");
var error = {body:JSON.stringify(body),apiResponse:apiResponse,apiAction:apiAction,billnum:"examination_kit",createTime:dateFormatLog.date};
openLinker(reqType, configRes.apiurl["apilog"], configRes.appcode, JSON.stringify(error));
throw new Error("生成其他出库失败,请检查");
}
return {};
}
}
exports({"entryPoint":MyTrigger});
体检单增加汇总耗材按钮,并增加点击事件打开创建的弹框
viewModel.get('button22rk') && viewModel.get('button22rk').on('click', function (params) {
// 月终汇总出库--单击
let data = {
//TODO:填写单据类型及单据号
billtype: 'Voucher',// 单据类型
billno: 'monthtotal',// 单据号
params: {
mode: 'add',
},
};
//打开一个单据,并在当前页面显示
cb.loader.runCommandLine('bill', data, viewModel);
});
移动端
注:移动端和pc端公用元数据、工作流、业务流,后端函数可公用。移动端的页面和前端脚本以及发布应用是独立的。
一.体检预约单
(一).列表页
1.页面设计
调整列表页显示字段:姓名、手机号、身份证号、体检套餐、预约日期、价格。如下图
2. 功能(只看本人创建的预约单)
viewModel.on('beforeSearch',(args)=>{
let userId = cb.context.getUserId();
args.params.condition.simpleVOs=[
{
"field": "creator",
"op": "eq",
"value1": userId
}
];
})
(二) .详情页
1.页面设计
表头: 手机号、姓名、客户分类:必填; 身份证号:必填 根据身份证号自动计算年龄和性别; 价格:不可修改,设置公式sum子表的使用金额,所有数值的精度为2 性别、年龄、客户:不可修改
效果如下图:
子表: 体检项目必填,其余字段不可编辑; 选择体检项目,其余字段自动带入;
效果如下图:
- 功能:根据身份证号自动计算年龄和性别
//身份证带出年龄和性别
viewModel.get('identity').on('afterValueChange',(data)=>{
debugger;
if(data && data.value != "") {
let identity = JSON.parse(data.value).identity;
let sex = parseInt(identity.substr(16, 1)) % 2 == 1 ? '1' : '2';
let age = getAge(identity);
viewModel.get('gender').setValue(sex);
viewModel.get('age').setValue(age);
}
})
function getAge(userCard) {
var myDate = new Date();
var month = myDate.getMonth() + 1;
var day = myDate.getDate();
var age = myDate.getFullYear() - userCard.substring(6, 10) - 1;
if (userCard.substring(10, 12) < month || userCard.substring(10, 12) == month && userCard.substring(12, 14) <= day) {
age++;
}
return age;
}
3 功能:详情增加动作扩展(和pc动作通用)
4 子表功能:体检项目必填,其余字段不可编辑
//editRowModel的字段状态
viewModel.on('afterLoadData',(args)=>{
viewModel.getGridModel().getEditRowModel().get("project_name").setState('bIsNull',false);
viewModel.getGridModel().getEditRowModel().get("examination_kit_name").setState('bCanModify',false);
viewModel.getGridModel().getEditRowModel().get("original_price").setState('bCanModify',false);
viewModel.getGridModel().getEditRowModel().get("price").setState('bCanModify',false);
viewModel.getGridModel().getEditRowModel().get("is_kit").setState('bCanModify',false);
viewModel.getGridModel().getEditRowModel().get("useprice").setState('bCanModify',false);
viewModel.getGridModel().getEditRowModel().get("product_standard_name").setState('bCanModify',false);
})
5、子加粗样式表功能:选择体检项目,其余字段自动带入
//选择项目带入其他字段
viewModel.getGridModel().getEditRowModel().get("project_name").on("afterValueChange",(args)=>{
let index = viewModel.getGridModel().getFocusedRowIndex();
viewModel.getGridModel().getEditRowModel().get('original_price').setValue(args.value.original_price);
viewModel.getGridModel().setCellValue(index,"original_price",args.value.original_price);
viewModel.getGridModel().setCellValue(index,'original_price',args.value.original_price);
// 所属套餐 examination_kit_name
viewModel.getGridModel().getEditRowModel().get('examination_kit').setValue(args.value.examination_kit_id);
viewModel.getGridModel().setCellValue(index,'examination_kit',args.value.examination_kit_id);
viewModel.getGridModel().getEditRowModel().get('examination_kit_name').setValue(args.value.examination_kit_id_name);
viewModel.getGridModel().setCellValue(index,'examination_kit_name',args.value.examination_kit_id_name);
//套餐价格
viewModel.getGridModel().getEditRowModel().get('price').setValue(args.value.price);
viewModel.getGridModel().setCellValue(index,'price',args.value.price);
//是否套餐
viewModel.getGridModel().getEditRowModel().get('is_kit').setValue("2");
viewModel.getGridModel().setCellValue(index,'is_kit',"2");
//使用金额
viewModel.getGridModel().getEditRowModel().get('useprice').setValue(args.value.original_price,true);
viewModel.getGridModel().setCellValue(index,'useprice',args.value.original_price);
//物料
viewModel.getGridModel().getEditRowModel().get('product_standard').setValue(args.value.product);
viewModel.getGridModel().setCellValue(index,'product_standard',args.value.product);
viewModel.getGridModel().getEditRowModel().get('product_standard_name').setValue(args.value.product_name);
viewModel.getGridModel().setCellValue(index,'product_standard_name',args.value.product_name);
})
6、子表功能:编辑页,拦截保存动作
viewModel.get("btnSubSave_1").on('click',(data)=>{
if(!viewModel.getGridModel().getEditRowModel().get("project_name").getValue()) {
cb.utils.alert("项目不能为空");
return false;
}
viewModel.biz.do('closePage',viewModel);
})
7、主子联动:选择主表所属套餐,子表自动增加下属项目
let gridModel = viewModel.getGridModel();
viewModel.get("pay_examination_kitList").on("afterValueChange",function(data){
//旧的数据 套餐id放数组
let oldData = gridModel.getDataSourceRows();
let oldRows = gridModel.getRows();
let rowsDataState = gridModel.__data.rowsDataState
let delIndex = [];
if(data && data.value.length > 0) {
let result = cb.rest.invokeFunction("AT17925AEA1C18000A.api.getProjectById", {data:data.value},function(err, res) {},viewModel, {async:false});
debugger;
//新的数据 套餐id放数组
let newData = result.result.res;
let newKit = [];
newData.forEach((item,index)=>{
newKit.push(item.examination_kit);
})
let oldKit = [];
oldRows.forEach((item,index)=>{
if(item.is_kit == '1') {
oldKit.push(item.examination_kit);
}
})
//多的数据就新增
let addRows = [];
newData.forEach((item,index)=>{
if(!oldKit.includes(item.examination_kit)) {
item.is_kit = '1';
addRows.push(item);
rowsDataState.push("Insert");
}
})
//少的套餐就删除
oldRows.forEach((item,index)=>{
if(item.is_kit == '1' && !newKit.includes(item.examination_kit)) {
delIndex.push(index);
}
})
gridModel.setDataSource(oldData.concat(addRows));
gridModel.__data.rowsDataState = rowsDataState;
rowsDataState.forEach((item,index)=>{
if(item == "Delete") {
delIndex.push(index);
}
})
} else {
if(data) {
oldRows.forEach((item,index)=>{
if(item.is_kit == '1') {
delIndex.push(index);
}
})
}
}
viewModel.setCache('remark',1);
gridModel.deleteRows(delIndex);
})
8、主子联动:子表删行,如果是删除的套餐项目,那么套餐中的其他项目更新使用金额=原价,是否套餐更新为否
gridModel.on('beforeDeleteRows',(args)=>{
let mark = viewModel.getCache('remark');
if(!mark) {
if(args[0].is_kit == '1') {
cb.utils.confirm("删除后不满足套餐的情况体检项将按照原价计算",function(){
let rows = gridModel.getRows();
let delIndex;
rows.forEach((item,index)=>{
//删除数据
if(item._id == args[0]._id) {
delIndex = index;
} else {
//更新同套餐数据
if((item.examination_kit == args[0].examination_kit) && (item.is_kit == '1')) {
item.is_kit = '2';
item.useprice = item.original_price;
gridModel.updateRow(index,item);
}
}
})
//删除数据
viewModel.setCache('remark',1);
gridModel.deleteRows([delIndex]);
//更新表头套餐
let kitList = viewModel.get("pay_examination_kitList").getData();
let newKitList = [];
let count = 0;
kitList.forEach((item,index)=>{
if(item.examination_kit == args[0].examination_kit) {
if(item.id) {
item._status = "Delete";
newKitList.push(item);
}
} else {
newKitList.push(item);
}
})
debugger;
viewModel.get("pay_examination_kitList").doPropertyChange('clearTag');
viewModel.get("pay_examination_kitList").setData(newKitList);
},function(){
return false;
})
return false;
}
} else {
viewModel.clearCache("remark");
}
})
二、体检单
(一). 列表页
1、页面设计:字段调整如下图
2、功能:只看本人创建的客户的体检单
//前端脚本
viewModel.on('beforeSearch',(args)=>{
let result = cb.rest.invokeFunction("AT17925AEA1C18000A.api.getCustomer", {},
function(err, res) {},viewModel,{async:false})
let arr = [];
result.result.res.forEach((item,index)=>{
arr.push(item.customer)
})
args.params.condition.simpleVOs=[
{
"field": "merchant",
"op": "in",
"value1": arr
}
];
})
//后端脚本
let AbstractAPIHandler = require('AbstractAPIHandler');
class MyAPIHandler extends AbstractAPIHandler {
execute(request){
let userId = ObjectStore.user().id;
let sql = "select customer from AT17925AEA1C18000A.AT17925AEA1C18000A.pay where creator= '"+ userId+"'";
var res = ObjectStore.queryByYonQL(sql);
return {res};
}
}
exports({"entryPoint":MyAPIHandler});