You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Apex开发批量更新任务以实现合同Uplift操作?

Apex入门与Batch Apex实现方案

一、Apex开发入口

  • 登录Salesforce后台,进入Setup,在快速搜索框输入Developer Console,点击进入即可编写、调试Apex代码,这是最基础的开发入口。
  • 习惯用IDE的话,安装VS Code并搭配Salesforce Extension Pack插件,配置好组织连接后,能更高效地写代码、部署和排查问题。

二、Apex入门路径(针对Java开发者)

  • 先梳理Apex与Java的核心差异:Apex是强类型语言但基于SObject数据模型,用SOQL/SOSL查询数据,自带批处理、触发器等Salesforce专属机制,优先看官方基础文档快速掌握这些特性。
  • 从匿名代码块练手:比如查询几条Account记录、做简单的SObject增删改,快速熟悉Salesforce的数据结构。
  • 重点理解Batch Apex的核心规则:必须实现Database.Batchable<SObject>接口,包含start(查询待处理数据)、execute(处理批次数据)、finish(收尾操作)三个核心方法。

三、需求对应的Batch Apex实现

核心逻辑

筛选出Uplift_Start_Date__c与StartDate间隔正好10个月的合同,为其生成修订报价;调整报价行项(旧行项数量设0、结束日期设为Uplift Start Date;新增行项沿用原产品/结束日期、起始日期设为Uplift Start Date);最后跳过验证完成报价。

代码实现

global class ContractUpliftBatch implements Database.Batchable<SObject>, Database.Stateful {
    // 定义查询逻辑,筛选符合条件的合同
    global Database.QueryLocator start(Database.BatchableContext bc) {
        String soql = 'SELECT Id, StartDate, Uplift_Start_Date__c, Uplift_Date__c, AccountId, ContractNumber ' +
                      'FROM Contract ' +
                      'WHERE Uplift_Start_Date__c = ADD_MONTHS(StartDate, 10) ' +
                      'AND Uplift_Start_Date__c != NULL AND StartDate != NULL';
        return Database.getQueryLocator(soql);
    }

    // 处理每个批次的合同数据
    global void execute(Database.BatchableContext bc, List<Contract> contractList) {
        List<Quote> newAmendmentQuotes = new List<Quote>();
        List<QuoteLineItem> oldQLIUpdates = new List<QuoteLineItem>();
        List<QuoteLineItem> newQLICreates = new List<QuoteLineItem>();

        // 收集合同ID,批量查询关联的原报价行项
        Set<Id> contractIds = new Set<Id>();
        for(Contract c : contractList) {
            contractIds.add(c.Id);
        }
        // 注意:此处关联关系需根据实际数据模型调整,比如Quote与Contract的关联字段
        List<QuoteLineItem> originalQLIs = [SELECT Id, QuoteId, Product2Id, Quantity, StartDate, EndDate, UnitPrice, Quote.ContractId__c
                                            FROM QuoteLineItem 
                                            WHERE Quote.ContractId__c IN :contractIds];

        // 为每个符合条件的合同创建修订报价
        for(Contract c : contractList) {
            Quote amendmentQuote = new Quote();
            amendmentQuote.ContractId__c = c.Id; // 替换为实际的合同关联字段API名
            amendmentQuote.AccountId = c.AccountId;
            amendmentQuote.Name = 'Amendment - ' + c.ContractNumber;
            amendmentQuote.Amendment_Date__c = c.Uplift_Date__c;
            amendmentQuote.Status = 'Draft';
            newAmendmentQuotes.add(amendmentQuote);
        }

        // 插入新报价,获取ID用于后续行项关联
        if(!newAmendmentQuotes.isEmpty()) {
            insert newAmendmentQuotes;
        }

        // 构建合同到新报价的映射,方便后续处理
        Map<Id, Quote> contractToQuoteMap = new Map<Id, Quote>();
        for(Quote q : newAmendmentQuotes) {
            contractToQuoteMap.put(q.ContractId__c, q);
        }

        // 处理报价行项:更新旧行项,创建新行项
        for(QuoteLineItem originalQLI : originalQLIs) {
            Quote targetQuote = contractToQuoteMap.get(originalQLI.Quote.ContractId__c);
            if(targetQuote == null) continue;

            // 更新原行项:数量设0,结束日期设为Uplift Start Date
            QuoteLineItem updatedQLI = new QuoteLineItem(Id = originalQLI.Id);
            updatedQLI.Quantity = 0;
            updatedQLI.EndDate = targetQuote.ContractId__r.Uplift_Start_Date__c;
            oldQLIUpdates.add(updatedQLI);

            // 创建新行项:沿用原产品、价格、结束日期,起始日期设为Uplift Start Date
            QuoteLineItem newQLI = new QuoteLineItem();
            newQLI.QuoteId = targetQuote.Id;
            newQLI.Product2Id = originalQLI.Product2Id;
            newQLI.Quantity = originalQLI.Quantity;
            newQLI.StartDate = targetQuote.ContractId__r.Uplift_Start_Date__c;
            newQLI.EndDate = originalQLI.EndDate;
            newQLI.UnitPrice = originalQLI.UnitPrice;
            newQLICreates.add(newQLI);
        }

        // 批量更新和插入行项
        if(!oldQLIUpdates.isEmpty()) {
            update oldQLIUpdates;
        }
        if(!newQLICreates.isEmpty()) {
            insert newQLICreates;
        }

        // 跳过验证完成报价
        List<Quote> quotesToFinalize = new List<Quote>();
        for(Quote q : newAmendmentQuotes) {
            Quote finalQuote = new Quote(Id = q.Id);
            finalQuote.Status = 'Approved'; // 替换为实际的完成状态值
            quotesToFinalize.add(finalQuote);
        }
        if(!quotesToFinalize.isEmpty()) {
            Database.update(quotesToFinalize, false); // false表示跳过验证规则
        }
    }

    // 收尾方法,可选:比如发送批处理结果通知
    global void finish(Database.BatchableContext bc) {
        AsyncApexJob jobInfo = [SELECT Id, Status, NumberOfErrors, TotalJobItems 
                                FROM AsyncApexJob 
                                WHERE Id = :bc.getJobId()];
        // 此处可添加邮件通知逻辑,比如发给管理员告知任务状态
    }
}

关键注意点

  • 所有自定义字段的API名称(如Uplift_Start_Date__c)需替换为你实际环境中的字段名,可在Setup的字段详情页查看。
  • 合同与Quote的关联关系要根据业务模型调整,代码中Quote.ContractId__c为假设的关联字段,若结构不同需修改SOQL和赋值逻辑。
  • Database.update(..., false)会跳过验证规则,生产环境需谨慎使用,建议先在沙盒测试确认不会破坏业务规则。
  • 必须编写测试类覆盖至少75%的代码才能部署到生产环境,测试类需模拟符合条件的合同、报价行项,执行批处理并验证结果是否符合预期。

四、运行Batch Apex的方式

在Developer Console的匿名窗口中执行以下代码:

ContractUpliftBatch batchJob = new ContractUpliftBatch();
Database.executeBatch(batchJob, 200); // 200为每个批次的记录数,可根据需求调整

执行后,可在Setup的Apex Jobs中查看任务运行状态。

内容的提问来源于stack exchange,提问作者Symbiote Scorns

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 00:40:38