package com.awspaas.user.apps.donenow_ctt.service;

import com.actionsoft.bpms.bo.engine.BO;
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.sdk.local.SDK;
import com.actionsoft.sdk.local.api.Logger;
import com.awspaas.user.apps.donenow_ctt.cttConstant;
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;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;


/**
 * 合同服务
 */
public class contractService {

    private static final Logger contractServiceLogger = SDK.getLogAPI().getLogger(contractService.class);//记录日志
    private final String[] excludesArray = new String[]{"ID", "ORGID", "BINDID", "CREATEDATE", "CREATEUSER", "UPDATEDATE", "UPDATEUSER", "PROCESSDEFID", "ISEND"};
    private static volatile contractService instance;

    private contractService() {
        // Constructor logic here, if any.
    }

    public static contractService getInstance() {
        if (instance == null) {
            synchronized (contractService.class) {
                if (instance == null) {
                    instance = new contractService();
                }
            }
        }
        return instance;
    }

    /**
     * 合同提交后，进行数据处理
     * @param uc
     * @param bindid
     * @param isSubmit
     * @return
     */
    public Map<String, Object> SaveContract(UserContext uc, String bindid, boolean isSubmit) {
        contractServiceLogger.info("合同提交后");
        Map<String, Object> resultDto = new HashMap<>();
        try {
            boolean idApply = true;//申请还是编辑
            BO contractBO = SDK.getBOAPI().getByProcess("BO_EU_DNCTT_CONTRACT_APPLY", bindid);//获取合同表单数据
            if (contractBO == null) {
                idApply = false;
                contractBO = SDK.getBOAPI().getByProcess("BO_EU_DNCTT_CONTRACT", bindid);//获取合同表单数据
            }
            if (contractBO.getString("IS_SUBMIT").equals("1"))//已提交
                isSubmit = true;
            String TYPE_ID = contractBO.getString("TYPE_ID");//合同类型
            if (isSubmit)
                contractBO.set("IS_SUBMIT", 1);
            else
                contractBO.set("IS_SUBMIT", 0);

            if (TYPE_ID.equals(cttConstant.CONTRACT_TYPE_SERVICE)) // 如果是服务合同
            {
                DBSql.update("UPDATE BO_EU_DNCTT_CONTRACT_SERVICE SET OLD_QUANTITY = QUANTITY, OLD_UNIT_PRICE = UNIT_PRICE, OLD_TOTAL_PRICE = TOTAL_PRICE, OLD_UNIT_COST=UNIT_COST, OLD_TOTAL_COST =TOTAL_COST WHERE BINDID=?", new Object[]{bindid});

                String PERIOD_TYPE = contractBO.getString("PERIOD_TYPE");//合同周期类型
                Integer OCCURRENCES = contractBO.get("OCCURRENCES", Integer.class);
                Date START_DATE = contractBO.get("START_DATE", Date.class);

                //通过 合同周期个数和合同周期类型计算结束日期
                if (OCCURRENCES != null && OCCURRENCES > 0) {
                    Date END_DATE = null;
                    // 使用 Calendar 进行日期操作
                    Calendar calendar = Calendar.getInstance();
                    calendar.setTime(START_DATE);
                    if (PERIOD_TYPE.equals(cttConstant.PERIOD_TYPE_HALFYEAR)) {
                        calendar.add(Calendar.MONTH, OCCURRENCES * 6);
                        calendar.add(Calendar.DAY_OF_MONTH, -1);
                        END_DATE = calendar.getTime();
                    } else if (PERIOD_TYPE.equals(cttConstant.PERIOD_TYPE_MONTH)) {
                        calendar.add(Calendar.MONTH, OCCURRENCES);
                        calendar.add(Calendar.DAY_OF_MONTH, -1);
                        END_DATE = calendar.getTime();
                    } else if (PERIOD_TYPE.equals(cttConstant.PERIOD_TYPE_QUARTER)) {
                        calendar.add(Calendar.MONTH, OCCURRENCES * 3);
                        calendar.add(Calendar.DAY_OF_MONTH, -1);
                        END_DATE = calendar.getTime();
                    } else if (PERIOD_TYPE.equals(cttConstant.PERIOD_TYPE_YEAR)) {
                        calendar.add(Calendar.MONTH, OCCURRENCES * 12);
                        calendar.add(Calendar.DAY_OF_MONTH, -1);
                        END_DATE = calendar.getTime();
                    } else {
                        resultDto.put("success", false);
                        resultDto.put("msg", "周期类型输入错误！");
                        return resultDto;
                    }

                    contractBO.set("END_DATE", END_DATE);

                }
            }

            contractBO.set("ADJUST_SETUP_FEE", contractBO.get("SETUP_FEE"));

            //计费客户
            if (StringUtils.isBlank(contractBO.getString("BILL_TO_ACCOUNT_ID"))) {
                contractBO.set("BILL_TO_ACCOUNT_ID", contractBO.getString("ACCOUNT_ID"));
                contractBO.set("BILL_TO_ACCOUNT_NAME", contractBO.getString("ACCOUNT_NAME"));
            }

            if (idApply)
                SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_APPLY", contractBO);//更新合同
            else {
                dn.recordFormChanges.record(uc, contractBO, "合同编辑");
                //SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT", contractBO);
                ContractExcManage(uc, bindid);//更新合同例外因素
            }

            if (!isSubmit)
                return resultDto;

            contractServiceLogger.info("合同提交后，进行数据处理");

            RowMap dto = new RowMap(contractBO.asMap());//合同对象

            if (idApply)
                insertContract(uc, dto);//创建合同详情，用于提交后查看和修改相关配置项、工单、服务等
            else {
                //更新
                DBSql.update("update BO_EU_DNCTT_CONTRACT_RATE set CONTRACT_ID=? where BINDID=?", new Object[]{dto.getString("ID"), dto.getString("BINDID")});
                DBSql.update("update BO_EU_DNCTT_CONTRACT_MILESTONE set CONTRACT_ID=? where BINDID=?", new Object[]{dto.getString("ID"), dto.getString("BINDID")});
                DBSql.update("update BO_EU_DNCTT_CONTRACT_SERVICE set CONTRACT_ID=? where BINDID=?", new Object[]{dto.getString("ID"), dto.getString("BINDID")});

                DBSql.update("update BO_EU_DNCTT_CONTRACT_COST_DEFAULT set CONTRACT_ID=? where BINDID=?", new Object[]{dto.getString("ID"), dto.getString("BINDID")});

                DBSql.update("update BO_EU_DNCTT_CONTRACT_BLOCK set CONTRACT_ID=? where BINDID=?", new Object[]{dto.getString("ID"), dto.getString("BINDID")});

            }


            if (TYPE_ID.equals(cttConstant.CONTRACT_TYPE_SERVICE)) // 如果是服务合同
            {
                try {

                    AddContractServiceProduct(uc, dto);//新增服务或者服务包--关联产品

                    boolean flag = AddServiceServiceBundle(uc, dto);//新增服务或者服务包

                    if (!flag) {
                        resultDto.put("success", false);
                        resultDto.put("msg", "新增服务或服务包失败！");
                        return resultDto;
                    }

                } catch (Exception e) {

                    e.printStackTrace();
                    contractServiceLogger.info("info--失败。。。");

                    resultDto.put("success", false);
                    resultDto.put("msg", e.getMessage());
                    return resultDto;
                }
            }

            resultDto.put("success", true);

            return resultDto;
        } catch (Exception e) {
            e.printStackTrace();
            contractServiceLogger.info("info--失败。。。");
        }
        resultDto.put("success", false);
        return resultDto;
    }

