global class DNUpsertBatch implements Database.Batchable<sObject>, Database.Stateful {
    public string rowData_Id;
    public transient static List<Id> orderReturnIdList;
    private class DNdetail {
        private string DeliveryNote;
        private Statu_Achievements_DN_details__c DNd;
    }

    global DNUpsertBatch() {
    }
    global DNUpsertBatch(string rowData_Id) {
        this.rowData_Id = rowData_Id;
    }
    global Database.QueryLocator start(Database.BatchableContext bc) {
        if (string.isBlank(rowData_Id)) {
            return Database.getQueryLocator(
                [
                    SELECT
                        Id,
                        Name,
                        Log__c,
                        ErrorLog__c,
                        Log2__c,
                        Log3__c,
                        Log4__c,
                        Log5__c,
                        Log6__c,
                        Log7__c,
                        Log8__c,
                        Log9__c,
                        Log10__c,
                        Log11__c,
                        Log12__c,
                        MessageGroupNumber__c,
                        retry_cnt__c,
                        DN_retry_cnt__c,
                        CreatedDate
                    FROM BatchIF_Log__c
                    WHERE
                        RowDataFlg__c = TRUE
                        AND IsUpsertDN__c = TRUE
                        AND (DN_retry_cnt__c = NULL
                        OR (DN_retry_cnt__c != 0
                        AND DN_retry_cnt__c < 3))
                ]
            );
        } else {
            return Database.getQueryLocator(
                [
                    SELECT
                        Id,
                        Name,
                        Log__c,
                        ErrorLog__c,
                        Log2__c,
                        Log3__c,
                        Log4__c,
                        Log5__c,
                        Log6__c,
                        Log7__c,
                        Log8__c,
                        Log9__c,
                        Log10__c,
                        Log11__c,
                        Log12__c,
                        MessageGroupNumber__c,
                        retry_cnt__c,
                        DN_retry_cnt__c,
                        CreatedDate
                    FROM BatchIF_Log__c
                    WHERE RowDataFlg__c = TRUE AND Id = :rowData_Id
                ]
            );
        }
    }
    global void execute(Database.BatchableContext BC, List<BatchIF_Log__c> rowDataList) {
        //add by rentx 20210727 start
        // date strdate = date.today();
        // String year = String.valueOf(strdate.year()) ;
        // String month = String.valueOf(strdate.month()) ;
        // String day = String.valueOf(strdate.day()) ;
        // String datestr = String.valueOf(year+month+day);
        //add by rentx 20210727 end

        BatchIF_Log__c rowData = rowDataList[0];
        rowData.IsUpsertDN__c = false;
        update rowData;
        //update by rentx  20210823
        //本次修改只针对于保有设备 之前使用年月日去插入时 会出现 同一天多批日志里有同一个设备 导致更新失败  现解决方案为精确到分
        Datetime nowTime = rowData.CreatedDate;
        String oyear = String.valueOf(nowTime.year());
        String omonth = String.valueOf(nowTime.month());
        String oday = String.valueOf(nowTime.day());
        String ohour = String.valueOf(nowTime.hour());
        String ominute = String.valueOf(nowTime.minute());
        String datestr = String.valueOf(oyear + omonth + oday + ohour + ominute);
        //update by rentx  20210823
        //之前使用年月日去插入时 会出现 同一天多批日志里有同一个设备 导致更新失败  现解决方案为精确到分

        StaticParameter.ConsumableAssetHanderTrigger = true;
        StaticParameter.EscapeConsumableOrderDetail2Trigger = true;
        Integer batch_retry_max_cnt = Integer.valueOf(System.Label.batch_retry_max_cnt);
        String logstr = rowData.MessageGroupNumber__c + ' start\n';
        BatchIF_Log__c iflog = new BatchIF_Log__c();

        iflog.Type__c = 'NFM110';
        iflog.MessageGroupNumber__c = rowData.MessageGroupNumber__c;
        iflog.Log__c = logstr;
        iflog.ErrorLog__c = '';
        insert iflog;
        String rowDataStr = NFMUtil.getRowDataStr(rowData);
        List<NFM110Rest.GeneralData> generalDataList = (List<NFM110Rest.GeneralData>) JSON.deserialize(
            rowDataStr,
            List<NFM110Rest.GeneralData>.class
        );
        if (generalDataList == null || generalDataList.size() == 0) {
            return;
        }
        // 电子签收单 start 优化原有select
        list<account> olympusAcclist = [
            SELECT Id, ParentId, AgentCode_Ext__c
            FROM Account
            WHERE AgentCode_Ext__c = '9999900' OR AgentCode_Ext__c = '9999999' OR AgentCode_Ext__c = '9999901'
        ];
        Account ocm, olympus, olympus_return;
        for (Account tempAcc : olympusAcclist) {
            if (tempAcc.AgentCode_Ext__c.equals('9999900')) {
                ocm = tempAcc;
            } else if (tempAcc.AgentCode_Ext__c.equals('9999999')) {
                olympus = tempAcc;
            } else if (tempAcc.AgentCode_Ext__c.equals('9999901')) {
                olympus_return = tempAcc;
            }
        }
        // 电子签收单 end
        NFM110Rest.isRunning = true;
        //orderoutIdList = new List<Id>();
        orderReturnIdList = new List<Id>();
        Savepoint sp = Database.setSavepoint();
        try {
            // 更新対応配列とMapなどをセット
            Set<String> managementCodeList = new Set<String>();
            Set<String> soNoList = new Set<String>();
            Set<String> inquiryNoList = new Set<String>();
            Set<String> productCodeList = new Set<String>();
            Set<String> productSerialNoList = new Set<String>();

            // CHAN-BBLAAP 汇总这条数据的DN号 start
            Set<String> DNNoSet = new Set<String>();
            // CHAN-BBLAAP 汇总这条数据的DN号 start
            for (nfm110rest.GeneralData infoH : generalDataList) {
                if (String.isBlank(infoH.DeliveryNote)) {
                    continue;
                }
                //汇总这条数据的DN号
                DNNoSet.add(infoH.DeliveryNote);

                if (infoH.DnInformation == null || infoH.DnInformation.size() == 0) {
                    continue;
                }
                // 病院と診療科の管理コード、TODO 販売店
                infoH.EndUserNo = NFMUtil.trimLeft(infoH.EndUserNo, '0');
                infoH.DepartmentNo = NFMUtil.trimLeft(infoH.DepartmentNo, '0');
                if (infoH.EndUserNo == null || infoH.EndUserNo == '') {
                    // 病院と診療科の管理コードがない場合、処理と飛ばす
                    continue;
                }
                managementCodeList.add(infoH.EndUserNo);

                if (String.isBlank(infoH.SoNo) == false) {
                    soNoList.add(infoH.SoNo);
                }
                // SWAG-BDV589 start
                string tempInquiryNo = '';
                // SWAG-BDV589 end
                if (String.isBlank(infoH.InquiryNo) == false) {
                    String[] Inquiry;
                    if (infoH.InquiryNo.contains(',')) {
                        Inquiry = infoH.InquiryNo.split(',');
                        // SWAG-BDV589 start
                        tempInquiryNo = Inquiry[0];
                        // SWAG-BDV589 end
                        inquiryNoList.add(Inquiry[0]);
                    } else {
                        //根据数据传的样式决定
                    }
                }
                if (!(infoH.ReturnMark == null || infoH.ReturnMark == '' || infoH.ReturnMark == '1' || infoH.ReturnMark == '3')) {
                    // ReturnMark = 上記以外の場合 Errorメッセージ出力
                    continue;
                }
                for (nfm110rest.DnInformation dnInfo : infoH.DnInformation) {
                    dnInfo.SerialNoorLotNo = checkSerialNoorLotNo(dnInfo.SerialNoorLotNo);
                    productCodeList.add(dnInfo.OTCode);
                    // CHAN-BBLAAP start
                    string Product_Serial_No = '';
                    if (dnInfo.SorLMark == 'S') {
                        Product_Serial_No = dnInfo.OTCode + ':' + dnInfo.SerialNoorLotNo;
                    } else {
                        Product_Serial_No = dnInfo.OTCode + ':' + dnInfo.SerialNoorLotNo + '(' + dnInfo.TracingCode + ')';
                    }
                    // CHAN-BBLAAP end
                    if (dnInfo.OTCode == null || dnInfo.OTCode == '' || dnInfo.SerialNoorLotNo == null || dnInfo.SerialNoorLotNo == '') {
                        // 商品コードがない場合、処理と飛ばす
                        continue;
                    }
                    // HHOA-AGN2EH start
                    //add by rentx 20210726 start
                    dnInfo.SerialNoorLotNo = dnInfo.SerialNoorLotNo == '.' ? datestr + dnInfo.OTCode : dnInfo.SerialNoorLotNo;
                    //update by rentx 20210726 end
                    productSerialNoList.add(dnInfo.SerialNoorLotNo);
                    productSerialNoList.add(dnInfo.SerialNoorLotNo + '(' + dnInfo.TracingCode + ')');
                }
            }
            // 电子签收单 start 额外检索end user
            List<Account> accList = [
                SELECT
                    Id,
                    Name,
                    Management_Code__c,
                    Department_Class__c,
                    Hospital__c,
                    ParentId,
                    RecordType.DeveloperName,
                    Department_Name__c,
                    OCM_man_province_txt__c
                FROM Account
                WHERE Management_Code__c IN :managementCodeList
            ];
            // HHOA-BLC8S6 update by vivek 2020-02-13 end
            Map<String, Account> accsMap = new Map<String, Account>();
            for (Account acc : accList) {
                accsMap.put(acc.Management_Code__c, acc);
            }
            Map<String, Opportunity> oppMap = new Map<String, Opportunity>();
            List<Opportunity> oppList = [
                SELECT
                    Id,
                    Purchase_Type__c,
                    Opportunity_No__c,
                    Strategic_department_Class_Name__c,
                    Sales_Root__c,
                    SAP_Province__c,
                    Trade__c,
                    Agency1__c,
                    Agency1__r.Name,
                    Agency1__r.OCM_man_province_txt__c,
                    Opportunity_Category__c,
                    OCM_man_province_cus__c,
                    Sales_assistant_name_text__c,
                    // 电子签收单 start
                    // 临时使用 用作试点经销商
                    Agency1__r.EsignTestAccount__c,
                    Agency1__r.EsignTestAccountType__c,
                    // 电子签收单 end
                    //集中采购询价 精琢技术 wql  start
                    Group_purchase_PCL__c,
                    //集中采购询价 精琢技术 wql  end
                    Is_Corrosion__c
                FROM Opportunity
                WHERE Opportunity_No__c IN :inquiryNoList
            ];

            for (Opportunity opp : oppList) {
                oppMap.put(opp.Opportunity_No__c, opp);
            }

            // 检索出DN 并生成DN Map
            Boolean flg = true;
            map<string, Statu_Achievements_DN__c> DNMap = new Map<string, Statu_Achievements_DN__c>();
            List<Statu_Achievements_DN__c> DNList = GetDNList(DNNoSet);
            // [select id, Name,
            //  DeliveryDate_Raw__c,
            //  DeliveryNote_Raw__c,
            //  DepartmentNo_Raw__c,
            //  EndUserNo_Raw__c,
            //  InquiryNo_Raw__c,
            //  ReturnMark_Raw__c,
            //  SoNo_Raw__c,
            //  Opportunity__c,
            //  Statu_Achievements__c,
            //  endUser__c
            //  //电子签收单试点用户赋值
            //  ,endUser__r.RecordType.Name
            //  // 电子签收单 start
            //  , Sales_assistant_name_text__c,
            //  RC_Manager__c,ReturnMark__c
            //  // 试点用户，届时需要删掉
            //  , EsignTestAccount__c
            //  // 电子签收单  end
            //  //ET召回品不生成签收单 start
            // , ETRecalledProducts__c
            //  //ET召回品不生成签收单 end
            //  //acc标识 start
            //  ,ACC_Text__c,
            //  Is_Corrosion__c,
            //  //无偿出库标识 thh 2021-11-01 start
            //  Statu_Achievements__r.orderType__c
            //  //无偿出库标识 thh 2021-11-01 end
            //  //acc标识 end
            //  from Statu_Achievements_DN__c where
            //  Name in: DNNoSet];
            for (Statu_Achievements_DN__c dn : DNList) {
                DNMap.put(dn.Name, dn);
            }
            List<Statu_Achievements_DN__c> UpsertDNList = new List<Statu_Achievements_DN__c>();

            //  需要插入的DN明细
            List<DNdetail> insertDNdetailList = new List<DNdetail>();

            // CHAN-BBLAAP  end
            List<Statu_Achievements__c> boList = [
                SELECT Id, Name
                FROM Statu_Achievements__c
                WHERE Name IN :soNoList AND OverviewStatus__c != '无效合同'
            ];
            Map<String, String> boMap = new Map<String, String>();
            for (Statu_Achievements__c bo : boList) {
                boMap.put(bo.Name, bo.Id);
            }

            List<Product2> prdList = [
                SELECT Id, Name, ProductCode, ProductCode_Ext__c, MDM_Model_No__c
                FROM Product2
                WHERE ProductCode_Ext__c IN :productCodeList
            ];
            Map<String, Product2> prdsMap = new Map<String, Product2>();
            for (Product2 prd : prdList) {
                prdsMap.put(prd.ProductCode_Ext__c, prd);
            }
            List<Asset> astList = [
                SELECT
                    Id,
                    Name,
                    Product2.ProductCode,
                    Product2.Dealer_special_Object__c,
                    Product2.Dealer_Object__c,
                    SerialNumber,
                    Product_Serial_No__c,
                    Repair_Count__c,
                    TracingCode__c,
                    Product2Id,
                    AccountId,
                    Account.Management_Code__c,
                    Department_Class__c,
                    Hospital__c,
                    Posting_Date__c,
                    InstallDate,
                    SLMark__c,
                    MDM_Model_No__c,
                    OT_CODE__c,
                    Guarantee_period_for_products__c,
                    Installation_Site__c,
                    Delete_Flag__c,
                    Return_Flag__c,
                    Extend_Gurantee_DateTo__c,
                    guarantee_period__c,
                    Proviston_period__c
                FROM Asset
                WHERE SerialNumber IN :productSerialNoList AND IsCompetitorProduct = FALSE AND Information_From__c != '失单'
            ];
            Map<String, Asset> astsMap = new Map<String, Asset>();
            for (Asset ast : astList) {
                astsMap.put(ast.MDM_Model_No__c + ast.SerialNumber, ast);
            }

            // 电子签收单 start
            // key 是省份名字， value 是对应的营业管理部担当 1 ,2 ,3
            Map<String, String> provinceOwnerMap = new Map<String, String>();
            //集中采购询价 构建助理map key是省份名称 value是助理
            Map<String, String> provinceGIMap = new Map<String, String>();
            //检索OCSM管理省对象
            //集中采购询价 增加检索助理
            List<OCM_Management_Province__c> ompList = [SELECT id, Name, SalesManage__c, GI_assistant__c FROM OCM_Management_Province__c];
            //存放map<省，担当>
            for (OCM_Management_Province__c omp : ompList) {
                provinceOwnerMap.put(omp.Name, omp.SalesManage__c);
                provinceGIMap.put(omp.Name, omp.GI_assistant__c);
            }
            // 电子签收单 end

            for (NFM110Rest.GeneralData infoH : generalDataList) {
                String InquiryNoStr = '';
                String InquiryNoStr2 = '';
                if (String.isBlank(infoH.InquiryNo) == false) {
                    if (infoH.InquiryNo.contains(',')) {
                        InquiryNoStr = infoH.InquiryNo.split(',')[0];
                        if (infoH.InquiryNo.split(',').size() > 1) {
                            InquiryNoStr2 = infoH.InquiryNo.split(',')[1];
                        }
                    }
                    if (String.isBlank(InquiryNoStr)) {
                        iflog.ErrorLog__c += 'Error! SoNo[' + infoH.SoNo + '] InquiryNo is required. This DN is skipped.\n';
                        continue;
                    }
                }
                if (String.isBlank(infoH.DeliveryNote)) {
                    iflog.ErrorLog__c += 'Error! SoNo[' + infoH.SoNo + '] DeliveryNote is required. This DN is skipped.\n';
                    continue;
                }
                if (infoH.DnInformation == null || infoH.DnInformation.size() == 0) {
                    iflog.ErrorLog__c +=
                        'Error! SoNo[' +
                        infoH.SoNo +
                        '] DeliveryNote[' +
                        infoH.DeliveryNote +
                        '] DnInformation is required. This DN is skipped.\n';
                    continue;
                }
                if (infoH.EndUserNo == null || infoH.EndUserNo == '') {
                    iflog.ErrorLog__c +=
                        'Error! SoNo[' +
                        infoH.SoNo +
                        '] DeliveryNote[' +
                        infoH.DeliveryNote +
                        '] EndUserNo is required. This DN is skipped.\n';
                    continue;
                }
                if (accsMap.get(infoH.EndUserNo) == null) {
                    iflog.ErrorLog__c +=
                        'Error! SoNo[' +
                        infoH.SoNo +
                        '] DeliveryNote[' +
                        infoH.DeliveryNote +
                        '] EndUserNo[' +
                        infoH.EndUserNo +
                        ']NotExist. This DN is skipped.\n';
                    continue;
                } else if (
                    accsMap.get(infoH.EndUserNo).RecordType.DeveloperName != 'HP' &&
                    accsMap.get(infoH.EndUserNo).RecordType.DeveloperName != 'Agency' &&
                    accsMap.get(infoH.EndUserNo).RecordType.DeveloperName != 'AgencyContract'
                ) {
                    iflog.ErrorLog__c +=
                        'Error! SoNo[' +
                        infoH.SoNo +
                        '] DeliveryNote[' +
                        infoH.DeliveryNote +
                        '] EndUserNo[' +
                        infoH.EndUserNo +
                        ']Is Not HP or Agency(Contract). This DN is skipped.\n';
                    continue;
                }
                Statu_Achievements_DN__c tempSADN = DNMap.get(infoH.DeliveryNote);
                if (infoH.DeliveryNote != null) {
                    if (tempSADN == null) {
                        tempSADN = new Statu_Achievements_DN__c();
                        tempSADN.Statu_Achievements__c = null;
                        tempSADN = SetSADN(tempSADN, infoH, boMap, oppMap, accsMap, provinceOwnerMap, provinceGIMap);
                        checkSA(tempSADN, UpsertDNList, iflog, infoH);
                    } else {
                        if (
                            infoH.SoNo != tempSADN.SoNo_Raw__c ||
                            infoH.InquiryNo != tempSADN.InquiryNo_Raw__c ||
                            infoH.ReturnMark != tempSADN.ReturnMark_Raw__c ||
                            infoH.DeliveryDate != tempSADN.DeliveryDate_Raw__c ||
                            infoH.EndUserNo != tempSADN.EndUserNo_Raw__c ||
                            infoH.DepartmentNo != tempSADN.DepartmentNo_Raw__c ||
                            infoH.DeliveryNote != tempSADN.DeliveryNote_Raw__c
                        ) {
                            string SADNID = tempSADN.id;
                            // 电子签收单  额外增加营业管理部担当赋值 start
                            tempSADN = SetSADN(tempSADN, infoH, boMap, oppMap, accsMap, provinceOwnerMap, provinceGIMap);
                            // 电子签收单  额外增加营业管理部担当赋值 end
                            tempSADN.id = SADNID;
                            checkSA(tempSADN, UpsertDNList, iflog, infoH);
                        }
                    }
                }
                integer tempDNdetailNumber = 0;
                for (NFM110Rest.DnInformation dnInfo : infoH.DnInformation) {
                    dnInfo.SerialNoorLotNo = checkSerialNoorLotNo(dnInfo.SerialNoorLotNo);
                    // CHAN-BBLAAP start
                    Product2 prd = prdsMap.get(dnInfo.OTCode);
                    Asset ast;
                    if (dnInfo.SorLMark == 'S') {
                        ast = astsMap.get(prd.MDM_Model_No__c + dnInfo.SerialNoorLotNo);
                    } else {
                        ast = astsMap.get(prd.MDM_Model_No__c + dnInfo.SerialNoorLotNo + '(' + dnInfo.TracingCode + ')');
                    }
                    //  CHAN-BBLAAP 插入保有设备明细时插入对应DN start
                    boolean tempReturnFlg = infoH.ReturnMark == '1' ? true : false;
                    DNdetail tempDNdetail = SetSADNDetails(
                        infoH.DeliveryNote,
                        tempReturnFlg,
                        dnInfo,
                        ast,
                        dnInfo.SerialNoorLotNo != null && dnInfo.SerialNoorLotNo.equals('.') ? tempDNdetailNumber++ : tempDNdetailNumber
                    );
                    insertDNdetailList.add(tempDNdetail);
                }
            }
            if (UpsertDNList.size() > 0 && !System.Test.isRunningTest()) {
                upsert UpsertDNList Statu_Achievements_DN__c.Fields.Name;
            }
            for (Statu_Achievements_DN__c tempSADN : UpsertDNList) {
                DNMap.put(tempSADN.Name, tempSADN);
            }
            DNList = GetDNList(DNMap.keySet());
            // [select id, Name,
            //  DeliveryDate_Raw__c,
            //  DeliveryNote_Raw__c,
            //  DepartmentNo_Raw__c,
            //  EndUserNo_Raw__c,
            //  InquiryNo_Raw__c,
            //  ReturnMark_Raw__c,
            //  SoNo_Raw__c,
            //  Opportunity__c,
            //  Statu_Achievements__c,
            //  endUser__c
            //  //电子签收单试点用户赋值
            //  ,endUser__r.RecordType.Name
            //  // 电子签收单 start
            //  , Sales_assistant_name_text__c,
            //  RC_Manager__c,ReturnMark__c
            //  // 试点用户，届时需要删掉
            //  , EsignTestAccount__c
            //  // 电子签收单  end
            //  //ET召回品不生成签收单 start
            // , ETRecalledProducts__c
            //  //ET召回品不生成签收单 end
            //  //acc标识 start
            //  ,ACC_Text__c,
            //  Is_Corrosion__c,
            //  Statu_Achievements__r.orderType__c
            //  //acc标识 end
            //  from Statu_Achievements_DN__c where
            //  Name in: DNMap.keySet()];
            for (Statu_Achievements_DN__c dn : DNList) {
                DNMap.put(dn.Name, dn);
            }

            list<Statu_Achievements_DN_details__c> insertSADNdetailList = new List<Statu_Achievements_DN_details__c>();
            //电子签收单 虚进虚出 tracingcode为空不生成签收单 精琢技术 2020/12/08 start
            map<string, Statu_Achievements_DN__c> DNRestructureMap = new Map<string, Statu_Achievements_DN__c>();
            //电子签收单 虚进虚出 tracingcode为空不生成签收单 精琢技术 2020/12/08 end
            for (DNdetail tempDNd : insertDNdetailList) {
                if (DNMap.containskey(tempDNd.DeliveryNote)) {
                    Statu_Achievements_DN__c tempSADN = DNMap.get(tempDNd.DeliveryNote);
                    tempDNd.DNd.Statu_Achievements_DN__c = tempSADN.id;
                    tempDNd.DNd.DNName__c = tempSADN.Name;
                    tempDNd.DNd.Statu_Achievements__c = tempSADN.Statu_Achievements__c;
                    insertSADNdetailList.add(tempDNd.DNd);

                    //电子签收单 虚进虚出 tracingcode为空不生成签收单 精琢技术 2020/12/08 start

                    if (tempSADN.ACC_Text__c != 'Y') {
                        if (String.isBlank(tempDNd.DNd.TracingCode_Raw__c)) {
                            tempSADN.ACC_Text__c = 'Y';
                        }
                    }
                    DNRestructureMap.put(tempSADN.Name, tempSADN);

                    //电子签收单 虚进虚出 tracingcode为空不生成签收单 精琢技术 2020/12/08 end
                }
            }
            List<Statu_Achievements_DN_details__c> DNdetailList = [
                SELECT Id
                FROM Statu_Achievements_DN_details__c
                WHERE Statu_Achievements_DN__r.Name IN :DNMap.keySet()
            ];

            if (DNdetailList.size() > 0 && !System.Test.isRunningTest()) {
                delete DNdetailList;
            }
            if (insertSADNdetailList.size() > 0 && !System.Test.isRunningTest()) {
                insert insertSADNdetailList;
            }
            // 电子签收单  2020/07/17 start
            // 检索出签收单，并生成签收单map key 为DN号
            map<string, eSignForm__c> eSFMap = new Map<string, eSignForm__c>();
            List<eSignForm__c> eSFList = [
                SELECT id, DNName__c, Hazardous_Chemicals__c, Statu_Achievements__c, Statu_Achievements_DN__c
                FROM eSignForm__c
                WHERE DNName__c IN :DNNoSet
            ];
            for (eSignForm__c eSF : eSFList) {
                eSFMap.put(eSF.DNName__c, eSF);
            }
            //电子签收单 虚进虚出 明细为racing code 为空的 不生成签收单 20201207 精琢技术 start
            list<eSignForm__c> UpsertESFList = SetupeSignForm(eSFMap, DNRestructureMap.values());

            //电子签收单 虚进虚出 明细为racing code 为空的 不生成签收单 20201207 精琢技术 end
            if (UpsertESFList.size() > 0) {
                upsert UpsertESFList;
            }
            // key 为 对应DN的ID
            map<string, eSignForm__c> eSFAllMap = new Map<string, eSignForm__c>();
            for (eSignForm__c tempeSignForm : UpsertESFList) {
                eSFAllMap.put(tempeSignForm.DNName__c, tempeSignForm);
            }

            // 检索现有签收单明细，并生成签收单明细map key 为DN号 + 产品OTCode
            map<string, eSignFormLineItem__c> eSFLMap = new Map<string, eSignFormLineItem__c>();
            List<eSignFormLineItem__c> eSFLList = [
                SELECT id, DNProductNo__c, Count__c, Product2__c, eSignForm__c, CaseNumber__c
                FROM eSignFormLineItem__c
                WHERE eSignForm__r.DNName__c IN :DNNoSet
            ];
            for (eSignFormLineItem__c eSFL : eSFLList) {
                eSFL.Count__c = 0;
                eSFLMap.put(eSFL.DNProductNo__c, eSFL);
            }
            // 构造签收单明细
            list<eSignFormLineItem__c> UpsertESFLList = SetupeSignFormLineItems(insertSADNdetailList, eSFLMap, eSFAllMap, prdsMap);
            if (UpsertESFLList.size() > 0) {
                upsert UpsertESFLList;
            }
            rowData.IsUpsertDN__c = false;
            rowData.DN_retry_cnt__c = 0;
            if (System.Test.isRunningTest()) {
                throw new ControllerUtil.myException('测试错误！');
            }
            // 电子签收单  2020/07/17 end
        } catch (Exception ex) {
            Database.rollback(sp);
            logstr += '\n' + ex.getMessage();
            rowData.IsUpsertDN__c = true;
            iflog.ErrorLog__c = ex.getMessage() + '\n' + ex.getStackTraceString() + '\n' + iflog.ErrorLog__c;
            if (rowData.DN_retry_cnt__c == null)
                rowData.DN_retry_cnt__c = 0;
            if (rowData.DN_retry_cnt__c < batch_retry_max_cnt) {
                rowData.DN_retry_cnt__c++;
                DNUpsertSchedule.assignOneMinute();
            }
            if (rowData.DN_retry_cnt__c >= batch_retry_max_cnt) {
                rowData.ErrorLog__c =
                    ex.getMessage() +
                    '\n' +
                    ex.getStackTraceString() +
                    '\n' +
                    rowData.ErrorLog__c +
                    '错误次数已经超过自动收信设定的最大次数，请手动收信';
            }
        }

        update rowData;
        iflog.Log__c = logstr;
        if (iflog.Log__c.length() > 131072) {
            iflog.Log__c = iflog.Log__c.subString(0, 131065) + ' ...';
        }
        if (iflog.ErrorLog__c.length() > 32768) {
            iflog.ErrorLog__c = iflog.ErrorLog__c.subString(0, 32760) + ' ...';
        }
        update iflog;
    }
    //优化两次sql查询 thh 2021-11-02 start
    private static list<Statu_Achievements_DN__c> GetDNList(Set<String> DNNoSet) {
        List<Statu_Achievements_DN__c> DNList = [
            SELECT
                id,
                Name,
                DeliveryDate_Raw__c,
                DeliveryNote_Raw__c,
                DepartmentNo_Raw__c,
                EndUserNo_Raw__c,
                InquiryNo_Raw__c,
                ReturnMark_Raw__c,
                SoNo_Raw__c,
                Opportunity__c,
                Statu_Achievements__c,
                endUser__c,
                //电子签收单试点用户赋值
                endUser__r.RecordType.Name,
                // 电子签收单 start
                Sales_assistant_name_text__c,
                RC_Manager__c,
                ReturnMark__c,
                // 试点用户，届时需要删掉
                EsignTestAccount__c,
                // 电子签收单  end
                //ET召回品不生成签收单 start
                ETRecalledProducts__c,
                //ET召回品不生成签收单 end
                //acc标识 start
                ACC_Text__c,
                Is_Corrosion__c,
                //无偿出库标识 thh 2021-11-01 start
                Statu_Achievements__r.orderType__c
            //无偿出库标识 thh 2021-11-01 end
            //acc标识 end
            FROM Statu_Achievements_DN__c
            WHERE Name IN :DNNoSet
        ];
        return DNList;
    }
    global void finish(Database.BatchableContext BC) {
    }
    /* 构建签收单：
     * eSFMap 是系统里面现有的签收单
     * key 为 DN号；
     * 退货DN不生成签收单
     */
    @TestVisible
    private static list<eSignForm__c> SetupeSignForm(map<string, eSignForm__c> eSFMap, list<Statu_Achievements_DN__c> DNList) {
        list<eSignForm__c> UpsertESFList = new List<eSignForm__c>();
        for (Statu_Achievements_DN__c tempDN : DNList) {
            // 获取现有签收单
            eSignForm__c tempeSF = eSFMap.get(tempDN.Name);
            if (tempeSF == null) {
                tempeSF = new eSignForm__c();
            }

            // 签收单名字 为： DN号-发货日年月日
            string datestr = tempDN.DeliveryDate_Raw__c == null ? Date.today().format().ReplaceAll('/', '') : tempDN.DeliveryDate_Raw__c;
            tempeSF.Name = string.valueof(integer.valueOf(tempDN.Name)) + '-' + datestr;

            tempeSF.DNName__c = tempDN.Name;
            tempeSF.Statu_Achievements__c = tempDN.Statu_Achievements__c;
            tempeSF.Statu_Achievements_DN__c = tempDN.ID;
            tempeSF.Sales_assistant_name_text__c = tempDN.Sales_assistant_name_text__c;
            tempeSF.RC_Manager__c = tempDN.RC_Manager__c;
            //生成签收单时，给是否无偿出库赋值  精琢技术 thh 2021-10-15 start
            if (tempDN.Statu_Achievements__r.orderType__c == 'ZM16') {
                tempeSF.isFreeDelivery__c = true;
            }
            //生成签收单时，给是否无偿出库赋值  精琢技术 thh 2021-10-15 end

            // 经销商试点用户
            //电子签收单 虚进虚出 明细为racing code 为空的 不生成签收单 20201207 精琢技术 start
            if (
                tempDN.EsignTestAccount__c &&
                !tempDN.ETRecalledProducts__c &&
                !tempDN.ReturnMark__c &&
                (tempDN.ACC_Text__c != 'Y' || (tempDN.ACC_Text__c == 'Y' && tempDN.Is_Corrosion__c))
            ) {
                //&&tempDN.Is_Corrosion__c
                //添加危化品标识
                if (tempDN.ACC_Text__c == 'Y' && tempDN.Is_Corrosion__c) {
                    //&&tempDN.Is_Corrosion__c
                    tempeSF.Hazardous_Chemicals__c = true;
                }
                UpsertESFList.add(tempeSF);
            }
            //电子签收单 虚进虚出 明细为racing code 为空的 不生成签收单 20201207 精琢技术 end
        }
        return UpsertESFList;
    }
    /*
     * 电子签收单
     * 构建签收单明细：
     * 1、insertSADNdetailList 是 需要插入的DN明细
     * 2、eSFLMap 是现有的 签收单明细
     * key 为 DN号 + 箱号 + 产品OTCode
     * 3、eSFAllMap 是这次更新的签收单
     * key 为 DN号；
     * 4、prdsMap 是检索到的产品，
     * key 为ot code
     * 退货DN明细不生成签收单
     */
    @TestVisible
    private static list<eSignFormLineItem__c> SetupeSignFormLineItems(
        list<Statu_Achievements_DN_details__c> insertSADNdetailList,
        map<string, eSignFormLineItem__c> eSFLMap,
        map<string, eSignForm__c> eSFAllMap,
        Map<String, Product2> prdsMap
    ) {
        // key 是 DN号 + 箱号 + 产品OTCode
        for (Statu_Achievements_DN_details__c tempDNDetail : insertSADNdetailList) {
            string key = tempDNDetail.DNName__c + '-' + tempDNDetail.CaseNumber__c + '-' + tempDNDetail.OTCode_Raw__c;
            //获取现有签收单明细
            eSignFormLineItem__c tempeSignFormLineItem = eSFLMap.get(key);
            if (tempeSignFormLineItem == null) {
                tempeSignFormLineItem = new eSignFormLineItem__c();
                tempeSignFormLineItem.Count__c = 0;
            }
            tempeSignFormLineItem.Count__c++;
            // Double CaseNumber;
            if (string.isBlank(tempDNDetail.CaseNumber__c)) {
                tempeSignFormLineItem.CaseNumber__c = null;
            } else {
                tempeSignFormLineItem.CaseNumber__c = Double.valueOf(tempDNDetail.CaseNumber__c);
            }
            // Double CaseNumber = tempDNDetail.CaseNumber__c == null ? tempDNDetail.CaseNumber__c : Double.valueOf(tempDNDetail.CaseNumber__c);
            tempeSignFormLineItem.DNProductNo__c = key;
            tempeSignFormLineItem.Name = tempDNDetail.CaseNumber__c == null
                ? String.valueOf(integer.valueOf(tempDNDetail.DNName__c)) + '-' + tempDNDetail.OTCode_Raw__c
                : String.valueOf(integer.valueOf(tempDNDetail.DNName__c)) +
                  '-' +
                  tempDNDetail.CaseNumber__c +
                  '-' +
                  tempDNDetail.OTCode_Raw__c;
            tempeSignFormLineItem.Product2__c = prdsMap.get(tempDNDetail.OTCode_Raw__c).ID;
            if (eSFAllMap.containskey(tempDNDetail.DNName__c)) {
                tempeSignFormLineItem.eSignForm__c = eSFAllMap.get(tempDNDetail.DNName__c).ID;
                eSFLMap.put(key, tempeSignFormLineItem);
            }
        }
        return eSFLMap.values();
    }
    // 设置发货DN
    private static Statu_Achievements_DN__c SetSADN(
        Statu_Achievements_DN__c tempSADN,
        NFM110Rest.GeneralData infoH,
        Map<String, String> boMap,
        Map<String, Opportunity> oppMap,
        Map<String, Account> accsMap,
        Map<String, String> provinceOwnerMap,
        Map<String, String> provinceGIMap
    ) {
        tempSADN.SoNo_Raw__c = infoH.SoNo;
        tempSADN.InquiryNo_Raw__c = infoH.InquiryNo;
        tempSADN.ReturnMark_Raw__c = infoH.ReturnMark;
        tempSADN.DeliveryDate_Raw__c = infoH.DeliveryDate;
        tempSADN.EndUserNo_Raw__c = infoH.EndUserNo;
        tempSADN.DepartmentNo_Raw__c = infoH.DepartmentNo;
        tempSADN.DeliveryNote_Raw__c = infoH.DeliveryNote;
        // 电子签收单 2020/07/17 start
        tempSADN.supplier__c = infoH.supplier;
        tempSADN.supplierNo__c = infoH.supplierNo;
        tempSADN.shippingUnit__c = infoH.shippingUnit;
        tempSADN.shippingAddress__c = infoH.shippingAddress;
        tempSADN.contactName__c = infoH.contactName;
        tempSADN.contactNumber__c = infoH.contactNumber;
        tempSADN.totalNumber__c = infoH.totalNumber;
        tempSADN.transportMode__c = infoH.transportMode;
        tempSADN.totalWeight__c = infoH.totalWeight;
        //tempSADN.GoodDetails__c = infoH.GoodDetails ;
        //tempSADN.weight__c = infoH.weight ;
        tempSADN.endUser__c = accsMap.get(infoH.EndUserNo) == null ? null : accsMap.get(infoH.EndUserNo).id;

        // 电子签收单 2020/07/17 end
        //增加发货DNacc标识 2020/11/23 start
        if (infoH.ACCMark != null) {
            if (infoH.ACCMark.trim() == 'Y') {
                tempSADN.ACC_Mark__c = 'Y';
            } else {
                tempSADN.ACC_Mark__c = 'N';
            }
        }
        tempSADN.name = infoH.DeliveryNote;
        tempSADN.ReturnMark__c = infoH.ReturnMark == '1' ? true : false;
        if (tempSADN.Statu_Achievements__c == null) {
            tempSADN.Statu_Achievements__c = boMap.get(infoH.SoNo);
        }
        tempSADN.DeliveryDate__c = NFMUtil.parseStr2Date(infoH.DeliveryDate);
        //发货日在2020/12/01以后的，才生成签收单 start
        Date afterDate = NFMUtil.parseStr2Date('20201201');
        String todate = NFMUtil.formatDate2Str(afterDate);
        String DeliveryDate = NFMUtil.formatDate2Str(tempSADN.DeliveryDate__c);
        //发货日在2020/12/01以后的，才生成签收单 end

        String[] Inquiry;
        if (infoH.InquiryNo != null && infoH.InquiryNo.contains(',')) {
            Inquiry = infoH.InquiryNo.split(',');
            if (oppMap.get(Inquiry[0]) != null) {
                opportunity opp = oppMap.get(Inquiry[0]);
                tempSADN.Opportunity__c = opp.id;
                tempSADN.Is_Corrosion__c = opp.Is_Corrosion__c;
                // 电子签收单  赋值营业助理和营业管理部担当 start
                //集中采购的询价 精琢技术 wql 2020/01/11 start
                if (opp.Group_purchase_PCL__c) {
                    tempSADN.Sales_assistant_name_text__c = provinceGIMap.get('集采课');
                    tempSADN.RC_Manager__c = provinceOwnerMap.get('集采课');
                } else {
                    tempSADN.Sales_assistant_name_text__c = opp.Sales_assistant_name_text__c;
                    tempSADN.RC_Manager__c = provinceOwnerMap.get(opp.OCM_man_province_cus__c);
                }
                //集中采购的询价 精琢技术 wql 2020/01/11 end

                //如果 发货日期 大于 2020/12/01 才生成签收单
                if (DeliveryDate >= todate) {
                    tempSADN.EsignTestAccount__c = true;
                } else {
                    tempSADN.EsignTestAccount__c = false;
                }

                //SAP里召回品换出订单的送达方地址如果为奥林巴斯办事处的地址，DN不生成待签收信息。
                String ContactName = tempSADN.ContactName__c.toUpperCase();
                if (ContactName.indexOf('OCSM') >= 0) {
                    tempSADN.ETRecalledProducts__c = true;
                } else {
                    tempSADN.ETRecalledProducts__c = false;
                }
            }
            if (Inquiry.size() > 1) {
                tempSADN.Order_No__c = Inquiry[1];
            }
        }
        return tempSADN;
    }
    private static void checkSA(
        Statu_Achievements_DN__c tempSADN,
        List<Statu_Achievements_DN__c> UpsertDNList,
        BatchIF_Log__c iflog,
        NFM110rest.GeneralData infoH
    ) {
        if (tempSADN.Statu_Achievements__c != null)
            UpsertDNList.add(tempSADN);
        else {
            iflog.ErrorLog__c += 'warning! SoNo[' + infoH.SoNo + '] Statu_Achievements__c  NotExist. The insert of this DN is skipped.\n';
        }
    }
    private static DNdetail SetSADNDetails(
        string DNnumber,
        boolean returnFlg,
        NFM110Rest.DnInformation dnInfo,
        asset ast,
        integer tempDNdetailNumber
    ) {
        DNdetail tempDNdetail = new DNdetail();
        Statu_Achievements_DN_details__c DNd = new Statu_Achievements_DN_details__c();
        DNd.Qty_Raw__c = dnInfo.QTY;
        DNd.QTY__c = String.isBlank(dnInfo.QTY) || Decimal.valueOf(dnInfo.QTY.trim()) == 0 ? 1 : Decimal.valueOf(dnInfo.QTY.trim());
        DNd.ShippedAmount_without_tax__c = String.isBlank(dnInfo.NetWorth)
            ? 0
            : returnFlg == false ? Decimal.valueOf(dnInfo.NetWorth.trim()) : -Decimal.valueOf(dnInfo.NetWorth.trim());

        DNd.ShippedAmount__c = String.isBlank(dnInfo.VALUE)
            ? 0
            : returnFlg == false ? Decimal.valueOf(dnInfo.VALUE.trim()) : -Decimal.valueOf(dnInfo.VALUE.trim());

        DNd.NetWorthNoProviston__c = String.isBlank(dnInfo.NetWorthNoProviston)
            ? 0
            : returnFlg == false ? Decimal.valueOf(dnInfo.NetWorthNoProviston.trim()) : -Decimal.valueOf(dnInfo.NetWorthNoProviston.trim());
        if (dnInfo.SorLMark == 'S') {
            DNd.SerialNumber__c = dnInfo.SerialNoorLotNo;
        } else {
            DNd.SerialNumber__c = dnInfo.SerialNoorLotNo + '(' + dnInfo.TracingCode + ')';
        }
        DNd.Product_Serial_No__c = dnInfo.OTCode + ':' + DNd.SerialNumber__c;
        DNd.DNProduct_Serial_No__c = DNnumber + ':' + DNd.Product_Serial_No__c;
        if (dnInfo.SerialNoorLotNo != null && dnInfo.SerialNoorLotNo.equals('.')) {
            DNd.DNProduct_Serial_No__c += tempDNdetailNumber;
        }
        // 电子签收单 start
        DNd.CaseNumber__c = NFMUtil.trimLeft(dnInfo.CaseNumber, '0');
        // 电子签收单 end

        DNd.Barcode_Raw__c = dnInfo.Barcode;
        DNd.OTCode_Raw__c = dnInfo.OTCode;
        DNd.SerialNoorLotNo_Raw__c = dnInfo.SerialNoorLotNo;
        DNd.SorLMark_Raw__c = dnInfo.SorLMark;
        DNd.GuaranteePeriod_Raw__c = dnInfo.GuaranteePeriod;
        DNd.TracingCode_Raw__c = dnInfo.TracingCode;
        DNd.ValidTo_Raw__c = dnInfo.ValidTo;
        DNd.NetWorth_Raw__c = dnInfo.NetWorth;
        DNd.GuaranteePrice_Raw__c = dnInfo.GuaranteePrice;
        DNd.GuaranteeType_Raw__c = dnInfo.GuaranteeType;
        DNd.MaintenancePrice_Raw__c = dnInfo.MaintenancePrice;
        DNd.NetWorthNoProviston_Raw__c = dnInfo.NetWorthNoProviston;
        DNd.ProduceDate_Raw__c = dnInfo.ProduceDate;
        DNd.ProvistonPeriod_Raw__c = dnInfo.ProvistonPeriod;
        DNd.ServicePrice_Raw__c = dnInfo.ServicePrice;
        DNd.VALUE_Raw__c = dnInfo.VALUE;
        DNd.NetWorth_Raw__c = dnInfo.NetWorth;
        DNd.NMPA_Approbation_No__c = dnInfo.NMPA_NO;
        DNd.ProduceCompany__c = dninfo.Provider;
        tempDNdetail.DeliveryNote = DNnumber;
        tempDNdetail.DNd = DNd;
        if (ast != null && ast.id != null) {
            tempDNdetail.DNd.asset__c = ast.id;
        }
        return tempDNdetail;
    }
    @TestVisible
    private static String checkSerialNoorLotNo(String sn) {
        if (sn == null || sn == '' || sn.length() < 8) {
            return sn;
        }
        String regEx = '[a-zA-Z]';
        Pattern pattern = Pattern.compile(regEx);
        boolean strCheck = true;
        boolean numCheck = true;
        for (Integer i = 0; i < 8; i++) {
            String s = sn.substring(sn.length() - i - 1, sn.length() - i);
            if (i < 5) {
                Matcher matcher = pattern.matcher(s);
                if (!matcher.matches()) {
                    strCheck = false;
                    break;
                }
            } else {
                if (i == 5) {
                    if (s != '0') {
                        numCheck = false;
                        break;
                    }
                } else if (i == 6) {
                    if (s != '5') {
                        numCheck = false;
                        break;
                    }
                } else if (i == 7) {
                    if (s != '2') {
                        numCheck = false;
                        break;
                    }
                }
            }
        }
        if (strCheck && numCheck) {
            return sn.substring(0, sn.length() - 8);
        }
        return sn;
    }
}
