# Design Document ## Overview The Price Rollback feature extends the existing Shopify Price Updater application to support reversing pricing changes by setting the main product price to the current compare-at price and removing the compare-at price. This functionality will be integrated into the existing application architecture, providing dual operation modes (price update and rollback) within the same codebase and infrastructure. ## Architecture The rollback functionality will be integrated into the existing modular architecture with minimal changes: ``` shopify-price-updater/ ├── src/ │ ├── config/ │ │ └── environment.js # Extended to support operation mode configuration │ ├── services/ │ │ ├── shopify.js # Existing Shopify API client (no changes) │ │ ├── product.js # Extended with rollback methods │ │ └── progress.js # Extended to log rollback operations │ ├── utils/ │ │ ├── price.js # Extended with rollback price utilities │ │ └── logger.js # Extended to distinguish operation types │ └── index.js # Extended to support dual operation modes ├── .env.example # Updated with operation mode variable ├── package.json # Updated scripts for rollback mode └── Progress.md # Contains both update and rollback logs ``` ## Operation Mode Selection The application will determine its operation mode through environment configuration: ### Environment Variable - `OPERATION_MODE`: Controls which operation to perform - `"update"` (default): Performs price updates with percentage adjustments - `"rollback"`: Performs price rollbacks using compare-at prices ### Backward Compatibility - If `OPERATION_MODE` is not specified, defaults to `"update"` mode - All existing environment variables remain unchanged - Existing scripts and workflows continue to work without modification ## Components and Interfaces ### Environment Configuration (`config/environment.js`) **Extensions:** - Add `OPERATION_MODE` environment variable loading and validation - Validate operation mode is either "update" or "rollback" - Export operation mode in configuration object - Maintain backward compatibility when operation mode is not specified **New Configuration Object:** ```javascript { shopDomain: string, accessToken: string, targetTag: string, priceAdjustmentPercentage: number, // Only used in update mode operationMode: 'update' | 'rollback' // New field } ``` ### Product Service (`services/product.js`) **New Methods:** 1. **`validateProductsForRollback(products)`** - Validates products have variants with compare-at prices - Skips products/variants without compare-at prices - Logs validation warnings for products that cannot be rolled back - Returns array of products with rollback-eligible variants 2. **`rollbackProductPrices(products)`** - Main rollback orchestration method - Processes products in batches to manage rate limits - Returns results object with rollback statistics - Logs progress and errors during rollback operations 3. **`processProductForRollback(product, results)`** - Processes individual product for rollback - Iterates through variants and performs rollback operations - Updates results object with success/failure counts - Handles variant-level error logging 4. **`rollbackVariantPrice(variant, productId)`** - Updates individual variant price to compare-at price value - Sets compare-at price to null (removes it) - Uses existing GraphQL mutation infrastructure - Returns success/failure result with error details **GraphQL Mutation for Rollback:** ```graphql mutation productVariantsBulkUpdate( $productId: ID! $variants: [ProductVariantsBulkInput!]! ) { productVariantsBulkUpdate(productId: $productId, variants: $variants) { productVariants { id price compareAtPrice } userErrors { field message } } } ``` **Rollback Mutation Variables:** ```javascript { productId: "gid://shopify/Product/123", variants: [{ id: "gid://shopify/ProductVariant/456", price: "750.00", // Set to current compare-at price compareAtPrice: null // Remove compare-at price }] } ``` ### Price Utilities (`utils/price.js`) **New Functions:** 1. **`validateRollbackEligibility(variant)`** - Checks if variant has a valid compare-at price - Validates compare-at price is different from current price - Returns boolean indicating rollback eligibility - Provides reason for ineligibility if applicable 2. **`prepareRollbackUpdate(variant)`** - Prepares price update object for rollback operation - Sets new price to current compare-at price value - Sets compare-at price to null - Validates the rollback operation is safe to perform **Example Usage:** ```javascript // Check if variant can be rolled back const isEligible = validateRollbackEligibility(variant); // Prepare rollback update const rollbackUpdate = prepareRollbackUpdate(variant); // Returns: { newPrice: 750.00, compareAtPrice: null } ``` ### Logger Utilities (`utils/logger.js`) **Extensions:** 1. **`logRollbackStart(config)`** - Logs rollback operation start with configuration - Distinguishes from price update operations in logs - Includes operation mode in log entries 2. **`logRollbackUpdate(updateDetails)`** - Logs successful rollback operations - Includes original price, compare-at price, and new price - Formats specifically for rollback operations 3. **`logRollbackSummary(results)`** - Logs rollback completion summary - Includes rollback-specific statistics - Distinguishes from price update summaries ### Main Application (`index.js`) **Extensions:** 1. **Operation Mode Detection:** - Read operation mode from configuration - Route to appropriate workflow based on mode - Maintain single entry point for both operations 2. **Rollback Workflow Methods:** - `fetchAndValidateProductsForRollback()`: Fetch and validate products for rollback - `rollbackPrices(products)`: Execute rollback operations - `displayRollbackSummary(results)`: Display rollback-specific summary 3. **Unified Error Handling:** - Extend existing error handling to support rollback operations - Maintain consistent error reporting across both modes - Preserve existing signal handling and graceful shutdown ## Data Models ### Rollback Validation Result ```javascript { isEligible: boolean, reason?: string, // Present if not eligible variant: { id: string, currentPrice: number, compareAtPrice: number | null } } ``` ### Rollback Update Object ```javascript { newPrice: number, // Set to current compare-at price compareAtPrice: null // Always null to remove compare-at price } ``` ### Rollback Results ```javascript { totalProducts: number, totalVariants: number, eligibleVariants: number, // New field for rollback successfulRollbacks: number, failedRollbacks: number, skippedVariants: number, // Variants without compare-at prices errors: Array<{ productId: string, productTitle: string, variantId: string, errorMessage: string }> } ``` ### Progress Entry for Rollback ```javascript { timestamp: Date, operationType: 'rollback', // Distinguishes from 'update' productId: string, productTitle: string, variantId: string, oldPrice: number, // Original price before rollback compareAtPrice: number, // Compare-at price being used as new price newPrice: number, // New price (same as compare-at price) status: 'success' | 'error' | 'skipped', errorMessage?: string, skipReason?: string // For variants without compare-at prices } ``` ## Error Handling ### Rollback-Specific Error Scenarios 1. **No Compare-At Price:** - Skip variant and log warning - Continue processing other variants - Include in skipped count for summary 2. **Invalid Compare-At Price:** - Skip variant if compare-at price is null, zero, or negative - Log validation warning with specific reason - Continue processing other variants 3. **Same Price Values:** - Skip variant if current price equals compare-at price - Log informational message (no rollback needed) - Continue processing other variants 4. **API Update Failures:** - Use existing retry logic for rate limiting - Log specific error messages from Shopify API - Continue processing remaining variants ### Enhanced Error Reporting - Distinguish rollback errors from update errors in logs - Provide rollback-specific error analysis in completion summary - Include skipped variant statistics in error reporting - Maintain existing error handling patterns for consistency ## Testing Strategy ### Unit Tests **New Test Cases:** - Test rollback price calculation utilities - Test rollback eligibility validation - Test rollback update object preparation - Test operation mode configuration loading **Extended Test Cases:** - Test product service rollback methods - Test logger rollback-specific methods - Test main application rollback workflow ### Integration Tests **New Test Scenarios:** - Test rollback operations with development store - Test rollback with products having various compare-at price scenarios - Test rollback mode selection and configuration - Test mixed scenarios (some variants eligible, some not) **Extended Test Scenarios:** - Test dual operation mode functionality - Test backward compatibility with existing configurations - Test progress logging for rollback operations ### End-to-End Tests **Rollback Workflow Tests:** - Test complete rollback workflow with test products - Verify prices are correctly set to compare-at values - Verify compare-at prices are removed after rollback - Test error recovery and continuation scenarios **Dual Mode Tests:** - Test switching between update and rollback modes - Test that both modes use same infrastructure correctly - Verify progress logging distinguishes between operations ### Manual Testing Checklist **Rollback Functionality:** - Test rollback with products having compare-at prices - Test rollback with products missing compare-at prices - Test rollback with mixed variant scenarios - Verify Progress.md logging for rollback operations **Integration Testing:** - Test backward compatibility with existing configurations - Test operation mode switching - Test that existing update functionality remains unchanged - Verify error handling consistency across both modes ## Implementation Considerations ### Code Reuse Strategy - Maximize reuse of existing Shopify API infrastructure - Extend existing classes rather than creating new ones - Maintain consistent error handling patterns - Preserve existing logging and progress tracking mechanisms ### Performance Considerations - Use same batching strategy as existing price updates - Apply same rate limiting and retry logic - Maintain existing pagination patterns for product fetching - Preserve existing delay mechanisms between operations ### Backward Compatibility - Default to existing behavior when operation mode not specified - Maintain all existing environment variables and their behavior - Preserve existing script entry points and command line usage - Ensure existing Progress.md format remains compatible ### Configuration Management - Add new environment variable with sensible default - Validate operation mode values during configuration loading - Provide clear error messages for invalid operation modes - Update .env.example with new configuration option