package nccloud.api.so.saleinvoice.operator;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import nc.bs.framework.common.InvocationInfoProxy;
import nc.bs.framework.common.NCLocator;
import nc.bs.logging.Logger;
import nc.itf.uap.IUAPQueryBS;
import nc.jdbc.framework.processor.ColumnProcessor;
import nc.jdbc.framework.processor.MapProcessor;
import nc.pubitf.so.m32.api.ISaleinvoiceQueryAPI;
import nc.vo.pub.BusinessException;
import nc.vo.pub.lang.UFBoolean;
import nc.vo.pub.lang.UFDate;
import nc.vo.pub.lang.UFDouble;
import nc.vo.pubapp.calculator.HslParseUtil;
import nc.vo.pubapp.pattern.model.entity.bill.AbstractBill;
import nc.vo.pubapp.pattern.tool.performance.DeepCloneTool;
import nc.vo.pubapp.pflow.PfUserObject;
import nc.vo.scmpub.res.billtype.SOBillType;
import nc.vo.so.m32.entity.SaleInvoiceBVO;
import nc.vo.so.m32.entity.SaleInvoiceHVO;
import nc.vo.so.m32.entity.SaleInvoiceVO;
import nc.vo.sscivm.ivsale.IVApplicationAggVO;
import nc.vo.sscivm.ivsale.IVApplicationBodyVO;
import nc.vo.sscivm.ivsale.IVApplicationHeadVO;
import nccloud.api.baseapp.exchange.convert.OpenApiConvertDataObject;
import nccloud.api.baseapp.exchange.convert.OpenApiConvertDataResult;
import nccloud.api.rest.utils.ResultMessageUtil;
import nccloud.commons.lang.StringUtils;
import nccloud.dto.scmpub.script.entity.SCMScriptResultDTO;
import nccloud.framework.core.exception.ExceptionUtils;
import nccloud.itf.sscivm.ivsale.service.IVApplicationInvoiceService;
import nccloud.pubitf.riart.pflow.CloudPFlowContext;
import nccloud.pubitf.riart.pflow.ICloudScriptPFlowService;
import nccloud.pubitf.scmpub.commit.service.IBatchRunScriptService;
import nccloud.pubitf.scmpub.ssc.service.ISSCService;
import nccloud.pubitf.so.saleinvoice.service.ISaleInvoiceToTaxInvService;
import nccloud.pubitf.ssctp.sscbd.lientage.ISSClientageMatchService;
import org.json.JSONString;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 销售发票红冲接口服务实现类
 *
 * @author mzr
 * @date 20250417
 */
public class IAPISaleInvMaitainImpl {

    public static String fplxStr = "";// 开票申请发票类型

    public static DeepCloneTool deepCloneTool;

    public static DeepCloneTool getCloneTool() {
        if (deepCloneTool == null) {
            deepCloneTool = new DeepCloneTool();
        }
        return deepCloneTool;
    }

    /**
     * 蓝字发票保存提交
     *
     * @param jobject BIP合同平台传参
     * @return
     * @throws Exception
     */
    public JSONString saveBlue(JSONObject jobject) throws Exception {
        JSONObject ufinterfaceObj = jsonObjectAss(jobject);
        String tsType = ufinterfaceObj.getString("tsType");
        if (tsType != null && tsType.equals("1")) {
            Exception e = new NullPointerException("销售订单查询关联失败！");
            return ResultMessageUtil.exceptionToJSON(e);
        }
        JSONObject bjects = ufinterfaceObj.getJSONObject("ufinterface");
        String billtype = bjects.getString("billtype");
        String account = bjects.getString("account");
        String omscode = bjects.getString("orgcode");
        String groupcode = bjects.getString("groupcode");

        if (billtype != null && account != null && groupcode != null && "32".equals(billtype)) {
            OpenApiConvertDataObject openApiconvertData0bject = new OpenApiConvertDataObject();
            openApiconvertData0bject.setAccount(account);
            openApiconvertData0bject.setBilltype(billtype);
            openApiconvertData0bject.setGroupcode(groupcode);
            openApiconvertData0bject.setOpenApiJsonData(ufinterfaceObj);

            OpenApiJsonConvertToExChangeXmlService oService = new OpenApiJsonConvertToExChangeXmlService();
            OpenApiConvertDataResult r = oService.changeToExchangeData(openApiconvertData0bject);
            JSONObject returnJson = r.getDesc();
            String content = "";
            // 逐层解析 JSON 数据
            JSONObject ufinterface = returnJson.getJSONObject("ufinterface");
            if (ufinterface != null) {
                JSONArray sendResultArray = ufinterface.getJSONArray("sendresult");
                if (sendResultArray != null && !sendResultArray.isEmpty()) {
                    // 提取 content 字段
                    content = sendResultArray.getJSONObject(0).getString("content");
                }
            }
            if (content == null || content.isEmpty()) {
                return r != null
                        ? ResultMessageUtil.toJSON(r.getDesc(), "0")
                        : ResultMessageUtil.exceptionToJSON(new NullPointerException("未知异常"));
            }
            String[] ids = {content};
            SaleInvoiceVO[] saleInvoiceVO = querySaleInvoiceByIds(ids);
            SaleInvoiceVO saleInvoiceVO22 = saleInvoiceVO[0];
            SaleInvoiceHVO saleInvoiceHVO = saleInvoiceVO22.getParentVO();
            // 销售发票审核开始
            InvocationInfoProxy.getInstance().setUserId(saleInvoiceHVO.getApprover());
            CloudPFlowContext context = new CloudPFlowContext();
            context.setBillType("32");
            context.setBillVos(new SaleInvoiceVO[]{saleInvoiceVO[0]});
            ISSCService sscService = (ISSCService) NCLocator.getInstance().lookup(ISSCService.class);
            String[] actionNames = sscService.isStartSSCWorkFlow(
                    (AbstractBill[]) ((AbstractBill[]) context.getBillVos()), ISSClientageMatchService.BusiUnitTypeEnum.SO);
            context.setActionName("APPROVE");
            context.setTrantype("32-02");
            context.setBatch(false);
            context.setBatchUserObj(new PfUserObject[]{new PfUserObject()});
            IBatchRunScriptService service = (IBatchRunScriptService) NCLocator.getInstance()
                    .lookup(IBatchRunScriptService.class);
            SCMScriptResultDTO result = service.runBacth(context, SaleInvoiceVO.class);
            // 销售发票审核结束
            ISaleInvoiceToTaxInvService invoiceService = NCLocator.getInstance()
                    .lookup(ISaleInvoiceToTaxInvService.class);
            // 数电票(增值税专用发票)=31,数电票(普通发票)=32,数电纸质发票(机动车销售统一发票)=36,数电纸质发票(增值税专用发票)=33,数电纸质发票(普通发票)=34,
            // 增值税电子普通发票=1,增值税电子专用发票=2,增值税普通发票=3,增值税专用发票=4,增值税电子普通发票（成品油）=8,成品油普通发票(卷式)=9,
            // 成品油普通发票=10,成品油专用发票=11,增值税普通发票(卷式)=12,增值税专用发票(机动车)=99
            Logger.error("vbillcode:" + saleInvoiceHVO.getVbillcode() + ",fplxStr:" + fplxStr);
            invoiceService.issueTaxInvoice(saleInvoiceVO, fplxStr);

            JSONObject desc = r.getDesc();
            if (desc != null) {
                // 发票的全部信息都返回给合同平台
                desc.put("vos", saleInvoiceVO);
            }
            return ResultMessageUtil.toJSON(desc, "0");
        } else {
            Exception e = new NullPointerException(
                    "billtype:" + billtype + ",account:" + account + ",groupcode:" + groupcode);
            return ResultMessageUtil.exceptionToJSON(e);
        }
    }