    /**
     * 插入合同详情
     * @param uc
     * @param contract
     */
    public void insertContract(UserContext uc, RowMap contract) throws SQLException {
        String contractBINDID = null;
        String APPLY_ID = contract.getString("ID");
        String APPLY_BINDID = contract.getString("BINDID");
        String exitsBOID = DBSql.getString("select ID from BO_EU_DNCTT_CONTRACT where APPLY_ID=?", new Object[]{APPLY_ID});
        String processDefId = "obj_4bac3fd23814406c8d224ef2094ac274";
        if (StringUtils.isNotBlank(exitsBOID)) {
            BO contractBO = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", exitsBOID);
            for (String key : excludesArray) {
                contract.remove(key);
            }
            contractBO.setAll(contract);
            contractBO.set("APPLY_ID", APPLY_ID);

            dn.recordFormChanges.record(uc, contractBO, "合同编辑");

            SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT", contractBO);
            contract.put("BINDID", contractBO.getBindId());
            contract.put("ID", contractBO.getId());
            contract.put("CONTRACT_ID", contractBO.getId());
            contractBINDID = contractBO.getBindId();
        } else {
            //新增
            ProcessInstance contractInstance = SDK.getProcessAPI().createBOProcessInstance(processDefId, uc.getUID(), contract.getString("NAME"));

            BO contractBO = new BO();
            for (String key : excludesArray) {
                contract.remove(key);
            }
            contractBO.setAll(contract);
            contractBO.set("APPLY_ID", APPLY_ID);
            contractBO.setBindId(contractInstance.getId());

            dn.recordFormChanges.record(uc, contractBO, "合同新增");

            SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT", contractBO, uc);
            exitsBOID = contractBO.getId();
            contract.put("BINDID", contractInstance.getId());
            contract.put("ID", contractBO.getId());
            contract.put("CONTRACT_ID", contractBO.getId());
            contractBINDID = contractBO.getBindId();
        }

        contract.put("PROCESSDEFID", processDefId);
        //更新
        DBSql.update("update BO_EU_DNCTT_CONTRACT_RATE set PROCESSDEFID=?,BINDID=?,CONTRACT_ID=? where BINDID=?", new Object[]{processDefId, contractBINDID, exitsBOID, APPLY_BINDID});
        DBSql.update("update BO_EU_DNCTT_CONTRACT_MILESTONE set PROCESSDEFID=?,BINDID=?,CONTRACT_ID=? where BINDID=?", new Object[]{processDefId, contractBINDID, exitsBOID, APPLY_BINDID});
        DBSql.update("update BO_EU_DNCTT_CONTRACT_SERVICE set PROCESSDEFID=?,BINDID=?,CONTRACT_ID=? where BINDID=?", new Object[]{processDefId, contractBINDID, exitsBOID, APPLY_BINDID});
        DBSql.update("update BO_EU_DNCTT_CONTRACT_BLOCK set PROCESSDEFID=?,BINDID=?,CONTRACT_ID=? where BINDID=?", new Object[]{processDefId, contractBINDID, exitsBOID, APPLY_BINDID});
    }

