收款处理-SAP反清账

This commit is contained in:
李贵强 2025-03-27 11:15:59 +08:00
parent f8f2438c71
commit 573f10a1ad
7 changed files with 356 additions and 68 deletions

View File

@ -171,6 +171,9 @@ public class ClaimConvertPlugin extends AbstractConvertPlugIn implements Plugin
targetEntry.set("shjh_detailid", entry.getString("shjh_detailid"));
targetEntry.set("shjh_usercode", entry.getString("shjh_usercode"));
targetEntry.set("shjh_deptcode", entry.getString("shjh_deptcode"));
targetEntry.set("shjh_postingdate", entry.getDate("shjh_postingdate"));
targetEntry.set("shjh_accountsap", entry.getDynamicObject("shjh_coaitemcode"));
targetEntry.set("shjh_yym", entry.getDynamicObject("shjh_reasoncode"));
}
}
}

View File

@ -1,5 +1,6 @@
package shjh.jhzj7.fi.fi.plugin.form;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.entity.datamodel.IDataModel;
import kd.bos.form.CloseCallBack;
@ -11,6 +12,9 @@ import kd.bos.form.events.ClosedCallBackEvent;
import kd.bos.form.plugin.AbstractFormPlugin;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.sdk.plugin.Plugin;
import shjh.jhzj7.fi.fi.plugin.form.info.ClaimFieldsInfo;
@ -204,6 +208,21 @@ public class ClaimBillButtonAssPlugin extends AbstractFormPlugin implements Plug
this.getModel().setValue(ClaimFieldsInfo.USER_CODE, returnData.get(i).getString(ClaimFieldsInfo.USER_CODE), i);
//部门编码
this.getModel().setValue(ClaimFieldsInfo.DEPT_CODE, returnData.get(i).getString(ClaimFieldsInfo.DEPT_CODE), i);
//过账日期
this.getModel().setValue("shjh_postingdate", returnData.get(i).getDate("shjh_postingdate"), i);
//科目
String accountCode = returnData.get(i).getString("shjh_coaitemcode");
DynamicObject account = BusinessDataServiceHelper.loadSingle("bd_accountview", (new QFilter("number", QCP.equals, accountCode)).toArray());
if (null!=account){
this.getModel().setValue("shjh_coaitemcode", account, i);
}
//原因码
String code = returnData.get(i).getString("shjh_reasoncode");
DynamicObject reason = BusinessDataServiceHelper.loadSingle("gl_cashflowitem", (new QFilter("number", QCP.equals, code)).toArray());
if (null!=reason){
this.getModel().setValue("shjh_reasoncode", reason, i);
}
}
}

View File