    /**
     * 红字发票保存接口
     *
     * @param jobject BIP合同平台传参
     * @return
     * @throws Exception
     */
    public JSONString saveRed(JSONObject jobject) throws Exception {
        /**
         * 销售发票红冲：
         *  1.根据bip传参中的销售发票主表id查询erp的销售发票VO
         *  2.根据查询的销售发票VO生成红冲的销售发票
         */
        JSONObject bject = jobject.getJSONObject("billhead");
        if (bject == null) {
            return ResultMessageUtil.exceptionToJSON(new NullPointerException("billhead:null"));
        }
        // 销售发票id
        String csaleinvoiceid = bject.getString("vdef40");
        // 根据销售发票主实体id查询销售发票
        String[] ids = {csaleinvoiceid};
        SaleInvoiceVO[] saleInvoiceVOs = querySaleInvoiceByIds(ids);
        if (saleInvoiceVOs == null || saleInvoiceVOs.length <= 0) {
            return ResultMessageUtil.exceptionToJSON(new NullPointerException("未在系统中查询到对应的销售发票"));
        }
        // 调用函数根据原销售发票组装新红冲发票
        SaleInvoiceVO saleInvoiceRedRushVO = makeNewRedRushSaleInvoice(saleInvoiceVOs[0], bject);
        // 生成红冲发票
        Logger.error("====================inv-red-save-start===============");
        ICloudScriptPFlowService flowService = NCLocator.getInstance().lookup(ICloudScriptPFlowService.class);
        CloudPFlowContext context = new CloudPFlowContext();
        context.setBillType("32");
        context.setBillVos(new SaleInvoiceVO[]{saleInvoiceRedRushVO});
        context.setActionName("WRITE");
        SaleInvoiceVO[] returnSaveSaleInvoiceVOs = (SaleInvoiceVO[]) flowService.exeScriptPFlow(context);
        if (returnSaveSaleInvoiceVOs == null || returnSaveSaleInvoiceVOs.length <= 0) {
            Exception e = new Exception("销售红冲发票保存失败");
            return ResultMessageUtil.exceptionToJSON(e);
        }
        Logger.error("inv-red-save = " + JSONArray.toJSONString(returnSaveSaleInvoiceVOs));
        Logger.error("=====================inv-red-save-end===============");

        Logger.error("=====================inv-red-audit-start===============");
        // 生成红冲发票成功后，执行销售发票审批动作
        context.setBillType("32");
        context.setBillVos(returnSaveSaleInvoiceVOs);
        context.setActionName("APPROVE");
        SaleInvoiceVO[] returnApproveSaleInvoiceVOs = (SaleInvoiceVO[]) flowService.exeScriptPFlow(context);
        if (returnApproveSaleInvoiceVOs == null || returnApproveSaleInvoiceVOs.length <= 0) {
            throw new BusinessException("生成销售发票审核失败");
        }
        Logger.error("inv-red-audit = " + JSONArray.toJSONString(returnApproveSaleInvoiceVOs));
        Logger.error("=====================inv-red-audit-end===============");

        // 查询新生成红冲发票的信息
        String newPk = returnSaveSaleInvoiceVOs[0].getParentVO().getCsaleinvoiceid();
        String[] idsNew = {newPk};
        // String[] idsNew = {"1001A11000000FSB45YA"};
        SaleInvoiceVO[] redVos = querySaleInvoiceByIds(idsNew);

        /**
         * 开票申请红冲逻辑：
         *  生成开票申请的旧逻辑，走不通已废弃 1.根据销售发票号查询下游开票申请 2.根据原开票申请生成新红冲
         *  新逻辑：根据税务云的发票号查询蓝字发票，根据蓝字和已生成的红字发票共同生成红字的开票申请
         */
        /*IVApplicationAggVO returnSaveIVApplicationAggVO = saveIVApplication(bject, redVos);
        if (returnSaveIVApplicationAggVO != null) {
            Logger.error("inv-redApply = " + JSONArray.toJSONString(returnSaveIVApplicationAggVO));
            // 把红冲发票信息返回给调用方
            return ResultMessageUtil.toJSON(redVos, "接口调用成功");
        } else {
            Exception e = new Exception("销售红冲发票保存审核成功，开票申请保存失败");
            return ResultMessageUtil.exceptionToJSON(e);
        }*/
        // 把红冲发票信息返回给调用方
        return ResultMessageUtil.toJSON(redVos, "接口调用成功");
    }

