Ver Fonte

Merge branch 'master' of http://210.51.45.41:3000/itcat_admin/aws_donenow

HULEI há 1 mês atrás
pai
commit
ce35ee15e5

+ 41 - 0
com.awspaas.user.apps.donenow_crm/template/page/CONF.html

@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <title><#formTitle></title>
+    <link rel="stylesheet" href="../commons/css/font/iconfont.css"/>
+    <link rel="stylesheet" href="../commons/css/awsui.css"/>
+    <style>
+        .awsui-col-lg-10 {
+            width:80%;
+        }
+        .awsui-col-lg-2 {
+            width:15%;
+        }
+    </style>
+</head>
+<body>
+<div class="awsui-code-content" id="confFormDiv" style="padding-top: 20px;">
+    <div class="awsui-container-fluid" id="confForm">
+
+
+
+    </div>
+</div>
+
+
+<input id="sid" type="hidden" name="sid" value="<#sid>">
+</body>
+<script language="JavaScript" src="../commons/js/jquery/scripts/jquery.js"></script>
+<script language="JavaScript" src="../commons/js/awsui.js"></script>
+<script src="../apps/com.awspaas.user.apps.donenow_crm/CONF.js"></script>
+<script>
+    const sid = "<#sid>";
+    const cateId = "<#cateId>";
+    const objectId = "<#objectId>";
+    const colFields = '<#colFields>';
+    $(function () {
+        init(colFields);
+    });
+</script>
+</html>

+ 106 - 0
com.awspaas.user.apps.donenow_crm/web/com.awspaas.user.apps.donenow_crm/CONF.js

@@ -0,0 +1,106 @@
+var colFieldIDs = [];
+function init(colFields) {
+	
+    const colFieldData = JSON.parse(colFields);
+    console.log(colFieldData);
+    for (var i = 0; i < colFieldData.length; i++) {
+        let colField = colFieldData[i];
+        colFieldIDs.push(colField.name);
+        if (colField.type == 526) {//单行文本
+            let htmlRow = '<div class="awsui-row" ' + (colField.readonly == 1 ? 'style="pointer-events: none;"' : '') + '><div class="awsui-col-xs-2 awsui-col-sm-2 awsui-col-md-2 awsui-col-lg-2 awsui-text">' + colField.comment + '</div><div class="awsui-col-xs-10 awsui-col-sm-10 awsui-col-md-10 awsui-col-lg-10 awsui-form-parent"><span class=" ' + (colField.required == 1 ? 'required' : '') + '" style="width:100%;"><input type="text" class="awsui-input" id="' + colField.name + '" name="' + colField.name + '"></span></div></div>';
+            $("#confForm").append(htmlRow);
+        } else if (colField.type == 527) {//多行文本
+            let htmlRow = '<div class="awsui-row" ' + (colField.readonly == 1 ? 'style="pointer-events: none;"' : '') + '><div class="awsui-col-xs-2 awsui-col-sm-2 awsui-col-md-2 awsui-col-lg-2 awsui-textar">' + colField.comment + '</div><div class="awsui-col-xs-10 awsui-col-sm-10 awsui-col-md-10 awsui-col-lg-10 awsui-form-parent"><span class=" ' + (colField.required == 1 ? 'required' : '') + '"  style="width:100%;"><textarea type="text"  rows="4" class="awsui-input" id="' + colField.name + '" name="' + colField.name + '">' + colField.value + '</textarea></span></div></div>';
+            $("#confForm").append(htmlRow);
+        } else if (colField.type == 528) {//日期
+            let htmlRow = '<div class="awsui-row" ' + (colField.readonly == 1 ? 'style="pointer-events: none;"' : '') + '><div class="awsui-col-xs-2 awsui-col-sm-2 awsui-col-md-2 awsui-col-lg-2 awsui-text">' + colField.comment + '</div><div class="awsui-col-xs-10 awsui-col-sm-10 awsui-col-md-10 awsui-col-lg-10 awsui-form-parent"><span class=" ' + (colField.required == 1 ? 'required' : '') + '"  style="width:100%;"><input type="text" class="awsui-datepicker awsui-input" id="' + colField.name + '" name="' + colField.name + '"/></span></div></div>';
+            $("#confForm").append(htmlRow);
+        } else if (colField.type == 529) {//数字
+            let htmlRow = '<div class="awsui-row" ' + (colField.readonly == 1 ? 'style="pointer-events: none;"' : '') + '><div class="awsui-col-xs-2 awsui-col-sm-2 awsui-col-md-2 awsui-col-lg-2 awsui-text">' + colField.comment + '</div><div class="awsui-col-xs-10 awsui-col-sm-10 awsui-col-md-10 awsui-col-lg-10 awsui-form-parent"><span class=" ' + (colField.required == 1 ? 'required' : '') + '"  style="width:100%;"><input type="number" class="awsui-input" id="' + colField.name + '" name="' + colField.name + '"></span></div></div>';
+            $("#confForm").append(htmlRow);
+        } else if (colField.type == 530) {//列表
+            let options = '';
+            for (var j = 0; j < colField.list.length; j++) {
+                let option = colField.list[j];
+                options += '<option value="' + option.VALUE + '">' + option.LABEL + '</option>';
+            }
+            console.log("options",options);
+            let htmlRow = '<div class="awsui-row" ' + (colField.readonly == 1 ? 'style="pointer-events: none;"' : '') + '><div class="awsui-col-xs-2 awsui-col-sm-2 awsui-col-md-2 awsui-col-lg-2 awsui-text">' + colField.comment + '</div><div class="awsui-col-xs-10 awsui-col-sm-10 awsui-col-md-10 awsui-col-lg-10 awsui-form-parent"><span class=" ' + (colField.required == 1 ? 'required' : '') + '"  style="width:100%;"><select id="' + colField.name + '" name="' + colField.name + '">' + options + '</select></span></div></div>';
+            $("#confForm").append(htmlRow);
+        } else if (colField.type == 532) {//布尔
+            let htmlRow = '<div class="awsui-row" ' + (colField.readonly == 1 ? 'style="pointer-events: none;"' : '') + '><div class="awsui-col-xs-2 awsui-col-sm-2 awsui-col-md-2 awsui-col-lg-2 awsui-text">' + colField.comment + '</div><div class="awsui-col-xs-10 awsui-col-sm-10 awsui-col-md-10 awsui-col-lg-10 awsui-form-parent"><span class=" ' + (colField.required == 1 ? 'required' : '') + '"  style="width:100%;"><input type="checkbox" id="' + colField.name + '" name="' + colField.name + '" ' + (colField.value == 1 ? 'checked' : '') + '></span></div></div>';
+            $("#confForm").append(htmlRow);
+        }
+
+
+        $("#" + colField.name).val(colField.value);
+        if (colField.type == 528) {
+            $("#" + colField.name).datepicker({
+                format: "yyyy-mm-dd",
+                autoclose: true
+            });
+        } else if (colField.type == 530) {
+            $("#" + colField.name).select2();
+            $(".select2-container").css("width", "100%");
+
+            $("#" + colField.name).val([colField.value]).trigger("change"); //单选赋值
+
+        } else if (colField.type == 532 && colField.value == 1) {
+            $("#" + colField.name).switchButton();
+        }
+    }
+
+   // $("#confFormDiv").css("height", colFieldIDs.length*50+"px");
+
+    $(".required").find(".awsui-input").focus(function () {
+        $.each($(".required").find(".awsui-input"), function (i, el) {
+            $(el).css("background-color", "#fff");
+        });
+        $(this).css("background-color", "#FFFFCC");
+    });
+    $(".required").find(".awsui-input").blur(function () {
+        var inputVal = $(this).val();
+        if (inputVal == "") {
+            $(this).tooltip({
+                positon: "top",
+                text: "不允许为空,请重新输入",
+                bgcolor: "#f9f9f9",
+                color: "red",
+                borderColor: "red",
+                close: false
+            });
+            return false;
+        }
+    });
+}
+
+function saveConf(objectId) {
+	
+    //必填校验
+    $(".required").find(".awsui-input").each(function () {
+        if ($(this).val() == "") {
+        
+        }
+    });
+
+    let confForm = {};
+    for (var i = 0; i < colFieldIDs.length; i++) {
+		
+        let id = colFieldIDs[i];
+		let aaa=$("#" + id).val();
+		debugger;
+        confForm[id] = aaa;
+    }
+    awsui.ajax.request({
+        type: "POST",
+        url: "./jd?cmd=com.awspaas.user.apps.donenow_crm_save&sid=" + sid,
+        data: "cateId="+cateId+"&objectId="+objectId+"&data="+JSON.stringify(confForm),
+        success: function (res) {
+            console.log(res);
+
+        }
+    });
+}
+
+
+