    /**
     * 新增服务或者服务包
     * @param uc
     * @param contract
     * @return
     */
    public boolean AddServiceServiceBundle(UserContext uc, RowMap contract) throws SQLException {

        contractServiceLogger.info("新增服务或服务包");

        Connection conn = null;
        try {
            conn = DBSql.open();
            conn.setAutoCommit(false);

            //获取服务列表
            List<BO> serviceList = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT_SERVICE").connection(conn).addQuery("BINDID=", contract.get("BINDID")).list();

            for (BO service : serviceList) {
                boolean isCreate = false;
                // 1、合同开始日期或结束日期变化
                LocalDate START_DATE = getLocalDate(contract.get("START_DATE"));//服务开始日期
                LocalDate END_DATE = getLocalDate(contract.get("END_DATE"));//合同结束日期
                LocalDate EFFECTIVE_DATE = getLocalDate(service.get("EFFECTIVE_DATE"));//服务生效日期
                String PERIOD = service.getString("PERIOD");//服务起止日期
                String ADJUST_PERIOD = service.getString("ADJUST_PERIOD");//服务调整的起止日期
                if (EFFECTIVE_DATE == null || (StringUtils.isBlank(PERIOD) && StringUtils.isBlank(ADJUST_PERIOD)))
                    isCreate = true;

                if (EFFECTIVE_DATE == null)
                    EFFECTIVE_DATE = START_DATE;

                if (StringUtils.isNotBlank(ADJUST_PERIOD)) {
                    LocalDate ADJUST_PERIOD_START = getLocalDate(ADJUST_PERIOD.split("~")[0]);
                    if (!ADJUST_PERIOD_START.isEqual(EFFECTIVE_DATE)) {
                        isCreate = true;
                    }
                    if (StringUtils.isBlank(PERIOD)) {
                        LocalDate ADJUST_PERIOD_END = getLocalDate(ADJUST_PERIOD.split("~")[1]);
                        if (!ADJUST_PERIOD_END.isEqual(END_DATE)) {
                            isCreate = true;
                        }
                    }
                }

                if (StringUtils.isNotBlank(PERIOD)) {
                    LocalDate PERIOD_END = getLocalDate(PERIOD.split("~")[1]);
                    if (!PERIOD_END.isEqual(END_DATE)) {
                        isCreate = true;
                    }
                    if (StringUtils.isBlank(ADJUST_PERIOD)) {
                        LocalDate PERIOD_START = getLocalDate(PERIOD.split("~")[0]);
                        if (!PERIOD_START.isEqual(EFFECTIVE_DATE)) {
                            isCreate = true;
                        }
                    }
                }


                //2、服务单价或数量变化???
                if (service.get("ADJUSTED_PRICE") == null || StringUtils.isBlank(service.getString("ADJUSTED_PRICE"))) {
                    isCreate = true;
                } else {
                    BigDecimal ADJUSTED_PRICE = new BigDecimal(service.getString("ADJUSTED_PRICE"));

                    ADJUSTED_PRICE = ADJUSTED_PRICE.setScale(8, RoundingMode.HALF_UP);

                    if (ADJUSTED_PRICE.compareTo(multiply(service.get("QUANTITY"), service.get("UNIT_PRICE"))) != 0) {
                        isCreate = true;
                    }
                }

                if (!isCreate)
                    continue;

                //服务已经审批并提交则不需要重新生成
                // 暂时不考虑 审批并提交之后修改服务，不需要重新生成
                if (DBSql.getInt("SELECT count(1) FROM BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST WHERE APPROVE_AND_POST_USER_ID IS NOT NULL AND LENGTH(APPROVE_AND_POST_USER_ID)>1 AND APPROVE_AND_POST_DATE IS NOT NULL AND  BINDID=? and CONTRACT_ID=? and CONTRACT_SERVICE_ID=? ", new Object[]{service.get("BINDID"), contract.get("ID"), service.get("ID")}) > 0) {
                    /*
                    boolean flag = EditServiceServiceBundle(conn, uc, contract, (RowMap) service.asMap());//调整未审批的服务
                    if (!flag) {
                        conn.rollback();
                        return false;
                    }
                     */
                    continue;
                }

                //暂时不考虑 审批并提交之后，修改服务，不需要重新生成
                if (DBSql.getInt("SELECT count(1) FROM BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD WHERE APPROVE_AND_POST_USER_ID IS NOT NULL AND LENGTH(APPROVE_AND_POST_USER_ID)>1 AND APPROVE_AND_POST_DATE IS NOT NULL AND  BINDID=? and CONTRACT_ID=? and CONTRACT_SERVICE_ID=? ", new Object[]{service.get("BINDID"), contract.get("ID"), service.get("ID")}) > 0) {
                    /*
                    boolean flag = EditServiceServiceBundle(conn, uc, contract, (RowMap) service.asMap());//调整未审批的服务
                    if (!flag) {
                        conn.rollback();
                        return false;
                    }

                     */
                    continue;
                }

                //删除历史数据
                if (StringUtils.isNotBlank(ADJUST_PERIOD)) {

                    List<String> csaIdList = DBSql.getList(conn, "select ID from BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST where BINDID=? and CONTRACT_ID=? and CONTRACT_SERVICE_ID=?", String.class, new Object[]{service.get("BINDID"), contract.get("ID"), service.get("ID")});

                    for (String csaId : csaIdList) {
                        DBSql.update(conn, "delete from BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST_BUNDLE_SERVICE where BINDID=? and CONTRACT_SERVICE_ADJUST_ID=? ", new Object[]{service.get("BINDID"), csaId});
                    }

                    DBSql.update(conn, "delete from BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST where BINDID=? and CONTRACT_ID=? and CONTRACT_SERVICE_ID=?", new Object[]{service.get("BINDID"), contract.get("ID"), service.get("ID")});
                }

                if (StringUtils.isNotBlank(PERIOD)) {
                    List<String> cspIdList = DBSql.getList(conn, "select ID from BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD where BINDID=? and CONTRACT_ID=? and OBJECT_ID=? and CONTRACT_SERVICE_ID=?", String.class, new Object[]{service.get("BINDID"), contract.get("ID"), service.get("OBJECT_ID"), service.get("ID")});
                    for (String cspId : cspIdList) {
                        DBSql.update(conn, "delete from BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD_BUNDLE_SERVICE where BINDID=? and CONTRACT_SERVICE_PERIOD_ID=? ", new Object[]{service.get("BINDID"), cspId});
                    }
                    DBSql.update(conn, "delete from BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD where BINDID=? and CONTRACT_ID=? and OBJECT_ID=? and CONTRACT_SERVICE_ID=?", new Object[]{service.get("BINDID"), contract.get("ID"), service.get("OBJECT_ID"), service.get("ID")});
                }

                service.set("CONTRACT_ID", contract.get("ID"));
                service.set("EFFECTIVE_DATE", EFFECTIVE_DATE);//生效日期

                if (service.getString("OBJECT_TYPE").equals("1"))//服务
                {
                    int NEED_PURCHASE = DBSql.getInt(conn, "select NEED_PURCHASE from BO_EU_DNIVT_SERVICE where ID=? ", new Object[]{service.getString("OBJECT_ID")});

                    if (NEED_PURCHASE == 1)//需要购买
                    {
                        service.set("STATUS_ID", cttConstant.CONTRACT_SERVICE_STATUS_WAIT_INSTALL);//待安装
                    } else {
                        service.set("STATUS_ID", cttConstant.CONTRACT_SERVICE_STATUS_NO_INSTALL);//不需要购买
                    }
                }

                int maxPeriod = contract.getInt("PERIOD_TYPE") > service.get("PERIOD_TYPE", Integer.class) ? contract.getInt("PERIOD_TYPE") : service.get("PERIOD_TYPE", Integer.class);  // 合同与服务周期类型中较大的周期类型

                BigDecimal rate = GetPeriodRate(contract.getString("PERIOD_TYPE"), service.getString("PERIOD_TYPE"));

                BigDecimal unit_cost = service.get("UNIT_COST", BigDecimal.class);
                BigDecimal unit_price = service.get("UNIT_PRICE", BigDecimal.class);

                // 合同周期更长，服务周期使用合同周期，单价和成本根据周期倍数翻倍
                if (rate.compareTo(BigDecimal.ONE) > 0) {
                    unit_cost = unit_cost.multiply(rate);
                    service.set("UNIT_COST", unit_cost);
                    unit_price = unit_price.multiply(rate);
                    service.set("UNIT_PRICE", unit_price);
                }

                contractServiceLogger.info("计算-service-ADJUSTED_PRICE");

                service.set("ADJUSTED_PRICE", multiply(service.get("QUANTITY"), service.get("UNIT_PRICE")));//总价

                SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE", service, conn);//保存

                List<String> sbsList = null;
                String serviceIds = DBSql.getString("select SERVICE_ID from BO_EU_DNIVT_SERVICE_BUNDLE where ID=?", new Object[]{service.get("OBJECT_ID")});
                if (StringUtils.isNotBlank(serviceIds)) {
                    sbsList = Arrays.asList(serviceIds.split(","));
                }
                /*
                if (service.getString("OBJECT_TYPE").equals("2"))//服务包
                    sbsList = DBSql.getMaps(conn, "SELECT * FROM BO_EU_DNIVT_SERVICE_BUNDLE_SERVICE WHERE SERVICE_BUNDLE_ID=?", new Object[]{service.getString("OBJECT_ID")});
                 */

                if (service.getString("OBJECT_TYPE").equals("2"))//服务包
                {
                    // 插入合同服务包对应的服务 ctt_contract_service_bundle_service
                    if (sbsList != null) {
                        for (String sbs : sbsList) {
                            if (DBSql.getInt(conn, "select count(1) from BO_EU_DNCTT_CONTRACT_SERVICE_BUNDLE_SERVICE where SERVICE_ID=? and CONTRACT_SERVICE_ID=?", new Object[]{sbs, service.get("ID")}) == 0) {
                                BO csbs = new BO();
                                csbs.set("SERVICE_ID", sbs);
                                csbs.set("CONTRACT_SERVICE_ID", service.get("ID"));
                                csbs.setBindId(service.getString("BINDID"));
                                SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT_SERVICE_BUNDLE_SERVICE", csbs, uc, conn);
                            }

                        }
                    }
                }


                String vendor_account_id = DBSql.getString(conn, "select VENDOR_ACCOUNT_ID from BO_EU_DNIVT_SERVICE where ID=?", new Object[]{service.getString("OBJECT_ID")});//供应商账号

                // 计算首周期起止日期，并判断是否需要新增服务调整记录
                Map<String, Object> firstPeriodDate = getFirstPeriodDate(START_DATE, END_DATE, EFFECTIVE_DATE, contract.getString("PERIOD_TYPE"), service.getString("PERIOD_TYPE"));
                boolean isStartOfPeriod = (boolean) firstPeriodDate.get("isStartOfPeriod");
                // 合同服务的周期起止日期
                LocalDate start = getLocalDate(firstPeriodDate.get("periodStart"));
                LocalDate end = getLocalDate(firstPeriodDate.get("periodEnd"));
                ADJUST_PERIOD = null;

                contractServiceLogger.info("getFirstPeriodDate" + firstPeriodDate.toString());

                //增加服务周期调整
                if (!isStartOfPeriod) {
                    LocalDate periodEnd = getNextPeriodStart(start, String.valueOf(maxPeriod)).minusDays(1);//周期结束日期
                    BigDecimal periodRate = divideToBigDecimal(GetPeriodDays(EFFECTIVE_DATE, end), GetPeriodDays(start, periodEnd));    // 首周期占整周期比例
                    BO csa = new BO();//合同服务调整
                    csa.set("CONTRACT_ID", contract.get("ID"));
                    csa.set("OBJECT_ID", service.get("ID"));
                    csa.set("OBJECT_TYPE", service.get("OBJECT_TYPE"));
                    csa.set("QUANTITY_CHANGE", service.get("QUANTITY"));
                    csa.set("CONTRACT_SERVICE_ID", service.get("ID"));
                    csa.set("EFFECTIVE_DATE", EFFECTIVE_DATE);
                    csa.set("END_DATE", end);

                    ADJUST_PERIOD = LocalDateYYYYMMDD(EFFECTIVE_DATE) + "~" + LocalDateYYYYMMDD(end);

                    csa.set("PRORATED_COST_CHANGE", service.get("ADJUSTED_PRICE"));

                    contractServiceLogger.info("计算-csa-ADJUST_PRORATED_PRICE_CHANGE");
                    csa.set("ADJUST_PRORATED_PRICE_CHANGE", multiply(service.get("ADJUSTED_PRICE"), periodRate));
                    csa.set("PRORATED_PRICE_CHANGE", csa.get("ADJUST_PRORATED_PRICE_CHANGE"));
                    csa.set("VENDOR_ACCOUNT_ID", vendor_account_id);
                    csa.setBindId(service.getString("BINDID"));
                    SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", csa, uc, conn);

                    // 插入服务调整表包含的服务ctt_contract_service_adjust_bundle_service
                    if (service.getString("OBJECT_TYPE").equals("2"))//服务包
                    {
                        //本周期成本
                        String prorated_cost_change = DBSql.getString(conn, "select UNIT_COST from BO_EU_DNIVT_SERVICE where ID=?", new Object[]{service.getString("OBJECT_ID")});//供应商账号

                        if (sbsList != null) {
                            for (String sbs : sbsList) {
                                BO csabs = new BO();
                                csabs.set("CONTRACT_SERVICE_ADJUST_ID", csa.getId());
                                csabs.set("SERVICE_ID", sbs);
                                csabs.set("VENDOR_ACCOUNT_ID", vendor_account_id);
                                csabs.set("PRORATED_COST_CHANGE", prorated_cost_change);
                                csabs.setBindId(service.getString("BINDID"));
                                SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST_BUNDLE_SERVICE", csabs, uc, conn);
                            }
                        }
                    }
                    start = end.plusDays(1);     // 周期开始日期指
                }

                PERIOD = null;
                //生成服务周期
                if (start.isBefore(END_DATE)) {
                    PERIOD = LocalDateYYYYMMDD(start);
                    //循环添加全部周期
                    while (!start.isAfter(END_DATE)) {
                        BigDecimal periodRate = BigDecimal.ONE;
                        LocalDate periodEnd = getNextPeriodStart(start, String.valueOf(maxPeriod)).minusDays(1);//本周期结束时间
                        if (periodEnd.isAfter(END_DATE)) {//如果结束日期大于合同结束日期，则计算结束日期和合同结束日期的差值，并计算该差值占整周期的比例
                            periodRate = divideToBigDecimal(GetPeriodDays(start, END_DATE), GetPeriodDays(start, periodEnd));
                            periodEnd = END_DATE;
                        }

                        BO csp = new BO();//服务周期
                        csp.set("CONTRACT_ID", contract.get("ID"));
                        csp.set("OBJECT_ID", service.get("OBJECT_ID"));
                        csp.set("OBJECT_TYPE", service.get("OBJECT_TYPE"));
                        csp.set("CONTRACT_SERVICE_ID", service.get("ID"));

                        csp.set("PERIOD_BEGIN_DATE", start);
                        csp.set("PERIOD_END_DATE", periodEnd);
                        csp.set("QUANTITY", service.get("QUANTITY"));

                        contractServiceLogger.info("计算-csp-PERIOD_PRICE");
                        BigDecimal period_price = multiply(service.get("UNIT_PRICE"), service.get("QUANTITY"));
                        period_price = periodRate.multiply(period_price);
                        csp.set("PERIOD_PRICE", period_price);

                        contractServiceLogger.info("计算-csp-PERIOD_COST");
                        BigDecimal period_cost = multiply(service.get("PERIOD_COST"), service.get("QUANTITY"));
                        period_cost = periodRate.multiply(period_cost);
                        csp.set("PERIOD_COST", period_cost);

                        csp.set("PERIOD_ADJUSTED_PRICE", csp.get("PERIOD_PRICE"));

                        csp.set("VENDOR_ACCOUNT_ID", vendor_account_id);
                        csp.setBindId(service.getString("BINDID"));
                        SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", csp, uc, conn);

                        if (service.getString("OBJECT_TYPE").equals("2"))//服务包
                        {
                            //本周期成本
                            String prorated_cost_change = DBSql.getString(conn, "select UNIT_COST from BO_EU_DNIVT_SERVICE where ID=?", new Object[]{service.getString("OBJECT_ID")});//供应商账号
                            if (sbsList != null) {
                                for (String sbs : sbsList) {
                                    BO cspbs = new BO();
                                    cspbs.set("CONTRACT_SERVICE_PERIOD_ID", csp.getId());
                                    cspbs.set("SERVICE_ID", sbs);
                                    cspbs.set("VENDOR_ACCOUNT_ID", vendor_account_id);
                                    cspbs.set("PERIOD_COST", prorated_cost_change);
                                    cspbs.setBindId(service.getString("BINDID"));
                                    SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD_BUNDLE_SERVICE", cspbs, uc, conn);
                                }
                            }
                        }

                        if (periodEnd != null) {
                            start = periodEnd.plusDays(1);
                        }
                    }

                    PERIOD = PERIOD + "~" + LocalDateYYYYMMDD(END_DATE);
                }

                //更新合同服务上的
                DBSql.update(conn, "update BO_EU_DNCTT_CONTRACT_SERVICE set PERIOD=?,ADJUST_PERIOD=? where ID=? and BINDID=?", new Object[]{PERIOD, ADJUST_PERIOD, service.get("ID"), service.get("BINDID")});
            }


            conn.commit();
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            conn.rollback();

            contractServiceLogger.error("AddServiceServiceBundle--error--失败。。。", e);

        } finally {
            if (conn != null)
                DBSql.close(conn);
        }
        return false;
    }

