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,Window1__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); //Commented by Li Jun 20230420
                provinceGIMap.put(omp.Name, omp.Window1__c);// Add By Li Jun 更新 签收单的【营业助理】值，从OCSM管理省对应的【营业窗口】字段获取
            }
            // 电子签收单 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
                 //Add by Li Jun 20230420 start 
                 ,Opportunity__r.OCM_man_province_cus__c
                 //Add By Li Jun 20230420 end
                 from Statu_Achievements_DN__c where
                 Name in: DNNoSet];
        return DNList;
    }
    global void finish(Database.BatchableContext BC) {
    }

    //Add By Li Jun 0420 Start query management province
    private static Map<String,OCM_Management_Province__c> provinceManagementMap{set;get;}
    public static Map<String,OCM_Management_Province__c> getManagementProvince(){ 
        if(provinceManagementMap !=null && provinceManagementMap.keySet().size()>0 ){
            return provinceManagementMap;
        }
        provinceManagementMap = new Map<String,OCM_Management_Province__c>();
        List <OCM_Management_Province__c > ompList = [select id, Name, SalesManage__c,GI_assistant__c,Window1__c   from OCM_Management_Province__c];
        for (OCM_Management_Province__c omp: ompList) {
            provinceManagementMap.put(omp.Name,omp);
        }
        return provinceManagementMap;
    }
      //Add By Li Jun 0420 End
    /* 构建签收单：
     * 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){
                        system.debug('集采课');
                        tempSADN.Sales_assistant_name_text__c = provinceGIMap.containskey('集采课')?provinceGIMap.get('集采课'):null; //Add  By Li Jun 20230523
                        //tempSADN.Sales_assistant_name_text__c = provinceGIMap.get('集采课'); //Commented By Li Jun 20230420
                        tempSADN.RC_Manager__c =  provinceOwnerMap.get('集采课');
                    }else{
                        system.debug('非集采课');
                        //tempSADN.Sales_assistant_name_text__c = opp.Sales_assistant_name_text__c; //Commented By Li Jun 20230420
                        tempSADN.Sales_assistant_name_text__c =opp.OCM_man_province_cus__c!='' && provinceGIMap.containskey(opp.OCM_man_province_cus__c)?provinceGIMap.get(opp.OCM_man_province_cus__c):null; //Add  By Li Jun 20230523
                        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;
    }
}