+ 184 - 0
com.awspaas.user.apps.donenow_crm/web/com.awspaas.user.apps.donenow_crm/template/formstyle/CONF.css

@@ -0,0 +1,184 @@
+/*以下固定模板,不能修改*/
+/*DoneNow主题*/
+/**/
+/*admin*/
+.aws-form-container .form-container .form-row .form-row-col {}
+.aws-form.awsui-form-table-line .form-container .el-form-item .aws-form-item__label {
+    white-space: normal;
+    text-overflow: ellipsis;
+    overflow: hidden
+}
+.form-content-box .form-row-col .form-words-title {
+    white-space: normal
+}
+.aws-form.awsui-form-table-line .form-container .form-row-col .el-form-item__label {
+    width: 250px !important
+}
+.form-content-box .form-row .el-form-item .el-input .el-input__wrapper {
+    box-shadow: none;
+    border-width: 1px;
+    border-style: solid
+}
+.form-content-box .form-row .form-row-col:not(.readonly) .el-form-item__content .component-effect:not(.aws-select,.awsui-address) {
+    border-style: solid;
+    border-width: 1px
+}
+.form-content-box .form-row .el-textarea__inner {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6 !important;
+    box-shadow: none
+}
+.form-content-box .form-row .el-date-editor.el-input__wrapper {
+    border-style: solid;
+    border-width: 1px
+}
+.form-content-box .form-row .field-contianer .span-style {
+    box-shadow: none;
+    border-width: 1px;
+    border-style: solid;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .card-content {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .related-query-table-vxe .vxe-table--render-default .vxe-table--border-line {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.awsui-form-table-line .form-content-box .form-row .form-textarea .el-textarea {
+    border-width: 0px
+}
+.form-content-box .form-row .el-input-number {
+    box-shadow: 0 0 0 0px var(--el-input-focus-border-color) inset
+}
+.form-content-box .form-row .el-textarea__inner:focus {
+    box-shadow: 0 0 0 0px var(--el-input-focus-border-color) inset
+}
+.form-content-box .form-row .el-textarea__inner:hover {
+    box-shadow: 0 0 0 0px var(--el-input-focus-border-color) inset;
+    --el-input-focus-border-color: #dcdfe6}
+.aws-form.awsui-form-table-line .form-container .form-row-col .el-textarea__inner {
+    border-style: solid;
+    border-width: 1px
+}
+.form-content-box .form-row .form-row-col:not(.readonly) .el-form-item__content .component-effect.form-combobox-design {
+    border: none
+}
+.aws-form:not(.awsui-form-table-line) .form-container .form-row .form-textarea .el-textarea {
+    border: none
+}
+.form-container-designer .form-container .form-row .form-textarea .el-textarea {
+    border: none
+}
+.form-content-box .form-row .el-form-item__content > .form-grid-div > .multiColumn-body .multiColumn-row:not(.field-style) {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .related-table .vxe-table--border-line {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .el-form-item .el-upload-dragger {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .custom-btn-wrapper .custom-btn-common {
+    border-style: solid;
+    border-width: 1px
+}
+.form-content-box .form-row .xcode .no-data {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .data-history .isDesign {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .data-history .nonDesign {
+    border-style: solid;
+    border-width: 1px;
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .form-row-col:not(.readonly) .awsui-address {
+    box-shadow: none;
+    border-width: 1px;
+    border-style: solid;
+    border-color: #dcdfe6 !important
+}
+.form-content-box .form-row .el-form-item .awsui-select .el-select__wrapper {
+    box-shadow: none;
+    border-width: 1px;
+    border-style: solid;
+    border-color: #dcdfe6 !important
+}
+.form-content-box .form-row .form-row-col:not(.readonly) .component-effect:not(.form-grid-div,.awsui-input-number,.awsui-address) {
+    border-color: #dcdfe6
+}
+.form-content-box .form-row .el-input.is-disabled .el-input__wrapper {
+    border-color: #dcdfe6 !important;
+    box-shadow: none;
+    border-width: 1px
+}
+.form-content-box .form-row .el-input__wrapper:hover {
+    border-color: #dcdfe6 !important;
+    box-shadow: none;
+    border-width: 1px
+}
+.form-content-box .form-row .el-input {
+    --el-input-focus-border-color: #dcdfe6;
+    --el-input-border-color: #dcdfe6}
+.form-content-box .form-row .el-select {
+    --el-select-input-focus-border-color: none;
+    --el-select-border-color-hover: #dcdfe6}
+.form-content-box .form-row .el-textarea {
+    --el-input-border-color: #dcdfe6}
+.form-content-box .form-row .el-date-editor--daterange {
+    border-color: #dcdfe6 !important;
+    box-shadow: none;
+    border-width: 1px
+}
+.form-content-box .form-row .el-input .el-input__wrapper {
+    border-color: #dcdfe6 !important;
+    box-shadow: none;
+    border-width: 1px
+}
+.form-container .form-row-col .el-textarea {
+    border-color: #dcdfe6 !important;
+    box-shadow: none;
+    border-width: 1px
+}
+.form-content-box .form-row .awsui-input-number {
+    border-color: #dcdfe6 !important;
+    box-shadow: none;
+    border-width: 1px
+}
+.form-content-box .form-row .related-query-table-vxe .vxe-table--render-default.border--full .vxe-header--column {
+    border-bottom-color: #dcdfe6
+}
+.form-content-box .form-row .related-query-table-vxe .vxe-table .vxe-table--header-wrapper .vxe-table--header-border-line {
+    --el-input-border-color: #dcdfe6}
+.form-content-box .form-row .aws-form-context-button .custom-btn-wrapper .custom-btn-common {
+    border-color: #dcdfe6
+}
+.form-content-box .el-form-item__content {
+    margin-top: 0px
+}
+.aws-form.awsui-form-table-line .form-container .form-row .form-row-col>.el-form-item>.el-form-item__content {
+    margin-top: 0px
+}
+.form-row .form-row-col .el-form-item__label {}
+.form-row .form-row-col .form-signature-img .awsui-button--text {}
+.form-content-box .form-row-col .doc-header .title {}
+.form-content-box .form-row-col .apply-user .userName {}
+.form-content-box .form-row-col .form-words {
+    font-size: 0px !important
+}

+ 116 - 0
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/QrCodeReaderUtil.java

@@ -0,0 +1,116 @@
+package com.awspaas.user.apps.donenow_ctt;
+
+import com.google.zxing.*;
+import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
+import com.google.zxing.common.HybridBinarizer;
+import org.apache.pdfbox.pdmodel.PDDocument;
+import org.apache.pdfbox.rendering.PDFRenderer;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+public class QrCodeReaderUtil {
+
+    /**
+     * 从图片或PDF文件中识别二维码。
+     * 如果是PDF,会逐页扫描,直到找到第一个二维码。
+     *
+     * @param file 图片文件(如.jpg, .png)或PDF文件(.pdf)
+     * @return 识别到的二维码内容。如果未找到或发生错误,返回相应的错误信息字符串。
+     */
+    public static String readQrCode(File file) {
+        if (!file.exists()) {
+            return "错误:文件不存在。";
+        }
+
+        String fileName = file.getName().toLowerCase();
+
+        try {
+            // 1. 判断文件类型并处理
+            if (fileName.endsWith(".pdf")) {
+                return readQrFromPdf(file);
+            } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg") || fileName.endsWith(".png") || fileName.endsWith(".gif")) {
+                return readQrFromImage(file);
+            } else {
+                return "错误:不支持的文件格式。请提供图片(.jpg, .png等)或PDF(.pdf)文件。";
+            }
+
+        } catch (Exception e) {
+            // 捕获所有其他未预料的异常
+            return "处理文件时发生未知错误: " + e.getMessage();
+        }
+    }
+
+    /**
+     * 从图片文件中识别二维码
+     */
+    private static String readQrFromImage(File imageFile) throws IOException {
+        BufferedImage image = ImageIO.read(imageFile);
+        if (image == null) {
+            return "无法加载图片或图片格式不支持。";
+        }
+        return decodeImage(image);
+    }
+
+    /**
+     * 从PDF文件中识别二维码
+     */
+    private static String readQrFromPdf(File pdfFile) throws IOException {
+        try (PDDocument document = PDDocument.load(pdfFile)) { // try-with-resources 确保文档被关闭
+            if (document.isEncrypted()) {
+                return "错误:无法处理加密的PDF文件。";
+            }
+
+            PDFRenderer renderer = new PDFRenderer(document);
+            int numPages = document.getNumberOfPages();
+
+            System.out.println("开始扫描PDF文件,共 " + numPages + " 页...");
+
+            for (int i = 0; i < numPages; i++) {
+                // 将第 i 页渲染成 BufferedImage
+                // 300 是 DPI (dots per inch),值越高图片越清晰,但识别速度越慢。300是一个不错的平衡。
+                BufferedImage image = renderer.renderImageWithDPI(i, 300);
+                String result = decodeImage(image);
+
+                // 如果在当前页识别到了二维码,直接返回结果
+                if (!result.startsWith("在图片中未找到二维码") && !result.startsWith("识别二维码时发生未知错误")) {
+                    System.out.println("在第 " + (i + 1) + " 页成功识别到二维码。");
+                    return result;
+                }
+            }
+            // 如果所有页都扫描完仍未找到
+            return "在PDF文件的所有页面中均未找到二维码。";
+        }
+    }
+
+    /**
+     * 核心解码逻辑,接收一个 BufferedImage 并尝试识别其中的二维码
+     */
+    private static String decodeImage(BufferedImage image) {
+        try {
+            LuminanceSource source = new BufferedImageLuminanceSource(image);
+            BinaryBitmap bitmap = new BinaryBitmap(new HybridBinarizer(source));
+
+            Map<DecodeHintType, Object> hints = new HashMap<>();
+            hints.put(DecodeHintType.POSSIBLE_FORMATS, EnumSet.of(BarcodeFormat.QR_CODE));
+            hints.put(DecodeHintType.CHARACTER_SET, "UTF-8");
+            hints.put(DecodeHintType.TRY_HARDER, Boolean.TRUE);
+
+            MultiFormatReader reader = new MultiFormatReader();
+            Result result = reader.decode(bitmap, hints);
+
+            return "内容: " + result.getText() + "; 格式: " + result.getBarcodeFormat();
+
+        } catch (NotFoundException e) {
+            // 这个异常是预期的,当图片中没有二维码时抛出
+            return "在图片中未找到二维码。";
+        } catch (Exception e) {
+            return "识别二维码时发生未知错误: " + e.getMessage();
+        }
+    }
+}

+ 20 - 2
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/controller/contractApproveController.java

@@ -164,7 +164,8 @@ public class contractApproveController {
     @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_recurring_services")
     public ResponseObject revokeRecurringServices(UserContext uc, String ids) throws SQLException {
         String result = contractService.getInstance().revokeRecurringServices(uc, ids);
-        if (StringUtils.isNotBlank(result)) return ResponseObject.newErrResponse(result);
+        if (StringUtils.isNotBlank(result))
+            return ResponseObject.newErrResponse(result);
 
         contractLogger.info("撤销定期服务成功");
         return ResponseObject.newOkResponse();
@@ -189,7 +190,8 @@ public class contractApproveController {
     @Mapping("com.awspaas.user.apps.donenow_ctt.revoke_cost")
     public ResponseObject revokeCost(UserContext uc, String ids) throws SQLException {
         String result = contractService.getInstance().revokeCost(uc, ids);
-        if (StringUtils.isNotBlank(result)) return ResponseObject.newErrResponse(result);
+        if (StringUtils.isNotBlank(result))
+            return ResponseObject.newErrResponse(result);
 
         contractLogger.info("撤销合同成本成功");
         return ResponseObject.newOkResponse();
@@ -944,6 +946,14 @@ public class contractApproveController {
             connUpdate.setAutoCommit(false);
             for (String id : ids.split(",")) {
                 BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);
+
+                //判断是否已经生成 发票
+                if (StringUtils.isNotBlank(bo.getString("INVOICE_ID"))) {
+                    if (!DBSql.getString("select IS_VOIDED from BO_EU_DNCTT_INVOICE where ID=?", new Object[]{bo.getString("INVOICE_ID")}).equals("1"))
+                        return ResponseObject.newErrResponse("该服务已经生成发票,请先删除发票");
+                       // throw new RuntimeException("该服务已经生成发票,请先删除发票");
+                }
+
                 bo.set("CLOSED", 1);
                 dn.recordFormChanges.record(uc, bo, "撤销工时审批");
 
@@ -987,6 +997,14 @@ public class contractApproveController {
             connUpdate.setAutoCommit(false);
             for (String id : ids.split(",")) {
                 BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);
+
+                //判断是否已经生成 发票
+                if (StringUtils.isNotBlank(bo.getString("INVOICE_ID"))) {
+                    if (!DBSql.getString("select IS_VOIDED from BO_EU_DNCTT_INVOICE where ID=?", new Object[]{bo.getString("INVOICE_ID")}).equals("1"))
+                        return ResponseObject.newErrResponse("该服务已经生成发票,请先删除发票");
+                    // throw new RuntimeException("该服务已经生成发票,请先删除发票");
+                }
+
                 bo.set("CLOSED", 1);
                 dn.recordFormChanges.record(uc, bo, "撤销费用审批");
 

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

@@ -405,9 +405,10 @@ public class contractCreateController {
 
             //如果已经做了服务调整,则不能重新生成服务周期, 跳过就行
 
-            if (DBSql.getInt("SELECT COUNT(1) CNT FROM BO_EU_DNCTT_CONTRACT_SERVICE_SALE_ADJUST WHERE CONTRACT_SERVICE_ID=? ", new Object[]{serviceId}) > 0) {
+            if (DBSql.getInt("SELECT COUNT(1) CNT FROM BO_EU_DNCTT_CONTRACT_SERVICE_SALE_ADJUST WHERE CONTRACT_SERVICE_ID=? ", new Object[]{serviceId}) > 0 && DBSql.getInt("SELECT COUNT(1) CNT FROM BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD WHERE CONTRACT_SERVICE_ID=? ", new Object[]{serviceId}) > 0) {
                 continue;
             }
+
             services.add(contraService);
         }
 

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

@@ -14,7 +14,6 @@ import com.awspaas.user.apps.donenow_ctt.service.contractServiceAdj;
 import org.apache.commons.lang3.StringUtils;
 
 import java.time.LocalDate;
-import java.util.List;
 
 import static com.awspaas.user.apps.donenow_ctt.service.contractService.LocalDateYYYYMMDD;
 import static com.awspaas.user.apps.donenow_ctt.service.contractService.getLocalDate;
@@ -147,23 +146,9 @@ public class contractServiceController {
 
 
     @Mapping(value = "com.awspaas.user.apps.donenow_ctt.service_purchase_adjustTEST")
-    public ResponseObject editServicePurchaseTest(UserContext uc) {
+    public ResponseObject editServicePurchaseTest(UserContext uc, String serviceId) {
 
-        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);
-
-        for (String serviceId : serviceIds) {
-            try {
-                //String bindid = contractServiceAdj.getInstance().editServicePurchase(uc, serviceId, "临时--分摊采购");
-                //String boId = DBSql.getString("SELECT ID FROM BO_EU_DNCTT_CONTRACT_SERVICE_PURCHASE_ADJUST where BINDID=?", new Object[]{bindid});
-                //contractServiceAdj.getInstance().execServicePurchaseAdj(uc, boId, null);
-
-                contractServiceAdj.getInstance().paySplitServiceMonth(uc, serviceId);
-
-            } catch (Exception ex) {
-
-            }
-
-        }
+        contractServiceAdj.getInstance().paySplitServiceMonth(uc, serviceId);
 
         return ResponseObject.newOkResponse();
     }

+ 37 - 3
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/controller/invoiceController.java

@@ -1,14 +1,20 @@
 package com.awspaas.user.apps.donenow_ctt.controller;
 
+import com.actionsoft.bpms.commons.formfile.model.delegate.FormFile;
 import com.actionsoft.bpms.commons.mvc.view.ResponseObject;
 import com.actionsoft.bpms.server.UserContext;
 import com.actionsoft.bpms.server.bind.annotation.Controller;
 import com.actionsoft.bpms.server.bind.annotation.Mapping;
+import com.actionsoft.bpms.server.fs.DCContext;
 import com.actionsoft.bpms.util.DBSql;
 import com.actionsoft.sdk.local.SDK;
+import com.awspaas.user.apps.donenow_ctt.QrCodeReaderUtil;
 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.nio.file.Path;
+import java.nio.file.Paths;
 import java.util.List;
 
 /**
@@ -26,8 +32,8 @@ 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();
     }
 
@@ -45,10 +51,14 @@ public class invoiceController {
 
         SDK.getLogAPI().consoleInfo("处理账单:" + accDeduIds + "账单开始时间:" + startDate + "账单结束时间:" + endDate);
 
+        if (StringUtils.isBlank(startDate) || StringUtils.isBlank(endDate))
+            return ResponseObject.newErrResponse("请选择账单时间");
+
         invoiceDealDto.setDateRangeFromStr(startDate);
         invoiceDealDto.setDateRangeToStr(endDate);
 
-        List<String> invoiceIds = InvoiceService.getInstance().processInvoice(uc, accDeduIds, invoiceDealDto, true);
+        String result = InvoiceService.getInstance().processInvoice(uc, accDeduIds, invoiceDealDto, true);
+        if (StringUtils.isNotBlank(result)) return ResponseObject.newErrResponse(result);
 
         return ResponseObject.newOkResponse();
     }
@@ -100,4 +110,28 @@ public class invoiceController {
         return ResponseObject.newOkResponse().put("taxRate", taxRate);
     }
 
+
+    /**
+     * 识别二维码
+     * @param uc
+     * @return
+     */
+    @Mapping("com.awspaas.user.apps.donenow_ctt.QRReader")
+    public ResponseObject QRReader(UserContext uc) {
+
+        String qrContent = "";
+
+        List<FormFile> jars = SDK.getBOAPI().getFiles("6da746b2-c84a-4502-ada8-f0b681d19ac3", "TU");
+        for (FormFile jar : jars) {
+            DCContext dcContext = SDK.getBOAPI().getFileDCContext(jar);
+
+            Path source = Paths.get(dcContext.getFilePath());
+
+            qrContent = qrContent + "\n" + QrCodeReaderUtil.readQrCode(source.toFile());
+
+        }
+
+        return ResponseObject.newOkResponse().put("qrContent", qrContent);
+    }
+
 }

+ 3 - 2
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;
 
@@ -151,7 +151,8 @@ public class InvoiceDealDto {
     }
 
     public LocalDate getInvoiceDate() {
-        if (invoiceDate == null) invoiceDate = LocalDate.now();
+        if (invoiceDate == null)
+            invoiceDate = LocalDate.now();
         return invoiceDate;
     }
 

+ 88 - 81
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/service/InvoiceService.java

@@ -5,10 +5,14 @@ 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 org.joda.time.format.DateTimeFormat;
 
+import java.math.BigDecimal;
 import java.time.LocalDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
@@ -40,14 +44,15 @@ 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,13 +62,13 @@ 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() || postedDataList.size() != dedIdArr.length)
+            return "所选条目有账单状态必须为未生成生成账单";
+
         // 统一Key为大写,避免字段名大小写不一致导致的NPE
         postedDataList.forEach(row -> row.forEach((key, value) -> {
             if (key.toUpperCase().equals("BILL_ACCOUNT_ID")) {
@@ -77,20 +82,46 @@ 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) 批量选择条目:计费客户相同、状态必须为----未生成生成账单
+
+        org.joda.time.format.DateTimeFormatter formatter = DateTimeFormat.forPattern("yyyy-MM-dd");
+
+        String BILL_ACCOUNT_ID = null;
+        for (RowMap row : postedDataList) {
+            String ITEM_DATE = row.getString("ITEM_DATE");
+            if (StringUtils.isNotBlank(ITEM_DATE)) {
+
+                LocalDate itemDate = LocalDate.parse(ITEM_DATE, formatter);
+
+                //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<>();
@@ -99,49 +130,43 @@ public class InvoiceService {
             List<RowMap> customerDedList = entry.getValue();
 
             // 4.1 客户信息校验:必须有客户顾问(否则跳过)
+
             RowMap customer = DBSql.getMap("SELECT * FROM BO_EU_DNCRM_ACCOUNT WHERE ID = ?", ACCOUNT_ID);
+
+            logger.info("处理客户ID:" + ACCOUNT_ID);
+            logger.info("RESOURCE_ID--" + customer.getString("RESOURCE_ID"));
+
             if (customer == null || StringUtils.isBlank(customer.getString("RESOURCE_ID"))) {
                 continue;
             }
 
             // 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());
+                logger.info("无PO账单数量:" + noPoPostedList.size());
+                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());
+                logger.info("处理采购订单:" + poNo);
+
+                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;
     }
 
 
@@ -153,67 +178,51 @@ 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();
             invoice.set("INVOICE_NO", invoiceNo);
             invoice.set("ACCOUNT_ID", customer.getString("ID"));
             invoice.set("ACCOUNT_NAME", customer.getString("NAME"));
             invoice.set("OWNER_RESOURCE_NAME", DBSql.getString("SELECT USERNAME FROM orguser WHERE USERID=?", new Object[]{customer.getString("RESOURCE_ID")}));
-            invoice.set("INVOICE_DATE", param.getInvoiceDate());
+
+            invoice.set("INVOICE_DATE", param.getInvoiceDate().toString());
+            invoice.set("DATE_RANGE_FROM", param.getDateRangeFrom().toString());
+            invoice.set("DATE_RANGE_TO", param.getDateRangeTo().toString());
+
             invoice.set("TOTAL", summary.totalMoney);
             invoice.set("TAX_VALUE", summary.totalTax);
             invoice.set("TOTAL_PRICE", summary.totalPrice);
             invoice.set("NOBILL_HOURS", summary.noBillHours);
             invoice.set("BILL_HOURS", summary.billHours);
             invoice.set("PREPAID_HOURS", summary.prepaidHours);
-            invoice.set("DATE_RANGE_FROM", param.getDateRangeFrom());
-            invoice.set("DATE_RANGE_TO", param.getDateRangeTo());
             invoice.set("PAYMENT_TERM_ID", param.getPaymentTermId());
             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")) || customer.getString("TAX_REGION_ID").equals("0") ? "" : 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());
 
             // 记录变更+保存发票BO
-
             SDK.getBOAPI().create("BO_EU_DNCTT_INVOICE", invoice, pi, uc);
-
             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");
@@ -228,12 +237,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);
@@ -243,19 +256,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()});
 
@@ -283,6 +291,8 @@ public class InvoiceService {
 
             return invoice.getId(); // 返回生成的发票ID
         } catch (Exception e) {
+            logger.info("错误---" + e.getMessage());
+            logger.error("错误--111-", e);
             e.printStackTrace();
             return null;
         }
@@ -360,13 +370,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) {

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

@@ -2651,7 +2651,7 @@ public class contractService {
                         CONTRACT_ID = bo.getString("CONTRACT_ID");
                         cad.set("CONTRACT_ID", CONTRACT_ID);
                         bo.set("APPROVE_AND_POST_DATE", approveDate);
-                        bo.set("SETUP_FEE_APPROVE_POST_USER_ID", uc.getUID());
+                        bo.set("APPROVE_AND_POST_USER_ID", uc.getUID());
                         SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", bo, connUpdate);
 
                         cad.set("TYPE_ID", cttConstant.ACCOUNT_DEDUCTION_TYPE_SERVICE);//类型(服务)
@@ -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")) {
@@ -2969,7 +2969,7 @@ public class contractService {
 
                 //判断是否已经生成 发票
                 if (StringUtils.isNotBlank(bo.getString("INVOICE_ID"))) {
-                    if (DBSql.getString("select IS_VOIDED from BO_EU_DNCTT_INVOICE where ID=?", new Object[]{bo.getString("INVOICE_ID")}).equals("1"))
+                    if (!DBSql.getString("select IS_VOIDED from BO_EU_DNCTT_INVOICE where ID=?", new Object[]{bo.getString("INVOICE_ID")}).equals("1"))
                         throw new RuntimeException("该服务已经生成发票,请先删除发票");
                 }
 
@@ -3030,6 +3030,13 @@ public class contractService {
 
             for (String id : ids.split(",")) {
                 BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);
+
+                //判断是否已经生成 发票
+                if (StringUtils.isNotBlank(bo.getString("INVOICE_ID"))) {
+                    if (!DBSql.getString("select IS_VOIDED from BO_EU_DNCTT_INVOICE where ID=?", new Object[]{bo.getString("INVOICE_ID")}).equals("1"))
+                        throw new RuntimeException("该服务已经生成发票,请先删除发票");
+                }
+
                 if (bo.get("TYPE_ID").equals(cttConstant.ACCOUNT_DEDUCTION_TYPE_MILESTONES)) //类型:里程碑
                 {
                     BO ccm = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT_MILESTONES").addQuery("CONTRACT_ID =", bo.getString("CONTRACT_ID")).addQuery("ID =", bo.getString("OBJECT_ID")).detail();
@@ -3071,6 +3078,13 @@ public class contractService {
             connUpdate.setAutoCommit(false);
             for (String id : ids.split(",")) {
                 BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);
+
+                //判断是否已经生成 发票
+                if (StringUtils.isNotBlank(bo.getString("INVOICE_ID"))) {
+                    if (!DBSql.getString("select IS_VOIDED from BO_EU_DNCTT_INVOICE where ID=?", new Object[]{bo.getString("INVOICE_ID")}).equals("1"))
+                        throw new RuntimeException("该服务已经生成发票,请先删除发票");
+                }
+
                 bo.set("CLOSED", 1);
                 dn.recordFormChanges.record(uc, bo, "撤销成本审批");
 

+ 126 - 54
com.awspaas.user.apps.donenow_ctt/src/com/awspaas/user/apps/donenow_ctt/service/contractServiceAdj.java

@@ -12,6 +12,7 @@ import com.awspaas.user.apps.donenow_ctt.PeriodCalculationUtil;
 import org.apache.commons.lang3.StringUtils;
 
 import java.math.BigDecimal;
+import java.math.RoundingMode;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.time.LocalDate;
@@ -122,6 +123,26 @@ public class contractServiceAdj {
 
         String PERIOD_TYPE = contractService.getString("PERIOD_TYPE");
 
+        //一次性收费
+        if (PERIOD_TYPE.equals("609")) {
+
+            BO periodBO = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD").addQuery("CONTRACT_SERVICE_ID =", orgContractService.getId()).detail();
+
+            periodBO.set("PERIOD_PRICE", contractService.get("UNIT_PRICE"));
+            periodBO.set("PARTYA_PERIOD_PRICE", contractService.get("TOTAL_PRICE"));
+            periodBO.set("PERIOD_ADJUSTED_PRICE", contractService.get("QUANTITY"));
+            SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", periodBO);
+
+            orgContractService.set("UNIT_PRICE", contractService.get("UNIT_PRICE"));
+            orgContractService.set("TOTAL_PRICE", contractService.get("TOTAL_PRICE"));
+            orgContractService.set("QUANTITY", contractService.get("QUANTITY"));
+            SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE", orgContractService);
+
+            DBSql.update("UPDATE bo_eu_dnctt_contract_service_period a SET a.PERIOD_COST =( SELECT SUM(b.PLAN_AMOUNT) FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN b WHERE a.CONTRACT_SERVICE_ID=? AND b.CONTRACT_SERVICE_ID = a.CONTRACT_SERVICE_ID) WHERE a.CONTRACT_SERVICE_ID=? AND EXISTS ( SELECT 1 FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN b JOIN bo_eu_dnctt_contract_service c ON b.CONTRACT_SERVICE_ID = c.ID WHERE b.CONTRACT_SERVICE_ID = a.CONTRACT_SERVICE_ID AND c.PERIOD_TYPE = 609 AND c.ID=?)", new Object[]{orgContractService.getId(), orgContractService.getId(), orgContractService.getId()});
+            return true;
+        }
+
+
         Connection conn = null;
         try {
             conn = DBSql.open();
@@ -269,7 +290,7 @@ public class contractServiceAdj {
 
             //计算各种不含税金额
             // 确定周期对应的月数
-            int periodMonths;
+            int periodMonths = 1;
             switch (contractService.getString("PERIOD_TYPE")) {
                 case "610":
                     periodMonths = 1; // 按月
@@ -290,14 +311,19 @@ public class contractServiceAdj {
 
 
             //月单价  UNIT_PRICE_MONTH
-            orgContractService.set("SERVICE_UNIT_PRICE", SERVICE_UNIT_PRICE.divide(BigDecimal.valueOf(periodMonths)));
+            // orgContractService.set("SERVICE_UNIT_PRICE", SERVICE_UNIT_PRICE.divide(BigDecimal.valueOf(periodMonths)));
+
+            BigDecimal result = SERVICE_UNIT_PRICE.divide(BigDecimal.valueOf(periodMonths), 4, // 精度:保留2位小数
+                    RoundingMode.HALF_UP // 舍入模式:四舍五入
+            );
+            orgContractService.set("SERVICE_UNIT_PRICE", result);
 
             RowMap taxCate = DBSql.getMap("SELECT OID,NAME,ORGID,GENERAL_TABLE_ID,EXT1,CLOSED,IS_ACTIVE FROM BO_EU_DND_GENERAL WHERE  GENERAL_TABLE_ID=44 AND ORGID=? AND OID=?", new Object[]{uc.getCompanyModel().getId(), orgContractService.getString("TAX_CATEGORY_ID")}); //税种
 
             if (taxCate != null) {
                 BigDecimal taxRate = new BigDecimal(taxCate.getString("EXT1"));
                 //月不含税单价 UNIT_PRICE_MONTH_NOTAX
-                BigDecimal taxExcluded = SERVICE_UNIT_PRICE.divide(BigDecimal.valueOf(periodMonths)).divide(BigDecimal.ONE.add(taxRate), 4, BigDecimal.ROUND_HALF_UP); // 保留4位小数
+                BigDecimal taxExcluded = SERVICE_UNIT_PRICE.divide(BigDecimal.valueOf(periodMonths), 4, BigDecimal.ROUND_HALF_UP).divide(BigDecimal.ONE.add(taxRate), 4, BigDecimal.ROUND_HALF_UP); // 保留4位小数
                 orgContractService.set("SERVICE_UNIT_PRICE_NOTAX", taxExcluded);
 
                 //周期不含税单价 UNIT_PRICE_NOTAX
@@ -319,7 +345,7 @@ public class contractServiceAdj {
             SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE", orgContractService, conn);
 
             conn.commit();
-            return true;
+
         } catch (SQLException e) {
 
             e.printStackTrace();
@@ -328,11 +354,14 @@ public class contractServiceAdj {
             } catch (SQLException e1) {
                 e1.printStackTrace();
             }
-            return false;
+
         } finally {
-            if (conn != null)
-                DBSql.close(conn);
+            if (conn != null) DBSql.close(conn);
         }
+
+        //分摊采购成本
+        paySplitServiceMonth(uc, orgContractService.getId());
+        return true;
     }
 
     /**
@@ -344,34 +373,48 @@ public class contractServiceAdj {
      */
     public String editServiceSaleEndDate(UserContext uc, String serviceId, LocalDate newEndDate) {
 
+        //一次性收费,无需修改
+        if (DBSql.getString("select PERIOD_TYPE from BO_EU_DNCTT_CONTRACT_SERVICE where CONTRACT_SERVICE_ID=?", new Object[]{serviceId}).equals("609")) {
+            return "无需修改";
+        }
+
         RowMap lastServicePeriod = DBSql.getMap("SELECT * FROM BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD WHERE CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE DESC", new Object[]{serviceId});//最后一笔服务周期
 
+
         LocalDate PERIOD_BEGIN_DATE = TypeUtil.convert(lastServicePeriod.get("PERIOD_BEGIN_DATE"), LocalDate.class);
         LocalDate PERIOD_END_DATE = TypeUtil.convert(lastServicePeriod.get("PERIOD_END_DATE"), LocalDate.class);
+        if (PERIOD_END_DATE.equals(newEndDate)) return "无需修改";
+
+        String EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_BEGIN_DATE) as PERIOD_BEGIN_DATE FROM BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD WHERE APPROVE_AND_POST_DATE IS NOT NULL and CONTRACT_SERVICE_ID=? AND  PERIOD_END_DATE >=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
+        if (StringUtils.isBlank(EFFECTIVE_DATE)) return "已审批不能修改了";
+
 
         String bindId = editServiceSale(uc, serviceId, "修改结束日期");
 
         BO adjBO = SDK.getBOAPI().getByProcess("BO_EU_DNCTT_CONTRACT_SERVICE_SALE_ADJUST", bindId);
 
-        if (PERIOD_END_DATE.equals(newEndDate))
-            return "无需修改";
-
         if (PERIOD_END_DATE.isAfter(newEndDate)) {
             //缩短
 
             if (PERIOD_BEGIN_DATE.isBefore(newEndDate)) {
-                if (lastServicePeriod.get("APPROVE_AND_POST_DATE") != null && StringUtils.isNotBlank(lastServicePeriod.getString("APPROVE_AND_POST_USER_ID")))
-                    return "已审批不能修改了";
 
                 adjBO.set("EFFECTIVE_DATE", PERIOD_BEGIN_DATE);
+
             } else {
 
-                String EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_BEGIN_DATE) as PERIOD_BEGIN_DATE FROM BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD WHERE APPROVE_AND_POST_DATE IS NULL and CONTRACT_SERVICE_ID=? AND  PERIOD_BEGIN_DATE <=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
+                EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_BEGIN_DATE) as PERIOD_BEGIN_DATE FROM BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD WHERE APPROVE_AND_POST_DATE IS NULL and CONTRACT_SERVICE_ID=? AND  PERIOD_BEGIN_DATE <=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
 
-                LocalDate EFFECTIVE_DATE1 = TypeUtil.convert(lastServicePeriod.get("EFFECTIVE_DATE"), LocalDate.class);
+                LocalDate EFFECTIVE_DATE1 = null;
+                if (StringUtils.isBlank(EFFECTIVE_DATE)) {
+                    EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_END_DATE) as PERIOD_END_DATE FROM BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD WHERE  CONTRACT_SERVICE_ID=? AND  PERIOD_BEGIN_DATE <=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
+
+                    EFFECTIVE_DATE1 = TypeUtil.convert(EFFECTIVE_DATE, LocalDate.class);
+                    EFFECTIVE_DATE1 = EFFECTIVE_DATE1.plusDays(1);
+
+                } else {
+                    EFFECTIVE_DATE1 = TypeUtil.convert(EFFECTIVE_DATE, LocalDate.class);
+                }
 
-                if (EFFECTIVE_DATE1.isAfter(newEndDate))
-                    return "已审批不能修改了";
 
                 adjBO.set("EFFECTIVE_DATE", EFFECTIVE_DATE1);
 
@@ -474,34 +517,47 @@ public class contractServiceAdj {
      * @return
      */
     public String editServicePurchaseEndDate(UserContext uc, String serviceId, LocalDate newEndDate) {
-
         RowMap lastServicePeriod = DBSql.getMap("SELECT * FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE DESC", new Object[]{serviceId});//最后一笔服务周期
 
+        //一次性收费,无需修改
+        if (DBSql.getString("select PURCHASE_PERIOD_TYPE from BO_EU_DNCTT_CONTRACT_SERVICE where CONTRACT_SERVICE_ID=?", new Object[]{serviceId}).equals("609")) {
+            return "无需修改";
+        }
+
         LocalDate PERIOD_BEGIN_DATE = TypeUtil.convert(lastServicePeriod.get("PERIOD_BEGIN_DATE"), LocalDate.class);
         LocalDate PERIOD_END_DATE = TypeUtil.convert(lastServicePeriod.get("PERIOD_END_DATE"), LocalDate.class);
 
-        String bindId = editServiceSale(uc, serviceId, "修改结束日期");
+        if (PERIOD_END_DATE.equals(newEndDate)) return "无需修改";
+
+        String EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_BEGIN_DATE) as PERIOD_BEGIN_DATE FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE  PAY_AMOUNT<>0 and PAY_AMOUNT is not NULL  and CONTRACT_SERVICE_ID=? AND  PERIOD_BEGIN_DATE >=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
+
+        if (StringUtils.isNotBlank(EFFECTIVE_DATE)) return "已审批不能修改了";
+
+
+        String bindId = editServicePurchase(uc, serviceId, "修改结束日期");
 
         BO adjBO = SDK.getBOAPI().getByProcess("BO_EU_DNCTT_CONTRACT_SERVICE_PURCHASE_ADJUST", bindId);
 
-        if (PERIOD_END_DATE.equals(newEndDate))
-            return "无需修改";
 
         if (PERIOD_END_DATE.isAfter(newEndDate)) {
             //缩短
             if (PERIOD_BEGIN_DATE.isBefore(newEndDate)) {
-                if (lastServicePeriod.get("PAY_AMOUNT") != null && lastServicePeriod.getDouble("PAY_AMOUNT") != 0)
-                    return "已审批不能修改了";
 
                 adjBO.set("PURCHASE_START_DATE", PERIOD_BEGIN_DATE);
             } else {
 
-                String EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_BEGIN_DATE) as PERIOD_BEGIN_DATE FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE  PAY_AMOUNT<>0 and PAY_AMOUNT is not NULL  and CONTRACT_SERVICE_ID=? AND  PERIOD_BEGIN_DATE <=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
+                EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_BEGIN_DATE) as PERIOD_BEGIN_DATE FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE PAY_AMOUNT<>0 and PAY_AMOUNT is not NULL and CONTRACT_SERVICE_ID=? AND  PERIOD_BEGIN_DATE <=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
 
-                LocalDate EFFECTIVE_DATE1 = TypeUtil.convert(lastServicePeriod.get("EFFECTIVE_DATE"), LocalDate.class);
+                LocalDate EFFECTIVE_DATE1 = null;
+                if (StringUtils.isBlank(EFFECTIVE_DATE)) {
+                    EFFECTIVE_DATE = DBSql.getString("SELECT max(PERIOD_END_DATE) as PERIOD_END_DATE FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN WHERE  CONTRACT_SERVICE_ID=? AND  PERIOD_BEGIN_DATE <=?", new Object[]{serviceId, LocalDateYYYYMMDD(newEndDate)});
 
-                if (EFFECTIVE_DATE1.isAfter(newEndDate))
-                    return "已审批不能修改了";
+                    EFFECTIVE_DATE1 = TypeUtil.convert(EFFECTIVE_DATE, LocalDate.class);
+                    EFFECTIVE_DATE1 = EFFECTIVE_DATE1.plusDays(1);
+
+                } else {
+                    EFFECTIVE_DATE1 = TypeUtil.convert(EFFECTIVE_DATE, LocalDate.class);
+                }
 
                 adjBO.set("PURCHASE_START_DATE", EFFECTIVE_DATE1);
 
@@ -530,9 +586,6 @@ public class contractServiceAdj {
         adjBO.set("PURCHASE_END_DATE", newEndDate);
         SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_PURCHASE_ADJUST", adjBO);
 
-        //execServicePurchaseAdj(uc, adjBO.getId());
-        //DBSql.update("update bo_eu_dnctt_contract_service set PURCHASE_END_DATE=? where ID=?", new Object[]{LocalDateYYYYMMDD(newEndDate), serviceId});
-
         if (execServicePurchaseAdj(uc, adjBO.getId(), null) == false) {
             DBSql.update("delete from BO_EU_DNCTT_CONTRACT_SERVICE_PURCHASE_ADJUST where ID=? AND IS_EFFECTIVE<>'已执行'", new Object[]{adjBO.getId()});
         } else {
@@ -559,6 +612,7 @@ public class contractServiceAdj {
             return false;
         }
         String PERIOD_TYPE = contractService.getString("PURCHASE_PERIOD_TYPE");
+        String PURCHASE_CALC_METHOD_1STPERIOD = contractService.getString("PURCHASE_CALC_METHOD_1STPERIOD");
 
         int periodMonths;
         switch (contractService.getString("PURCHASE_PERIOD_TYPE")) {
@@ -582,7 +636,7 @@ public class contractServiceAdj {
         if (StringUtils.isBlank(boName)) {
             boName = "BO_EU_DNIVT_ORDER_PAYMENT_PLAN";
         } else {
-            PERIOD_TYPE = "610";//按月
+            if (PERIOD_TYPE.equals("609") == false) PERIOD_TYPE = "610";//按月
         }
 
         LocalDate EFFECTIVE_DATE = getLocalDate(contractService.get("PURCHASE_START_DATE"));//服务生效日期
@@ -600,7 +654,11 @@ public class contractServiceAdj {
             conn.setAutoCommit(false);
 
             //生效日期之后的服务周期删除
-            DBSql.update(conn, "DELETE FROM " + boName + " WHERE  CONTRACT_SERVICE_ID=? and PERIOD_BEGIN_DATE >=? and (PAY_AMOUNT=0 or PAY_AMOUNT is NULL)", new Object[]{contractService.get("CONTRACT_SERVICE_ID"), LocalDateYYYYMMDD(EFFECTIVE_DATE)});
+            if (PERIOD_TYPE.equals("609")) {
+                DBSql.update(conn, "DELETE FROM " + boName + " WHERE  CONTRACT_SERVICE_ID=? ", new Object[]{contractService.get("CONTRACT_SERVICE_ID")});
+            } else {
+                DBSql.update(conn, "DELETE FROM " + boName + " WHERE  CONTRACT_SERVICE_ID=? and PERIOD_BEGIN_DATE >=? and (PAY_AMOUNT=0 or PAY_AMOUNT is NULL)", new Object[]{contractService.get("CONTRACT_SERVICE_ID"), LocalDateYYYYMMDD(EFFECTIVE_DATE)});
+            }
 
             String lastPeriodId = DBSql.getString(conn, "select ID from " + boName + " where CONTRACT_SERVICE_ID=? and PERIOD_END_DATE >=? order by PERIOD_BEGIN_DATE asc", new Object[]{contractService.get("CONTRACT_SERVICE_ID"), LocalDateYYYYMMDD(EFFECTIVE_DATE)});
 
@@ -616,7 +674,7 @@ public class contractServiceAdj {
                 //获取第一笔
 
                 //首月成本
-                if (PERIOD_TYPE.equals("4598") || PERIOD_TYPE.equals("4595") || PERIOD_TYPE.equals("4592")) {
+                if (PURCHASE_CALC_METHOD_1STPERIOD.equals("4598") || PURCHASE_CALC_METHOD_1STPERIOD.equals("4595") || PURCHASE_CALC_METHOD_1STPERIOD.equals("4592")) {
 
                 } else {
                     RowMap firstContractService = DBSql.getMap("select * from BO_EU_DNCTT_CONTRACT_SERVICE_PURCHASE_ADJUST where  CONTRACT_SERVICE_ID=?  and IS_EFFECTIVE='已执行' order by PURCHASE_START_DATE ASC", new Object[]{contractService.get("CONTRACT_SERVICE_ID")});
@@ -629,8 +687,7 @@ public class contractServiceAdj {
                         SERVICE_UNIT_COST = TypeUtil.convert(contractService.get("UNIT_COST_1STMONTH"), BigDecimal.class);
                         SERVICE_TOTAL_COST = multiply(SERVICE_UNIT_COST, contractService.get("QUANTITY"));
 
-                        BigDecimal ratio = new BigDecimal(EFFECTIVE_DATE.lengthOfMonth() - EFFECTIVE_DATE.getDayOfMonth() + 1)
-                                .divide(new BigDecimal(EFFECTIVE_DATE.lengthOfMonth()), 10, BigDecimal.ROUND_HALF_UP);
+                        BigDecimal ratio = new BigDecimal(EFFECTIVE_DATE.lengthOfMonth() - EFFECTIVE_DATE.getDayOfMonth() + 1).divide(new BigDecimal(EFFECTIVE_DATE.lengthOfMonth()), 10, BigDecimal.ROUND_HALF_UP);
 
                         COST_1STMONTH = SERVICE_TOTAL_COST.multiply(ratio);
 
@@ -757,8 +814,7 @@ public class contractServiceAdj {
             }
 
         } finally {
-            if (conn != null)
-                DBSql.close(conn);
+            if (conn != null) DBSql.close(conn);
         }
 
         //生成每月的采购计划
@@ -773,24 +829,32 @@ public class contractServiceAdj {
     }
 
 
+    /**
+     * 将合同服务的采购成本按照服务周期进行分摊处理。
+     * <p>
+     * 该方法根据合同服务ID查询相关的付款计划和服务周期数据,
+     * 并根据不同服务类型(如一次性收费或分期服务)将付款金额分摊至各个服务周期中。
+     * 分摊结果会更新到服务周期表中的 PERIOD_COST 字段。
+     *
+     * @param uc 当前用户上下文信息,用于权限控制等用途(当前未使用)
+     * @param contractServiceId 合同服务ID,作为主键用于查询相关数据
+     */
     //合同服务采购成本平摊到服务周期上面
     public void paySplitServiceMonth(UserContext uc, String contractServiceId) {
 
         RowMap service = DBSql.getMap("select * from BO_EU_DNCTT_CONTRACT_SERVICE where ID=?", new Object[]{contractServiceId});
 
-        //付款计划
+        // 查询与该合同服务关联的所有付款计划,并按开始时间排序
         List<RowMap> payPlanList = DBSql.getMaps("select * from BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH where CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE", new Object[]{contractServiceId});
 
-        if (payPlanList.isEmpty() || payPlanList.size() == 0)
-            return;
+        if (payPlanList.isEmpty() || payPlanList.size() == 0) return;
 
-        //收款
+        // 查询与该合同服务对应的服务周期记录,并按开始时间排序
         List<RowMap> contractServicePeriodList = DBSql.getMaps("select * from BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD where CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE", new Object[]{contractServiceId});
 
-        if (contractServicePeriodList.isEmpty() || contractServicePeriodList.size() == 0)
-            return;
+        if (contractServicePeriodList.isEmpty() || contractServicePeriodList.size() == 0) return;
 
-        //服务:一次性收费
+        // 处理“一次性收费”类型的合同服务:所有付款计划总和直接赋给第一个服务周期的成本
         if (service.getString("PERIOD_TYPE").equals("609")) {
             BigDecimal SUM_PLAN_AMOUNT = BigDecimal.ZERO;
             for (RowMap payPlan : payPlanList) {
@@ -802,34 +866,42 @@ public class contractServiceAdj {
             return;
         }
 
-        LocalDate LAST_PERIOD_END_DATE = null;
+        LocalDate LAST_DATE = null;
         for (RowMap servicePeriod : contractServicePeriodList) {
-            //服务周期
-            LocalDate PERIOD_BEGIN_DATE = TypeUtil.convert(servicePeriod.get("PERIOD_BEGIN_DATE"), LocalDate.class);//开始日期
-            LocalDate PERIOD_END_DATE = TypeUtil.convert(servicePeriod.get("PERIOD_END_DATE"), LocalDate.class);//结束日期
+            // 获取当前服务周期的结束日期
+            LocalDate PERIOD_END_DATE = TypeUtil.convert(servicePeriod.get("PERIOD_END_DATE"), LocalDate.class);
+            SDK.getLogAPI().consoleInfo("PERIOD_END_DATE:" + PERIOD_END_DATE.toString());
 
             BigDecimal SUM_PLAN_AMOUNT = BigDecimal.ZERO;
+
             for (RowMap payPlan : payPlanList) {
-                LocalDate PAY_PERIOD_BEGIN_DATE = TypeUtil.convert(payPlan.get("PERIOD_BEGIN_DATE"), LocalDate.class);//开始日期
-                LocalDate PAY_PERIOD_END_DATE = TypeUtil.convert(payPlan.get("PERIOD_END_DATE"), LocalDate.class);//结束日期
+                LocalDate PAY_PERIOD_BEGIN_DATE = TypeUtil.convert(payPlan.get("PERIOD_BEGIN_DATE"), LocalDate.class);
+
+                SDK.getLogAPI().consoleInfo("PAY_PERIOD_BEGIN_DATE:" + PAY_PERIOD_BEGIN_DATE.toString());
 
-                if (PAY_PERIOD_END_DATE.isAfter(PERIOD_END_DATE)) {
+                // 跳过已处理过的付款计划(通过上一个周期末日期+1判断)
+                if (LAST_DATE != null && PAY_PERIOD_BEGIN_DATE.isBefore(LAST_DATE)) {
                     continue;
                 }
 
-                if (LAST_PERIOD_END_DATE != null && PAY_PERIOD_END_DATE.isAfter(LAST_PERIOD_END_DATE) == false) {
+                // 若付款计划起始日在当前服务周期之后,则跳过
+                if (PERIOD_END_DATE.isBefore(PAY_PERIOD_BEGIN_DATE)) {
                     continue;
                 }
 
-                //合计付款计划
+                SDK.getLogAPI().consoleInfo("PLAN_AMOUNT : " + payPlan.getString("PLAN_AMOUNT"));
+
+                // 累加符合时间段内的付款计划金额
                 SUM_PLAN_AMOUNT = SUM_PLAN_AMOUNT.add(TypeUtil.convert(payPlan.get("PLAN_AMOUNT"), BigDecimal.class));
 
             }
+            // 更新当前服务周期的成本字段
             DBSql.update("update BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD set PERIOD_COST=? where ID=? ", new Object[]{SUM_PLAN_AMOUNT, servicePeriod.getString("ID")});
 
-            contractServiceLogger.info("服务周期:" + servicePeriod.getString("ID") + ":" + SUM_PLAN_AMOUNT);
+            SDK.getLogAPI().consoleInfo("服务周期:" + servicePeriod.getString("ID") + ":" + SUM_PLAN_AMOUNT);
 
-            LAST_PERIOD_END_DATE = PERIOD_END_DATE;
+            // 设置下一轮比较的时间起点为当前周期结束后一天
+            LAST_DATE = PERIOD_END_DATE.plusDays(1);
         }
 
     }

+ 45 - 24
com.awspaas.user.apps.donenow_ivt/src/com/awspaas/user/apps/donenow_ivt/service/PaymentPlanService.java

@@ -157,7 +157,7 @@ public class PaymentPlanService {
                 break;
         }
 
-        if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH"))//按月采购
+        if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH") && !RULE_CATE.equals("609"))//按月采购
             RULE_CATE = "610";
 
         LocalDate PERIOD_BEGIN_DATE = service.get("PURCHASE_START_DATE", LocalDate.class);
@@ -183,6 +183,14 @@ public class PaymentPlanService {
                 SDK.getBOAPI().createDataBO(boName, paymentPlan, uc, conn);
             }
 
+            //一次性收费
+            DBSql.update("UPDATE bo_eu_dnctt_contract_service_period a SET a.PERIOD_COST =( SELECT SUM(b.PLAN_AMOUNT) FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN b WHERE a.CONTRACT_SERVICE_ID=? AND b.CONTRACT_SERVICE_ID = a.CONTRACT_SERVICE_ID) WHERE a.CONTRACT_SERVICE_ID=? AND EXISTS ( SELECT 1 FROM BO_EU_DNIVT_ORDER_PAYMENT_PLAN b JOIN bo_eu_dnctt_contract_service c ON b.CONTRACT_SERVICE_ID = c.ID WHERE b.CONTRACT_SERVICE_ID = a.CONTRACT_SERVICE_ID AND c.PERIOD_TYPE = 609 AND c.ID=?)", new Object[]{SERVICE_ID, SERVICE_ID, SERVICE_ID});
+
+            if (boName.equals("BO_EU_DNIVT_ORDER_PAYMENT_PLAN")) {
+                createServicePaymentPlan(uc, orderId, SERVICE_ID, VENDOR_ACCOUNT_ID, orderProduct, "BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH", conn);
+                //paySplitServiceMonth(uc, SERVICE_ID);
+            }
+
             return;
         }
 
@@ -213,7 +221,7 @@ public class PaymentPlanService {
         String firstPeriodBeginDate = null;
 
         //首月单独计算
-        if (RULE_CATE.equals("4598") || RULE_CATE.equals("4595") || RULE_CATE.equals("4592")) {
+        if (PURCHASE_CALC_METHOD_1STPERIOD.equals("4598") || PURCHASE_CALC_METHOD_1STPERIOD.equals("4595") || PURCHASE_CALC_METHOD_1STPERIOD.equals("4592")) {
             //签约之日开始算周期,则不需要
 
         } else {
@@ -281,31 +289,33 @@ public class PaymentPlanService {
 
     /**
      * 分摊采购成本
-     * @param uc
-     * @param contractServiceId
+     * 将合同服务的采购成本按照服务周期进行分摊。对于一次性收费的服务,
+     * 直接将所有付款计划金额汇总后赋给唯一的服务周期;对于其他类型服务,
+     * 按照服务周期的时间范围匹配对应的付款计划,并计算该周期内的总费用。
+     *
+     * @param uc 当前用户上下文信息
+     * @param contractServiceId 合同服务ID,用于查询相关服务、付款计划和服务周期数据
      */
+    //合同服务采购成本平摊到服务周期上面
     public void paySplitServiceMonth(UserContext uc, String contractServiceId) {
 
         RowMap service = DBSql.getMap("select * from BO_EU_DNCTT_CONTRACT_SERVICE where ID=?", new Object[]{contractServiceId});
 
-
-        //付款计划
+        // 查询与该合同服务关联的所有付款计划,并按开始时间排序
         List<RowMap> payPlanList = DBSql.getMaps("select * from BO_EU_DNIVT_ORDER_PAYMENT_PLAN_MONTH where CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE", new Object[]{contractServiceId});
 
-        if (payPlanList.isEmpty() || payPlanList.size() == 0)
-            return;
+        if (payPlanList.isEmpty() || payPlanList.size() == 0) return;
 
-        //收款
+        // 查询与该合同服务对应的服务周期记录,并按开始时间排序
         List<RowMap> contractServicePeriodList = DBSql.getMaps("select * from BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD where CONTRACT_SERVICE_ID=? ORDER BY PERIOD_BEGIN_DATE", new Object[]{contractServiceId});
 
-        if (contractServicePeriodList.isEmpty() || contractServicePeriodList.size() == 0)
-            return;
+        if (contractServicePeriodList.isEmpty() || contractServicePeriodList.size() == 0) return;
 
-        //服务:一次性收费
+        // 处理“一次性收费”类型的合同服务:所有付款计划总和直接赋给第一个服务周期的成本
         if (service.getString("PERIOD_TYPE").equals("609")) {
             BigDecimal SUM_PLAN_AMOUNT = BigDecimal.ZERO;
             for (RowMap payPlan : payPlanList) {
-                //合计付款计划
+
                 SUM_PLAN_AMOUNT = SUM_PLAN_AMOUNT.add(TypeUtil.convert(payPlan.get("PLAN_AMOUNT"), BigDecimal.class));
             }
 
@@ -313,35 +323,46 @@ public class PaymentPlanService {
             return;
         }
 
-        LocalDate LAST_PERIOD_END_DATE = null;
+        LocalDate LAST_DATE = null;
         for (RowMap servicePeriod : contractServicePeriodList) {
-            //服务周期
-            LocalDate PERIOD_BEGIN_DATE = TypeUtil.convert(servicePeriod.get("PERIOD_BEGIN_DATE"), LocalDate.class);//开始日期
-            LocalDate PERIOD_END_DATE = TypeUtil.convert(servicePeriod.get("PERIOD_END_DATE"), LocalDate.class);//结束日期
+            // 获取当前服务周期的结束日期
+            LocalDate PERIOD_END_DATE = TypeUtil.convert(servicePeriod.get("PERIOD_END_DATE"), LocalDate.class);
+            SDK.getLogAPI().consoleInfo("PERIOD_END_DATE:" + PERIOD_END_DATE.toString());
 
             BigDecimal SUM_PLAN_AMOUNT = BigDecimal.ZERO;
+
             for (RowMap payPlan : payPlanList) {
-                LocalDate PAY_PERIOD_BEGIN_DATE = TypeUtil.convert(payPlan.get("PERIOD_BEGIN_DATE"), LocalDate.class);//开始日期
-                LocalDate PAY_PERIOD_END_DATE = TypeUtil.convert(payPlan.get("PERIOD_END_DATE"), LocalDate.class);//结束日期
+                LocalDate PAY_PERIOD_BEGIN_DATE = TypeUtil.convert(payPlan.get("PERIOD_BEGIN_DATE"), LocalDate.class);
 
-                if (PAY_PERIOD_END_DATE.isAfter(PERIOD_END_DATE)) {
+                SDK.getLogAPI().consoleInfo("PAY_PERIOD_BEGIN_DATE:" + PAY_PERIOD_BEGIN_DATE.toString());
+
+                // 跳过已处理过的付款计划(通过上一个周期末日期+1判断)
+                if (LAST_DATE != null && PAY_PERIOD_BEGIN_DATE.isBefore(LAST_DATE)) {
                     continue;
                 }
 
-                if (LAST_PERIOD_END_DATE != null && PAY_PERIOD_END_DATE.isAfter(LAST_PERIOD_END_DATE) == false) {
+                // 若付款计划起始日在当前服务周期之后,则跳过
+                if (PERIOD_END_DATE.isBefore(PAY_PERIOD_BEGIN_DATE)) {
                     continue;
                 }
 
-                //合计付款计划
+                SDK.getLogAPI().consoleInfo("PLAN_AMOUNT : " + payPlan.getString("PLAN_AMOUNT"));
+
+                // 累加符合时间段内的付款计划金额
                 SUM_PLAN_AMOUNT = SUM_PLAN_AMOUNT.add(TypeUtil.convert(payPlan.get("PLAN_AMOUNT"), BigDecimal.class));
 
             }
+            // 更新当前服务周期的成本字段
             DBSql.update("update BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD set PERIOD_COST=? where ID=? ", new Object[]{SUM_PLAN_AMOUNT, servicePeriod.getString("ID")});
-            LAST_PERIOD_END_DATE = PERIOD_END_DATE;
-        }
 
+            SDK.getLogAPI().consoleInfo("服务周期:" + servicePeriod.getString("ID") + ":" + SUM_PLAN_AMOUNT);
+
+            // 设置下一轮比较的时间起点为当前周期结束后一天
+            LAST_DATE = PERIOD_END_DATE.plusDays(1);
+        }
     }
 
+
     /**
      * 获取周期列表
      * @param RULE_CATE