Przeglądaj źródła

已确认应收款: 完成
1) 增加生成账单入口:
i) 弹出窗口输入账单起止日期,保存时需要校验所选条目是否在账单起止日期内
ii) 批量选择条目:计费客户相同、状态必须为----未生成生成账单
2) 取消审批增加检验:
i) 关联发票ID字段非空,不允许取消审批
3) 查询结果中关联账单字段,增加链接到:账单详情

zhangyao 1 miesiąc temu
rodzic
commit
85f0562b7f

+ 1 - 1
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/controller/contractServiceController.java

@@ -149,7 +149,7 @@ public class contractServiceController {
     @Mapping(value = "com.awspaas.user.apps.donenow_ctt.service_purchase_adjustTEST")
     public ResponseObject editServicePurchaseTest(UserContext uc) {
 
-        List<String> serviceIds = DBSql.getList("SELECT ID FROM bo_eu_dnctt_contract_service where NEED_PURCHASE=1 AND ID in (SELECT CONTRACT_SERVICE_ID FROM bo_eu_dnctt_contract_service_period) ORDER BY CREATEDATE", String.class);
+        List<String> serviceIds = DBSql.getList("SELECT ID FROM bo_eu_dnctt_contract_service where NEED_PURCHASE=1 AND ID in (SELECT CONTRACT_SERVICE_ID FROM bo_eu_dnctt_contract_service_period) AND ID NOT in (SELECT CONTRACT_SERVICE_ID FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH) ORDER BY CREATEDATE", String.class);
 
         for (String serviceId : serviceIds) {
             try {

+ 6 - 8
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/controller/invoiceController.java

@@ -10,8 +10,6 @@ import com.awspaas.user.apps.donenow_ctt.service.InvoiceDealDto;
 import com.awspaas.user.apps.donenow_ctt.service.InvoiceService;
 import org.apache.commons.lang3.StringUtils;
 
-import java.util.List;
-
 /**
  * 账单管理
  */
@@ -27,8 +25,9 @@ public class invoiceController {
     public ResponseObject previewInvoice(UserContext uc, String accDeduIds) {
         InvoiceDealDto invoiceDealDto = new InvoiceDealDto();
         invoiceDealDto.setIds(accDeduIds);
-        List<String> invoiceIds = InvoiceService.getInstance().processInvoice(uc, accDeduIds, invoiceDealDto, false);
-
+        String result = InvoiceService.getInstance().processInvoice(uc, accDeduIds, invoiceDealDto, false);
+        if (StringUtils.isNotBlank(result))
+            return ResponseObject.newErrResponse(result);
         return ResponseObject.newOkResponse();
     }
 
@@ -52,10 +51,9 @@ public class invoiceController {
         invoiceDealDto.setDateRangeFromStr(startDate);
         invoiceDealDto.setDateRangeToStr(endDate);
 
-        List<String> invoiceIds = InvoiceService.getInstance().processInvoice(uc, accDeduIds, invoiceDealDto, true);
-
-        if (invoiceIds == null || invoiceIds.isEmpty())
-            return ResponseObject.newErrResponse("已生成账单,不可重复生成");
+        String result = InvoiceService.getInstance().processInvoice(uc, accDeduIds, invoiceDealDto, true);
+        if (StringUtils.isNotBlank(result))
+            return ResponseObject.newErrResponse(result);
 
         return ResponseObject.newOkResponse();
     }

+ 1 - 1
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/service/InvoiceDealDto.java

@@ -1,8 +1,8 @@
 package com.awspaas.user.apps.donenow_ctt.service;
 
 import org.apache.commons.lang3.StringUtils;
+import org.joda.time.LocalDate;
 
-import java.time.LocalDate;
 import java.util.List;
 import java.util.Map;
 

+ 69 - 75
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/service/InvoiceService.java

@@ -5,10 +5,13 @@ import com.actionsoft.bpms.bpmn.engine.model.run.delegate.ProcessInstance;
 import com.actionsoft.bpms.commons.database.RowMap;
 import com.actionsoft.bpms.server.UserContext;
 import com.actionsoft.bpms.util.DBSql;
+import com.actionsoft.bpms.util.TypeUtil;
 import com.actionsoft.sdk.local.SDK;
 import com.actionsoft.sdk.local.api.Logger;
 import org.apache.commons.lang3.StringUtils;
+import org.joda.time.LocalDate;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
@@ -40,14 +43,16 @@ public class InvoiceService {
     }
 
     // ====================== 1. 核心入口方法:处理发票生成 ======================
-    public List<String> processInvoice(UserContext uc, String accDeduIds, InvoiceDealDto param, boolean isDeal) {
+    public String processInvoice(UserContext uc, String accDeduIds, InvoiceDealDto param, boolean isDeal) {
 
         logger.info("处理发票生成");
         logger.info("accDeduIds:" + accDeduIds);
         logger.info("日期范围:" + param.getDateRangeFrom() + "--" + param.getDateRangeTo());
 
         // 1. 基础校验:空值过滤
-        if (StringUtils.isBlank(accDeduIds)) return new ArrayList<>();
+        if (StringUtils.isBlank(accDeduIds))
+            return "参数错误";
+
         String[] dedIdArr = accDeduIds.split(",");
 
         StringBuilder where = new StringBuilder("(");
@@ -57,15 +62,12 @@ public class InvoiceService {
         String sqlWhere = where.toString(); // 复用:构建IN条件
 
         // 2. 初始化基础数据(3张核心表查询)
-        List<RowMap> postedDataList = DBSql.getMaps(
-                "SELECT * FROM VIEW_EU_DNV_POSTED_ALL d WHERE d.ID IN " + sqlWhere + " AND (d.invoice_id is null OR d.invoice_id='' OR NOT EXISTS(SELECT 1 FROM bo_eu_dnctt_invoice i WHERE i.id=d.invoice_id AND IS_VOIDED=0))  AND  d.bill_account_id is NOT NULL AND d.ORGID='" + uc.getCompanyModel().getId() + "' ORDER BY ACCOUNT_ID,BILL_ACCOUNT_ID",
-                dedIdArr
-        );
+        List<RowMap> postedDataList = DBSql.getMaps("SELECT * FROM VIEW_EU_DNV_POSTED_ALL d WHERE d.ID IN " + sqlWhere + " AND (d.invoice_id is null OR d.invoice_id='' OR NOT EXISTS(SELECT 1 FROM bo_eu_dnctt_invoice i WHERE i.id=d.invoice_id AND IS_VOIDED=0))  AND  d.bill_account_id is NOT NULL AND d.ORGID='" + uc.getCompanyModel().getId() + "' ORDER BY ACCOUNT_ID,BILL_ACCOUNT_ID", dedIdArr);
 
         logger.info("账单个数:" + postedDataList.size());
 
-        if (postedDataList.isEmpty())
-            return null;
+        if (postedDataList.isEmpty() || postedDataList.size() != dedIdArr.length)
+            return "所选条目有账单状态必须为未生成生成账单";
 
         // 统一Key为大写,避免字段名大小写不一致导致的NPE
         postedDataList.forEach(row -> row.forEach((key, value) -> {
@@ -80,20 +82,44 @@ public class InvoiceService {
             }
         }));
 
-        List<RowMap> accountDedList = DBSql.getMaps(
-                "SELECT * FROM BO_EU_DNCRM_ACCOUNT_DEDUCTION WHERE ID IN " + sqlWhere + " ORDER BY ACCOUNT_ID,BILL_ACCOUNT_ID",
-                dedIdArr
-        );
-        List<RowMap> itemTypeDict = DBSql.getMaps(
-                "SELECT OID,NAME FROM BO_EU_DND_GENERAL WHERE GENERAL_TABLE_ID='121' AND ORGID=?", new Object[]{uc.getCompanyModel().getId()}
-        );
+        //ITEM_DATE   i) 弹出窗口输入账单起止日期,保存时需要校验所选条目是否在账单起止日期内
+        //            ii) 批量选择条目:计费客户相同、状态必须为----未生成生成账单
+
+        String BILL_ACCOUNT_ID = null;
+        for (RowMap row : postedDataList) {
+            String ITEM_DATE = row.getString("ITEM_DATE");
+            if (StringUtils.isNotBlank(ITEM_DATE)) {
+                LocalDate itemDate = TypeUtil.convert(ITEM_DATE, LocalDate.class);
+                //i) 弹出窗口输入账单起止日期,保存时需要校验所选条目是否在账单起止日期内
+                if (itemDate.isBefore(param.getDateRangeFrom()) || itemDate.isAfter(param.getDateRangeTo())) {
+                    return "所选条目有账单起止日期不符合要求";
+                }
+
+                //  ii) 批量选择条目:计费客户相同、状态必须为----未生成生成账单
+                if (StringUtils.isBlank(BILL_ACCOUNT_ID))
+                    BILL_ACCOUNT_ID = row.getString("BILL_ACCOUNT_ID");
+                else {
+                    if (!BILL_ACCOUNT_ID.equals(row.getString("BILL_ACCOUNT_ID"))) {
+                        return "所选条目有账单客户必须相同";
+                    }
+                }
+
+                //RECEIVE_STATUS  4661 未生成生成账单
+                String RECEIVE_STATUS = row.getString("RECEIVE_STATUS");
+                if (RECEIVE_STATUS.equals("4661") == false)
+                    return "所选条目有账单状态必须为未生成生成账单";
+            }
+
+        }
+
+
+        List<RowMap> accountDedList = DBSql.getMaps("SELECT * FROM BO_EU_DNCRM_ACCOUNT_DEDUCTION WHERE ID IN " + sqlWhere + " ORDER BY ACCOUNT_ID,BILL_ACCOUNT_ID", dedIdArr);
+        List<RowMap> itemTypeDict = DBSql.getMaps("SELECT OID,NAME FROM BO_EU_DND_GENERAL WHERE GENERAL_TABLE_ID='121' AND ORGID=?", new Object[]{uc.getCompanyModel().getId()});
 
         // 3. 按「账单客户ID」分组抵扣数据(核心分组逻辑)
-        Map<String, List<RowMap>> customerDedMap = accountDedList.stream()
-                .filter(ded -> ded.get("BILL_ACCOUNT_ID") != null)
-                .collect(Collectors.groupingBy(ded -> ded.get("BILL_ACCOUNT_ID").toString()));
-        if (customerDedMap.isEmpty())
-            return null;
+        Map<String, List<RowMap>> customerDedMap = accountDedList.stream().filter(ded -> ded.get("BILL_ACCOUNT_ID") != null).collect(Collectors.groupingBy(ded -> ded.get("BILL_ACCOUNT_ID").toString()));
+        if (customerDedMap.isEmpty()) return null;
+
 
         // 4. 循环处理每个客户的发票(拆分「有无采购订单」逻辑)
         List<String> invoiceIds = new ArrayList<>();
@@ -108,43 +134,29 @@ public class InvoiceService {
             }
 
             // 4.2 处理「无采购订单」的发票
-            List<RowMap> noPoDedList = customerDedList.stream()
-                    .filter(ded -> StringUtils.isBlank(ded.getString("PURCHASE_ORDER_NO")))
-                    .collect(Collectors.toList());
+            List<RowMap> noPoDedList = customerDedList.stream().filter(ded -> StringUtils.isBlank(ded.getString("PURCHASE_ORDER_NO"))).collect(Collectors.toList());
             if (!noPoDedList.isEmpty()) {
-                List<RowMap> noPoPostedList = postedDataList.stream()
-                        .filter(p -> StringUtils.isBlank(p.getString("PURCHASE_ORDER_NO")))
-                        .filter(p -> ACCOUNT_ID.equals(p.get("ACCOUNT_ID")) || ACCOUNT_ID.equals(p.get("BILL_ACCOUNT_ID")))
-                        .collect(Collectors.toList());
-                String noPoInvoiceId = createInvoiceAndDetails(
-                        uc, customer, noPoPostedList, noPoDedList, itemTypeDict, param, isDeal, ""
-                );
+                List<RowMap> noPoPostedList = postedDataList.stream().filter(p -> StringUtils.isBlank(p.getString("PURCHASE_ORDER_NO"))).filter(p -> ACCOUNT_ID.equals(p.get("ACCOUNT_ID")) || ACCOUNT_ID.equals(p.get("BILL_ACCOUNT_ID"))).collect(Collectors.toList());
+                String noPoInvoiceId = createInvoiceAndDetails(uc, customer, noPoPostedList, noPoDedList, itemTypeDict, param, isDeal, "");
                 if (StringUtils.isNotBlank(noPoInvoiceId)) {
                     invoiceIds.add(noPoInvoiceId);
                 }
             }
 
             // 4.3 处理「有采购订单」的发票(按PO分组)
-            Map<String, List<RowMap>> poDedMap = customerDedList.stream()
-                    .filter(ded -> StringUtils.isNotBlank(ded.getString("PURCHASE_ORDER_NO")))
-                    .collect(Collectors.groupingBy(ded -> ded.getString("PURCHASE_ORDER_NO")));
+            Map<String, List<RowMap>> poDedMap = customerDedList.stream().filter(ded -> StringUtils.isNotBlank(ded.getString("PURCHASE_ORDER_NO"))).collect(Collectors.groupingBy(ded -> ded.getString("PURCHASE_ORDER_NO")));
             for (Map.Entry<String, List<RowMap>> poEntry : poDedMap.entrySet()) {
                 String poNo = poEntry.getKey();
                 List<RowMap> poDedList = poEntry.getValue();
-                List<RowMap> poPostedList = postedDataList.stream()
-                        .filter(p -> poNo.equals(p.getString("PURCHASE_ORDER_NO")))
-                        .filter(p -> ACCOUNT_ID.equals(p.get("ACCOUNT_ID")) || ACCOUNT_ID.equals(p.get("BILL_ACCOUNT_ID")))
-                        .collect(Collectors.toList());
-                String poInvoiceId = createInvoiceAndDetails(
-                        uc, customer, poPostedList, poDedList, itemTypeDict, param, isDeal, poNo
-                );
+                List<RowMap> poPostedList = postedDataList.stream().filter(p -> poNo.equals(p.getString("PURCHASE_ORDER_NO"))).filter(p -> ACCOUNT_ID.equals(p.get("ACCOUNT_ID")) || ACCOUNT_ID.equals(p.get("BILL_ACCOUNT_ID"))).collect(Collectors.toList());
+                String poInvoiceId = createInvoiceAndDetails(uc, customer, poPostedList, poDedList, itemTypeDict, param, isDeal, poNo);
                 if (StringUtils.isNotBlank(poInvoiceId)) {
                     invoiceIds.add(poInvoiceId);
                 }
             }
         }
         logger.info("发票ID:" + invoiceIds.toString());
-        return invoiceIds;
+        return null;
     }
 
 
@@ -156,33 +168,21 @@ public class InvoiceService {
         summary.totalTax = postedList.stream().mapToDouble(p -> p.getDouble("TAX_DOLLARS")).sum();
         summary.totalPrice = summary.totalMoney - summary.totalTax;
         // 工时汇总(不计费、计费、预付)
-        summary.noBillHours = postedList.stream()
-                .filter(p -> StringUtils.isBlank(p.getString("BILLABLE")))
-                .mapToDouble(p -> p.getDouble("BILLABLE_HOURS")).sum();
-        summary.billHours = postedList.stream()
-                .filter(p -> StringUtils.isNotBlank(p.getString("BILLABLE")))
-                .mapToDouble(p -> p.getDouble("BILLABLE_HOURS")).sum();
-        summary.prepaidHours = postedList.stream()
-                .filter(p -> StringUtils.isNotBlank(p.getString("CONTRACT_BLOCK_ID")))
-                .mapToDouble(p -> p.getDouble("BILLABLE_HOURS")).sum();
+        summary.noBillHours = postedList.stream().filter(p -> StringUtils.isBlank(p.getString("BILLABLE"))).mapToDouble(p -> p.getDouble("BILLABLE_HOURS")).sum();
+        summary.billHours = postedList.stream().filter(p -> StringUtils.isNotBlank(p.getString("BILLABLE"))).mapToDouble(p -> p.getDouble("BILLABLE_HOURS")).sum();
+        summary.prepaidHours = postedList.stream().filter(p -> StringUtils.isNotBlank(p.getString("CONTRACT_BLOCK_ID"))).mapToDouble(p -> p.getDouble("BILLABLE_HOURS")).sum();
         return summary;
     }
 
     // ====================== 5. 核心复用方法4:创建发票头+明细+更新抵扣 ======================
-    private String createInvoiceAndDetails(UserContext uc, RowMap customer,
-                                           List<RowMap> postedList, List<RowMap> dedList, List<RowMap> itemTypeDict,
-                                           InvoiceDealDto param, boolean isDeal, String poNo) {
+    private String createInvoiceAndDetails(UserContext uc, RowMap customer, List<RowMap> postedList, List<RowMap> dedList, List<RowMap> itemTypeDict, InvoiceDealDto param, boolean isDeal, String poNo) {
         try {
             // 1. 计算汇总数据
             InvoiceSummary summary = calculateSummary(postedList);
 
             // 2. 生成发票编号+创建流程实例
             String invoiceNo = SDK.getRuleAPI().executeAtScript("B@left(@date,4)@mid(@date,6,2).@mid(@sequenceMonth(@companyId_invoice,4,0,1),7)", uc);
-            ProcessInstance pi = SDK.getProcessAPI().createProcessInstance(
-                    "obj_a7d46a9dd4df40b899ddcf02113fe0d2",
-                    uc.getUID(),
-                    "新增账单" + invoiceNo
-            );
+            ProcessInstance pi = SDK.getProcessAPI().createProcessInstance("obj_a7d46a9dd4df40b899ddcf02113fe0d2", uc.getUID(), "新增账单" + invoiceNo);
 
             // 3. 构建并保存发票头
             BO invoice = new BO();
@@ -203,8 +203,7 @@ public class InvoiceService {
             invoice.set("PURCHASE_ORDER_NO", poNo); // 有PO则赋值,无PO为空
             invoice.set("NOTES", param.getNotes());
             // 税区名称查询
-            String taxRegionName = StringUtils.isBlank(customer.getString("TAX_REGION_ID")) ? "" :
-                    DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ?", new Object[]{customer.getString("TAX_REGION_ID")});
+            String taxRegionName = StringUtils.isBlank(customer.getString("TAX_REGION_ID")) ? "" : DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ?", new Object[]{customer.getString("TAX_REGION_ID")});
             invoice.set("TAX_REGION_NAME", taxRegionName);
             invoice.setBindId(pi.getId());
 
@@ -215,8 +214,7 @@ public class InvoiceService {
             dn.recordFormChanges.record(uc, invoice, "保存发票:" + invoiceNo);
 
             // 4. 构建并保存发票明细(批量处理)
-            Map<String, RowMap> dedIdMap = dedList.stream()
-                    .collect(Collectors.toMap(ded -> ded.getString("ID"), ded -> ded));
+            Map<String, RowMap> dedIdMap = dedList.stream().collect(Collectors.toMap(ded -> ded.getString("ID"), ded -> ded));
             int lineNo = 1;
             for (RowMap posted : postedList) {
                 String dedId = posted.getString("ID");
@@ -231,12 +229,16 @@ public class InvoiceService {
                 detail.set("RESOURCE_NAME", posted.get("RESOURCE_NAME"));
                 detail.set("QUANTITY", posted.get("QUANTITY"));
                 detail.set("RATE", posted.get("RATE"));
-                detail.set("DOLLARS", posted.get("DOLLARS"));
+                detail.set("DOLLARS", posted.get("DOLLARS"));//含税总价
+                detail.set("TAX_DOLLARS", posted.get("TAX_DOLLARS"));//税额
+
+                BigDecimal DOLLARS_NOTAX =(posted.get("DOLLARS")==null?BigDecimal.ZERO: TypeUtil.convert(posted.get("DOLLARS"), BigDecimal.class)).subtract((posted.get("TAX_DOLLARS")==null?BigDecimal.ZERO: TypeUtil.convert(posted.get("TAX_DOLLARS"), BigDecimal.class)));
+
+                detail.set("DOLLARS_NOTAX", DOLLARS_NOTAX);//不含税总价
+
                 detail.set("HOURLY_RATE", posted.get("HOURLY_RATE"));
                 detail.set("ROLE_NAME", posted.get("ROLE_NAME"));
                 detail.set("WORK_TYPE", posted.get("WORK_TYPE"));
-                detail.set("TAX_DOLLARS", posted.get("TAX_DOLLARS"));
-
                 detail.set("BILL_ACCOUNT_ID", ded.getString("BILL_ACCOUNT_ID"));//计费客户
                 detail.set("ACCOUNT_ID", ded.getString("ACCOUNT_ID"));//客户
                 detail.set("ACCOUNT_DEDUCTION_ID", dedId);
@@ -246,19 +248,14 @@ public class InvoiceService {
                 String billableHours = posted.getString("BILLABLE_HOURS");
                 if ("1199".equals(contractTypeId) || "1201".equals(contractTypeId)) {
                     billableHours = "合同已包";
-                } else if (("1202".equals(contractTypeId) || "1203".equals(contractTypeId))
-                        && StringUtils.isNotBlank(posted.getString("BILLABLE"))) {
+                } else if (("1202".equals(contractTypeId) || "1203".equals(contractTypeId)) && StringUtils.isNotBlank(posted.getString("BILLABLE"))) {
                     billableHours = "预支付";
                 }
                 detail.set("BILLABLE_HOURS", billableHours);
 
                 // 4.3 特殊处理:条目类型(OID转名称)
                 String itemTypeOid = posted.getString("ITEM_TYPE");
-                String itemTypeName = itemTypeDict.stream()
-                        .filter(type -> itemTypeOid.equals(type.getString("OID")))
-                        .findFirst()
-                        .map(type -> type.getString("NAME"))
-                        .orElse(null);
+                String itemTypeName = itemTypeDict.stream().filter(type -> itemTypeOid.equals(type.getString("OID"))).findFirst().map(type -> type.getString("NAME")).orElse(null);
                 if (itemTypeName == null)
                     itemTypeName = DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ? AND ORGID=?", new Object[]{itemTypeOid, uc.getCompanyModel().getId()});
 
@@ -363,13 +360,10 @@ public class InvoiceService {
                 invoice.set("IS_VOIDED", 1);//作废
                 invoice.set("VOIDED_TIME", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
                 invoice.set("VOIDED_RESOURCE_ID", uc.getUID());
-
                 dn.recordFormChanges.record(uc, invoice, "作废发票");
-
                 SDK.getBOAPI().update("BO_EU_DNCTT_INVOICE", invoice);
             }
 
-
             if (isDelete || dealStatus.equals("作废发票")) {
                 List<BO> deductions = SDK.getBOAPI().query("BO_EU_DNCRM_ACCOUNT_DEDUCTION").addQuery("INVOICE_ID =", invoiceId).list();
                 for (BO deduction : deductions) {

+ 1 - 1
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/service/contractService.java

@@ -2664,7 +2664,7 @@ public class contractService {
                         cad.set("BILL_ACCOUNT_ID", DBSql.getString("SELECT BILL_TO_ACCOUNT_ID FROM BO_EU_DNCTT_CONTRACT WHERE ID = ?", new Object[]{bo.getString("CONTRACT_ID")}));
 
                         cad.set("EXTENDED_PRICE", bo.get("PERIOD_ADJUSTED_PRICE"));
-
+                        cad.set("EXTENDED_COST", bo.get("PERIOD_COST"));
  /*
                         String costCodeId = null;
                         if (bo.getString("OBJECT_TYPE").equals("1")) {