    /**
     * 蓝字发票封装给交换平台的传参
     *
     * @param originalJson
     * @return
     * @throws Exception
     */
    public JSONObject jsonObjectAss(JSONObject originalJson) throws Exception {
        try {
            // 构建目标 JSON 格式
            JSONObject resultJson = new JSONObject();
            // 添加 ufinterface 字段
            JSONObject ufinterface = new JSONObject();
            ufinterface.put("billtype", "32");
            ufinterface.put("sender", "BIP_NC");
            ufinterface.put("level", "0");
            ufinterface.put("replace", "Y");
            ufinterface.put("roottag", "bill");
            ufinterface.put("isexchange", "Y");
            ufinterface.put("account", "01");
            ufinterface.put("groupcode", "00");
            // 处理 bill 数组
            JSONArray billArray = new JSONArray();
            JSONObject bill = new JSONObject();
            // billhead 结构
            JSONObject billhead = new JSONObject();
            billhead = originalJson.getJSONObject("billhead");// 传入JSON对象
            billhead.put("pk_org_v", originalJson.getJSONObject("billhead").getString("pk_org"));// 开票组织版本=开票组织
            billhead.put("pk_group", "00");// 集团
            fplxStr = originalJson.getJSONObject("billhead").getString("vdef22");
            billhead.put("vtrantypecode", "32-02");// 发票类型编码
            billhead.put("cbiztypeid", "SO01");// 业务流程
            billhead.put("approver", "BIP");// 审批人
            billhead.put("fstatusflag", 1);// 单据状态
            billhead.put("billmaker", "BIP");// 制单人
            billhead.put("csendcountryid", "CN");// 发货国家
            billhead.put("crececountryid", "CN");// 收货国家
            billhead.put("ctaxcountryid", "CN");// 报税国家
            // 2025-2-8付业要求修改，根据发票类型Q（自定义档案）对照发票类型
            String ctrantypeidStr = "32-02";// 默认普通发票
            if (fplxStr != null && fplxStr.equals("31")) {
                ctrantypeidStr = "32-01";// 数电票(增值税专用发票)
            } else if (fplxStr != null && fplxStr.equals("32")) {
                ctrantypeidStr = "32-02";// 数电票(普通发票)
            } else if (fplxStr != null && fplxStr.equals("36")) {
                ctrantypeidStr = "32-Cxx-03";// 机动车销售发票
            }
            billhead.put("ctrantypeid", ctrantypeidStr);// 发票类型
            // billhead.put("ctrantypeid", "32-02");//发票类型
            billhead.put("fbuysellflag", "1");// 购销类型
            billhead.put("creator", "BIP");// 创建人
            billhead.put("ccurrencyid", "CNY");// 本位币
            billhead.put("corigcurrencyid", "CNY");// 币种
            billhead.put("nexchangerate", 1);// 折本汇率
            billhead.put("btriatradeflag", 0);// 三角贸易
            JSONObject newItem = new JSONObject();
            JSONObject otherJson = new JSONObject();
            JSONArray csaleinvoicebid = new JSONArray();
            // 遍历原始数据中的 csaleinvoicebid 数组
            for (int i = 0; i < originalJson.getJSONObject("billhead").getJSONArray("csaleinvoicebid").size(); i++) {
                JSONObject item = originalJson.getJSONObject("billhead").getJSONArray("csaleinvoicebid")
                        .getJSONObject(i);

                JSONObject itemDetails = new JSONObject();
                itemDetails = item;
                itemDetails.put("carorgid", originalJson.getJSONObject("billhead").getString("pk_org"));// 应收组织
                itemDetails.put("csendstockorgid", originalJson.getJSONObject("billhead").getString("pk_org"));// 库存组织原始版本
                itemDetails.put("cmaterialid", item.getString("cmaterialvid"));// 物料编码
                Map<String, Object> value2 = getSaleorderVo(item.getString("csrcbid"));
                if (value2 == null) {
                    resultJson = new JSONObject();
                    resultJson.put("tsType", "1");
                    return resultJson;
                }
                itemDetails.put("vfirsttype", "30");// 源头单据类型
                itemDetails.put("vfirstcode", value2.get("vbillcode"));// 源头单据号
                itemDetails.put("blargessflag", value2.get("blargessflag"));// 赠品
                itemDetails.put("vfirsttrantype", "30-01");// 源头交易类型
                itemDetails.put("vfirstrowno", value2.get("crowno"));// 源头单据行号
                itemDetails.put("cfirstid", item.getString("csrcid"));// 源头单据主表
                itemDetails.put("cfirstbid", item.getString("csrcbid"));// 源头单据子表
                itemDetails.put("vsrctype", "30");// 来源单据类型
                itemDetails.put("vsrccode", value2.get("vbillcode"));// 来源单据号
                itemDetails.put("vsrctrantype", "30-01");// 来源交易类型
                itemDetails.put("vsrcrowno", value2.get("crowno"));// 来源单据行号
                // 通过 ntaxrate 获取 taxcode
                String taxcodeStr = getTaxcode(item.getString("ntaxrate"));
                // 将 taxcodeStr 放入 bodyMap
                itemDetails.put("ctaxcodeid", taxcodeStr);// 税码
                itemDetails.put("ftaxtypeflag", 1);// 扣税类别
                itemDetails.put("pk_group", "00");// 集团
                itemDetails.put("pk_org", originalJson.getJSONObject("billhead").getString("pk_org"));// 开票组织
                // 单位
                String castunitid = item.getString("castunitid");
                itemDetails.put("cunitid", castunitid);// 主单位
                itemDetails.put("cqtunitid", castunitid);// 报价单位
                String vchangerate = value2.getOrDefault("vchangerate", "1/1") + "";
                itemDetails.put("vchangerate", vchangerate);// 换算率

                // 从销售订单中获取币种
                String currencycode = getString_TrimAsNull(value2.get("currencycode"));
                otherJson.put("corigcurrencyid", currencycode.isEmpty() ? "CNY" : currencycode);
                // 从销售订单中获取折本汇率
                BigDecimal nexchangerate = getUFDouble_NullAsZero(value2.get("nexchangerate")).toBigDecimal();
                otherJson.put("nexchangerate", nexchangerate);

                // 处理精度：数量(2)、单价(4位)和金额(2位)
                UFDouble nnum = new UFDouble(item.getString("nnum"));// 主数量
                nnum = nnum.setScale(2, UFDouble.ROUND_HALF_UP);
                UFDouble nqtorigprice = new UFDouble(item.getString("nqtorigprice"));// 无税单价
                nqtorigprice = nqtorigprice.setScale(4, UFDouble.ROUND_HALF_UP);
                UFDouble norigprice = new UFDouble(item.getString("norigprice"));// 主无税单价
                norigprice = norigprice.setScale(4, UFDouble.ROUND_HALF_UP);
                UFDouble norigmny = new UFDouble(item.getString("norigmny"));// 本币无税金额
                norigmny = norigmny.setScale(2, UFDouble.ROUND_HALF_UP);
                UFDouble nmny = new UFDouble(item.getString("nmny"));// 本币无税金额
                nmny = nmny.setScale(2, UFDouble.ROUND_HALF_UP);
                UFDouble norigtaxmny = new UFDouble(item.getString("norigtaxmny"));// 价税合计
                norigtaxmny = norigtaxmny.setScale(2, UFDouble.ROUND_HALF_UP);
                UFDouble norigtaxprice = new UFDouble(item.getString("norigtaxprice"));// 主含税单价
                norigtaxprice = norigtaxprice.setScale(4, UFDouble.ROUND_HALF_UP);
                UFDouble norignetprice = new UFDouble(item.getString("norignetprice"));// 主无税净价
                norignetprice = norignetprice.setScale(4, UFDouble.ROUND_HALF_UP);
                UFDouble nqtorigtaxprice = new UFDouble(item.getString("nqtorigtaxprice"));// 含税单价
                nqtorigtaxprice = nqtorigtaxprice.setScale(4, UFDouble.ROUND_HALF_UP);
                UFDouble ntax = new UFDouble(item.getString("ntax"));// 税额
                ntax = ntax.setScale(2, UFDouble.ROUND_HALF_UP);
                UFDouble norigtaxnetprice = new UFDouble(item.getString("norigtaxnetprice"));// 主含税净价
                norigtaxnetprice = norigtaxnetprice.setScale(4, UFDouble.ROUND_HALF_UP);
                UFDouble nnetprice = new UFDouble(item.getString("nnetprice"));// 主本币无税净价
                nnetprice = nnetprice.setScale(4, UFDouble.ROUND_HALF_UP);
                UFDouble ntaxmny = new UFDouble(item.getString("ntaxmny"));// 本币价税合计
                ntaxmny = ntaxmny.setScale(2, UFDouble.ROUND_HALF_UP);

                itemDetails.put("nastnum", nnum);// 数量
                itemDetails.put("nqtorigprice", nqtorigprice);// 无税单价
                itemDetails.put("norigprice", norigprice);// 主无税单价
                itemDetails.put("norigmny", norigmny);// 无税金额
                itemDetails.put("nmny", nmny);// 本币无税金额
                itemDetails.put("norigtaxmny", norigtaxmny);// 价税合计
                itemDetails.put("norigtaxprice", norigtaxprice);// 主含税单价
                itemDetails.put("norignetprice", norignetprice);// 主无税净价
                itemDetails.put("nqtorigtaxprice", nqtorigtaxprice);// 含税单价
                itemDetails.put("ntax", ntax);// 税额
                itemDetails.put("ncaltaxmny", nmny);// 计税金额
                itemDetails.put("norigtaxnetprice", norigtaxnetprice);// 主含税净价
                itemDetails.put("nnetprice", nnetprice);// 主本币无税净价
                itemDetails.put("ntaxmny", ntaxmny);// 本币价税合计
                itemDetails.put("vqtunitrate", vchangerate);// 报价换算率
                UFDouble[] measrateNums = HslParseUtil.parseHsl(vchangerate);
                UFDouble measrateNum = UFDouble.ONE_DBL;
                if (null != measrateNums && measrateNums.length != 0) {
                    measrateNum = measrateNums[0].div(measrateNums[1]).setScale(2, 4);
                }
                // 报价数量
                itemDetails.put("nqtunitnum", nnum.multiply(measrateNum).setScale(2, UFDouble.ROUND_HALF_UP));
                csaleinvoicebid.add(itemDetails);

            }
            newItem.put("item", csaleinvoicebid);
            billhead.put("csaleinvoicebid", newItem);
            if (otherJson != null && !otherJson.isEmpty()) {
                // 查询销售订单中的币种和汇率
                billhead.put("corigcurrencyid", otherJson.get("corigcurrencyid"));// 币种
                billhead.put("nexchangerate", otherJson.get("nexchangerate"));// 折本汇率
            }
            bill.put("billhead", billhead);
            bill.put("id", "");
            // 将 bill 数组添加到 ufinterface 中
            billArray.add(bill);
            ufinterface.put("bill", billArray);
            // 将 ufinterface 添加到最终的 resultJson 中
            resultJson.put("ufinterface", ufinterface);
            return resultJson;
        } catch (Exception e) {
            Logger.error("saveCommit-jsonObjectAss-exp: ", e);
            return (JSONObject) ResultMessageUtil.exceptionToJSON(e);
        }
    }