    /**
     * 调整服务
     * @param uc
     * @param contract
     * @param service
     * @return
     */
    public boolean EditServiceServiceBundle(Connection conn, UserContext uc, RowMap contract, RowMap service) {


        return true;
    }

    /**
     * 一个服务对应添加一个合同产品
     * @param uc
     * @return
     */
    private boolean AddContractServiceProduct(UserContext uc, RowMap contract) throws SQLException {
        contractServiceLogger.info("新增服务或服务包");

        Connection conn = null;
        try {
            conn = DBSql.open();
            conn.setAutoCommit(false);
            //获取服务列表
            List<BO> serviceList = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT_SERVICE").connection(conn).addQuery("BINDID=", contract.get("BINDID")).list();

            for (BO service : serviceList) {

                if (service.getString("OBJECT_TYPE").equals("2"))//服务包
                {
                    String serviceIDS = DBSql.getString(conn, "select SERVICE_ID from BO_EU_DNIVT_SERVICE_BUNDLE where ID=?", new Object[]{service.getString("OBJECT_ID")});
                    for (String serviceID : serviceIDS.split(",")) {
                        String contractCostId = DBSql.getString(conn, "select ID from BO_EU_DNCTT_CONTRACT_COST where CONTRACT_ID=? and SERVICE_ID=? and PRODUCT_ID in(select PRODUCT_ID from BO_EU_DNIVT_SERVICE where ID=?)", new Object[]{contract.getString("ID"), service.getString("ID"), serviceID});

                        Map<String, String> contractCost = new HashMap<String, String>();
                        contractCost.put("ID", contractCostId);
                        contractCost.put("SERVICE_ID", service.getString("ID"));
                        contractCost.put("serviceID", serviceID);
                        contractCost.put("CONTRACT_ID", contract.getString("ID"));
                        AddContractServiceProduct(uc, conn, contractCost);
                    }
                } else {
                    String contractCostId = DBSql.getString(conn, "select ID from BO_EU_DNCTT_CONTRACT_COST where CONTRACT_ID=? and SERVICE_ID=?", new Object[]{contract.getString("ID"), service.getString("ID")});
                    Map<String, String> contractCost = new HashMap<String, String>();
                    contractCost.put("ID", contractCostId);
                    contractCost.put("SERVICE_ID", service.getString("ID"));
                    contractCost.put("serviceID", service.getString("OBJECT_ID"));
                    contractCost.put("CONTRACT_ID", contract.getString("ID"));
                    AddContractServiceProduct(uc, conn, contractCost);
                }
            }
            conn.commit();
            return true;
        } catch (SQLException e) {
            e.printStackTrace();
            conn.rollback();
        } finally {
            if (conn != null)
                DBSql.close(conn);
        }
        return false;
    }


    /**
     * 新增合同产品
     * @param uc
     * @param service
     * @return
     */
    private boolean AddContractServiceProduct(UserContext uc, Connection conn, Map<String, String> service) {
        contractServiceLogger.info("新增合同产品");
        BO contractCost = null;
        if (service.get("ID") != null) {
            contractCost = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_COST", service.get("ID"));
        } else {
            ProcessInstance contractInstance = SDK.getProcessAPI().createBOProcessInstance("obj_87c6ef75d18f44cc8e85d3d4e818c303", uc.getUID(), "新增合同产品");
            contractCost = new BO();
            contractCost.setBindId(contractInstance.getId());
        }
        contractCost.set("CONTRACT_ID", service.get("CONTRACT_ID"));
        contractCost.set("SERVICE_ID", service.get("SERVICE_ID"));
        contractCost.set("PRODUCT_ID", DBSql.getString(conn, "select PRODUCT_ID from BO_EU_DNIVT_SERVICE where ID=?", new Object[]{service.get("serviceID")}));

        BO product = SDK.getBOAPI().get("BO_EU_DNIVT_PRODUCT", contractCost.getString("PRODUCT_ID"));

        contractCost.set("NAME", product.getString("NAME"));

        contractCost.set("DESCRIPTION", product.getString("DESCRIPTION"));
        contractCost.set("COST_CODE_ID", product.getString("COST_CODE_ID"));
        contractCost.set("UNIT_COST", product.getString("UNIT_COST"));
        contractCost.set("UNIT_PRICE", product.getString("UNIT_PRICE"));
        contractCost.set("TAX_CATEGORY_ID", DBSql.getString("select TAX_CATEGORY_ID from BO_EU_DND_COST_CODE where ID=?", new Object[]{product.getString("COST_CODE_ID")}));
        contractCost.set("COST_CODE_NAME", DBSql.getString("select NAME from BO_EU_DND_COST_CODE where ID=?", new Object[]{product.getString("COST_CODE_ID")}));
        contractCost.set("PRODUCT_NAME", product.getString("NAME"));

        if (contractCost.isNew()) {
            SDK.getBOAPI().create("BO_EU_DNCTT_CONTRACT_COST", contractCost, contractCost.getBindId(), uc.getUID());
        } else {
            SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_COST", contractCost);
        }
        return false;
    }

    private static String LocalDateYYYYMMDD(LocalDate date) {
        return date.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    }

    /**
     * 数值相乘
     * @param obj1
     * @param obj2
     * @return
     */
    public static BigDecimal multiply(Object obj1, Object obj2) {
        try {
            if (obj1 == null || obj2 == null)
                return BigDecimal.ZERO;

            BigDecimal num1 = new BigDecimal(String.valueOf(obj1));
            BigDecimal num2 = new BigDecimal(String.valueOf(obj2));
            // 执行乘法运算
            BigDecimal result = num1.multiply(num2).setScale(8, RoundingMode.HALF_UP);
            ;
            // 输出结果
            contractServiceLogger.info("Result: " + result);
            return result;
        } catch (Exception e) {
            contractServiceLogger.error("multiply--info--失败。。。" + obj1 + "*" + obj2);
        }
        return BigDecimal.ZERO; // 返回默认值 0
    }

