Ver código fonte

feat(form-plugin): 新增费用预提单与对公报销单的结算表单插件

该插件用于实现主辅单据的自动核销功能,支持以下操作:
- 加载并展示符合条件的费用预提单和对公报销单数据
- 支持手动选择主辅单据行项进行结算- 实现逐行匹配金额并完成写入核销金额
- 提供刷新按钮以重新加载最新单据数据
- 自动更新视图显示结算后的结果

主要功能包括:
- 表单初始化时查询并填充待结算数据- 点击结算按钮后获取选中行并执行核销逻辑
- 核销过程中动态调整剩余未核销金额
- 完成后反写更新界面对应字段值
- 提供日志输出便于调试跟踪核销过程
turborao 1 semana atrás
pai
commit
e4bd922542

+ 378 - 0
code/fi/nckd-xtpoc-fi/src/main/java/nckd/xtpoc/fi/app/plugin/form/SettlebillFormPlugin.java

@@ -0,0 +1,378 @@
+package nckd.xtpoc.fi.app.plugin.form;
+
+
+import kd.bos.dataentity.entity.DynamicObject;
+import kd.bos.dataentity.entity.DynamicObjectCollection;
+import kd.bos.dataentity.metadata.dynamicobject.DynamicObjectType;
+import kd.bos.form.control.EntryGrid;
+import kd.bos.form.control.Toolbar;
+import kd.bos.form.control.events.ItemClickEvent;
+import kd.bos.form.events.BeforeDoOperationEventArgs;
+import kd.bos.form.operate.FormOperate;
+import kd.bos.form.plugin.AbstractFormPlugin;
+import kd.bos.orm.query.QCP;
+import kd.bos.orm.query.QFilter;
+import kd.bos.servicehelper.QueryServiceHelper;
+import kd.sdk.plugin.Plugin;
+
+import java.math.BigDecimal;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.List;
+
+/**
+ * 动态表单插件
+ */
+public class SettlebillFormPlugin extends AbstractFormPlugin implements Plugin {
+
+
+    /**
+     * 结算规则  费用预提单
+     */
+    private static final String settlerule = "nckd_settlerule";
+    /**
+     * 主方单据   费用预提单
+     */
+    private static final String main = "er_withholdingbill";
+    /**
+     * 辅方单据   对公报销
+     */
+    private static final String assist = "er_publicreimbursebill";
+    /**
+     * 单据条件
+     */
+    private String mainFilter = "";
+    private String assistFilter = "";
+    /**
+     * 分录列表
+     */
+    private String mainEntry = "nckd_entry1";
+    private String assistEntry = "nckd_entry11";
+
+    List<DynamicObject> mainlist = new ArrayList<>();
+    List<DynamicObject> assistlist = new ArrayList<>();
+
+    @Override
+    public void registerListener(EventObject e) {
+        super.registerListener(e);
+        Toolbar mbar = this.getView().getControl("nckd_flash");
+        mbar.addItemClickListener( this);
+
+    }
+
+    @Override
+    public void itemClick(ItemClickEvent evt) {
+        super.itemClick(evt);
+        String itemkey = evt.getOperationKey();
+        if("settle".equals(itemkey)){
+            //evt.getParamsMap().get("selectedRow");
+
+        }
+    }
+
+    @Override
+    public void beforeBindData(EventObject e) {
+        super.beforeBindData(e);
+
+        QFilter filter = new QFilter("number", QCP.equals, "test001"); // 启用
+        String selectFields1 = "id,number,nckd_largetext,nckd_largetext2";
+        DynamicObjectCollection settleruleDyns = QueryServiceHelper.query(settlerule, selectFields1, new QFilter[]{filter});
+        for (DynamicObject data : settleruleDyns) {
+            mainFilter  = data.getString("nckd_largetext");
+            assistFilter  = data.getString("nckd_largetext");
+        }
+
+        setBillEntry();
+
+    }
+
+    @Override
+    public void beforeDoOperation(BeforeDoOperationEventArgs args) {
+        super.beforeDoOperation(args);
+        FormOperate formOperate = (FormOperate)args.getSource();
+        String key = formOperate.getOperateKey();
+        switch (key) {
+            case "settle":
+
+            case "auto":
+
+                settleBill();
+                break;
+            case "refresh":
+                refreshBill();
+                break;
+
+        }
+    }
+
+    public void refreshBill(){
+
+        this.getModel().deleteEntryData(mainEntry);
+        this.getModel().deleteEntryData(assistEntry);
+        setBillEntry();
+        this.getModel().updateCache(); // 强制更新缓存
+        this.getView().updateView(mainEntry);
+        this.getView().updateView(assistEntry);
+    }
+
+    public void settleBill(){
+
+        EntryGrid entryGrid = this.getControl(mainEntry);
+        int[] selectRows = entryGrid.getSelectRows();
+
+        DynamicObjectCollection entity=this.getModel().getEntryEntity(mainEntry);
+        if(selectRows!=null && selectRows.length>0){
+            for(int selectRow :selectRows){
+                DynamicObject dynamicObject = entity.get(selectRow);  //获取选中行的单据体数据
+                mainlist.add(dynamicObject);
+            }
+        }
+
+        EntryGrid assistEntryGrid = this.getControl(assistEntry);
+        int[] assistSelectRows = assistEntryGrid.getSelectRows();
+
+        DynamicObjectCollection assistEntity = this.getModel().getEntryEntity(assistEntry);
+        if(assistSelectRows != null && assistSelectRows.length > 0){
+            for(int assistSelectRow :assistSelectRows){
+                DynamicObject dynamicObject = assistEntity.get(assistSelectRow);  //获取选中行的单据体数据
+                assistlist.add(dynamicObject);
+            }
+        }
+
+        writeOffMainAndAssistLists();
+
+        if(selectRows != null && selectRows.length > 0){
+            for(int selectRow :selectRows){
+                DynamicObject dynamicObject = entity.get(selectRow);  //获取选中行的单据体数据
+                for(DynamicObject mainBill : mainlist){
+                    if(dynamicObject.get("nckd_fid").equals(mainBill.get("nckd_fid"))){
+                        dynamicObject.set("nckd_writeamount", mainBill.get("nckd_writeamount"));
+                    }
+                }
+            }
+        }
+        if(assistSelectRows != null && assistSelectRows.length > 0){
+            for(int assistSelectRow :assistSelectRows){
+                DynamicObject dynamicObject = assistEntity.get(assistSelectRow);  //获取选中行的单据体数据
+                for(DynamicObject assistBill : assistlist){
+                    if(dynamicObject.get("nckd_fid1").equals(assistBill.get("nckd_fid1"))){
+                        dynamicObject.set("nckd_writeoffamount", assistBill.get("nckd_writeoffamount"));
+                    }
+                }
+            }
+        }
+        /**
+         * 反写单据
+         */
+
+
+        this.getView().updateView(mainEntry);
+        this.getView().updateView(assistEntry);
+    }
+
+    public void setBillEntry(){
+
+        this.getModel().getEntryEntity(mainEntry).clear();
+        this.getModel().getEntryEntity(assistEntry).clear();
+
+        String selectField1 = "id,billno,fiperiod,company.name,expenseentryentity.id,expenseentryentity.expenseamount,expenseentryentity.orgiexpebalanceamount";
+        QFilter qFilter1 = new QFilter("expenseentryentity.expenseamount", QCP.large_equals, 0); // 启用
+
+        DynamicObjectCollection mainDyns = QueryServiceHelper.query(main, selectField1,new QFilter[]{qFilter1},"fiperiod");
+
+        DynamicObjectCollection descEntries = this.getModel().getEntryEntity(mainEntry);
+        DynamicObjectType entryType = descEntries.getDynamicObjectType();
+        for (DynamicObject maintDyn : mainDyns) {
+            DynamicObject newEntry = new DynamicObject(entryType);
+            newEntry.set("nckd_fid", maintDyn.getLong("id"));
+            newEntry.set("nckd_fentryid", maintDyn.getLong("expenseentryentity.id"));
+            newEntry.set("nckd_billtype", "费用预提单");
+            newEntry.set("nckd_billno", maintDyn.getString("billno"));
+            newEntry.set("nckd_date", maintDyn.getDate("fiperiod"));
+            newEntry.set("nckd_supp", "");
+            newEntry.set("nckd_amount", maintDyn.getBigDecimal("expenseentryentity.expenseamount"));
+            newEntry.set("nckd_writeamount", maintDyn.getBigDecimal("expenseentryentity.expenseamount"));
+            descEntries.add(newEntry);
+        }
+//        for (int i = 0; i < 5; i++) {
+//            DynamicObject newEntry = new DynamicObject(entryType);
+//            newEntry.set("nckd_fid", 100+i);
+//            newEntry.set("nckd_billtype", "费用预提单");
+//            newEntry.set("nckd_billno", "bill00002");
+//            newEntry.set("nckd_date", new Date());
+//            newEntry.set("nckd_supp", "供应商200");
+//            newEntry.set("nckd_amount", new BigDecimal(300));
+//            newEntry.set("nckd_writeamount", new BigDecimal(300));
+//            descEntries.add(newEntry);
+//        }
+
+        String selectField2 = "id,billno,bizdate,company.name,billpayerid.name,expenseentryentity.id,expenseentryentity.expenseamount,expenseentryentity.wbamount";
+        QFilter qFilter2 = new QFilter("expenseentryentity.expenseamount", QCP.large_equals, 0); // 启用
+        QFilter qFilter3 = new QFilter("billpayertype", QCP.equals, "bd_supplier");
+
+        DynamicObjectCollection assistDyns = QueryServiceHelper.query(assist, selectField2,new QFilter[]{qFilter2,qFilter3},"bizdate");
+
+        DynamicObjectCollection descEntries1 = this.getModel().getEntryEntity(assistEntry);
+        DynamicObjectType entryType1 = descEntries1.getDynamicObjectType();
+        for (DynamicObject assistDyn : assistDyns) {
+            DynamicObject newEntry = new DynamicObject(entryType1);
+            newEntry.set("nckd_fid1", assistDyn.getLong("id"));
+            newEntry.set("nckd_fentryid1", assistDyn.getLong("expenseentryentity.id"));
+            newEntry.set("nckd_billtype1", "对公报销");
+            newEntry.set("nckd_billno1", assistDyn.getString("billno"));
+            newEntry.set("nckd_date1", assistDyn.getDate("bizdate"));
+            newEntry.set("nckd_supp1", assistDyn.getString("billpayerid.name"));
+            newEntry.set("nckd_amount1", assistDyn.getBigDecimal("expenseentryentity.expenseamount"));
+            newEntry.set("nckd_writeoffamount", assistDyn.getBigDecimal("expenseentryentity.expenseamount"));
+            descEntries1.add(newEntry);
+        }
+//        for (int i = 0; i < 4; i++) {
+//            DynamicObject newEntry = new DynamicObject(entryType1);
+//            newEntry.set("nckd_fid1", 100+i);
+//            newEntry.set("nckd_billtype1", "对公报销");
+//            newEntry.set("nckd_billno1", "bill00001");
+//            newEntry.set("nckd_date1", new Date());
+//            newEntry.set("nckd_supp1", "供应商100");
+//            newEntry.set("nckd_amount1", new BigDecimal(200));
+//            newEntry.set("nckd_writeoffamount", new BigDecimal(200));
+//            descEntries1.add(newEntry);
+//        }
+
+    }
+
+    /**
+     * 逐行核销主体金额集与辅助金额集,直到未核销金额扣减为0
+     */
+    public void writeOffMainAndAssistLists() {
+        if (mainlist.isEmpty() || assistlist.isEmpty()) {
+            System.out.println("主体金额集或辅助金额集为空,无法进行核销");
+            return;
+        }
+
+        System.out.println("开始执行主体与辅助金额集逐行核销");
+
+        // 遍历主体金额集逐行核销
+        for (int i = 0; i < mainlist.size(); i++) {
+            DynamicObject mainBill = mainlist.get(i);
+
+            // 获取主体行未核销金额
+            BigDecimal mainUnwrittenAmount = mainBill.getBigDecimal("nckd_writeamount");
+            if (mainUnwrittenAmount == null || mainUnwrittenAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                System.out.println("第" + (i+1) + "行主体金额已无未核销金额,跳过");
+                continue;
+            }
+
+            System.out.println("处理第" + (i+1) + "行主体金额,未核销金额:" + mainUnwrittenAmount);
+
+            // 对当前主体行与辅助集进行逐行核销
+            BigDecimal remainingMainAmount = mainUnwrittenAmount;
+
+            for (int j = 0; j < assistlist.size() && remainingMainAmount.compareTo(BigDecimal.ZERO) > 0; j++) {
+                DynamicObject assistBill = assistlist.get(j);
+
+                // 获取辅助行未核销金额
+                BigDecimal assistUnwrittenAmount = assistBill.getBigDecimal("nckd_writeoffamount");
+                if (assistUnwrittenAmount == null || assistUnwrittenAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                    continue; // 跳过已核销完的辅助行
+                }
+
+                // 计算本次可核销金额(取主体剩余金额和辅助未核销金额的较小值)
+                BigDecimal writeOffAmount = remainingMainAmount.min(assistUnwrittenAmount);
+
+                // 执行核销操作
+                performRowWriteOff(mainBill, assistBill, writeOffAmount);
+
+                // 更新主体行未核销金额
+                remainingMainAmount = remainingMainAmount.subtract(writeOffAmount);
+                mainBill.set("nckd_writeamount", remainingMainAmount);
+
+                // 更新辅助行未核销金额
+                BigDecimal newAssistUnwrittenAmount = assistUnwrittenAmount.subtract(writeOffAmount);
+                assistBill.set("nckd_writeoffamount", newAssistUnwrittenAmount);
+
+                System.out.println("主体行" + (i+1) + "与辅助行" + (j+1) + "核销金额:" + writeOffAmount +
+                        ",主体剩余未核销:" + remainingMainAmount +
+                        ",辅助剩余未核销:" + newAssistUnwrittenAmount);
+            }
+
+            if (remainingMainAmount.compareTo(BigDecimal.ZERO) <= 0) {
+                System.out.println("第" + (i+1) + "行主体金额已全部核销完成");
+            } else {
+                System.out.println("第" + (i+1) + "行主体金额部分核销,剩余未核销金额:" + remainingMainAmount);
+            }
+        }
+
+        // 检查辅助金额集中是否有未核销完的金额
+        checkRemainingAssistAmounts();
+
+        System.out.println("主体与辅助金额集逐行核销处理完成");
+    }
+
+    /**
+     * 检查辅助金额集中的剩余未核销金额
+     */
+    private void checkRemainingAssistAmounts() {
+        boolean hasRemaining = false;
+        for (int i = 0; i < assistlist.size(); i++) {
+            DynamicObject assistBill = assistlist.get(i);
+            BigDecimal assistUnwrittenAmount = assistBill.getBigDecimal("nckd_writeoffamount");
+            if (assistUnwrittenAmount != null && assistUnwrittenAmount.compareTo(BigDecimal.ZERO) > 0) {
+                System.out.println("辅助行" + (i+1) + "仍有未核销金额:" + assistUnwrittenAmount);
+                hasRemaining = true;
+            }
+        }
+
+        if (!hasRemaining) {
+            System.out.println("所有辅助金额均已核销完成");
+        }
+    }
+
+    /**
+     * 执行单笔核销操作
+     * @param mainBill 主体金额行
+     * @param assistBill 辅助金额行
+     * @param amount 核销金额
+     */
+    private void performRowWriteOff(DynamicObject mainBill, DynamicObject assistBill, BigDecimal amount) {
+        // 获取单据相关信息
+        String mainBillNo = mainBill.getString("nckd_billno");
+        String assistBillNo = assistBill.getString("nckd_billno1");
+
+        System.out.println("执行核销:主体单据[" + mainBillNo + "] 与 辅助单据[" + assistBillNo + "] 核销金额:" + amount);
+
+        // TODO: 在此处添加实际的核销业务逻辑,如:
+        // 1. 更新数据库中的核销状态
+        // 2. 生成核销记录
+        // 3. 更新相关账务信息
+        // 4. 更新界面显示状态等
+    }
+
+    /**
+     * 检查是否所有金额都已核销完成
+     * @return 是否全部核销完成
+     */
+    public boolean isAllWriteOffCompleted() {
+        // 检查主体金额是否全部核销完成
+        for (DynamicObject mainBill : mainlist) {
+            BigDecimal unwrittenAmount = mainBill.getBigDecimal("nckd_writeamount");
+            if (unwrittenAmount != null && unwrittenAmount.compareTo(BigDecimal.ZERO) > 0) {
+                System.out.println("存在未完成核销的主体金额");
+                return false;
+            }
+        }
+
+        // 检查辅助金额是否全部核销完成
+        for (DynamicObject assistBill : assistlist) {
+            BigDecimal unwrittenAmount = assistBill.getBigDecimal("nckd_writeoffamount");
+            if (unwrittenAmount != null && unwrittenAmount.compareTo(BigDecimal.ZERO) > 0) {
+                System.out.println("存在未完成核销的辅助金额");
+                return false;
+            }
+        }
+
+        System.out.println("所有金额均已核销完成");
+        return true;
+    }
+
+
+}