    private String getTaxcode(String taxrateStr) throws BusinessException {
        IUAPQueryBS queryBS = NCLocator.getInstance().lookup(IUAPQueryBS.class);
        String sql = " select code FROM bd_taxrate tt inner join bd_taxcode tc on tt.pk_taxcode=tc.pk_taxcode where tc.dr = 0 " +
                " AND taxrate='" + taxrateStr + "' AND ROWNUM = 1 AND tc.enablestate = 2 ";
        String taxcodeStr = (String) queryBS.executeQuery(sql, new ColumnProcessor());
        return taxcodeStr;
    }

    private Map<String, Object> getSaleorderVo(String csourcebillbidStr) throws BusinessException {
        IUAPQueryBS queryBS = NCLocator.getInstance().lookup(IUAPQueryBS.class);
        String sql = " select s.vbillcode,s.csaleorderid, s.corigcurrencyid," +
                " sb.csaleorderbid,sb.crowno,sb.blargessflag,sb.nexchangerate,sb.vchangerate," +
                " bdc.code currencycode" +
                " from so_saleorder_b sb" +
                " inner join so_saleorder s on sb.csaleorderid=s.csaleorderid" +
                " left join bd_currtype bdc on s.corigcurrencyid=bdc.pk_currtype" +
                " where sb.csaleorderbid='" + csourcebillbidStr + "' ";
        Map<String, Object> value2 = (Map<String, Object>) queryBS.executeQuery(sql, new MapProcessor());
        return value2;
    }