    /**
     * 返回合同周期天数除以服务周期天数的比例，保留8位小数
     * @param contractPeriod
     * @param servicePeriod
     * @return
     */
    public BigDecimal GetPeriodRate(String contractPeriod, String servicePeriod) {
        if (Objects.equals(contractPeriod, servicePeriod)) {
            return BigDecimal.valueOf(1);
        }
        if (contractPeriod.equals(cttConstant.PERIOD_TYPE_ONE_TIME) || servicePeriod.equals(cttConstant.PERIOD_TYPE_ONE_TIME)) {
            return BigDecimal.valueOf(0);
        }
        int ctt = GetPeriodMonth(contractPeriod);
        int svc = GetPeriodMonth(servicePeriod);
        if (ctt == 0 || svc == 0) {
            return BigDecimal.valueOf(0);
        }

        // 将 ctt 和 svc 转换为 BigDecimal
        BigDecimal cttDecimal = BigDecimal.valueOf(ctt);
        BigDecimal svcDecimal = BigDecimal.valueOf(svc);

        // 执行除法并保留 8 位小数，使用四舍五入
        return cttDecimal.divide(svcDecimal, 8, RoundingMode.HALF_UP);
    }


    /**
     * 获取合同服务首周期的开始和结束日期
     *
     * @param contractStart  合同开始日期
     * @param contractEnd    合同结束日期
     * @param serviceEffect  服务生效日期
     * @param contractPeriod 合同周期
     * @param servicePeriod  服务周期
     * @return 包含首周期开始日期、结束日期以及是否为周期起始日期的结果对象
     */
    public Map<String, Object> getFirstPeriodDate(LocalDate contractStart, LocalDate contractEnd, LocalDate serviceEffect, String contractPeriod, String servicePeriod) {
        // 创建一个Map对象用于存储计算结果
        Map<String, Object> dto = new HashMap<>();

        // 将合同周期和服务周期转换为月数
        int cttPeriod = GetPeriodMonth(contractPeriod);
        int svcPeriod = GetPeriodMonth(servicePeriod);

        // 初始化开始日期为合同开始日期
        LocalDate start = contractStart;
        LocalDate end;

        // 遍历每个合同周期，直到合同结束日期
        while (!start.isAfter(contractEnd)) {
            // 计算当前周期的结束日期
            end = getNextPeriodStart(start, contractPeriod).minusDays(1);

            // 检查服务是否在当前合同周期内生效
            if (!serviceEffect.isBefore(start) && !serviceEffect.isAfter(end)) {
                // 服务生效，计算服务周期的开始和结束日期
                if (end.isAfter(contractEnd)) {
                    end = contractEnd;
                }
                LocalDate periodEnd;
                if (svcPeriod > cttPeriod) {
                    // 如果服务周期大于合同周期，计算服务周期的结束日期???
                    periodEnd = getNextPeriodStart(start, servicePeriod).minusDays(1);
                } else {
                    // 否则，服务周期结束日期与合同周期相同
                    periodEnd = end;
                }
                // 判断是否为服务周期的起始日期
                // 将结果存入Map中
                dto.put("periodStart", start);
                dto.put("periodEnd", periodEnd);
                dto.put("isStartOfPeriod", start.isEqual(serviceEffect));
                // 返回结果
                return dto;
            } else {
                // 服务未生效，移动到下一个合同周期
                start = end.plusDays(1);
            }
        }
        // 如果没有找到服务周期，返回默认值
        dto.put("periodStart", LocalDate.MIN);
        dto.put("periodEnd", LocalDate.MIN);
        dto.put("isStartOfPeriod", false);
        // 返回结果
        return dto;
    }


    /**
     * 根据不同的周期类型转换为对应的月数
     * 此方法用于将给定的周期类型字符串转换为相应的月数
     * 主要用于处理不同时间周期的转换，以便统一时间单位
     *
     * @param period 周期类型字符串，表示不同的周期类型
     * @return 对应的月数如果输入的周期类型不匹配已知类型，则返回0
     */
    private int GetPeriodMonth(String period) {
        switch (period) {
            case cttConstant.PERIOD_TYPE_MONTH:
                // 当周期类型为月时，返回1个月
                return 1;
            case cttConstant.PERIOD_TYPE_QUARTER:
                // 当周期类型为季度时，返回3个月
                return 3;
            case cttConstant.PERIOD_TYPE_HALFYEAR:
                // 当周期类型为半年时，返回6个月
                return 6;
            case cttConstant.PERIOD_TYPE_YEAR:
                // 当周期类型为年时，返回12个月
                return 12;
            default:
                // 当周期类型不匹配已知类型时，返回0
                return 0;
        }
    }

    /**
     * 获取下一周期开始时间
     * @param start 当前周期开始时间
     * @param periodType 周期类型
     * @return 下一周期开始时间
     */
    public static LocalDate getNextPeriodStart(LocalDate start, String periodType) {
        // 根据不同的周期类型计算下一周期的开始时间
        switch (periodType) {
            case cttConstant.PERIOD_TYPE_MONTH:
                // 如果是月周期，则在当前时间基础上增加1个月
                return start.plus(1, ChronoUnit.MONTHS);
            case cttConstant.PERIOD_TYPE_QUARTER:
                // 如果是季度周期，则在当前时间基础上增加3个月
                return start.plus(3, ChronoUnit.MONTHS);
            case cttConstant.PERIOD_TYPE_HALFYEAR:
                // 如果是半年周期，则在当前时间基础上增加6个月
                return start.plus(6, ChronoUnit.MONTHS);
            case cttConstant.PERIOD_TYPE_YEAR:
                // 如果是年周期，则在当前时间基础上增加1年
                return start.plus(1, ChronoUnit.YEARS);
            default:
                // 如果周期类型不匹配任何已知类型，则返回原始开始时间
                return start;
        }
    }

    /**
     * object 转换成LocalDate
     */
    public static LocalDate getLocalDate(Object obj) {
        try {
            if (obj == null)
                return null;
            if (obj instanceof LocalDate) {
                return (LocalDate) obj;
            } else if (obj instanceof String) {
                if (StringUtils.isBlank(obj.toString()))
                    return null;
                // 定义日期格式
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                // 解析字符串为 LocalDate
                contractServiceLogger.info("getLocalDate" + obj.toString());
                return LocalDate.parse(obj.toString(), formatter);
            } else if (obj instanceof Date) {
                return ((Date) obj).toInstant().atZone(ZoneId.systemDefault()).toLocalDate();
            } else {
                contractServiceLogger.info("Invalid object type: " + obj.getClass().getName());
                //throw new IllegalArgumentException("Invalid object type: " + obj.getClass().getName());
                return null;
            }
        } catch (Exception e) {
            contractServiceLogger.info("getLocalDate" + e.getMessage());
        }
        return null;
    }

    /**
     * 计算两个日期之间的天数
     * @param start 开始日期
     * @param end   结束日期
     * @return 两个日期之间的天数
     */
    public static int GetPeriodDays(LocalDate start, LocalDate end) {
        return (int) ChronoUnit.DAYS.between(start, end) + 1;
    }

    // 定义一个方法，将两个 int 相除并返回 BigDecimal
    public static BigDecimal divideToBigDecimal(int numerator, int denominator) {
        if (denominator == 0) {
            throw new ArithmeticException("分母不能为零");
        }

        // 使用 BigDecimal 进行精确计算
        BigDecimal num = new BigDecimal(numerator);
        BigDecimal den = new BigDecimal(denominator);

        // 设置小数点后的位数和舍入模式
        return num.divide(den, 10, RoundingMode.HALF_UP); // 保留 10 位小数
    }

