Class Name: PromotionSyncLogic¶
Last Updated: 2025-10-22
API Name: PromotionSyncLogic Type: Batch/Queueable/Schedulable Test Coverage: Unknown (Test Class: PromotionSyncLogicTest)
Business Purpose¶
This class maintains data consistency for auto-renewal discount percentages across AANP's promotion and membership systems. When renewal rates change on Account records or auto-renewal discount percentages change on Product2 records, it automatically updates corresponding PromotionTarget records and synchronizes Account renewal rates with their associated product discount rates, ensuring members see accurate and consistent pricing across all touchpoints.
Class Overview¶
Scope and Sharing¶
- Sharing Model: with sharing
- Access Modifier: public
- Interfaces Implemented: Database.Batchable
Key Responsibilities¶
- Detects changes to Renewal_Rate__c on Account records and Auto_Renewal_Discount_Percent__c on Product2 records
- Updates PromotionTarget.AdjustmentPercent to match changed discount percentages
- Executes batch process to synchronize Account renewal rates with product discount percentages
- Maintains bidirectional synchronization between Individual Membership Auto-Renewal Promotion and source data
- Provides test bypass mechanism for unit testing
Public Methods¶
execute (Static)¶
Purpose: Entry point for processing Account or Product2 record changes, detecting discount percentage modifications and triggering synchronization logic.
Parameters:
- updatedRecordsMap (MapoldRecordsMap (Map
Returns: - void
Usage Example:
// Called from Account or Product2 trigger
Map<Id, Account> newAccountMap = Trigger.newMap;
Map<Id, Account> oldAccountMap = Trigger.oldMap;
PromotionSyncLogic.execute(newAccountMap, oldAccountMap);
Business Logic: - Iterates through updated records and retrieves appropriate field API name from discountApiFieldNameMapping - Compares old and new discount percentage values - Builds map of changed record IDs to new percentages - Calls updatePromoTargets to update PromotionTarget records immediately - Executes batch job to synchronize Account renewal rates with product discount percentages - Respects bypassTest flag to prevent recursive batch execution in test context
start (Batch Interface)¶
Purpose: Initializes batch processing by building query for Accounts with auto-renewal enabled that are linked to products with changed discount percentages.
Parameters:
- bc (Database.BatchableContext): Batch context provided by the platform
Returns:
- Database.QueryLocator: Query locator for Account records to process
Business Logic: - Constructs dynamic SOQL query to find Accounts where: - AANP_Membership__r.Order_Product__r.Product2Id is in the set of changed product IDs - Auto_Renew__c = true - Retrieves Account.Renewal_Rate__c and related Product2.Auto_Renewal_Discount_Percent__c - Uses Database.getQueryLocatorWithBinds with SYSTEM_MODE for elevated data access - Traverses complex relationship: Account → AANP_Membership → Order_Product → Product2
execute (Batch Interface)¶
Purpose: Processes each batch of Account records by updating their Renewal_Rate__c to match the associated product's Auto_Renewal_Discount_Percent__c.
Parameters:
- bc (Database.BatchableContext): Batch context provided by the platform
- scope (List
Business Logic:
- Casts scope to List
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¶
updatePromoTargets¶
Purpose: Updates AdjustmentPercent on PromotionTarget records that match changed parent record IDs.
Called By: execute (static method)
Business Logic: - Queries PromotionTarget records where: - TargetId matches changed Account or Product IDs - AdjustmentType = 'PercentageDiscount' - Promotion.Name = 'Individual Membership Auto-Renewal Promotion' - Updates AdjustmentPercent to new percentage value from parent record - Performs immediate DML update
Constructor¶
Purpose: Initializes batch instance with query string and product ID set.
Parameters:
- productIds (Set
Business Logic: - Builds dynamic SOQL query string targeting Accounts with memberships linked to changed products - Stores product IDs for use in bind variables during batch start method
Dependencies¶
Apex Classes¶
None - standalone class with no external class dependencies
Salesforce Objects¶
Account: Target object for renewal rate synchronization (Fields: Id, Renewal_Rate__c, Auto_Renew__c)Product2: Source object for discount percentages (Field: Auto_Renewal_Discount_Percent__c)PromotionTarget: Promotion discount configuration (Fields: Id, TargetId, AdjustmentPercent, AdjustmentType, Promotion.Name)AANP_Membership__c: Custom membership object linking Accounts to Order ProductsOrderItem: (Referenced as Order_Product__r) Links memberships to products
Custom Settings/Metadata¶
None
External Services¶
None
Design Patterns¶
- Batch Processing Pattern: Implements Database.Batchable for large-scale Account updates
- Static Factory Method: Static execute method provides entry point for trigger invocation
- Bidirectional Sync Pattern: Updates both PromotionTarget immediately and Account records via batch
- Bind Variable Pattern: Uses Database.getQueryLocatorWithBinds for secure parameter passing
- Test Visibility Pattern: Uses @TestVisible for test bypass flag
Governor Limits Considerations¶
SOQL Queries: - execute (static): 1 query for PromotionTarget records - start (batch): 1 query via QueryLocator - execute (batch): 0 queries (uses data from scope)
DML Operations: - execute (static): 1 DML update for PromotionTarget records - execute (batch): 1 DML update per batch chunk for Account records
CPU Time: Low - simple field assignments with no complex calculations
Heap Size: - Batch processing limits heap usage to batch size (default 200 records) - PromotionTarget query size depends on number of affected records
Bulkification: Yes - processes records in batches with bulk DML operations
Async Processing: Yes - batch job runs asynchronously after initial PromotionTarget update
Error Handling¶
Strategy: Minimal error handling - No try-catch blocks in any methods - No validation of field values or relationships - Batch update failures will throw uncaught exceptions
Logging: - No logging of changes, errors, or progress - No tracking of which records were updated or failed
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 - PromotionTarget query does not use WITH SECURITY_ENFORCED
CRUD Permissions: - No explicit CRUD permission checks - Relies on running user's permissions for PromotionTarget update - Batch uses SYSTEM_MODE for Account queries
Input Validation: - No validation of discount percentage ranges (could be negative or > 100%) - No null checks on relationship fields - No validation that percentage changes are meaningful
Test Class¶
Test Class: PromotionSyncLogicTest Coverage: Unknown Test Scenarios Covered: - Details not available from source code
Changes & History¶
- 2025-01-08: Initial creation by Ryan O'Sullivan
- PR-32304: Auto-renewal percentage synchronization feature
Pre-Go-Live Concerns¶
CRITICAL - Fix Before Go-Live¶
- No Error Handling: No try-catch blocks around DML operations. Failed updates will throw uncaught exceptions causing trigger failures.
- Complex Relationship Query: Deep nested relationship (Account → AANP_Membership → Order_Product → Product2) may fail if any link is null. Missing null checks.
- Missing Validation: No validation that discount percentages are valid (0-100 range). Could set invalid values.
- Recursive Trigger Risk: Account updates in batch could trigger Account triggers, potentially causing recursive execution if not properly controlled.
HIGH - Address Soon After Go-Live¶
- No Audit Trail: Changes to renewal rates and promotion targets are not logged. Difficult to track why rates changed or troubleshoot discrepancies.
- Batch Monitoring: No logging in finish method to track batch completion or success. Failed batches won't be detected.
- Single Promotion Hardcoded: Query filters for 'Individual Membership Auto-Renewal Promotion' specifically. Not extensible to other promotions.
- No Rollback Capability: Changes to PromotionTarget and Account records cannot be easily reversed if synchronization errors occur.
MEDIUM - Future Enhancement¶
- Test Bypass in Production: bypassTest flag could potentially be manipulated or misused if accessed outside test context. Consider using Test.isRunningTest() instead.
- Performance: Two separate update operations (PromotionTarget immediate, Account batch) could be optimized or consolidated.
- Empty Finish Method: No cleanup, logging, or chaining to next process. Consider adding completion metrics.
- Limited Object Support: Hard-coded mapping supports only Account and Product2. Not easily extensible to other objects.
LOW - Monitor¶
- Code Comments: Minimal inline comments make complex relationship logic harder to understand for maintainers.
- Query String Construction: Building query as string rather than using dynamic SOQL could be error-prone for future modifications.
- No Performance Metrics: Processing time and throughput not tracked.
Maintenance Notes¶
Complexity: High Recommended Review Schedule: Quarterly
Key Maintainer Notes: - This class orchestrates critical pricing synchronization across multiple objects. Any failures can cause member-facing pricing discrepancies. - The relationship chain (Account → AANP_Membership → Order_Product → Product2) is complex and fragile. Add comprehensive null checks before production. - Changes are triggered from Account and Product2 triggers. Ensure trigger handlers properly call this class and handle any exceptions. - The batch uses SYSTEM_MODE to access membership and product data. This bypasses sharing rules intentionally but must be monitored for security implications. - Consider implementing a logging mechanism to track all discount percentage changes for audit and troubleshooting purposes. - The hard-coded promotion name 'Individual Membership Auto-Renewal Promotion' makes this code fragile. Consider using Custom Metadata or Custom Settings for configuration. - Test thoroughly with scenarios where AANP_Membership or Order_Product relationships don't exist. - Monitor for potential circular update scenarios where Account updates trigger this logic which updates Accounts again.
Areas that need careful testing when modified: - All variations of the Account → AANP_Membership → Order_Product → Product2 relationship chain - Scenarios with missing or null relationships at any level - Concurrent updates to both Account and Product2 records - Validation rules and triggers on Account and PromotionTarget objects - Performance with large volumes of accounts with auto-renewal enabled - Edge cases with discount percentages (0%, 100%, negative values, null values)