    /**
     * 根据主键查询销售发票
     *
     * @param ids
     * @return
     * @throws BusinessException
     */
    private SaleInvoiceVO[] querySaleInvoiceByIds(String[] ids) throws BusinessException {
        return NCLocator.getInstance().lookup(ISaleinvoiceQueryAPI.class).queryVOByIDs(ids);
    }

    /**
     * 构造红冲发票VO
     *
     * @param originalVO
     * @param bject
     * @return
     * @throws Exception
     */
    public SaleInvoiceVO makeNewRedRushSaleInvoice(SaleInvoiceVO originalVO, JSONObject bject) throws Exception {
        try {
            SaleInvoiceVO saleInvoiceVO = new SaleInvoiceVO();
            // 发票主实体
            SaleInvoiceHVO saleInvoiceHVO = originalVO.getParentVO();
            // 发票子实体数组
            SaleInvoiceBVO[] saleInvoiceBVOs = originalVO.getChildrenVO();
            // 发票新主实体
            SaleInvoiceHVO newSaleInvoiceHVO = new SaleInvoiceHVO();

            // 单据日期
            UFDate dbilldate = new UFDate(bject.getString("dbilldate"));
            // 制单日期
            UFDate dmakedate = new UFDate(bject.getString("creationtime"));

            // 子实体属性赋值
            UFDouble sumNum = new UFDouble(); // 合计数量
            UFDouble sumNtax = new UFDouble(); // 合计税额
            UFDouble sumNcaltaxmny = new UFDouble(); // 合计计税金额
            UFDouble sumNorigmny = new UFDouble(); // 合计无税金额
            UFDouble sumNorigtaxmny = new UFDouble(); // 合计价税合计
            int i = 0;
            // 参数子实体数组
            com.alibaba.fastjson.JSONArray bvoJsonArr = (com.alibaba.fastjson.JSONArray) bject.get("csaleinvoicebid");
            List<JSONObject> bvoList = bvoJsonArr.toJavaList(JSONObject.class);
            SaleInvoiceBVO[] newSaleInvoiceBVOs = new SaleInvoiceBVO[bvoList.size()]; // 新子实体
            for (SaleInvoiceBVO saleInvoiceBVO : saleInvoiceBVOs) {
                String csaleinvoicebid = saleInvoiceBVO.getCsaleinvoicebid(); // 子表id
                // 筛选参数红冲的子表集合
                List<JSONObject> newBvoArr = bvoList.stream().filter(item -> {
                    String parCsaleinvoicebid = item.getString("vbdef15") + ""; // 子表id
                    return csaleinvoicebid.equals(parCsaleinvoicebid);
                }).toList();
                if (newBvoArr.size() <= 0) {
                    continue;
                }
                JSONObject bipBvoJson = newBvoArr.get(0); // 参数子表
                SaleInvoiceBVO newInvBVO = new SaleInvoiceBVO();
                // 克隆取值原子表销售发票
                newInvBVO = (SaleInvoiceBVO) getCloneTool().deepClone(saleInvoiceBVO);
                // 赋值之后修改子实体红冲时字段
                newInvBVO.setCsaleinvoicebid(null); // 发票子实体id
                newInvBVO.setDbilldate(dbilldate); // 开票日期
                String vchangerate = "1/1"; // 换算率
                Map<String, Object> soMap = getSaleOrderInfo(bipBvoJson.getString("csrcbid"));
                if (soMap != null && StringUtils.isNotEmpty(soMap.getOrDefault("vchangerate", "") + "")) {
                    vchangerate = soMap.get("vchangerate") + ""; // 销售订单的换算率
                }
                newInvBVO.setVchangerate(vchangerate); // 换算率
                UFDouble[] vchangerateNums = HslParseUtil.parseHsl(vchangerate);
                UFDouble vchangerateNum = UFDouble.ONE_DBL;
                if (null != vchangerateNums && vchangerateNums.length != 0) {
                    vchangerateNum = vchangerateNums[0].div(vchangerateNums[1]).setScale(2, 4);
                }
                // BIP合同平台的数量对应BIP2312的主数量，BIP2312的 数量=主数量*换算关系
                UFDouble nnum = new UFDouble(bipBvoJson.getString("nnum"));
                UFDouble nastnum = nnum.multiply(vchangerateNum).setScale(2, 4);
                newInvBVO.setNastnum(nastnum); // 数量
                newInvBVO.setNnum(nnum); // 主数量
                newInvBVO.setNqtunitnum(nastnum); // 报价数量
                newInvBVO.setNqtorigtaxprice(new UFDouble(bipBvoJson.getString("nqtorigtaxprice"))); // 含税单价
                newInvBVO.setNqtorigprice(new UFDouble(bipBvoJson.getString("nqtorigprice"))); // 无税单价
                newInvBVO.setNorigtaxprice(new UFDouble(bipBvoJson.getString("norigtaxprice"))); // 主含税单价
                newInvBVO.setNorigprice(new UFDouble(bipBvoJson.getString("norigprice"))); // 主无税单价
                newInvBVO.setNorigtaxnetprice(new UFDouble(bipBvoJson.getString("norigtaxnetprice"))); // 主含税净价
                newInvBVO.setNorignetprice(new UFDouble(bipBvoJson.getString("norignetprice"))); // 主无税净价
                newInvBVO.setNqtorigtaxnetprc(new UFDouble(bipBvoJson.getString("nqtorigtaxnetprc"))); // 含税净价
                newInvBVO.setNqtorignetprice(new UFDouble(bipBvoJson.getString("nqtorignetprice"))); // 无税净价
                newInvBVO.setNtaxprice(new UFDouble(bipBvoJson.getString("ntaxprice"))); // 主本币含税单价
                newInvBVO.setNprice(new UFDouble(bipBvoJson.getString("nprice"))); // 主本币无税单价
                newInvBVO.setNtaxnetprice(new UFDouble(bipBvoJson.getString("ntaxnetprice"))); // 主本币含税净价
                newInvBVO.setNnetprice(new UFDouble(bipBvoJson.getString("nnetprice"))); // 主本币无税净价
                newInvBVO.setNqttaxprice(new UFDouble(bipBvoJson.getString("nqttaxprice"))); // 本币含税单价
                newInvBVO.setNqtprice(new UFDouble(bipBvoJson.getString("nqtprice"))); // 本币无税单价
                newInvBVO.setNqttaxnetprice(new UFDouble(bipBvoJson.getString("nqttaxnetprice"))); // 本币含税净价
                newInvBVO.setNqtnetprice(new UFDouble(bipBvoJson.getString("nqtnetprice"))); // 本币无税净价
                newInvBVO.setNtax(new UFDouble(bipBvoJson.getString("ntax"))); // 税额
                newInvBVO.setNorigmny(new UFDouble(bipBvoJson.getString("norigmny"))); // 无税金额
                newInvBVO.setNorigtaxmny(new UFDouble(bipBvoJson.getString("norigtaxmny"))); // 价税合计
                newInvBVO.setNmny(new UFDouble(bipBvoJson.getString("nmny"))); // 本币无税金额
                newInvBVO.setNtaxmny(new UFDouble(bipBvoJson.getString("ntaxmny"))); // 本币价税合计
                newInvBVO.setNcaltaxmny(new UFDouble(bipBvoJson.getString("nmny"))); // 计税金额=本币无税金额
                newInvBVO.setCopposesrcbid(bipBvoJson.getString("vbdef15")); // 对冲来源子表id

                newInvBVO.setCsaleinvoiceid(null); // 发票关联主表id
                newInvBVO.setNtotalcostnum(null); // 累计成本结算数量
                newInvBVO.setNtotalincomemny(null); // 累计确认应收金额
                newInvBVO.setNtotalincomenum(null); // 累计确认应收数量
                newInvBVO.setVbdef10(null); // 回写BIP成功标志
                newInvBVO.setVbdef14(bipBvoJson.getString("vbdef14")); // bip旗舰版发票明细主键

                newSaleInvoiceBVOs[i++] = newInvBVO;
                sumNum = sumNum.add(new UFDouble(bipBvoJson.getString("nnum")));
                sumNtax = sumNtax.add(new UFDouble(bipBvoJson.getString("ntax")));
                sumNcaltaxmny = sumNcaltaxmny.add(new UFDouble(bipBvoJson.getString("ncaltaxmny")));
                sumNorigmny = sumNorigmny.add(new UFDouble(bipBvoJson.getString("norigmny")));
                sumNorigtaxmny = sumNorigtaxmny.add(new UFDouble(bipBvoJson.getString("norigtaxmny")));
            }

            // 克隆取值原子表销售发票
            newSaleInvoiceHVO = (SaleInvoiceHVO) getCloneTool().deepClone(saleInvoiceHVO);
            // 赋值之后修改主实体红冲时字段
            newSaleInvoiceHVO.setCsaleinvoiceid(null);  // 发票主实体id
            newSaleInvoiceHVO.setVbillcode(null);  // 发票号
            newSaleInvoiceHVO.setDbilldate(dbilldate);  // 开票日期
            newSaleInvoiceHVO.setDmakedate(dmakedate);  // 制单日期
            newSaleInvoiceHVO.setBsubunitflag(UFBoolean.TRUE);  // 冲抵标记
            newSaleInvoiceHVO.setFopposeflag(2);  // 对冲标记 （2：对冲生成）
            newSaleInvoiceHVO.setFstatusflag(1);  // 单据状态 （1：自由态）
            newSaleInvoiceHVO.setNtotalastnum(null);  // 总数量
            newSaleInvoiceHVO.setNtotalorigsubmny(null);  // 冲抵金额
            newSaleInvoiceHVO.setNtotalorigmny(null);  // 价税合计
            newSaleInvoiceHVO.setVopposesrccode(saleInvoiceHVO.getVbillcode());  // 对冲来源发票号
            newSaleInvoiceHVO.setCopposesrcid(saleInvoiceHVO.getCsaleinvoiceid());  // 对冲来源发票id
            newSaleInvoiceHVO.setApprover(null);  // 审批人
            newSaleInvoiceHVO.setTaudittime(null);  // 审批日期
            newSaleInvoiceHVO.setVdef13(null);  // 发票号码
            newSaleInvoiceHVO.setVdef17(bject.getString("vdef17"));  // BIP申请单号
            newSaleInvoiceHVO.setVdef20(null);  // 电子发票回传标识
            newSaleInvoiceHVO.setVdef34(null);  // 回写BIP成功标志
            newSaleInvoiceHVO.setVdef35(null);  // 回写BIP失败原因
            newSaleInvoiceHVO.setVdef38(bject.getString("vdef38"));  // bip旗舰版发票主键

            // 组装VO
            saleInvoiceVO.setParentVO(newSaleInvoiceHVO);
            saleInvoiceVO.setChildrenVO(newSaleInvoiceBVOs);
            return saleInvoiceVO;
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
    }

    public IVApplicationAggVO saveIVApplication(JSONObject bject, SaleInvoiceVO[] redVos) throws Exception {
        SaleInvoiceHVO redHvo = redVos[0].getParentVO();
        SaleInvoiceBVO[] redBodyVos = redVos[0].getChildrenVO();
        // 获取 红字发票传开票申请的数据
        ApplicationRedInvoiceService applicationRedInvoiceService = new ApplicationRedInvoiceService();
        List<Map<String, String>> jeMap = applicationRedInvoiceService.getData(redVos);

        IVApplicationAggVO applyVo = null;
        // BIP合同平台传的蓝字销售发票的主键
        String invId = bject.getOrDefault("vdef40", "") + "";

        // 查询蓝字发票号对应的税务云开出的发票号
        String invCode = getSaleInvCode(invId);
        if (StringUtils.isBlank(invCode) || "null".equals(invCode)) {
            ExceptionUtils.wrapBusinessException("税务云的发票号不能为空，请等待税务云单号回写后再手动生单");
        }
        IVApplicationInvoiceService ivService = NCLocator.getInstance().lookup(IVApplicationInvoiceService.class);
        JSONObject jsonObject1 = new JSONObject();
        // 构造开票申请的VO
        IVApplicationAggVO[] applicationAggVOS = ivService.getRedInvoice("", invCode, jsonObject1);
        if (applicationAggVOS == null || applicationAggVOS.length == 0) {
            ExceptionUtils.wrapBusinessException("构造开票申请的VO失败！");
        }
        Logger.error("saveIVApplication-getRedInvoice-jsonObject1 = " + jsonObject1);
        // 开票申请的上游单据信息赋值
        String src_billid = redHvo.getCsaleinvoiceid();
        String src_pk_org = redHvo.getPk_org();
        String src_billno = redHvo.getVbillcode();
        // String src_billtypeid = "";
        String src_tradetypeid = redHvo.getCtrantypeid();
        applicationAggVOS[0].getParentVO().setSrc_jx_billid(src_billid);
        applicationAggVOS[0].getParentVO().setSrc_jx_billno(src_billno);
        applicationAggVOS[0].getParentVO().setSrc_jx_billtypeid("32");
        applicationAggVOS[0].getParentVO().setSrc_jx_sporg(src_pk_org);
        // applicationAggVOS[0].getParentVO().setSrc_jx_spr(src_spr);
        applicationAggVOS[0].getParentVO().setSrc_jx_tradetypeid(src_tradetypeid);

        Map<String, UFDouble> ntaxmnyMap = new HashMap<>();
        for (SaleInvoiceBVO redBodyVo : redBodyVos) {
            // vbdef6 商品名称 vbdef12 规格 vbdef13 型号
            String key = redBodyVo.getVbdef6() + redBodyVo.getVbdef12() + redBodyVo.getVbdef13();
            // ntaxmny	本币价税合计
            UFDouble ntaxmny = redBodyVo.getNtaxmny();
            ntaxmnyMap.put(key, ntaxmny);
        }
        Logger.error("saveIVApplication-ntaxmnyMap = " + ntaxmnyMap);
        JSONObject jsonObject = new JSONObject();
        // ntotalorigmny 价税合计
        UFDouble ntotalorigmny = redHvo.getNtotalorigmny() == null ? UFDouble.ZERO_DBL : redHvo.getNtotalorigmny();
        for (IVApplicationAggVO applicationAggVO : applicationAggVOS) {
            // 在开具红字发票时，如果红冲的发票为增值税专用发票，红字信息申请表不能为空
            IVApplicationHeadVO headVO = applicationAggVO.getParentVO();
            // 红字信息申请表-赋值
            // headVO.setHzxxsqb("红字信息申请表");

            IVApplicationBodyVO[] bodyVOs = applicationAggVO.getBodyVOs();
            UFDouble ntotalorigmnyDiv = ntotalorigmny.div(new UFDouble(bodyVOs.length)).setScale(2, UFDouble.ROUND_HALF_UP);
            for (IVApplicationBodyVO bodyVO : bodyVOs) {
                // bchcje 本次红冲金额的赋值(负数转正数)
                bodyVO.setBchcje(new UFDouble(ntotalorigmnyDiv).multiply(new UFDouble(-1)));
            }
        }
        // 红字发票生成开票申请动作
        IVApplicationAggVO[] ivApplicationVOs = ivService.makeRedInvoice(applicationAggVOS, jsonObject, jeMap);
        if (ivApplicationVOs != null && ivApplicationVOs.length > 0) {
            applyVo = ivApplicationVOs[0];
        }
        Logger.error("saveIVApplication-makeRedInvoice-msg = " + jsonObject);
        return applyVo;
    }


    private Map<String, Object> getSaleOrderInfo(String csaleorderbid) throws BusinessException {
        IUAPQueryBS queryBS = NCLocator.getInstance().lookup(IUAPQueryBS.class);
        String sql = " select s.vbillcode,s.csaleorderid, s.corigcurrencyid," +
                " sb.csaleorderbid,sb.crowno,sb.blargessflag,sb.nexchangerate," +
                " nvl(sb.vchangerate, '1/1') vchangerate, bdc.code currencycode" +
                " from so_saleorder_b sb" +
                " inner join so_saleorder s on sb.csaleorderid=s.csaleorderid" +
                " left join bd_currtype bdc on s.corigcurrencyid=bdc.pk_currtype" +
                " where sb.csaleorderbid='" + csaleorderbid + "' ";
        return (Map<String, Object>) queryBS.executeQuery(sql, new MapProcessor());
    }

    /**
     * 查询蓝票的税务云单号
     *
     * @param invId
     * @return
     * @throws BusinessException
     */
    private String getSaleInvCode(String invId) throws BusinessException {
        IUAPQueryBS queryBS = NCLocator.getInstance().lookup(IUAPQueryBS.class);
        String sql = " select nvl(vdef13, '') vdef13" +
                " from so_saleinvoice" +
                " where nvl(dr,0) = 0 and csaleinvoiceid = '" + invId + "' ";
        Map<String, String> resMap = (Map<String, String>) queryBS.executeQuery(sql, new MapProcessor());
        return resMap.getOrDefault("vdef13", "");
    }

    private String getString_TrimAsNull(Object value) {
        if ((value == null) || (value.toString().trim().isEmpty())) {
            return "";
        }
        return value.toString().trim();
    }

    private UFDouble getUFDouble_NullAsZero(Object value) {
        if ((value == null) || (value.toString().trim().isEmpty()) || (value.toString().trim().equals("~")))
            return UFDouble.ONE_DBL;
        if ((value instanceof UFDouble))
            return (UFDouble) value;
        if ((value instanceof BigDecimal)) {
            return new UFDouble((BigDecimal) value);
        }
        return new UFDouble(value.toString().trim());
    }

    /**
     * 构造红冲开票申请VO-废弃
     *
     * @param applyHeadVO          原开票申请主实体
     * @param ivApplicationBodyVOS 原开票申请子实体
     * @param bject                bip参数
     * @param redVos               新生成的红冲销售发票实体
     * @return
     * @throws Exception
     */
    public IVApplicationAggVO makeNewRedRushIVApplicationAggVO(IVApplicationHeadVO applyHeadVO, IVApplicationBodyVO[] ivApplicationBodyVOS, JSONObject bject, SaleInvoiceVO[] redVos) throws Exception {
        try {
            IVApplicationAggVO applicationAggVO = new IVApplicationAggVO();
            IVApplicationHeadVO newivApplicationHeadVO = new IVApplicationHeadVO(); // 新主实体

            // 取新生成红冲发票的主实体数据
            SaleInvoiceHVO rpSaleInvoiceHVO = redVos[0].getParentVO();

            // 主实体属性赋值
            // 克隆取值原子表开票申请
            newivApplicationHeadVO = (IVApplicationHeadVO) getCloneTool().deepClone(applyHeadVO);
            // 根据参数判断是否为部分红冲
            boolean isPartHCFlag = false;
            UFDouble paramNtotalorigmny = new UFDouble(bject.getString("ntotalorigmny")); // 参数价税合计（主表红冲金额）
            UFDouble ntotalorigmny = applyHeadVO.getJshj(); // 原开票申请主表价税合计
            // 红冲金额与开票申请源价税合计比较如果和值大于零，则为部分红冲
            if (ntotalorigmny.add(paramNtotalorigmny).compareTo(UFDouble.ZERO_DBL) > 0) {
                isPartHCFlag = true;
            }
            UFDouble sumXmsl = new UFDouble(); // 合计数量
            UFDouble sumXmje = new UFDouble(); // 合计金额
            UFDouble sumXmjshj = new UFDouble(); // 合计价税合计
            UFDouble sumBchcje = new UFDouble(); // 合计本次红冲金额
            UFDouble sumSe = new UFDouble(); // 合计税额
            // 子实体属性赋值
            int i = 0;
            // 参数子实体数组
            com.alibaba.fastjson.JSONArray bvoJsonArr = (com.alibaba.fastjson.JSONArray) bject.get("csaleinvoicebid");
            List<JSONObject> bvoList = bvoJsonArr.toJavaList(JSONObject.class);
            IVApplicationBodyVO[] newivApplicationBodyVOS = new IVApplicationBodyVO[bvoList.size()]; // 新子实体
            for (IVApplicationBodyVO ivApplicationBodyVO : ivApplicationBodyVOS) {
                String src_pkdetail = ivApplicationBodyVO.getSrc_pkdetail(); // 来源单据行id
                // 筛选参数红冲的子表集合
                List<JSONObject> newBvoArr = bvoList.stream().filter(item -> {
                    String parCsaleinvoicebid = item.getString("vbdef15") + ""; // 子表id
                    return src_pkdetail.equals(parCsaleinvoicebid);
                }).toList();
                if (newBvoArr.size() <= 0) {
                    continue;
                }
                JSONObject bipBvoJson = newBvoArr.get(0); // 参数子表
                IVApplicationBodyVO newivApplicationBodyVO = new IVApplicationBodyVO();
                // 克隆取值原子表开票申请
                newivApplicationBodyVO = (IVApplicationBodyVO) getCloneTool().deepClone(ivApplicationBodyVO);
                // 赋值之后修改子实体红冲时字段
                newivApplicationBodyVO.setPk_ivappdetail(null); // 开票申请子实体id
                newivApplicationBodyVO.setBillno(null); // 开票申请子实体单据号
                newivApplicationBodyVO.setSe(new UFDouble(bipBvoJson.getString("ntax"))); // 税额
                newivApplicationBodyVO.setXmsl(new UFDouble(bipBvoJson.getString("nnum"))); // 数量
                newivApplicationBodyVO.setXmje(new UFDouble(bipBvoJson.getString("norigmny"))); // 金额
                newivApplicationBodyVO.setXmjshj(new UFDouble(bipBvoJson.getString("norigtaxmny"))); // 价税合计
                // 本次红冲金额 = 本次参数红冲金额
                newivApplicationBodyVO.setBchcje(new UFDouble(bipBvoJson.getString("norigtaxmny")).multiply(new UFDouble(-1)));
                newivApplicationBodyVOS[i++] = newivApplicationBodyVO;
                sumSe = sumSe.add(new UFDouble(bipBvoJson.getString("ntax")));
                sumXmsl = sumXmsl.add(new UFDouble(bipBvoJson.getString("nnum")));
                sumXmje = sumXmje.add(new UFDouble(bipBvoJson.getString("norigmny")));
                sumXmjshj = sumXmjshj.add(new UFDouble(bipBvoJson.getString("norigtaxmny")));
                sumBchcje = sumBchcje.add(new UFDouble(bipBvoJson.getString("norigtaxmny")).multiply(new UFDouble(-1)));
            }
            // 赋值之后修改主实体红冲时字段
            newivApplicationHeadVO.setPk_ivapplication(null);  // 开票申请单主实体id
            newivApplicationHeadVO.setBillno(null);  // 单据号
            newivApplicationHeadVO.setPreparedate(new UFDate(bject.getString("dbilldate")));  // 单据日期
            newivApplicationHeadVO.setJshj(sumXmjshj);  // 价税合计
            newivApplicationHeadVO.setHjje(sumXmje);  // 合计金额
            newivApplicationHeadVO.setHjse(sumSe);  // 合计税额
            newivApplicationHeadVO.setBchcje(sumBchcje);  // 本次红冲金额
            newivApplicationHeadVO.setHzfp(UFBoolean.TRUE);  // 红字发票
            newivApplicationHeadVO.setTranstypecode("SSCIVA-kpsq");  // 交易类型编码-填充默认值
            newivApplicationHeadVO.setSrc_billtype(SOBillType.Invoice.getCode());  // 来源单据类型
            newivApplicationHeadVO.setSrc_tradetype(rpSaleInvoiceHVO.getCtrantypeid());  // 来源交易类型
            newivApplicationHeadVO.setSrc_pkbusibill(rpSaleInvoiceHVO.getCsaleinvoiceid());  // 来源单据id
            newivApplicationHeadVO.setSrc_billno(rpSaleInvoiceHVO.getVbillcode());  // 来源单据编号
            // 红冲原因：1=销货退回; 2=开票有误; 3=服务中止; 4=销售折让;
            newivApplicationHeadVO.setHcyy("1");

            // 组装VO
            applicationAggVO.setParentVO(newivApplicationHeadVO);
            applicationAggVO.setChildrenVO(newivApplicationBodyVOS);
            return applicationAggVO;
        } catch (Exception e) {
            throw new BusinessException(e.getMessage());
        }
    }

}
