global class ConsumInventoryReportDetailArchiveBatch implements Database.Batchable, Database.Stateful { private Inventory_Header__c ih_new = new Inventory_Header__c(); private String docBodyAsset = ''; // TODO 5M と batch heap size 問題 // private String docBodyRaesd = ''; // TODO 5M と batch heap size 問題 private String docBodyDiushi = ''; // TODO 5M と batch heap size 問題 //private String docBodyRepair = ''; // TODO 5M と batch heap size 問題 private Set defaultAssetField; private Set defaultRaesdField; //private Set defaultRepairField; private List cListAssetField = new List(); private List cListConsumedField = new List(); private List cListRaesdField = new List(); //private List cListRepairField = new List(); private Map fieldAPIName = new Map(); global List emailMessages; global Integer totalCount = 0; // 总件数 (Consum_Inventory_Detail__c (Sync_Asset_Record_Flag__c = true)) global Integer failedCount = 0; @TestVisible private static List messagesForTest; /** * 批准后, Trigger 会来调 */ global ConsumInventoryReportDetailArchiveBatch(Inventory_Header__c ih_new) { this.emailMessages = new List(); this.ih_new = ih_new; } global Database.QueryLocator start(Database.BatchableContext bc) { bp3_Setting__c conf = bp3_Setting__c.getOrgDefaults(); if (String.isBlank(conf.InventoryDetailArchiveFolder__c)) { this.emailMessages.add('未设置CSV保存文件夹,不能开始Batch'); failedCount = 0; return Database.getQueryLocator([SELECT Id FROM Consum_Inventory_Detail__c LIMIT 0]); } Inventory_Batch_Mapping__mdt columns; List keyList; defaultAssetField = new Set(); defaultRaesdField = new Set(); //defaultRepairField = new Set(); columns = [select From_Columns__c, Inventory_Columns__c from Inventory_Batch_Mapping__mdt where DeveloperName = 'ConsumAsset']; keyList = columns.Inventory_Columns__c.split(','); keyList.addAll(new List { 'Inventory_Zaiku_Count_Jia__c', 'Inventory_Shipan_Count_Jia__c', //'Inventory_Weixiu_Count_Jia__c', 'Inventory_Chujie_Count_Jia__c', //'Inventory_Daibaofei_Diushi_Count_Jia__c', 'Inventory_Quantity_Jia__c', 'Inventory_Profit_Quantity__c', 'Inventory_Loss_Quantity__c', 'Inventory_Deviation_Jia__c', 'Barcode__c', 'Consumed_Count__c', 'Inventory_Remarks__c', 'Remarks_Person__r.Name' }); String cListAssetFieldStr = 'Asset__c,Internal_asset_location__c,Salesdepartment__c,Fixture_Model_No__c,Asset_Name__c,SerialNumber__c,Equipment_Type__c,Consumable_Guaranteen_end__c,Manage_type__c,Loaner_accsessary__c,AssetManageConfirm__c,Fixture_QRCode__c,WH_location__c,Fixture_Status__c,Inventory_Zaiku_Count_Jia__c,Inventory_Shipan_Count_Jia__c,Inventory_Chujie_Count_Jia__c,Inventory_Quantity_Jia__c,Inventory_Profit_Quantity__c,Inventory_Loss_Quantity__c,Inventory_Deviation_Jia__c,Inventory_Remarks__c,Barcode__c,Consumed_Count__c,Remarks_Person__r.Name'; cListAssetField = cListAssetFieldStr.split(','); for (Integer i = 0; i < keyList.size(); i++) { defaultAssetField.add(keyList[i]); } String cListConsumedFieldStr = 'Consum_Apply_Name__c,Account__c,RA_Status__c,Salesdept__c,WorkPlace__c,Person_In_Charge_New__c,Person_In_Charge__c,demo_purpose2__c,RAES_No__c,RAES_Status__c,RAESD_Name__c,RAESD_Status__c,Fixture_Model_No__c,SerialNumber__c,Internal_asset_location__c,Equipment_Type__c,Salesdepartment__c,WH_location__c,Consum_Start_Date__c,Show_demonstration__c,Operation_Type__c,Case_OR_animal_organ__c,Trial_User__c,Follower_User__r.Name,Spare__c,Request_approval_time__c,Select_Time__c,Request_shipping_day__c,Shipment_request_time2__c,Shippment_loaner_time__c,Loaner_received_time__c,Consum_Received_Day__c,Asset_Center_Confirm_Time__c,Lost_item_check_time__c,Lost_item_check_time_Final__c,AssetManageConfirmYN__c,RAESD__c,Consum_Apply__c,Consum_Apply_Equipment_Set__c,Asset__c'; cListConsumedField = cListConsumedFieldStr.split(','); String cListRaesdFieldStr = 'Consum_Apply_Name__c,Account__c,RA_Status__c,Salesdept__c,WorkPlace__c,Person_In_Charge_New__c,Person_In_Charge__c,demo_purpose2__c,RAES_No__c,RAES_Status__c,RAESD_Name__c,RAESD_Status__c,Fixture_Model_No__c,SerialNumber_F__c,Internal_asset_location__c,Equipment_Type__c,Salesdepartment__c,WH_location__c,Consum_Start_Date__c,Show_demonstration__c,Operation_Type__c,Case_OR_animal_organ__c,Trial_User__c,Follower_User__r.Name,Spare__c,Request_approval_time__c,Select_Time__c,Request_shipping_day__c,Shipment_request_time2__c,Shippment_loaner_time__c,Loaner_received_time__c,Asset_return_time__c,Consum_Received_Day__c,Asset_Center_Confirm_Time__c,AssetManageConfirmYN__c,RAESD__c,Consum_Apply__c,Consum_Apply_Equipment_Set__c,Asset__c'; cListRaesdField = cListRaesdFieldStr.split(cListRaesdFieldStr); columns = [select From_Columns__c, Inventory_Columns__c from Inventory_Batch_Mapping__mdt where DeveloperName = 'CAESD']; keyList = columns.Inventory_Columns__c.split(','); for (Integer i = 0; i < keyList.size(); i++) { defaultRaesdField.add(keyList[i]); } //获取label String objectType ='Consum_Inventory_Detail__c'; Map schemaMap = Schema.getGlobalDescribe(); Schema.SObjectType leadSchema = schemaMap.get(objectType); Map fieldMap = leadSchema.getDescribe().fields.getMap(); for(String fieldName : fieldMap.keySet()){ fieldAPIName.put(fieldName, fieldMap.get(fieldName).getDescribe().getLabel()); } Id ih_new_Id = ih_new.Id; String querysql = 'select ' + String.join(new List(defaultAssetField), ', ') + ' from Consum_Inventory_Detail__c ' + 'where Sync_Asset_Record_Flag__c = true' + ' and Inventory_Header__c = :ih_new_Id'; return Database.getQueryLocator(querysql); } global void execute(Database.BatchableContext BC, List syncAssetList) { totalCount += syncAssetList.size(); try { String executeBodyAsset = ''; // TODO 5M と batch heap size 問題 // String executeBodyRaesd = ''; // TODO 5M と batch heap size 問題 String executeBodyDiushi = ''; // TODO 5M と batch heap size 問題 //String executeBodyRepair = ''; // TODO 5M と batch heap size 問題 String querysql; List assetIds = new List(); for (Consum_Inventory_Detail__c idc : syncAssetList) { assetIds.add(idc.Asset__c); } Id ih_new_Id = ih_new.Id; List statusArList = [ SELECT Asset_Status__c status, sum(Amount__c) quantity , sum(Inventory_Count__c) invShipanCount, Asset__c assetId , sum(Inventory_Deviation__c) deviaCount , sum(Sync_Asset_Frozen_Quantity__c) lossCount , sum(Sync_Asset_Profit_Quantity__c) profitCount , sum(Asset__r.Abandoned_Inventory__c) abCount FROM Consum_Inventory_Detail__c WHERE Asset__c in :assetIds AND Inventory_Header__c = :ih_new_Id AND Asset_Status__c != '已消耗明细' GROUP BY Asset_Status__c, Asset__c]; system.debug(statusArList); Map assetCountMap = new Map(); for (AggregateResult ar : statusArList) { InventoryReportDetailController.StatusCount statusCount = new InventoryReportDetailController.StatusCount(0); Integer amount = (Integer.valueOf(ar.get('quantity')) == null) ? 0 : Integer.valueOf(ar.get('quantity')); // 应盘数 Integer acAmount = (Integer.valueOf(ar.get('invShipanCount')) == null) ? 0 : Integer.valueOf(ar.get('invShipanCount')); // 实盘数 Integer wuchaAmount = (Integer.valueOf(ar.get('deviaCount')) == null) ? 0 : Integer.valueOf(ar.get('deviaCount')); // 盘点误差(盘盈/盘亏) Integer lossAmount = (Integer.valueOf(ar.get('lossCount')) == null) ? 0 : Integer.valueOf(ar.get('lossCount')); // 盘点误差(盘盈/盘亏) Integer profitAmount = (Integer.valueOf(ar.get('profitCount')) == null) ? 0 : Integer.valueOf(ar.get('profitCount')); // 盘点误差(盘盈/盘亏) Integer abAmount = (Integer.valueOf(ar.get('abCount')) == null) ? 0 : Integer.valueOf(ar.get('abCount')); Id assId = String.valueof(ar.get('assetId')); if (assetCountMap.containsKey(assId)) { statusCount = assetCountMap.get(assId); } if (ar.get('status') == '在库') { statusCount.zaikuNum += amount; statusCount.quantity += abAmount; } //else if (ar.get('status') == '维修中') { // statusCount.weixiuNum += amount; // 应盘维修中 //} else if (ar.get('status') == '出借中') { statusCount.chujieNum += amount; // 应盘出借中 statusCount.quantity += amount; // 应盘数量 } else if (ar.get('status') == '已消耗') { statusCount.diushiNum += amount; // 应盘已消耗 } else if (ar.get('status') == '冻结') { statusCount.zaikuNum += amount; // 盘点在库数='在库'应盘数+'冻结'应盘数 } if(ar.get('status') == '在库' || ar.get('status') == '冻结'){ statusCount.quantity += amount; // 应盘数量 statusCount.shipanNum += acAmount; // 实盘 statusCount.wuchaNum += wuchaAmount; // 盘点误差(盘盈/盘亏) statusCount.panyinNum += profitAmount; statusCount.pankuiNum += lossAmount; } assetCountMap.put(assId, statusCount); } for (Consum_Inventory_Detail__c idc : syncAssetList) { InventoryReportDetailController.StatusCount eachSCount = assetCountMap.get(idc.Asset__c); if (eachSCount != null) { idc.Inventory_Zaiku_Count_Jia__c = eachSCount.zaikuNum; idc.Inventory_Shipan_Count_Jia__c = eachSCount.shipanNum; //idc.Inventory_Weixiu_Count_Jia__c = eachSCount.weixiuNum; idc.Inventory_Chujie_Count_Jia__c = eachSCount.chujieNum; //idc.Inventory_Daibaofei_Diushi_Count_Jia__c = eachSCount.diushiNum; idc.Consumed_Count__c = eachSCount.diushiNum; idc.Inventory_Quantity_Jia__c = eachSCount.quantity; idc.Inventory_Deviation_Jia__c = eachSCount.wuchaNum; idc.Inventory_Profit_Quantity__c = eachSCount.panyinNum; idc.Inventory_Loss_Quantity__c = eachSCount.pankuiNum; executeBodyAsset += setDocBodyObject(idc, 'Asset', false); } } //借出明细 // querysql = 'select ' + String.join(new List(defaultRaesdField), ', ') // + ' from Inventory_Detail__c where Asset__c IN :assetIds and Asset_Status__c = \'出借中\''; // for (Inventory_Detail__c dl : Database.query(querysql)) { // executeBodyRaesd += setDocBodyObject(dl, 'Raesd', false); // } // 已消耗明细(原丢失借出明细) querysql = 'select ' + String.join(new List(cListConsumedField), ', ') + ' from Consum_Inventory_Detail__c where Asset__c IN :assetIds and Inventory_Header__c = :ih_new_Id and Asset_Status__c = \'已消耗明细\''; for (Consum_Inventory_Detail__c dl : Database.query(querysql)) { executeBodyDiushi += setDocBodyObject(dl, 'Lost', false); } //修理id //querysql = 'select ' + String.join(new List(defaultRepairField), ', ') // + ' from Consum_Inventory_Detail__c where Asset__c IN :assetIds and Inventory_Header__c = :ih_new_Id and Asset_Status__c = \'维修中\''; //for (Consum_Inventory_Detail__c rp : Database.query(querysql)) { // executeBodyRepair += setDocBodyObject(rp, 'Repair', false); //} docBodyAsset += executeBodyAsset; // docBodyRaesd += executeBodyRaesd; docBodyDiushi += executeBodyDiushi; //docBodyRepair += executeBodyRepair; } catch (Exception e) { failedCount += syncAssetList.size(); System.debug(LoggingLevel.ERROR, e.getMessage() + '\n' + e.getStackTraceString()); this.emailMessages.add(e.getMessage() + '\n' + e.getStackTraceString()); } } private String setDocBodyObject(sObject obj, String typeStr, boolean label) { List toCol; if (typeStr == 'Asset') { toCol = cListAssetField; //} else if (typeStr == 'Repair') { // toCol = cListRepairField; } else if(typeStr == 'Lost'){ toCol = cListConsumedField; } else { toCol = cListRaesdField; } Schema.SObjectType targetType = Consum_Inventory_Detail__c.sObjectType; Schema.DescribeSObjectResult sobjResult = targetType.getDescribe(); Map m = sobjResult.fields.getMap(); String docBody = ''; for (Integer i = 0; i < toCol.size(); i++) { List cols = toCol[i].split('\\.'); if (obj == null) { if (label) { docBody += fieldAPIName.get(toCol[i].toLowerCase().replace('__r.name','__c')).escapeCsv(); } else { docBody += toCol[i].escapeCsv(); } } else { Object val = CreateRelationListPagingCmpCtrl.getObjectValue(obj, cols); if (val != null) { Schema.DescribeFieldResult descField = m.get(toCol[i].toLowerCase().replace('__r.name','__c')).getDescribe(); if(descField.getType() == DisplayType.DATETIME && String.isNotBlank('' + val)){ docBody += (''+((Datetime) val).addHours(8)).escapeCsv(); } else{ docBody += ('' + val).escapeCsv(); } } } // 加 , 和 行末 的换行 if (i < toCol.size() - 1) { docBody += ','; } else { docBody += '\r\n'; } } return docBody; } public Document getArchiveFilenameForUpsert(String typeStr, Inventory_Header__c ih_new) { bp3_Setting__c conf = bp3_Setting__c.getOrgDefaults(); Id folderId = conf.InventoryDetailArchiveFolder__c; String filename = ih_new.Inventory_Start_Date__c.year() + ('' + ih_new.Inventory_Start_Date__c.month()).leftPad(2, '0') + '_' + ih_new.Internal_asset_location__c + '_'; if(typeStr == 'Asset'){ filename += '耗材资产.csv'; } else if(typeStr == 'Lost') { filename += '耗材已消耗记录.csv'; } else{ filename += '耗材' + typeStr + '.csv'; } Document ret = null; List retList = [SELECT Id , Name , FolderId , Body FROM Document WHERE FolderId = :folderId AND Name =:filename]; if (retList.size() > 0) { ret = retList[0]; } if (ret == null) { ret = new Document(); ret.Name = filename; ret.FolderId = folderId; ret.Type = 'csv'; insert ret; } String docBody = setDocBodyObject(null, typeStr, false); docBody += setDocBodyObject(null, typeStr, true); ret.Body = Blob.valueOf(docBody); return ret; } global void finish(Database.BatchableContext BC) { Integer csvCount = 2; // 总件数 (CSV) Integer failedCsvCount = 0; if (docBodyAsset == '') { failedCsvCount++; this.emailMessages.add('耗材资产数据不存在'); } else { Document assetDoc = getArchiveFilenameForUpsert('Asset', ih_new); assetDoc.Body = Blob.valueOf(assetDoc.Body.toString() + docBodyAsset); Database.SaveResult saveResult = Database.update(assetDoc, false); if (!saveResult.isSuccess()) failedCsvCount++; this.emailMessages = FixtureUtil.setSaveError(new List{saveResult}, Document.sObjectType, new List{assetDoc}, this.emailMessages); } //if (docBodyRepair == '') { // failedCsvCount++; // this.emailMessages.add('Repair数据不存在'); //} else { // Document repairDoc = getArchiveFilenameForUpsert('Repair', ih_new); // repairDoc.Body = Blob.valueOf(repairDoc.Body.toString() + docBodyRepair); // Database.SaveResult saveResult = Database.update(repairDoc, false); // if (!saveResult.isSuccess()) failedCsvCount++; // this.emailMessages = FixtureUtil.setSaveError(new List{saveResult}, Document.sObjectType, new List{repairDoc}, this.emailMessages); //} // if (docBodyRaesd == '') { // failedCsvCount++; // this.emailMessages.add('Raesd数据不存在'); // } else { // Document raesdDoc = getArchiveFilenameForUpsert('Raesd', ih_new); // raesdDoc.Body = Blob.valueOf(raesdDoc.Body.toString() + docBodyRaesd); // Database.SaveResult saveResult = Database.update(raesdDoc, false); // if (!saveResult.isSuccess()) failedCsvCount++; // this.emailMessages = FixtureUtil.setSaveError(new List{saveResult}, Document.sObjectType, new List{raesdDoc}, this.emailMessages); // } if (docBodyDiushi == '') { failedCsvCount++; this.emailMessages.add('已消耗数据不存在'); } else { Document lostDoc = getArchiveFilenameForUpsert('Lost', ih_new); lostDoc.Body = Blob.valueOf(lostDoc.Body.toString() + docBodyDiushi); Database.SaveResult saveResult = Database.update(lostDoc, false); if (!saveResult.isSuccess()) failedCsvCount++; this.emailMessages = FixtureUtil.setSaveError(new List{saveResult}, Document.sObjectType, new List{lostDoc}, this.emailMessages); } // TODO wwf email ConsumInventoryReportDetailArchiveBatch.messagesForTest = this.emailMessages; // 发 mail BatchEmailUtil be = new BatchEmailUtil(); String[] toList = new String[]{}; String title = '存档耗材盘点CSV数据(保有设备和已消耗明细)'; String[] ccList = new String[]{}; Inventory_Header__c iheader = [select Id, Inventory_Status__c, Name, Internal_asset_location__c, Inventory_Start_Date__c from Inventory_Header__c where Id = :ih_new.Id]; String text = '耗材盘点报告书编号:' + iheader.Name + ' 盘点地点:' + iheader.Internal_asset_location__c + ' 盘点开始日:' + iheader.Inventory_Start_Date__c; text += '\n处理对象'+totalCount + '件 保有设备(同期的 耗材盘点明细)'; text += '\n处理失败'+failedCount + '件 保有设备(同期的 耗材盘点明细)'; if(this.emailMessages.size() == 0){ toList.add(UserInfo.getUserEmail()); be.successMail(toList,ccList, title, csvCount, text); }else{ String emailLabel = 'BatchNotify'; for (OrgWideEmailAddress tmpEmailObj : [SELECT Id, Address, DisplayName FROM OrgWideEmailAddress WHERE DisplayName like :emailLabel]) { ccList.add(tmpEmailObj.Address); } for(String email : System.Label.Inventory_Result_Email.split(',')){ ccList.add(email); } be.failedMail(toList, ccList, title, String.join(this.emailMessages, '\n'), csvCount, csvCount - failedCsvCount, failedCsvCount); } be.send(); } }