    /**
     * 合同例外因素更新（添加和删除）
     * @param uc
     * @param bindid
     * @return
     */
    public boolean ContractExcManage(UserContext uc, String bindid) {

        RowMap rowMap = DBSql.getMap("SELECT ID,BINDID,EXCLUSION_CONTRACT_ID,EXCLUSION_COST_CODE,EXCLUSION_ROLE FROM BO_EU_DNCTT_CONTRACT WHERE BINDID= ?", bindid);

        String exclusionContractId = rowMap.getString("EXCLUSION_CONTRACT_ID");// 获取排除合同ID
        String exclusionCostCode = rowMap.getString("EXCLUSION_COST_CODE");// 获取排除费用代码
        String exclusionRole = rowMap.getString("EXCLUSION_ROLE");// 获取排除角色

        // 判断排除费用代码是否为空，如果为空，则清空排除费用代码
        if (StringUtils.isBlank(exclusionCostCode)) {
            //清空
            DBSql.update("UPDATE BO_EU_DNCTT_CONTRACT_EXCLUSION_COST_CODE SET CLOSED =1 WHERE BINDID = ? OR CONTRACT_ID=?", new Object[]{rowMap.getString("ID"), rowMap.getString("BINDID")});
        } else {
            String[] exclusionCostCodeArr = exclusionCostCode.split(",");
            for (String costCode : exclusionCostCodeArr) {
                String ccID = DBSql.getString("SELECT ID FROM BO_EU_DNCTT_CONTRACT_EXCLUSION_COST_CODE WHERE (BINDID =? OR CONTRACT_ID=?) AND COST_CODE_ID = ?", new Object[]{rowMap.getString("BINDID"), rowMap.getString("ID"), costCode});
                //更新
                if (StringUtils.isNotBlank(ccID)) {
                    DBSql.update("UPDATE BO_EU_DNCTT_CONTRACT_EXCLUSION_COST_CODE SET CLOSED =0,BINDID =?,CONTRACT_ID=? WHERE ID = ?", new Object[]{rowMap.getString("BINDID"), rowMap.getString("ID"), ccID});
                    continue;
                }
                //新增
                BO bo = new BO();
                bo.set("BINDID", rowMap.getString("BINDID"));
                bo.set("CONTRACT_ID", rowMap.getString("ID"));
                bo.set("COST_CODE_ID", costCode);
                SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT_EXCLUSION_COST_CODE", bo, uc);
            }

            //删除
            DBSql.update("UPDATE BO_EU_DNCTT_CONTRACT_EXCLUSION_COST_CODE SET CLOSED =1 WHERE (BINDID = ? OR CONTRACT_ID=?) AND COST_CODE_ID NOT IN (?)", new Object[]{rowMap.getString("BINDID"), rowMap.getString("ID"), exclusionCostCode});
        }

        if (StringUtils.isBlank(exclusionRole)) {
            //清空
            DBSql.update("UPDATE BO_EU_DNCTT_CONTRACT_EXCLUSION_ROLE SET CLOSED =1 WHERE BINDID = ? OR CONTRACT_ID=?", new Object[]{rowMap.getString("BINDID"), rowMap.getString("ID")});
        } else {
            String[] exclusionRoleArr = exclusionRole.split(",");
            for (String role : exclusionRoleArr) {
                String roleID = DBSql.getString("SELECT ID FROM BO_EU_DNCTT_CONTRACT_EXCLUSION_ROLE WHERE (BINDID =? OR CONTRACT_ID=?) AND ROLE_ID = ?", new Object[]{rowMap.getString("BINDID"), rowMap.getString("ID"), role});
                //更新
                if (StringUtils.isNotBlank(roleID)) {
                    DBSql.update("UPDATE BO_EU_DNCTT_CONTRACT_EXCLUSION_ROLE SET CLOSED =0,BINDID =?,CONTRACT_ID=? WHERE ID = ?", new Object[]{rowMap.getString("BINDID"), rowMap.getString("ID"), roleID});
                    continue;
                }
                //新增
                BO bo = new BO();
                bo.set("BINDID", rowMap.getString("BINDID"));
                bo.set("CONTRACT_ID", rowMap.getString("ID"));
                bo.set("ROLE_ID", role);
                SDK.getBOAPI().createDataBO("BO_EU_DNCTT_CONTRACT_EXCLUSION_ROLE", bo, uc);
            }

            //删除
            DBSql.update("UPDATE BO_EU_DNCTT_CONTRACT_EXCLUSION_ROLE SET CLOSED =1 WHERE (BINDID = ? OR CONTRACT_ID=?) AND ROLE_ID NOT IN (?)", new Object[]{rowMap.getString("BINDID"), rowMap.getString("ID"), exclusionRole});
        }
        return true;
    }


    /**
     * 提交服务费费用
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     */
    public boolean postRecurringServices(UserContext uc, String approveDate, String approveIds) throws SQLException {

        Connection connUpdate = null;
        try {
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);

            for (String approveId : approveIds.split(",")) {

                BO cad = new BO();// crm_account_deduction();//审批并提交表实体对象
                String TAX_CATEGORY_ID = null;//物料计费代码
                String CONTRACT_SERVICE_ID = null;//合同服务ID
                String CONTRACT_ID = null;//合同ID

                //BO_EU_DNCTT_CONTRACT
                BO bo = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", approveId);//合同初始费用
                if (bo != null) {
                    CONTRACT_ID = bo.getId();
                    bo.set("APPROVE_AND_POST_DATE", approveDate);
                    bo.set("SETUP_FEE_APPROVE_POST_USER_ID", uc.getUID());
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT", bo, connUpdate);

                    cad.set("TYPE_ID", cttConstant.ACCOUNT_DEDUCTION_TYPE_INITIAL_COST);//类型(合同初始费用)
                    cad.set("OBJECT_ID", approveId);
                    cad.set("CONTRACT_ID", approveId);
                    cad.set("QUANTITY", 1);
                    cad.set("BILL_CREATE_USER_ID", bo.getCreateUser());//创建人
                    cad.set("ACCOUNT_ID", bo.getString("ACCOUNT_ID"));
                    cad.set("BILL_ACCOUNT_ID", bo.getString("BILL_TO_ACCOUNT_ID"));
                    cad.set("EXTENDED_PRICE", bo.get("ADJUST_SETUP_FEE"));

                    if (StringUtils.isNotBlank(bo.getString("SETUP_FEE_COST_CODE_ID"))) {
                        TAX_CATEGORY_ID = SDK.getBOAPI().get("BO_EU_DND_COST_CODE", bo.getString("SETUP_FEE_COST_CODE_ID"), "TAX_CATEGORY_ID").toString();
                    }
                } else {
                    //BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST
                    bo = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", approveId);
                    if (bo != null) {
                        CONTRACT_SERVICE_ID = bo.getString("CONTRACT_SERVICE_ID");
                        CONTRACT_ID = bo.getString("CONTRACT_ID");

                        bo.set("APPROVE_AND_POST_DATE", approveDate);
                        bo.set("SETUP_FEE_APPROVE_POST_USER_ID", uc.getUID());
                        SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", bo, connUpdate);


                        cad.set("TYPE_ID", cttConstant.ACCOUNT_DEDUCTION_TYPE_SERVICE_AJUST);//类型(服务调整)
                        cad.set("OBJECT_ID", approveId);
                        cad.set("CONTRACT_ID", bo.getString("CONTRACT_ID"));
                        cad.set("QUANTITY", bo.get("QUANTITY_CHANGE"));

                        cad.set("BILL_CREATE_USER_ID", bo.getCreateUser());//创建人
                        cad.set("ACCOUNT_ID", DBSql.getString("SELECT ACCOUNT_ID FROM BO_EU_DNCTT_CONTRACT WHERE ID = ?", new Object[]{bo.getString("CONTRACT_ID")}));
                        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("ADJUST_PRORATED_PRICE_CHANGE"));

                        String costCodeId = null;
                        if (bo.getString("OBJECT_TYPE").equals("1")) {
                            costCodeId = DBSql.getString("SELECT COST_CODE_ID FROM BO_EU_DNIVT_SERVICE WHERE ID = ?", new Object[]{bo.getString("OBJECT_ID")});
                        } else {
                            costCodeId = DBSql.getString("SELECT COST_CODE_ID FROM BO_EU_DNIVT_SERVICE_BUNDLE WHERE ID = ?", new Object[]{bo.getString("OBJECT_ID")});
                        }

                        TAX_CATEGORY_ID = SDK.getBOAPI().get("BO_EU_DND_COST_CODE", costCodeId, "TAX_CATEGORY_ID").toString();

                    } else {
                        //BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD
                        bo = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", approveId);
                        if (bo != null) {
                            CONTRACT_SERVICE_ID = bo.getString("CONTRACT_SERVICE_ID");
                            CONTRACT_ID = bo.getString("CONTRACT_ID");

                            bo.set("APPROVE_AND_POST_DATE", approveDate);
                            bo.set("SETUP_FEE_APPROVE_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);//类型(服务)
                            cad.set("OBJECT_ID", approveId);
                            cad.set("CONTRACT_ID", bo.getString("CONTRACT_ID"));
                            cad.set("QUANTITY", bo.get("QUANTITY"));

                            cad.set("BILL_CREATE_USER_ID", bo.getCreateUser());//创建人
                            cad.set("ACCOUNT_ID", DBSql.getString("SELECT ACCOUNT_ID FROM BO_EU_DNCTT_CONTRACT WHERE ID = ?", new Object[]{bo.getString("CONTRACT_ID")}));
                            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"));

                            String costCodeId = null;
                            if (bo.getString("OBJECT_TYPE").equals("1")) {
                                costCodeId = DBSql.getString("SELECT COST_CODE_ID FROM BO_EU_DNIVT_SERVICE WHERE ID = ?", new Object[]{bo.getString("OBJECT_ID")});
                            } else {
                                costCodeId = DBSql.getString("SELECT COST_CODE_ID FROM BO_EU_DNIVT_SERVICE_BUNDLE WHERE ID = ?", new Object[]{bo.getString("OBJECT_ID")});
                            }
                            TAX_CATEGORY_ID = SDK.getBOAPI().get("BO_EU_DND_COST_CODE", costCodeId, "TAX_CATEGORY_ID").toString();
                        }
                    }
                }

                //按照合同税率
                if (SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_SERVICE", CONTRACT_SERVICE_ID, "IS_CTRTAX_APPLIED").equals("1")) {
                    TAX_CATEGORY_ID = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", CONTRACT_ID, "TAX_CATEGORY_ID").toString();
                }

                RowMap ca = DBSql.getMap("select TAX_REGION_ID,IS_TAX_EXEMPT from BO_EU_DNCRM_ACCOUNT where ID=?", new Object[]{cad.get("ACCOUNT_ID")});
                String tax_category_name = null;//税收种类
                String tax_region_name = null;//税区
                double tax_rate = 0;//税率

                if (StringUtils.isNotBlank(TAX_CATEGORY_ID)) {
                    tax_category_name = DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ? AND ORGID=?", new Object[]{TAX_CATEGORY_ID, uc.getCompanyModel().getId()});
                }

                if (ca.containsKey("TAX_REGION_ID") && StringUtils.isNotBlank(ca.getString("TAX_REGION_ID"))) {
                    tax_region_name = DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ? AND ORGID=?", new Object[]{ca.getString("TAX_REGION_ID"), uc.getCompanyModel().getId()});
                }

                if (tax_category_name != null && tax_region_name != null)
                    tax_rate = DBSql.getDouble("SELECT TOTAL_EFFECTIVE_TAX_RATE FROM BO_EU_DND_TAX_REGION_CATE WHERE TAX_REGION_ID='" + ca.getString("TAX_REGION_ID") + "' AND TAX_CATE_ID='" + TAX_CATEGORY_ID + "' ", "TOTAL_EFFECTIVE_TAX_RATE");

                cad.set("POSTED_DATE", approveDate);
                cad.set("TAX_CATEGORY_NAME", tax_category_name);
                cad.set("TAX_CATEGORY_ID", TAX_CATEGORY_ID);
                cad.set("TAX_REGION_ID", ca.getString("TAX_REGION_ID"));
                cad.set("TAX_REGION_NAME", tax_region_name);
                cad.set("EFFECTIVE_TAX_RATE", tax_rate);

                // cad.set("EXTENDED_COST", tax_rate);
                // cad.extended_cost = GetCost(id);


                if (ca.getString("IS_TAX_EXEMPT").equals("1")) {
                    double tax_dollars = cad.get("EXTENDED_PRICE", Double.class) - cad.get("EXTENDED_PRICE", Double.class) / (1 + tax_rate);//税额
                    cad.set("TAX_DOLLARS", tax_dollars);
                }
                SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", cad, uc, connUpdate);
            }
            connUpdate.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            if (connUpdate != null && !connUpdate.isClosed())
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
        } finally {
            if (connUpdate != null && !connUpdate.isClosed())
                DBSql.close(connUpdate);
        }

