1、票据管理:开票/收票登记、开票申请,票据类型根据单据发布应用决定票据类型过滤、默认票据类型BUG处理

2、开票登记触发下推计息单,下推反写开票登记、费用明细
3、计息单删除校验及删除上游两表关联记录
This commit is contained in:
XiangLingFeng 2025-12-10 17:30:55 +08:00
parent 4af45886af
commit b5c75191ee
3 changed files with 339 additions and 9 deletions

View File

@ -38,15 +38,10 @@ public class CdmBillTypeBillPlugin extends AbstractBillPlugIn implements Plugin,
String appId = showParameter.getAppId(); String appId = showParameter.getAppId();
//信用证供应链金融 票据类型赋默认值 //信用证供应链金融 票据类型赋默认值
String billTypeNum = ""; String billTypeNum = "";
switch (appId){ if (TYPE_XYZ.equals(appId)){
//信用证 billTypeNum = XZY_NUM;
case TYPE_XYZ: } else if (TYPE_GYL.equals(appId)) {
billTypeNum = XZY_NUM; billTypeNum = GYL_NUM;
break;
//供应链金融
case TYPE_GYL:
billTypeNum = GYL_NUM;
break;
} }
if (StringUtils.isNotBlank(billTypeNum)){ if (StringUtils.isNotBlank(billTypeNum)){
DynamicObject billType = BusinessDataServiceHelper.loadSingle("cdm_billtype", "id", DynamicObject billType = BusinessDataServiceHelper.loadSingle("cdm_billtype", "id",

View File

@ -0,0 +1,247 @@
package kdsz.zyf25.tmc.cfm.plugin.operate;
import kd.bos.dataentity.OperateOption;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.entity.EntityMetadataCache;
import kd.bos.entity.botp.runtime.TableDefine;
import kd.bos.entity.operate.result.IOperateInfo;
import kd.bos.entity.operate.result.OperationResult;
import kd.bos.entity.plugin.AbstractOperationServicePlugIn;
import kd.bos.entity.plugin.PreparePropertysEventArgs;
import kd.bos.entity.plugin.args.BeforeOperationArgs;
import kd.bos.orm.query.QCP;
import kd.bos.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.QueryServiceHelper;
import kd.bos.servicehelper.operation.OperationServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.sdk.plugin.Plugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.YearMonth;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.*;
/**
* 票据管理计息
*/
public class CdmInterestAccrualPlugin extends AbstractOperationServicePlugIn implements Plugin {
private static final Logger logger = LoggerFactory.getLogger(CdmInterestAccrualPlugin.class);
private static HashSet<String> feeTypeNumSet = new HashSet<>();
private static final String FeeBill_KEY = "cfm_feebill";//费用明细标识
private static final String INTERESTACCRUAL_KEY = "kdsz_interestaccrual";//计息单标识
private static Long FeeBill_TableID = 0L;
static {
feeTypeNumSet.add("FY_011");
feeTypeNumSet.add("FY_012");
feeTypeNumSet.add("FY_013");
TableDefine tableDefine = EntityMetadataCache.loadTableDefine("cfm_feebill", "cfm_feebill");
FeeBill_TableID = tableDefine.getTableId();
}
@Override
public void onPreparePropertys(PreparePropertysEventArgs e) {
super.onPreparePropertys(e);
e.getFieldKeys().add("billno");
e.getFieldKeys().add("issuedate");
e.getFieldKeys().add("draftbillexpiredate");
e.getFieldKeys().add("kdsz_payentry.kdsz_interestbill");
e.getFieldKeys().add("kdsz_payentry.kdsz_interestbillno");
e.getFieldKeys().add("kdsz_payentry.kdsz_interestbillid");
e.getFieldKeys().add("kdsz_payentry.kdsz_interestbillstatus");
e.getFieldKeys().add("kdsz_payentry.kdsz_valuedate");
e.getFieldKeys().add("kdsz_payentry.kdsz_settledate");
e.getFieldKeys().add("kdsz_payentry.kdsz_paydate");
}
@Override
public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
super.beforeExecuteOperationTransaction(e);
DynamicObject[] bills = e.getDataEntities();
for (DynamicObject bill : bills) {
String billNo = bill.getString("billno");
Date issueDate = bill.getDate("issuedate");//出票日
Date draftbillexpireDate = bill.getDate("draftbillexpiredate");//票据到期日
//生成计息单除费用明细外的参数
HashMap<String, Object> dataMap = new HashMap<>();
dataMap.put("issueDate",issueDate);
dataMap.put("draftbillexpireDate",draftbillexpireDate);
QFilter filter = new QFilter("entry.srcbillno", QCP.equals, billNo);
filter.and("feetype.number",QCP.in, feeTypeNumSet).and("kdsz_pushjx",QCP.not_equals,"B");
//费用明细所需字段
String FeeBillSelectField = "id,billno,org,feetype,currency,amountrate,payamt,kdsz_pushjx";
DynamicObject[] feeBills = BusinessDataServiceHelper.load(FeeBill_KEY, FeeBillSelectField, new QFilter[]{filter});
if (feeBills == null || feeBills.length == 0){
logger.info("应付票据:" + billNo + "没有费用明细无需下推!");
continue;
}
OperationResult result = genJXBill(dataMap,feeBills);
if (result.isSuccess()){
List<Object> successPkIds = result.getSuccessPkIds();
if (successPkIds.size() == feeBills.length){
logger.info("应付票据:" + billNo + "下推计息单成功!");
DynamicObjectCollection payEntries = bill.getDynamicObjectCollection("kdsz_payentry");
int size = payEntries.size();
//开票登记应付票据记录下推信息
for (Object successPkId : successPkIds) {
DynamicObject jxBill = BusinessDataServiceHelper.loadSingle(successPkId, INTERESTACCRUAL_KEY);
if (jxBill == null)continue;
DynamicObject payEntry = payEntries.addNew();
payEntry.set("seq", ++size);
payEntry.set("kdsz_interestbill",successPkId);
payEntry.set("kdsz_interestbillno",jxBill.getString("billno"));
payEntry.set("kdsz_interestbillid",successPkId);
// payEntry.set("",);
// payEntry.set("",);
// payEntry.set("",);
}
SaveServiceHelper.save(new DynamicObject[]{bill});
for (DynamicObject feeBill : feeBills) {
feeBill.set("kdsz_pushjx", "B");
}
SaveServiceHelper.update(feeBills);
}
// else {
// //部分生成则删除
// OperationResult deleteResult = deleteBills(successPkIds);
// if (!deleteResult.isSuccess()){
// logger.info("应付票据:" + billNo + "下推计息单部分成功的单据删除失败!");
// }else {
// logger.info("应付票据:" + billNo + "下推计息单部分成功的单据删除成功!");
// }
// }
}else {
List<IOperateInfo> errors = result.getAllErrorOrValidateInfo();
StringBuilder errorStr = new StringBuilder();
for (IOperateInfo error : errors) {
String message = error.getMessage();
errorStr.append(message).append("\n");
}
logger.error("应付票据:" + billNo + "下推计息单失败!\n" + errorStr);
}
}
}
private OperationResult genJXBill(HashMap<String,Object> dataMap,DynamicObject[] feeBills) {
DynamicObjectCollection JXBills = new DynamicObjectCollection();
Date issueDate = (Date)dataMap.get("issueDate");
Date draftbillexpireDate = (Date)dataMap.get("draftbillexpireDate");
for (DynamicObject feeBill : feeBills) {
DynamicObject JXBill = BusinessDataServiceHelper.newDynamicObject(INTERESTACCRUAL_KEY);
//参数组装
DynamicObject org = feeBill.getDynamicObject("org");//组织
DynamicObject feeType = feeBill.getDynamicObject("feetype");//费用类型
DynamicObject currency = feeBill.getDynamicObject("currency");//币种
BigDecimal amountRate = feeBill.getBigDecimal("amountrate");//费率
BigDecimal feeAmt = feeBill.getBigDecimal("payamt");//费用金额
// Date 转换为 LocalDate忽略时间部分
LocalDate localIssueDate = issueDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
LocalDate localExpireDate = draftbillexpireDate.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
// 计算天数差
long term = Math.abs(ChronoUnit.DAYS.between(localIssueDate, localExpireDate));
BigDecimal payAmt = feeAmt.multiply(new BigDecimal(term)).multiply(amountRate)
.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);//付息金额
JXBill.set("billstatus","A");
JXBill.set("org", org);//业务组织
JXBill.set("kdsz_valuedate", issueDate);//计息开始日
JXBill.set("kdsz_maturitydate", draftbillexpireDate);//计息结束日
JXBill.set("kdsz_feetype", feeType);//费用类型
JXBill.set("kdsz_currency", currency);//付息币别
JXBill.set("kdsz_feerate", amountRate);//费率
JXBill.set("kdsz_totalamount", payAmt);//付息金额
JXBill.set("kdsz_srcentity",FeeBill_KEY);//源单类型
JXBill.set("kdsz_srcbillno",feeBill.getString("billno"));//源单编号
JXBill.set("kdsz_srcbillid",feeBill.getPkValue());//源单id
//生成按月计息明细
DynamicObjectCollection entries = JXBill.getDynamicObjectCollection("entryentity");
ArrayList<HashMap<String, Date>> dateList = genJXEntry(issueDate, draftbillexpireDate);
int monthCount = dateList.size();
int seq = entries.size();
for (HashMap<String, Date> map : dateList) {
Date startDate = map.get("start");
Date endDate = map.get("end");
DynamicObject entry = entries.addNew();
entry.set("seq", ++seq);
entry.set("kdsz_e_valuedate",startDate);
entry.set("kdsz_e_maturitydate",endDate);
entry.set("kdsz_e_principal",payAmt.divide(new BigDecimal(monthCount),2, RoundingMode.HALF_UP));
entry.set("kdsz_e_feerate",amountRate);
}
//关联费用明细
DynamicObjectCollection billheadLk = JXBill.getDynamicObjectCollection("billhead_lk");
DynamicObject lk = billheadLk.addNew();
lk.set("billhead_lk_stableid",FeeBill_TableID);
lk.set("billhead_lk_sbillid",feeBill.getPkValue());
lk.set("billhead_lk_sid",feeBill.getPkValue());
JXBills.add(JXBill);
}
if (JXBills.size() > 0){
DynamicObject[] JXBillArray = JXBills.toArray(new DynamicObject[0]);
return OperationServiceHelper.executeOperate("save", INTERESTACCRUAL_KEY, JXBillArray, OperateOption.create());
}
OperationResult failResult = new OperationResult();
failResult.setSuccess(false);
return failResult;
}
private ArrayList<HashMap<String,Date>> genJXEntry(Date start, Date end) {
ArrayList<HashMap<String,Date>> dateList = new ArrayList<>();
LocalDate startDate = start.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
LocalDate endDate = end.toInstant()
.atZone(ZoneId.systemDefault())
.toLocalDate();
if (startDate == null || endDate == null || startDate.isAfter(endDate)) {
return dateList;
}
LocalDate currentStart = startDate;
while (currentStart.isBefore(endDate) || currentStart.isEqual(endDate)) {
HashMap<String, Date> map = new HashMap<>();
// 获取当前月份的最后一天
YearMonth currentMonth = YearMonth.from(currentStart);
LocalDate monthEnd = currentMonth.atEndOfMonth();
// 如果月份结束日期大于结束日期则使用结束日期
LocalDate rangeEnd = monthEnd.isBefore(endDate) ? monthEnd : endDate;
map.put("start", Date.from(currentStart.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant()));
map.put("end", Date.from(rangeEnd.atStartOfDay()
.atZone(ZoneId.systemDefault())
.toInstant()));
dateList.add(map);
// 下一段从下个月的第一天开始
currentStart = rangeEnd.plusDays(1);
// 如果已经到达结束日期退出循环
if (rangeEnd.isEqual(endDate)) {
break;
}
}
return dateList;
}
private OperationResult deleteBills(List<Object> ids) {
OperationResult result = new OperationResult();
if (ids == null || ids.size() == 0){
result.setSuccess(false);
return result;
}
DynamicObject[] deleteBills = BusinessDataServiceHelper.load(INTERESTACCRUAL_KEY, "id",
new QFilter[]{new QFilter("id", QCP.in, ids)});
for (int i = 0; i < deleteBills.length; i++) {
deleteBills[i] = BusinessDataServiceHelper.loadSingle(deleteBills[i].getPkValue(), INTERESTACCRUAL_KEY);
}
return OperationServiceHelper.executeOperate("delete", INTERESTACCRUAL_KEY, deleteBills, OperateOption.create());
}
}

View File

@ -0,0 +1,88 @@
package kdsz.zyf25.tmc.cfm.plugin.operate;
import kd.bos.dataentity.entity.DynamicObject;
import kd.bos.dataentity.entity.DynamicObjectCollection;
import kd.bos.entity.ExtendedDataEntity;
import kd.bos.entity.filter.QFilterDto;
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.orm.query.QFilter;
import kd.bos.servicehelper.BusinessDataServiceHelper;
import kd.bos.servicehelper.operation.SaveServiceHelper;
import kd.sdk.plugin.Plugin;
import java.util.HashSet;
/**
* 计息单删除校验以及反写上游开票登记费用明细下推状态
*/
public class InterestAccrualDeletePlugin extends AbstractOperationServicePlugIn implements Plugin {
@Override
public void onPreparePropertys(PreparePropertysEventArgs e) {
super.onPreparePropertys(e);
e.getFieldKeys().add("kdsz_srcentity");
e.getFieldKeys().add("kdsz_srcbillid");
e.getFieldKeys().add("id");
e.getFieldKeys().add("entryentity.kdsz_e_paybill");
}
@Override
public void onAddValidators(AddValidatorsEventArgs e) {
e.addValidator(new AbstractValidator() {
@Override
public void validate() {
ExtendedDataEntity[] extBills = this.getDataEntities();
for (ExtendedDataEntity extBill : extBills) {
DynamicObject bill = extBill.getDataEntity();
DynamicObjectCollection entries = bill.getDynamicObjectCollection("entryentity");
for (DynamicObject entry : entries) {
DynamicObject payBill = entry.getDynamicObject("kdsz_e_paybill");
if (payBill != null) {
addErrorMessage(extBill, "计息单:"+extBill.getBillNo()+"已生成付款单,不允许删除");
}
}
}
}
});
}
@Override
public void afterExecuteOperationTransaction(AfterOperationArgs e) {
super.afterExecuteOperationTransaction(e);
DynamicObject[] bills = e.getDataEntities();
for (DynamicObject bill : bills) {
String srcEntity = bill.getString("kdsz_srcentity");
Object srcBillId = bill.get("kdsz_srcbillid");
DynamicObject feeBill = BusinessDataServiceHelper.loadSingle(srcBillId, srcEntity);
if (feeBill != null) {
feeBill.set("kdsz_pushjx","A");
SaveServiceHelper.update(feeBill);
DynamicObjectCollection feeEntries = feeBill.getDynamicObjectCollection("entry");
HashSet<Long> srcBillIds = new HashSet<>();
for (DynamicObject feeEntry : feeEntries) {
long pjId = feeEntry.getLong("srcbillid");
srcBillIds.add(pjId);
}
QFilter filter = new QFilter("id", "in", srcBillIds);
// TODO: 2025/12/10 暂时定为开票登记
DynamicObject[] srcBills = BusinessDataServiceHelper.load("cdm_payablebill",
"id,kdsz_payentry.kdsz_interestbillid", new QFilter[]{filter});
for (DynamicObject srcBill : srcBills) {
DynamicObjectCollection srcPayEntries = srcBill.getDynamicObjectCollection("kdsz_payentry");
for (int i = srcPayEntries.size() - 1; i > 0 ; i--) {
DynamicObject srcPayEntry = srcPayEntries.get(i);
long srcJXBillId = srcPayEntry.getLong("kdsz_interestbillid");
long id = bill.getLong("id");
if (srcJXBillId == id){
srcPayEntries.remove( i);
}
}
}
SaveServiceHelper.save(srcBills);
}
}
}
}