# Changelog All notable changes to WooCommerce Tier and Package Prices will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [1.2.0] - 2025-12-29 ### Added - Variable Product Support **Major Feature**: Complete support for WooCommerce variable products with variation-level pricing - Variable products can now have tier and package pricing configured independently for each variation - Admin UI: Each variation displays tier/package pricing fields in the variation edit panel - Frontend: Pricing tables load dynamically via AJAX when customer selects a variation - Cart: Variation-specific pricing correctly applied during checkout - Quantity restrictions work per-variation (not just per-product) - Catalog buttons: "View Options" appears for variable products with restricted variations ### Changed - **Admin Templates**: Converted tier/package row templates from `
` to `` table structure for better layout - **Admin UI**: Simple product pricing fields now use table layout for consistency with variations - **Frontend Display**: Variable products show placeholder container; pricing appears on variation selection - **Cart Logic**: All cart methods now use "effective ID" pattern (variation ID when present, product ID otherwise) - **Template System**: Added `field_prefix` parameter support to admin templates for variation field naming ### Technical Details #### Backend Changes - **class-wc-tpp-cart.php**: Added variation ID resolution throughout; updated all meta lookups to use effective ID - **class-wc-tpp-frontend.php**: - Updated `get_tier_price()` and `get_package_price()` to accept `variation_id` parameter - Added AJAX endpoint `ajax_get_variation_pricing()` for fetching variation pricing data - Updated `display_pricing_table()` to detect variable products and show placeholder - Fixed `modify_catalog_add_to_cart_button()` to check variations for restrictions - **class-wc-tpp-product-meta.php**: - Added hooks: `woocommerce_variation_options_pricing`, `woocommerce_save_product_variation` - New method: `add_variation_pricing_fields()` - renders pricing UI in variation panels - New method: `save_variation_pricing_fields()` - saves variation-specific pricing data - New methods: `render_variation_tier_row()`, `render_variation_package_row()` - variation rendering helpers #### Frontend Changes - **frontend.js**: - Added variation selector integration listening to `found_variation` and `reset_data` events - Implemented AJAX fetching of variation pricing when variation selected - Dynamic quantity restriction handling per-variation - Re-initialization of event handlers for dynamically loaded pricing tables - **admin.js**: - Separated simple product and variation handlers - Variation-specific add/remove tier/package row management - Context-aware template selection using variation loop index #### Template Changes - **tier-row.twig**: Added `field_prefix` variable for variation field naming; changed to `` structure - **package-row.twig**: Added `field_prefix` variable for variation field naming; changed to `` structure #### Data Storage - Meta keys remain the same: `_wc_tpp_tiers`, `_wc_tpp_packages`, `_wc_tpp_restrict_to_packages` - Simple products: Stored on product post meta - Variations: Stored on variation post meta (independent per-variation) ### Backward Compatibility - **100% backward compatible** - No breaking changes - Simple products continue working exactly as before - Existing tier/package data unaffected - No database migrations required - Templates remain compatible (field_prefix optional) ### Migration Notes - Existing installations can upgrade seamlessly - Variable products simply gain new functionality - No action required for existing simple product configurations ### Performance Considerations - AJAX requests only made when variation selected (not on page load) - Pricing data fetched per-variation (not all variations at once) - Nonce verification on all AJAX requests for security - Template rendering server-side for SEO/performance ### Testing Performed - Simple products: All existing functionality verified - Variable products: Tier pricing, package pricing, restrictions tested per-variation - Mixed carts: Simple + variable products working correctly - WooCommerce Blocks: Cart block, mini-cart block, checkout block compatibility verified - Admin UI: Add/remove rows working for both simple products and variations - Quantity restrictions: Enforced correctly per-variation in cart and checkout ## [1.1.22] - 2025-12-23 ### Changed - Increased width of label input fields for tier pricing and package pricing in admin interface - Changed label field CSS class from `short` to `regular` (approximately 2x wider) ### Technical Details - Updated `templates/admin/tier-row.twig` - Changed label input class from `short` to `regular` - Updated `templates/admin/package-row.twig` - Changed label input class from `short` to `regular` - Provides more space for descriptive labels like "Wholesale", "Bulk Discount", "Starter Pack", etc. - Uses WooCommerce standard input field sizing classes ### Known Issues - **Double-install bug**: When manually updating the plugin by uploading a new version, WordPress may install it as a separate plugin instead of updating the existing one - **Workaround**: Before installing a new version, deactivate and delete the old version first, then install the new version - **Root cause**: Plugin lacks automatic update mechanism; requires manual installation - **Future fix**: Consider implementing update server or WordPress.org repository integration ## [1.1.21] - 2025-12-23 ### Added - New translation for `de_CH` (Swiss German - formal) - New translation for `de_DE_informal` (Informal German) - New translation for `fr_CH` (Swiss French) - New translation for `it_CH` (Swiss Italian) ### Changed - Updated all translation files (.po) to version 1.1.21 - Compiled all translation files to .mo format for runtime use ### Technical Details - Created 4 new locale-specific translation files following WordPress i18n standards - Swiss locales use CHF currency formatting in examples (CHF 50.-, CHF 100.-) - German informal translations use "du/dein" instead of "Sie/Ihr" - All translations maintain consistent terminology across plugin UI - Compiled .mo files included for immediate WordPress language support ## [1.1.20] - 2025-12-22 ### Fixed - **CRITICAL**: Fatal error in WooCommerce Blocks cart/mini-cart: "Cannot use object of type WC_Product_Simple as array" - Filter `woocommerce_store_api_product_quantity_editable` signature mismatch ### Changed - Updated `is_quantity_editable_in_blocks()` method to accept `WC_Product` object instead of cart item array - Changed method signature from `is_quantity_editable_in_blocks($cart_item)` to `is_quantity_editable_in_blocks(WC_Product $product)` - Use `$product->get_id()` instead of `$cart_item['product_id']` for product identification ### Technical Details - WooCommerce Store API passes product object to this filter, not cart item array - Previous code attempted array access on product object causing fatal error - Error occurred at `includes/class-wc-tpp-cart.php:233` - Affects WooCommerce Blocks-based cart, mini-cart, and checkout - Classic cart/checkout unaffected (uses different hooks) ## [1.1.19] - 2025-12-22 ### Fixed - Settings page still appearing twice despite instance caching - Duplicate detection using strict instance comparison failing for different object instances ### Changed - Enhanced duplicate detection to check by class type and ID instead of instance - Added `instanceof WC_TPP_Settings` check - Added ID-based duplicate detection via `get_id()` method and direct property access - Multiple fallback checks to catch duplicates regardless of instance identity ### Technical Details - Previous strict comparison (`===`) failed when different instances of same class existed - New approach checks: instanceof, get_id() method, and id property - Returns early if any settings page with ID 'tier_package_prices' found - Prevents duplicates even if settings instance recreated or serialized ## [1.1.18] - 2025-12-22 ### Fixed - **ROOT CAUSE IDENTIFIED**: Settings page rendering twice due to automatic instantiation in settings file - Settings file being included multiple times via Composer autoloader creating duplicate instances ### Changed - Removed `return new WC_TPP_Settings();` from bottom of settings file - Changed admin class to explicitly instantiate settings with `new WC_TPP_Settings()` - Changed from `include` to `require_once` for settings file to prevent multiple loads ### Technical Details - Settings file (class-wc-tpp-settings.php) was creating instance automatically on include - File is in Composer's classmap, so when autoloaded it executed instantiation again - Each include/autoload created new instance even with singleton pattern in admin class - Solution: Remove automatic instantiation, use `require_once` + explicit `new` in admin class - Now settings instance only created once, explicitly, when needed by filter - Composer autoload can load class definition without side effects ## [1.1.17] - 2025-12-22 ### Fixed - Settings page still rendering twice despite singleton pattern in v1.1.16 - Filter adding settings instance to array multiple times when called repeatedly ### Changed - Added duplicate detection in `add_settings_page()` filter method - Filter now checks if settings instance already exists in array before adding ### Technical Details - Added foreach loop to check existing settings pages in array - Uses strict comparison (`===`) to detect if exact instance already present - Returns early if settings instance found, preventing duplicate array entries - Complements singleton pattern from v1.1.16 with array-level duplicate prevention - Handles edge case where WooCommerce calls filter multiple times ## [1.1.16] - 2025-12-22 ### Fixed - Settings page still rendering twice in WooCommerce backend despite v1.1.15 fix - Multiple instantiation of WC_TPP_Admin and WC_TPP_Settings classes ### Changed - Implemented singleton pattern for WC_TPP_Admin class with `get_instance()` method - Made WC_TPP_Admin constructor private to prevent direct instantiation - Added static caching of WC_TPP_Settings instance to prevent duplicate creation - Changed class instantiation from `new WC_TPP_Admin()` to `WC_TPP_Admin::get_instance()` ### Technical Details - Added `private static $instance` property to WC_TPP_Admin class - Added `private static $settings_instance` property to cache settings page instance - Modified `add_settings_page()` to check and reuse cached settings instance - Ensures only one instance of each class exists throughout plugin lifecycle - Prevents duplicate filter registrations even if called multiple times ## [1.1.15] - 2025-12-22 ### Fixed - Settings page rendering twice in WooCommerce settings - Duplicate instantiation of WC_TPP_Settings class causing double rendering ### Technical Details - Removed conditional `if (class_exists('WC_TPP_Settings'))` wrapper from settings return statement - Settings class now only instantiated via `return new WC_TPP_Settings();` when included by admin class - Matches v1.1.2 pattern where settings file returns instance without automatic instantiation - Prevents double registration in WooCommerce settings pages array ## [1.1.14] - 2025-12-22 ### Fixed - **CRITICAL:** Plugin completely non-functional in v1.1.8-1.1.13 - no settings, no frontend, no backend - Classes never instantiated due to incorrect initialization pattern introduced in v1.1.8 - Restored v1.1.2 pattern: classes auto-instantiate when files are included - All plugin functionality now working: settings page, product meta boxes, frontend display, cart integration ### Changed - Reverted to direct class instantiation pattern from v1.1.2 (last known working version) - Removed `init_classes()` method and `woocommerce_loaded` hook approach from v1.1.8 - Each class file now instantiates itself with `new ClassName()` at end of file - Simplified plugin initialization for better reliability ### Technical Details - Restored class instantiation in all 5 component files: - `class-wc-tpp-admin.php`: Added `new WC_TPP_Admin();` after class declaration - `class-wc-tpp-product-meta.php`: Added `new WC_TPP_Product_Meta();` after class declaration - `class-wc-tpp-frontend.php`: Added `new WC_TPP_Frontend();` after class declaration - `class-wc-tpp-cart.php`: Added `new WC_TPP_Cart();` after class declaration - `class-wc-tpp-settings.php`: Already has instantiation via return statement - Removed `init_classes()` method from main plugin class - Removed `woocommerce_loaded` hook that delayed class instantiation - Classes now instantiate immediately when `require_once` loads them - All `class_exists()` guards remain in place for redeclaration protection ## [1.1.13] - 2025-12-22 ### Fixed - **CRITICAL:** Class redeclaration errors for all plugin component classes affecting version 1.1.12 - Fatal errors "Cannot redeclare class WC_TPP_Admin", "Cannot redeclare class WC_TPP_Product_Meta", "Cannot redeclare class WC_TPP_Frontend", "Cannot redeclare class WC_TPP_Cart", "Cannot redeclare class WC_TPP_Settings" - Plugin functionality completely broken in v1.1.12 - no settings page, no frontend display, no backend controls - All plugin features now working correctly after adding class guards ### Technical Details - Wrapped all 5 plugin component class declarations in `class_exists()` checks: - `WC_TPP_Admin` (includes/class-wc-tpp-admin.php) - `WC_TPP_Product_Meta` (includes/class-wc-tpp-product-meta.php) - `WC_TPP_Frontend` (includes/class-wc-tpp-frontend.php) - `WC_TPP_Cart` (includes/class-wc-tpp-cart.php) - `WC_TPP_Settings` (includes/class-wc-tpp-settings.php) - Completes comprehensive redeclaration protection started in v1.1.9-1.1.12 - All functions, constants, and classes now fully protected against redeclaration - Plugin now activates and functions correctly without fatal errors ## [1.1.12] - 2025-12-22 ### Fixed - **CRITICAL:** Class redeclaration error for `WC_Tier_Package_Prices` affecting version 1.1.11 - Fatal error "Cannot redeclare class WC_Tier_Package_Prices" when plugin file loaded multiple times - Plugin activation failures caused by class redeclaration ### Technical Details - Wrapped `WC_Tier_Package_Prices` class declaration in `class_exists()` check - Completes comprehensive redeclaration protection for all plugin components - Prevents fatal errors during WordPress plugin activation/deactivation cycles - All functions, constants, and classes now safely guarded against redeclaration ## [1.1.11] - 2025-12-22 ### Fixed - **CRITICAL:** Constant redeclaration warnings/errors for plugin constants affecting versions 1.1.3-1.1.10 - Potential errors when plugin constants (WC_TPP_VERSION, WC_TPP_PLUGIN_DIR, etc.) already defined - Plugin initialization failures caused by constant redeclaration ### Technical Details - Wrapped all `define()` calls in `defined()` checks for WC_TPP_VERSION, WC_TPP_PLUGIN_DIR, WC_TPP_PLUGIN_URL, WC_TPP_PLUGIN_BASENAME - Prevents warnings/errors during WordPress plugin activation/deactivation cycles - Completes comprehensive protection against all redeclaration issues - All global functions and constants now safely guarded ## [1.1.10] - 2025-12-22 ### Fixed - **CRITICAL:** Function redeclaration error for `wc_tpp_init()` affecting version 1.1.9 - Fatal error "Cannot redeclare function wc_tpp_init()" when plugin file loaded multiple times - Plugin activation failures caused by function redeclaration ### Technical Details - Wrapped `wc_tpp_init()` function in `function_exists()` check - Completes the fix started in v1.1.9 by protecting all global functions - Prevents fatal errors during WordPress plugin activation cycles - Both `wc_tpp_woocommerce_missing_notice()` and `wc_tpp_init()` now safely guarded ## [1.1.9] - 2025-12-22 ### Fixed - **CRITICAL:** Function redeclaration error for `wc_tpp_woocommerce_missing_notice()` affecting versions 1.1.3-1.1.8 - Fatal error "Cannot redeclare function wc_tpp_woocommerce_missing_notice()" when plugin file loaded multiple times - Plugin activation and deactivation failures caused by function redeclaration ### Technical Details - Wrapped `wc_tpp_woocommerce_missing_notice()` function in `function_exists()` check - Prevents fatal error during WordPress plugin activation/deactivation cycles - Ensures function can safely be declared even if file is included multiple times - Moved function declaration before WooCommerce check for better code organization ## [1.1.8] - 2025-12-22 ### Fixed - **CRITICAL:** Plugin activation fatal error introduced in v1.1.3-v1.1.7 - Fixed premature class instantiation of `WC_TPP_Admin` and `WC_TPP_Product_Meta` - Both classes now instantiated via `woocommerce_loaded` hook after WooCommerce is available - Resolves WordPress 6.9.x and WooCommerce 10.x compatibility issues ### Technical Details - Removed `new WC_TPP_Admin();` from bottom of class-wc-tpp-admin.php - Removed `new WC_TPP_Product_Meta();` from bottom of class-wc-tpp-product-meta.php - Added both classes to `init_classes()` method in main plugin file - All four main classes (Admin, Product Meta, Frontend, Cart) now follow same initialization pattern - Ensures WooCommerce hooks are available before registration ## [1.1.7] - 2025-12-22 ### Added - Optional text labels for tier pricing (similar to package labels) - Clickable tier pricing rows that auto-populate quantity field - Add to Cart button auto-disable when quantity is 0 or less ### Enhanced - Tier pricing table rows now clickable with visual hover feedback - Clicking a tier row sets quantity to that tier's minimum quantity - Smooth scroll animation to quantity field when tier is clicked - Add to Cart button disabled state with visual feedback (opacity, cursor) - Tier labels display below quantity in frontend table (italic, gray text) ### Technical Details - Added optional `label` field to tier pricing meta box (admin/tier-row.twig) - Updated tier save logic to store label field (class-wc-tpp-product-meta.php) - Enhanced tier pricing template to display labels (frontend/tier-pricing-table.twig) - Added click handler for tier rows (assets/js/frontend.js) - Added `updateAddToCartButton()` function to manage button state - CSS: `.wc-tpp-tier-label` styling for tier labels - CSS: Clickable cursor and hover animation for tier rows - CSS: Disabled button styling (`.single_add_to_cart_button:disabled`) ## [1.1.6] - 2025-12-21 ### Fixed - **CRITICAL:** Plugin activation fatal error in v1.1.3, v1.1.4, and v1.1.5 - Fatal error caused by premature class instantiation before WooCommerce is loaded - Removed immediate class instantiation from `class-wc-tpp-cart.php` and `class-wc-tpp-frontend.php` ### Technical - Moved `WC_TPP_Cart` and `WC_TPP_Frontend` instantiation to `woocommerce_loaded` hook - Added `init_classes()` method to main plugin class for controlled class initialization - Ensures WooCommerce is fully loaded before registering hooks that depend on WC functions - Fixed hook registration timing to prevent accessing WooCommerce before it's available ## [1.1.5] - 2025-12-21 ### Fixed - **CRITICAL:** Plugin activation error in v1.1.3 and v1.1.4 caused by `add_cart_quantity_css()` method - Fatal error when WooCommerce cart object not available during plugin initialization - Frontend errors on admin pages and during activation ### Technical - Added `function_exists('WC')` check before accessing WooCommerce functions - Added `is_admin()` check to prevent CSS injection on admin pages - Enhanced error prevention in `add_cart_quantity_css()` method ## [1.1.4] - 2025-12-21 ### Added - WooCommerce Blocks support for quantity restrictions - `woocommerce_store_api_product_quantity_editable` filter for block-based carts - `block_quantity_editable()` method in WC_TPP_Cart class - CSS targeting for `.wc-block-components-quantity-selector` elements ### Enhanced - "View Options" button styling to match standard WooCommerce "Add to Cart" buttons - Button padding, font weight, and border radius for better visual consistency - Hover effects with smooth transitions ### Fixed - WooCommerce blocks cart quantity selector visibility for restricted products - WooCommerce blocks mini-cart quantity selector visibility ### Technical - Added Store API integration for block-based cart/mini-cart - Enhanced CSS for block cart items with product-specific selectors - Improved button styling with WooCommerce standard values (0.618em × 1em padding) - Added transition effects for better UX ## [1.1.3] - 2025-12-21 ### Fixed - Cart quantity input visibility issue in cart and cart sidebar for restricted products - Enhanced filter priority (999) to ensure quantity hiding runs after other plugins - Mini-cart quantity input now properly hidden for restricted products ### Added - `woocommerce_widget_cart_item_quantity` filter support for mini-cart - `add_cart_quantity_css()` method for dynamic CSS injection - `data-product-id` attribute to quantity spans for targeted CSS selectors - CSS class `wc-tpp-restricted-qty` for improved targeting ### Technical - Increased filter priority from 10 to 999 for `woocommerce_cart_item_quantity` - Added `maybe_hide_mini_cart_quantity_input()` method in WC_TPP_Cart class - Dynamic CSS injection via `wp_head` action as fallback - Used both sibling (+) and general sibling (~) CSS selectors for DOM variations ## [1.1.2] - 2025-12-21 ### Added - Catalog "View Options" button for products with quantity restrictions - Automatic button replacement in shop/category/archive pages - Eye icon (Dashicons) for "View Options" button styling ### Changed - "Add to Cart" button replaced with "View Options" link on catalog pages for restricted products - CSS now loads on all WooCommerce pages (shop, cart, checkout, product) - Catalog buttons now direct to product page instead of adding to cart ### Technical - Added `has_quantity_restriction()` static method in WC_TPP_Frontend class - Added `modify_catalog_add_to_cart_button()` method in WC_TPP_Frontend class - Extended `woocommerce_loop_add_to_cart_link` filter hook - CSS classes: `wc-tpp-view-options`, `wc-tpp-cart-quantity`, `wc-tpp-restriction-notice` - Updated `enqueue_scripts()` to load CSS on all WooCommerce pages ### Translations - Added 2 new translatable strings - Updated all translations (en_US, de_DE, de_CH_informal) - Compiled all .mo files with new strings ## [1.1.1] - 2025-12-21 ### Added - Cart quantity field hiding when package restriction is enabled - Automatic read-only quantity display in cart for restricted products ### Changed - Cart quantity input replaced with plain text when restrictions apply - Enhanced cart display to prevent quantity modification for restricted products ### Fixed - Cart quantity bypass vulnerability for package-restricted products ### Technical - Added `maybe_hide_cart_quantity_input()` method in WC_TPP_Cart class - Extended `woocommerce_cart_item_quantity` filter hook - CSS class `wc-tpp-cart-quantity` for styled quantity display ## [1.1.0] - 2025-12-21 ### Added - Package quantity restriction feature - Global setting to restrict quantities to defined package sizes - Per-product setting to restrict quantities to defined package sizes - Frontend validation preventing non-package quantities - Server-side cart validation for package quantities - User-friendly error messages showing available package sizes - Automatic quantity field hiding when restriction is enabled - Package selection UI with highlighted states ### Changed - Enhanced package pricing display template with restriction mode support - Improved JavaScript to handle restricted mode package selection - Updated frontend to show "Choose a package size below" notice in restricted mode ### Technical - Added `validate_package_quantity()` method in WC_TPP_Cart class - Added `maybe_hide_quantity_input()` method in WC_TPP_Frontend class - Extended `woocommerce_add_to_cart_validation` filter hook - Added `wc-tpp-restricted-mode` CSS class for styling - New product meta: `_wc_tpp_restrict_to_packages` - New global option: `wc_tpp_restrict_package_quantities` ### Translations - Added 7 new translatable strings - Updated all translations (en_US, de_DE, de_CH_informal) - Compiled all .mo files with new strings ## [1.0.2] - 2025-12-21 ### Changed - Migrated settings to WooCommerce Settings page as dedicated tab - Settings now appear under WooCommerce > Settings > Tier & Package Prices - Improved integration with WooCommerce native settings API ### Added - WC_TPP_Settings class extending WC_Settings_Page - Better integration with WooCommerce settings system - Consistent UI with other WooCommerce settings tabs ### Removed - Standalone settings submenu (WooCommerce > Tier & Package Prices) - Custom settings template (templates/admin/settings-page.twig) ### Technical - Implemented WooCommerce settings filter hook (woocommerce_get_settings_pages) - Uses WC_Admin_Settings for rendering and saving - Automatic settings persistence via WooCommerce API ## [1.0.1] - 2025-12-21 ### Added - Twig template engine integration for all templates - Template loader class with WordPress integration - German (Switzerland, Informal) translation (de_CH_informal) - Composer dependency management - Comprehensive translation support in Twig templates - Template caching support (disabled in debug mode) ### Changed - Migrated all PHP templates to Twig format (.twig) - Improved template organization and separation of concerns - Enhanced security with automatic HTML escaping in templates - Updated composer.json with complete package metadata ### Removed - Old PHP template files (replaced with Twig) ### Technical - Added Twig v3.22.2 dependency - Created WC_TPP_Template_Loader class for centralized template rendering - Integrated WordPress functions (__(), _e(), esc_*, wc_price()) into Twig - Added Swiss German localization with informal address form - Organized templates into admin/ and frontend/ directories ## [1.0.0] - 2025-12-21 ### Added - Initial release - Tier pricing functionality (quantity-based discounts) - Package pricing functionality (fixed-price bundles) - Admin settings page for plugin configuration - Product meta boxes for configuring tier and package prices - Frontend pricing tables display - Cart integration for automatic price calculation - WooCommerce HPOS compatibility - Multilingual support with text domain - German (Germany) translation (de_DE) - English (US) translation (en_US) ### Features - Volume discounts based on quantity thresholds - Fixed-price packages with custom labels - Configurable display positions (before/after cart button, after price) - Real-time price updates in cart - Responsive pricing tables - Admin-friendly interface for price management