        return true;
    }

    /**
     * 提交里程碑
     * @param uc
     * @param approveIds
     * @return
     */
    public boolean postMilestone(UserContext uc, String approveDate, String approveIds) throws SQLException {
        Connection connUpdate = null;
        try {
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);
            for (String approveId : approveIds.split(",")) {
                BO bo = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_MILESTONE", approveId);
                if (bo != null) {
                    bo.set("STATUS_ID", 1267);//进行中
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_MILESTONE", bo, connUpdate);

                    BO cad = new BO();
                    cad.set("TYPE_ID", cttConstant.ACCOUNT_DEDUCTION_TYPE_MILESTONES);//类型(里程碑)

                    String TAX_CATEGORY_ID = null;
                    //按照合同税率
                    if (bo.getString("IS_CTRTAX_APPLIED").equals("1")) {
                        TAX_CATEGORY_ID = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", bo.getString("CONTRACT_ID"), "TAX_CATEGORY_ID").toString();
                    } else {
                        TAX_CATEGORY_ID = DBSql.getString("SELECT TAX_CATEGORY_ID FROM BO_EU_DND_COST_CODE WHERE ID=?", new Object[]{bo.getString("COST_CODE_ID")});
                    }

                    cad.set("OBJECT_ID", approveId);
                    cad.set("TAX_CATEGORY_ID", TAX_CATEGORY_ID);
                    cad.set("TAX_CATEGORY_NAME", DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ? AND ORGID=?", new Object[]{TAX_CATEGORY_ID, uc.getCompanyModel().getId()}));

                    cad.set("TAX_REGION_ID", DBSql.getString("SELECT TAX_REGION_ID FROM BO_EU_DNCRM_ACCOUNT WHERE ID=(SELECT ACCOUNT_ID FROM BO_EU_DNCTT_CONTRACT WHERE ID=?)", new Object[]{bo.getString("CONTRACT_ID")}));
                    cad.set("TAX_REGION_NAME", DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ? AND ORGID=?", new Object[]{cad.get("TAX_REGION_ID"), uc.getCompanyModel().getId()}));

                    cad.set("ACCOUNT_ID", DBSql.getString("SELECT ACCOUNT_ID FROM BO_EU_DNCTT_CONTRACT WHERE ID=?", new Object[]{bo.getString("CONTRACT_ID")}));
                    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("POSTED_DATE", approveDate);

                    cad.set("CONTRACT_ID", bo.get("CONTRACT_ID"));


                    double tax_rate = DBSql.getDouble("SELECT TOTAL_EFFECTIVE_TAX_RATE FROM BO_EU_DND_TAX_REGION_CATE WHERE TAX_REGION_ID='" + cad.getString("TAX_REGION_ID") + "' AND TAX_CATE_ID='" + TAX_CATEGORY_ID + "' ", "TOTAL_EFFECTIVE_TAX_RATE");
                    cad.set("EFFECTIVE_TAX_RATE", tax_rate);

                    cad.set("EXTENDED_PRICE", bo.get("DOLLARS"));
                    if (DBSql.getInt("select COUNT(1) cnt from BO_EU_DNCRM_ACCOUNT WHERE ID=? AND IS_TAX_EXEMPT=1 ", new Object[]{cad.getString("ACCOUNT_ID")}) > 0) {
                        double tax_dollars = cad.get("EXTENDED_PRICE", Double.class) - cad.get("EXTENDED_PRICE", Double.class) / (1 + tax_rate);//税额
                        cad.set("TAX_DOLLARS", tax_dollars);
                    }

                    SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", cad, uc, connUpdate);

                }
            }
            connUpdate.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            if (connUpdate != null && !connUpdate.isClosed())
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
        } finally {
            if (connUpdate != null && !connUpdate.isClosed())
                DBSql.close(connUpdate);
        }
        return true;
    }

    /**
     * 审批并提交合同产品
     * @param uc
     * @param approveDate
     * @param approveIds
     * @return
     * @throws SQLException
     */
    public boolean postCost(UserContext uc, String approveDate, String approveIds) throws SQLException {

        Connection connUpdate = null;
        try {
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);

            for (String approveId : approveIds.split(",")) {

                BO ccc = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_COST", approveId);//合同成本
                BO cc = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", ccc.getString("CONTRACT_ID"));//合同
                BO ca = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT", ccc.getString("ACCOUNT_ID"));//客户
                // BO dcc = SDK.getBOAPI().get("BO_EU_DND_COST_CODE", ccc.getString("COST_CODE_ID"));//物料计费代码

                String tax_category_name = null;//税收种类
                String tax_region_name = null;//税区
                double tax_rate = 0;//税率

                String TAX_CATEGORY_ID = null;
                //按照合同税率
                if (ccc.getString("IS_CTRTAX_APPLIED").equals("1")) {
                    TAX_CATEGORY_ID = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT", ccc.getString("CONTRACT_ID"), "TAX_CATEGORY_ID").toString();
                } else {
                    TAX_CATEGORY_ID = ccc.getString("TAX_CATEGORY_ID");
                }

                if (StringUtils.isNotBlank(TAX_CATEGORY_ID))
                    tax_category_name = DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ? AND ORGID=?", new Object[]{TAX_CATEGORY_ID, uc.getCompanyModel().getId()});

                if (ca.get("TAX_REGION_ID") != null)
                    tax_region_name = DBSql.getString("SELECT NAME FROM BO_EU_DND_GENERAL WHERE OID = ? AND ORGID=?", new Object[]{ca.get("TAX_REGION_ID"), uc.getCompanyModel().getId()});

                if (StringUtils.isNotBlank(TAX_CATEGORY_ID) && ca.get("TAX_REGION_ID") != null)
                    tax_rate = DBSql.getDouble("SELECT TOTAL_EFFECTIVE_TAX_RATE FROM BO_EU_DND_TAX_REGION_CATE WHERE TAX_REGION_ID='" + ca.getString("TAX_REGION_ID") + "' AND TAX_CATE_ID='" + TAX_CATEGORY_ID + "' ", "TOTAL_EFFECTIVE_TAX_RATE");

                BO cad = new BO();
                cad.set("OBJECT_ID", ccc.getString("ID"));//成本表id
                cad.set("TYPE_ID", cttConstant.ACCOUNT_DEDUCTION_TYPE_CHARGE);
                cad.set("POSTED_DATE", getLocalDate(approveDate));//审批时间
                cad.set("BILL_ACCOUNT_ID", cc.getString("BILL_ACCOUNT_ID"));//计费客户
                cad.set("TASK_ID", ccc.getString("TASK_ID"));
                cad.set("CONTRACT_ID", ccc.getString("CONTRACT_ID"));
                cad.set("BILL_CREATE_USER_ID", ccc.getCreateUser());//创建人
                cad.set("ACCOUNT_ID", ca.getString("ID"));//客户
                cad.set("QUANTITY", ccc.get("QUANTITY"));//数量
                cad.set("TAX_CATEGORY_NAME", tax_category_name);
                cad.set("TAX_REGION_NAME", tax_region_name);
                cad.set("EFFECTIVE_TAX_RATE", tax_rate);
                cad.set("PURCHASE_ORDER_NO", ccc.getString("PURCHASE_ORDER_NO"));//采购订单号
                cad.set("EXTENDED_COST", multiply(ccc.get("UNIT_COST"), ccc.get("QUANTITY")));//总成本

                //关联预付费合同
                //暂不实现


                //不是 关联预付费合同
                cad.set("EXTENDED_PRICE", ccc.get("EXTENDED_PRICE"));//总价
                if (!ca.getString("IS_TAX_EXEMPT").equals("1")) {
                    double tax_dollars = cad.get("EXTENDED_PRICE", Double.class) - cad.get("EXTENDED_PRICE", Double.class) / (1 + tax_rate);//税额
                    cad.set("TAX_DOLLARS", tax_dollars);
                }
                SDK.getBOAPI().createDataBO("BO_EU_DNCRM_ACCOUNT_DEDUCTION", cad, uc, connUpdate);

                //改变状态
                ccc.set("BILL_STATUS", "1");
                SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_COST", ccc, connUpdate);

            }
            connUpdate.commit();
        } catch (SQLException e) {
            e.printStackTrace();
            if (connUpdate != null && !connUpdate.isClosed())
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
        } finally {
            if (connUpdate != null && !connUpdate.isClosed())
                DBSql.close(connUpdate);
        }
        return true;
    }

    /**
     * 撤销定期服务
     * @param uc
     * @param ids
     * @return
     */
    public String revokeRecurringServices(UserContext uc, String ids) throws SQLException {

        Connection connUpdate = null;
        try {
            connUpdate = DBSql.open();
            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("该服务已经生成发票，请先删除发票");
                }

                if (bo.get("TYPE_ID").equals(cttConstant.ACCOUNT_DEDUCTION_TYPE_SERVICE)) //类型：服务
                {
                    BO ccsp = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD").addQuery("CONTRACT_ID=", bo.getString("CONTRACT_ID")).addQuery("ID=", bo.getString("OBJECT_ID")).detail();
                    ccsp.set("APPROVE_AND_POST_DATE", null);
                    ccsp.set("APPROVE_AND_POST_USER_ID", null);
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_PERIOD", ccsp, connUpdate);

                } else if (bo.get("TYPE_ID").equals(cttConstant.ACCOUNT_DEDUCTION_TYPE_SERVICE_AJUST)) //类型：服务调整
                {
                    BO ccsa = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST").addQuery("CONTRACT_ID=", bo.getString("CONTRACT_ID")).addQuery("ID=", bo.getString("OBJECT_ID")).detail();
                    ccsa.set("APPROVE_AND_POST_DATE", null);
                    ccsa.set("APPROVE_AND_POST_USER_ID", null);
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_SERVICE_ADJUST", ccsa, connUpdate);

                } else if (bo.get("TYPE_ID").equals(cttConstant.ACCOUNT_DEDUCTION_TYPE_INITIAL_COST)) //类型：初始费用
                {
                    BO ccc = SDK.getBOAPI().query("BO_EU_DNCTT_CONTRACT").addQuery("ID=", bo.getString("CONTRACT_ID")).detail();
                    ccc.set("SETUP_FEE_APPROVE_AND_POST_USER_ID", null);
                    ccc.set("SETUP_FEE_APPROVE_AND_POST_DATE", null);
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT", ccc, connUpdate);
                }
                bo.set("CLOSED", 1);
                SDK.getBOAPI().update("BO_EU_DNCRM_ACCOUNT_DEDUCTION", bo, connUpdate);
            }
            connUpdate.commit();
        } catch (SQLException e) {
            if (connUpdate != null && !connUpdate.isClosed())
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            return e.getMessage();
        } finally {
            if (connUpdate != null && !connUpdate.isClosed())
                DBSql.close(connUpdate);
        }

        return null;
    }

    /**
     * 撤销里程碑审批
     * @param uc
     * @param ids
     * @return
     * @throws SQLException
     */
    public String revokeMilestones(UserContext uc, String ids) throws SQLException {
        Connection connUpdate = null;
        try {
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);

            for (String id : ids.split(",")) {
                BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);
                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();
                    ccm.set("STATUS_ID", 1266);
                    SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_MILESTONES", ccm, connUpdate);
                }
                bo.set("CLOSED", 1);
                SDK.getBOAPI().update("BO_EU_DNCRM_ACCOUNT_DEDUCTION", bo, connUpdate);
            }

            connUpdate.commit();
        } catch (SQLException e) {
            if (connUpdate != null && !connUpdate.isClosed())
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            return e.getMessage();
        } finally {
            if (connUpdate != null && !connUpdate.isClosed())
                DBSql.close(connUpdate);
        }
        return null;
    }


    /**
     * 撤销成本审批
     * @param uc
     * @param ids
     * @return
     * @throws SQLException
     */
    public String revokeCost(UserContext uc, String ids) throws SQLException {
        Connection connUpdate = null;
        try {
            connUpdate = DBSql.open();
            connUpdate.setAutoCommit(false);
            for (String id : ids.split(",")) {
                BO bo = SDK.getBOAPI().get("BO_EU_DNCRM_ACCOUNT_DEDUCTION", id);
                bo.set("CLOSED", 1);
                SDK.getBOAPI().update("BO_EU_DNCRM_ACCOUNT_DEDUCTION", bo, connUpdate);

                BO ccc = SDK.getBOAPI().get("BO_EU_DNCTT_CONTRACT_COST", bo.getString("OBJECT_ID"));//合同成本
                ccc.set("BILL_STATUS", 0);
                ccc.set("EXTENDED_PRICE", multiply(ccc.get("UNIT_PRICE"), ccc.get("QUANTITY")));//总成本
                SDK.getBOAPI().update("BO_EU_DNCTT_CONTRACT_COST", ccc, connUpdate);

            }
            connUpdate.commit();
        } catch (SQLException e) {
            if (connUpdate != null && !connUpdate.isClosed())
                try {
                    connUpdate.rollback();
                } catch (SQLException ex) {
                    ex.printStackTrace();
                }
            return e.getMessage();
        } finally {
            if (connUpdate != null && !connUpdate.isClosed())
                DBSql.close(connUpdate);
        }
        return null;
    }

}