@ -313,20 +313,23 @@ public class FeeControlApiPlugin extends AbstractFormPlugin implements Plugin {
this.getModel().setValue("shjh_verificationnum", top.getString("StrColumn20"), i);//核销单凭证号
this.getModel().setValue("shjh_fiscalyear", top.getString("GLDate").substring(0, 4), i);//会计年度
this.getModel().setValue("shjh_billheaderid", top.getString("ID"), i);
this.getModel().setValue("shjh_usercode", top.getString("RequestUserCode"), i);
this.getModel().setValue("shjh_deptcode", top.getString("RequestDeptCode"), i);
this.getModel().setValue("shjh_usercode", top.getString("RequestUserCode"), i);//员工工号
this.getModel().setValue("shjh_deptcode", top.getString("RequestDeptCode"), i);//部门编码
this.getModel().setValue("shjh_postingdate", top.getString("GLDate"), i);//过账日期
if (voucherItems != null && voucherItems.size() != 0) {
String id = top.getString("ID");
for (int i1 = 0; i1 < voucherItems.size(); i1++) {
JSONObject voucherItem = (JSONObject) voucherItems.get(i1);
if (voucherItem.getString("BillHeaderID").equals(id)) {
this.getModel().setValue("shjh_coaitemfullname", voucherItem.getString("COAItemFullName"), i);
this.getModel().setValue("shjh_coaitemcode", voucherItem.getString("COAItemCode"), i);
this.getModel().setValue("shjh_coaitemname", voucherItem.getString("COAItemName"), i);
this.getModel().setValue("shjh_reasoncode", voucherItem.getString("ReasonCode"), i);
this.getModel().setValue("shjh_reasoncodename", voucherItem.getString("ReasonCodeName"), i);
this.getModel().setValue("shjh_coaitemfullname", voucherItem.getString("COAItemFullName"), i);//会计科目全称
this.getModel().setValue("shjh_coaitemcode", voucherItem.getString("COAItemCode"), i);//会计科目编码
this.getModel().setValue("shjh_coaitemname", voucherItem.getString("COAItemName"), i);//会计科目名称
this.getModel().setValue("shjh_reasoncode", voucherItem.getString("ReasonCode"), i);//原因码
this.getModel().setValue("shjh_reasoncodename", voucherItem.getString("ReasonCodeName"), i);//原因码名称
break;
}
}
}
} catch (Exception e) {
logger.error("赋值异常: " + e.getMessage(), e);
}
@ -393,6 +396,7 @@ public class FeeControlApiPlugin extends AbstractFormPlugin implements Plugin {
/**
* 获取当前操作用户工号
*
* @return
*/
public String getUserCode() {
@ -421,6 +425,7 @@ public class FeeControlApiPlugin extends AbstractFormPlugin implements Plugin {
/**
* 日志记录
*
* @param response 响应参数
* @param billNumber 本单单号
* @param body 请求体

View File

@ -63,14 +63,14 @@ public class RecBillChangeListExtendPlugin extends AbstractListPlugin implements
String qzState = recBill.getString("shjh_qzzt");
BigDecimal actrecamt = recBill.getBigDecimal("actrecamt");
// 状态 + 来源校验
if (!("C".equals(billStatus) && (
"cas_claimcenterbill".equals(sourceBillType) ||
"bei_intelrec".equals(sourceBillType)))) {
this.getView().showTipNotification("所选单据不满足变更条件,只有状态=“已收款”且源单类型=“收款入账中心/认领通知单”的收款单才允许变更。");
evt.setCancel(true);
return;
}
// 状态 + 来源校验-----标品已有
// if (!("C".equals(billStatus) && (
// "cas_claimcenterbill".equals(sourceBillType) ||
// "bei_intelrec".equals(sourceBillType)))) {
// this.getView().showTipNotification("所选单据不满足变更条件,只有状态=“已收款”且源单类型=“收款入账中心/认领通知单”的收款单才允许变更。");
// evt.setCancel(true);
// return;
// }
// 凭证号非空
if (voucherNum == null || voucherNum.trim().isEmpty()) {

View File

@ -0,0 +1,198 @@
package shjh.jhzj7.fi.fi.plugin.operate;
import kd.bos.context.RequestContext;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.dataentity.utils.StringUtils;
import kd.bos.entity.ExtendedDataEntity;
import kd.bos.entity.formula.RowDataModel;
import kd.bos.entity.operate.result.OperateErrorInfo;
import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
import kd.bos.entity.plugin.AddValidatorsEventArgs;
import kd.bos.entity.plugin.PreparePropertysEventArgs;
import kd.bos.entity.plugin.args.AfterOperationArgs;
import kd.bos.entity.validate.AbstractValidator;
import kd.bos.entity.validate.ErrorLevel;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.sdk.plugin.Plugin;
import shjh.jhzj7.fi.fi.utils.ApiUtils;
import shjh.jhzj7.fi.fi.utils.EsbUtils;
import shjh.jhzj7.fi.fi.utils.ObjUtils;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 单据操作插件
* SAP反清账接口按钮
*/
public class RecRedPushOperation extends AbstractOperationServicePlugIn implements Plugin {
private final static Log logger = LogFactory.getLog(RecRedPushOperation.class);
private static final String INTERFACE_ID ="ReversalVoucher";//识别被调接口并进行路由-SAP反清账
private static final String RECEIVER_ID ="SAP";//定义的发送者
/**
* 操作标识
*/
private static final String KEY_RED_PUSH= "redpunch";
@Override
public void onPreparePropertys(PreparePropertysEventArgs e) {
super.onPreparePropertys(e);
e.getFieldKeys().add(RedPushValidator.KEY_HOT_ACCOUNT);
}
@Override
public void onAddValidators(AddValidatorsEventArgs e) {
super.onAddValidators(e);
e.addValidator(new RedPushValidator());
}
/**
* 自定义校验器
*/
static class RedPushValidator extends AbstractValidator {
/**
* 单据编号
*/
public final static String KEY_BILL_NUM = "billno";
/**
* 红冲标识
*/
public final static String KEY_HOT_ACCOUNT = "hotaccount";
/**
* 已推送SAP
*/
public final static String IS_PUSH_SAP = "shjh_ispushsap";
@Override
public void validate() {
//取数模型
RowDataModel rowDataModel = new RowDataModel(this.entityKey, this.getValidateContext().getSubEntityType());
ExtendedDataEntity[] dataEntities = this.getDataEntities();
if (dataEntities != null && dataEntities.length >= 1) {
for (ExtendedDataEntity dataEntity : dataEntities) {
if (dataEntity != null) {
rowDataModel.setRowContext(dataEntity.getDataEntity());
String billNum = (String) rowDataModel.getValue(KEY_BILL_NUM);
String hotAccount = (String) rowDataModel.getValue(KEY_HOT_ACCOUNT);
if (!"2".equals(hotAccount)) {
this.addErrorMessage(dataEntity, billNum+"所选单据不符合反清账要求。");
}
Boolean isPushSap = (Boolean) rowDataModel.getValue(IS_PUSH_SAP);
if (isPushSap){
this.addErrorMessage(dataEntity, billNum+"所选单据已反清账。");
}
}
}
}
}
}
@Override
public void afterExecuteOperationTransaction(AfterOperationArgs e) {
String operationKey = e.getOperationKey();
if (StringUtils.equals(KEY_RED_PUSH, operationKey)) {
DynamicObject[] dataEntities = e.getDataEntities();
if (dataEntities != null && dataEntities.length >= 1) {
StringBuilder message = new StringBuilder();
for (DynamicObject dataEntity : dataEntities) {
DynamicObject recBill = BusinessDataServiceHelper.loadSingle(dataEntity.getLong("id"), "cas_recbill");
if (null != recBill) {
//单号
String billNumber = recBill.getString("billno");
HashMap<String, String> responseHead = ApiUtils.buildHead(INTERFACE_ID,RECEIVER_ID);
HashMap<String, Object> responseBody = this.assembleRequest(billNumber, recBill,message);
try {
String response = ApiUtils.sendPost(responseHead, responseBody, "https://hipint-stg.jahwa.com.cn:6443/gateway/HIP_ReceiveFromFM/1.0/fm/send");
if (!response.isEmpty()) {
boolean success = ApiUtils.parseResponse(response, billNumber, responseBody, INTERFACE_ID, message);
if (success){
recBill.set("shjh_ispushsap",true);
SaveServiceHelper.update(recBill);
}
}
} catch (IOException ex) {
message.append("收款处理【").append(billNumber).append("】:").append(ex.getMessage()).append("\n");
}
if (message.length()!=0){
OperateErrorInfo operateErrorInfo = new OperateErrorInfo();
operateErrorInfo.setMessage(String.valueOf(message));
operateErrorInfo.setErrorLevel(ErrorLevel.Error.name());
operateErrorInfo.setPkValue(dataEntity.getPkValue());
this.operationResult.addErrorInfo(operateErrorInfo);
}
}
}
}
}
}
/**
* 请求头组装
*
* @param billNumber 单据编号
* @param recBill 收款处理
* @return
*/
private HashMap<String, Object> assembleRequest(String billNumber, DynamicObject recBill, StringBuilder message){
HashMap<String, Object> responseBody = null;
try {
// 生成唯一事务ID
String rootContextId = UUID.randomUUID().toString();
// 获取当前请求时间格式为 yyyy-MM-dd HH:mm:ss.SSS
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
String requestTime = sdf.format(new Date());
responseBody = new HashMap<>(5);
responseBody.put("rootContextID", rootContextId);
responseBody.put("requestTime", requestTime);
HashMap<String, Object> data = new HashMap<>(10);
List<Map<String, Object>> IT_ITEM = new ArrayList<>();
//用户名
DynamicObject userinfo = BusinessDataServiceHelper.loadSingleFromCache(RequestContext.get().getCurrUserId(), "bos_user");
String oaUser = userinfo.getString("shjh_oauser");
//冲销原因
String cause="";
Long id = ObjUtils.getLongSafely(recBill, "sourcebillid");
if (id!=0L){
DynamicObject changeBill = BusinessDataServiceHelper.loadSingle("cas_recchgbill", (new QFilter("sourcebillid", QCP.equals, id).toArray()));
if (null!=changeBill){
cause=changeBill.getString("chgreson");
}
}
DynamicObjectCollection entryCollection = recBill.getDynamicObjectCollection("entry");
if (!entryCollection.isEmpty()){
for (DynamicObject entry : entryCollection) {
Map<String, Object> IT_ITEMS = new HashMap<>(6);
IT_ITEMS.put("BELNR",entry.getString("shjh_verificationnum"));//会计凭证编号
IT_ITEMS.put("BUKRS",entry.getString("realreccompany.number"));//公司代码
IT_ITEMS.put("GJAHR",entry.getString("shjh_fiscalyear"));//会计年度
IT_ITEMS.put("STGRD",cause);//冲销原因
IT_ITEMS.put("UNAME",oaUser);//用户名
IT_ITEMS.put("BUDAT",entry.getDate("shjh_postingdate").toString());//凭证中的过帐日期
IT_ITEM.add(IT_ITEMS);
}
}
data.put("IT_ITEM", IT_ITEM);
responseBody.put("data", data);
} catch (Exception e) {
message.append("收款处理【").append(billNumber).append("】:").append(e.getMessage()).append("\n");
EsbUtils.saveLog(billNumber, INTERFACE_ID, null, e.getMessage(), false, "ESBPushApi", "数据异常");
}
return responseBody;
}
}

View File

@ -16,8 +16,11 @@ import kd.bos.servicehelper.operation.OperationServiceHelper;
import kd.sdk.plugin.Plugin;
import shjh.jhzj7.fi.fi.plugin.form.info.RecFieldsInfo;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* 后台任务插件
@ -29,41 +32,70 @@ public class RecPushSapTask extends AbstractTask implements Plugin {
@Override
public void execute(RequestContext requestContext, Map<String, Object> map) throws KDException {
//收款已生成金蝶凭证未推送SAP
QFilter qFilter = new QFilter("billstatus", QCP.equals, "D");
qFilter.and(new QFilter("isvoucher", QCP.equals, true));
qFilter.and(new QFilter("shjh_ispushsap", QCP.equals, false));
// 查询条件收款单已生成凭证未推送SAP金额0
QFilter qFilter = new QFilter("billstatus", QCP.equals, "D")
.and(new QFilter("isvoucher", QCP.equals, true))
.and(new QFilter("shjh_ispushsap", QCP.equals, false))
.and(new QFilter("actrecamt", QCP.not_equals, BigDecimal.ZERO));
DynamicObject[] recBillList = BusinessDataServiceHelper.load("cas_recbill", "id", qFilter.toArray());
DynamicObject[] recBillList = BusinessDataServiceHelper.load("cas_recbill", "id,billno,actrecamt", qFilter.toArray());
if (recBillList.length != 0) {
ArrayList<Long> ids = new ArrayList<>();
for (DynamicObject dynamicObject : recBillList) {
ids.add(dynamicObject.getLong("id"));
if (recBillList.length == 0) {
logger.info("未找到符合条件的收款单");
return;
}
Map<Object, DynamicObject> recBillMap = BusinessDataServiceHelper.loadFromCache(ids.toArray(), "cas_recbill");
// 按金额正负分组
List<DynamicObject> normalBills = new ArrayList<>(); // 正数单据
List<DynamicObject> redBills = new ArrayList<>(); // 负数单据红冲
OperateOption operateOption = OperateOption.create();
// 不执行警告级别校验器
operateOption.setVariableValue(OperateOptionConst.IGNOREWARN, String.valueOf(true));
// 不显示交互提示自动执行到底
operateOption.setVariableValue(OperateOptionConst.IGNOREINTERACTION, String.valueOf(true));
// 全部校验通过才保存
operateOption.setVariableValue(OperateOptionConst.STRICTVALIDATION, String.valueOf(true));
//同一个用户在多个界面操作同一张也不允许操作
operateOption.setVariableValue(OperateOptionConst.MUTEX_ISSTRICT, String.valueOf(true));
// Convert map values to array for batch operation
DynamicObject[] billArray = recBillMap.values().toArray(new DynamicObject[0]);
OperationResult operationResult = OperationServiceHelper.executeOperate("pushvoucher", "cas_recbill", billArray, operateOption);
if (operationResult.isSuccess()) {
logger.info("执行 pushvoucher 成功,共处理 " + billArray.length + " 张单据");
for (DynamicObject bill : recBillList) {
if (bill.getBigDecimal("actrecamt").compareTo(BigDecimal.ZERO) > 0) {
normalBills.add(bill);
} else {
logger.error("批量推送SAP失败: " + operationResult.getMessage());
redBills.add(bill);
}
}
// 配置操作选项共用
OperateOption operateOption = createStrictOperateOption();
// 并行处理两组数据
CompletableFuture<Void> normalFuture = processBillsAsync(normalBills, "pushvoucher", operateOption);
CompletableFuture<Void> redFuture = processBillsAsync(redBills, "redpunch", operateOption);
// 等待所有任务完成
CompletableFuture.allOf(normalFuture, redFuture).join();
}
// 创建严格的操作选项
private OperateOption createStrictOperateOption() {
OperateOption option = OperateOption.create();
option.setVariableValue(OperateOptionConst.IGNOREWARN, "true");
option.setVariableValue(OperateOptionConst.IGNOREINTERACTION, "true");
option.setVariableValue(OperateOptionConst.STRICTVALIDATION, "true");
option.setVariableValue(OperateOptionConst.MUTEX_ISSTRICT, "true");
return option;
}
// 异步处理单据组
private CompletableFuture<Void> processBillsAsync(List<DynamicObject> bills, String operation, OperateOption option) {
return CompletableFuture.runAsync(() -> {
if (bills.isEmpty()) return;
try {
DynamicObject[] billArray = bills.toArray(new DynamicObject[0]);
OperationResult result = OperationServiceHelper.executeOperate(operation, "cas_recbill", billArray, option);
if (result.isSuccess()) {
logger.info(String.format("【%s】批量处理成功%d 张单据", operation, bills.size()));
} else {
logger.error(String.format("【%s】处理失败%s", operation, result.getMessage()));
// 可在此添加失败重试逻辑
}
} catch (Exception e) {
logger.error(String.format("【%s】执行异常", operation), e);
}
});
}
}

View File

@ -0,0 +1,31 @@
package shjh.jhzj7.fi.fi.utils;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.logging.Log;
import kd.bos.logging.LogFactory;
public class ObjUtils {
private final static Log logger = LogFactory.getLog(ObjUtils.class);
/**
* 安全获取源单ID
* @param obj 数据包
* @param fieldName 字段名
* @return id
*/
public static Long getLongSafely(DynamicObject obj, String fieldName) {
if (obj == null) {
return null;
}
try {
Object value = obj.get(fieldName);
return value != null ? Long.parseLong(value.toString()) : 0L;
} catch (Exception e) {
logger.error("转换字段[" + fieldName + "]到Long失败", e);
return null;
}
}
}