Class Name: RenewalPriceUpdate¶
Last Updated: 2025-10-22
API Name: RenewalPriceUpdate Type: Batch/Queueable/Schedulable Test Coverage: Unknown (Test Class: RenewalPriceUpdateTest)
Business Purpose¶
This class ensures auto-renewal orders reflect current pricing when product prices change after the renewal order has been generated but before payment processing. It calculates price deltas from PricebookEntry changes and updates corresponding OrderItem prices in renewal orders, maintaining pricing accuracy for AANP's subscription revenue streams and ensuring members are charged the correct current rates for their renewals.
Class Overview¶
Scope and Sharing¶
- Sharing Model: with sharing
- Access Modifier: public
- Interfaces Implemented: Database.Batchable
, Database.Stateful
Key Responsibilities¶
- Detects price changes in PricebookEntry records by comparing Previous_Price__c with UnitPrice
- Calculates price deltas (increases or decreases) from pricebook entry changes
- Updates OrderItem.unitPrice in non-activated renewal orders with price deltas
- Handles Order status transitions for orders that need temporary status changes during price updates
- Excludes orders with EffectiveDate = TODAY to avoid conflicts with same-day payment processing
Public Methods¶
run¶
Purpose: Entry point for processing PricebookEntry price changes, calculating price deltas and initiating batch job if changes are detected.
Parameters:
- changedProducts (List
Returns: - void
Usage Example:
// Called from PricebookEntry trigger
List<PricebookEntry> changedEntries = Trigger.new;
RenewalPriceUpdate priceUpdater = new RenewalPriceUpdate();
priceUpdater.run(changedEntries);
Business Logic: - Iterates through each changed PricebookEntry - Uses Previous_Price__c if available, otherwise uses current UnitPrice as baseline - Calculates priceDelta as: UnitPrice - previousPrice - Only tracks products where priceDelta != 0 (actual price change) - Stores product ID to price delta mapping in instance variable - Executes batch job if any price changes are detected
start (Batch Interface)¶
Purpose: Initializes batch processing by building query for OrderItem records in renewal orders that need price updates.
Parameters:
- bc (Database.BatchableContext): Batch context provided by the platform
Returns:
- Database.QueryLocator: Query locator for OrderItem records to process
Business Logic: - Constructs dynamic SOQL query to find OrderItem records where: - PricebookEntryId is in the set of changed product IDs - Order.Status != 'Activated' - Order.EffectiveDate != TODAY (avoids same-day payment conflicts) - Order.Renewal_Order__c = true - Retrieves Order.Status, Order.StatusCode, Order.ActivatedById, Order.ActivatedDate for status management - Uses Database.getQueryLocatorWithBinds with SYSTEM_MODE for elevated data access
execute (Batch Interface)¶
Purpose: Processes each batch of OrderItem records by applying price deltas and handling Order status transitions for activated orders.
Parameters:
- bc (Database.BatchableContext): Batch context provided by the platform
- scope (List
Business Logic: - Builds maps for tracking Order status changes (previous and current) - For OrderItems with StatusCode = 'Activated': - Records Order needs temporary status change to 'Ready for Activation' - Captures original status, ActivatedById, and ActivatedDate for restoration - Applies price delta to each OrderItem.unitPrice (unitPrice += priceDelta) - Performs three-phase update for activated orders: 1. Update Orders to 'Ready for Activation' status 2. Update OrderItems with new prices 3. Restore Orders to original Activated status with audit fields - For non-activated orders, performs single OrderItem update
finish (Batch Interface)¶
Purpose: Completes batch execution with no additional processing.
Parameters:
- bc (Database.BatchableContext): Batch context provided by the platform
Business Logic: - Empty implementation - no cleanup or logging
Private/Helper Methods¶
Constructor¶
Purpose: Initializes batch instance with dynamic query for OrderItem records.
Parameters: None
Business Logic: - Builds SOQL query string targeting OrderItem records in renewal orders - Sets up filters for non-activated, non-today effective date orders - Query uses bind variables for productIds set
Dependencies¶
Apex Classes¶
None - standalone class with no external class dependencies
Salesforce Objects¶
PricebookEntry: Source for price changes (Fields: Id, UnitPrice, Previous_Price__c)OrderItem: Target for price updates (Fields: Id, unitPrice, PricebookEntryId, OrderId, Order.Status, Order.StatusCode, Order.ActivatedById, Order.ActivatedDate, Order.EffectiveDate, Order.Renewal_Order__c)Order: Parent object for status management (Fields: Id, Status, StatusCode, ActivatedById, ActivatedDate, EffectiveDate, Renewal_Order__c)
Custom Settings/Metadata¶
None
External Services¶
None
Design Patterns¶
- Batch Processing Pattern: Implements Database.Batchable for processing order item price updates
- Stateful Pattern: Uses Database.Stateful to maintain changedProductsToNewListPriceMap across batch execution
- Three-Phase Update Pattern: Temporarily changes Order status, updates items, then restores status to allow price changes on activated orders
- Delta Calculation Pattern: Calculates and stores price changes rather than absolute values
Governor Limits Considerations¶
SOQL Queries: - run method: 0 queries (receives data via parameter) - start method: 1 query via QueryLocator - execute method: 0 queries (uses data from scope)
DML Operations: - execute method: 3 DML statements for activated orders (Order status change, OrderItem update, Order status restore) - execute method: 1 DML statement for non-activated orders (OrderItem update only)
CPU Time: Low - simple arithmetic operations for price delta calculations
Heap Size: - Batch processing limits heap usage to batch size (default 200 records) - changedProductsToNewListPriceMap size depends on number of changed products
Bulkification: Yes - processes records in batches with bulk DML operations
Async Processing: Yes - runs as asynchronous batch job
Error Handling¶
Strategy: Minimal error handling - No try-catch blocks in any methods - No validation of price calculations or Order status transitions - Failed updates will throw uncaught exceptions
Logging: - No logging of price changes, errors, or progress - No tracking of which orders/items were updated - Incomplete code comment "Need to" on line 63 suggests unfinished implementation
User Notifications: None - no error notifications or completion alerts
Security Considerations¶
Sharing Rules: Respects sharing rules (with sharing keyword)
Field-Level Security: - Uses SYSTEM_MODE in batch query to bypass sharing and FLS - No WITH SECURITY_ENFORCED in queries
CRUD Permissions: - No explicit CRUD permission checks - Relies on running user's permissions for DML operations - Batch uses SYSTEM_MODE for queries
Input Validation: - No validation of price delta ranges (could result in negative prices) - No null checks on Previous_Price__c field - No validation that price changes are reasonable or within business rules
Test Class¶
Test Class: RenewalPriceUpdateTest Coverage: Unknown Test Scenarios Covered: - Details not available from source code
Changes & History¶
- 2024-12-20: Initial creation by Ryan O'Sullivan
- PR-32721: Feature to update renewal order prices when product prices change after order generation
Pre-Go-Live Concerns¶
CRITICAL - Fix Before Go-Live¶
- Complex Order Status Manipulation: Temporarily changing Order status to 'Ready for Activation', updating items, then restoring status creates significant risk. Could interfere with payment processing, workflows, or other automations.
- No Error Handling: No try-catch blocks around three-phase update. If any phase fails, Orders could be left in inconsistent state (wrong status, missing audit fields).
- Data Corruption Risk: Updating ActivatedById and ActivatedDate on already-activated orders could corrupt audit trail and compliance data.
- Missing Validation: No checks that resulting prices are valid (positive, reasonable). Could set negative or zero prices if delta calculations are incorrect.
- Incomplete Implementation: Comment "Need to" on line 63 suggests code may not be finished or tested.
HIGH - Address Soon After Go-Live¶
- No Transaction Control: Three separate DML statements without proper transaction management. Partial failures could leave data in inconsistent state.
- Payment Processing Conflicts: Even with EffectiveDate != TODAY filter, status changes could conflict with payment processing workflows running concurrently.
- Missing Audit Trail: Price changes on orders are not logged. Difficult to explain to customers why renewal price changed or troubleshoot discrepancies.
- No Notification: Finance team and customers are not notified when renewal prices are adjusted.
MEDIUM - Future Enhancement¶
- Empty Finish Method: No completion logging, error summary, or chaining to next process.
- Test Visibility Flag: @testVisible on changedProductsToNewListPriceMap suggests testing challenges or workarounds.
- No Rollback Capability: Price changes cannot be easily reversed if incorrect deltas are applied.
- Performance: Three DML statements per batch could be optimized or consolidated.
LOW - Monitor¶
- Query String Construction: Building query as string rather than using dynamic SOQL could be error-prone.
- No Performance Metrics: Processing time and throughput not tracked.
- Limited Comments: Complex logic not well-documented inline.
Maintenance Notes¶
Complexity: High Recommended Review Schedule: Quarterly
Key Maintainer Notes: - CRITICAL: The Order status manipulation (Ready for Activation → update → restore to Activated) is extremely risky and could cause data integrity issues. Thoroughly test all scenarios and coordinate with payment processing team. - The incomplete comment "Need to" on line 63 must be investigated. Ensure implementation is complete and properly tested before deployment. - This class can modify already-activated orders, which is unusual and risky. Verify this is the intended business requirement and document the rationale. - Coordinate closely with finance team on price change procedures and timing. Consider implementing approval workflow before price updates are applied. - The Previous_Price__c field on PricebookEntry is critical for this logic. Ensure it's properly maintained by whatever process updates pricebook entries. - Test extensively with orders at all status values, especially 'Activated' status which has special handling. - Consider implementing a "dry run" mode that calculates changes but doesn't apply them, allowing review before execution. - Monitor for conflicts with payment processing, order activation workflows, and other order-related automations. - The SYSTEM_MODE access could bypass important validation rules. Ensure all business rules are properly enforced in code or triggers.
Areas that need careful testing when modified: - All Order status values and transitions, especially Activated orders - Concurrent execution with payment processing and order activation - Edge cases with price calculations (zero prices, negative deltas, very large changes) - Orders with complex product/pricing configurations - Impact on revenue recognition and financial reporting - Validation rules and triggers on Order and OrderItem objects - Recovery scenarios when any of the three DML phases fails