diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..3a9c1e4 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,429 @@ +# WooCommerce Tier and Package Prices - AI Context Document + +**Last Updated:** 2025-12-23 +**Current Version:** 1.1.20 +**Author:** Marco Graetsch +**Project Status:** Production-ready WordPress plugin + +## Project Overview + +This is a WooCommerce plugin that adds flexible pricing capabilities to products through two distinct pricing models: + +1. **Tier Pricing (Volume Discounts)**: Progressive discounts based on quantity ranges (e.g., 1-9 items @ $12, 10-24 @ $10, 25+ @ $8) +2. **Package Pricing (Fixed Bundles)**: Exact quantity packages at fixed prices (e.g., exactly 10 items for $95, exactly 25 for $200) + +### Key Fact: 100% AI-Generated +This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase was created through AI assistance. + +## Technical Stack + +- **Language:** PHP 7.4+ +- **Framework:** WordPress Plugin API +- **E-commerce:** WooCommerce 8.0+ (tested up to 10.x) +- **Template Engine:** Twig 3.0 (via Composer) +- **Frontend:** Vanilla JavaScript + jQuery +- **Styling:** Custom CSS +- **Dependency Management:** Composer +- **Internationalization:** WordPress i18n (.pot/.po/.mo files) + +### Dependencies +```json +{ + "twig/twig": "^3.0", + "symfony/polyfill-ctype": "^1.x", + "symfony/polyfill-mbstring": "^1.x" +} +``` + +## Architecture + +### Directory Structure +``` +wc-tier-and-package-prices/ +├── wc-tier-and-package-prices.php # Main plugin file (entry point) +├── includes/ # PHP classes +│ ├── class-wc-tpp-admin.php # Admin settings integration +│ ├── class-wc-tpp-settings.php # WooCommerce settings page +│ ├── class-wc-tpp-product-meta.php # Product edit page meta boxes +│ ├── class-wc-tpp-frontend.php # Product page display logic +│ ├── class-wc-tpp-cart.php # Cart price calculations +│ └── class-wc-tpp-template-loader.php # Twig template loader +├── templates/ # Twig templates +│ ├── admin/ # Admin interface templates +│ │ ├── tier-row.twig # Single tier input row +│ │ └── package-row.twig # Single package input row +│ └── frontend/ # Customer-facing templates +│ ├── pricing-table.twig # Main pricing display wrapper +│ ├── tier-pricing-table.twig # Tier pricing display +│ └── package-pricing-display.twig # Package buttons/cards +├── assets/ +│ ├── css/ +│ │ ├── admin.css # Backend styling +│ │ └── frontend.css # Product page & cart styling +│ └── js/ +│ ├── admin.js # Meta box interaction (add/remove rows) +│ └── frontend.js # Dynamic price updates, package selection +├── languages/ # Translation files +│ ├── *.pot # Translation template +│ ├── *.po # Translation sources +│ └── *.mo # Compiled translations +├── vendor/ # Composer dependencies (included in releases) +├── releases/ # Release packages (not in git) +└── *.md # Documentation files + +``` + +### Class Responsibilities + +#### 1. `WC_Tier_Package_Prices` (Main Plugin Class) +- **Location:** `wc-tier-and-package-prices.php` +- **Pattern:** Singleton +- **Responsibilities:** + - Plugin initialization and bootstrapping + - Loading all component classes via `includes()` + - HPOS (High-Performance Order Storage) compatibility declaration + - Text domain loading for internationalization + - Activation/deactivation hooks + +#### 2. `WC_TPP_Admin` +- **Location:** `includes/class-wc-tpp-admin.php` +- **Pattern:** Singleton +- **Responsibilities:** + - Enqueues admin CSS/JS + - Registers WooCommerce settings page via filter + - Manages settings page instance (cached to prevent duplicates) + - Product meta box asset loading + +#### 3. `WC_TPP_Settings` +- **Location:** `includes/class-wc-tpp-settings.php` +- **Extends:** `WC_Settings_Page` (WooCommerce core) +- **Responsibilities:** + - Creates "Tier & Package Prices" tab in WooCommerce settings + - Defines global plugin settings (enable/disable features, display position, etc.) + - Setting persistence through WooCommerce options API + +**Global Settings:** +- `wc_tpp_enable_tier_pricing` (yes/no) +- `wc_tpp_enable_package_pricing` (yes/no) +- `wc_tpp_display_table` (yes/no) - Show pricing tables on product pages +- `wc_tpp_display_position` (before_add_to_cart / after_add_to_cart / after_price) +- `wc_tpp_restrict_package_quantities` (yes/no) - Global quantity restrictions + +#### 4. `WC_TPP_Product_Meta` +- **Location:** `includes/class-wc-tpp-product-meta.php` +- **Responsibilities:** + - Adds tier/package pricing fields to product edit page + - Renders Twig templates for meta box rows + - Saves tier/package data to post meta + - Nonce verification and capability checks for security + - Prevents autosave from corrupting data + +**Product Meta Keys:** +- `_wc_tpp_tiers` - Array of tier objects `[{min_qty, price, label}]` +- `_wc_tpp_packages` - Array of package objects `[{qty, price, label}]` +- `_wc_tpp_restrict_to_packages` - Per-product quantity restriction (yes/no) + +#### 5. `WC_TPP_Frontend` +- **Location:** `includes/class-wc-tpp-frontend.php` +- **Responsibilities:** + - Enqueues frontend CSS/JS on product pages + - Displays pricing tables via Twig templates + - Localizes currency settings to JavaScript + - Hides quantity inputs for restricted products + - Modifies catalog "Add to Cart" buttons to "View Options" for restricted products + - Static methods for price lookups (`get_tier_price()`, `get_package_price()`) + +#### 6. `WC_TPP_Cart` +- **Location:** `includes/class-wc-tpp-cart.php` +- **Responsibilities:** + - **MOST CRITICAL CLASS** - Handles all cart price calculations + - Applies tier/package pricing during cart totals calculation + - Stores pricing metadata in cart items for display + - Customizes cart item display (price labels, quantity indicators) + - Validates package quantities on add-to-cart + - Hides/disables quantity inputs for restricted products (classic cart + blocks) + - **WooCommerce Blocks support** via `woocommerce_store_api_product_quantity_editable` filter + +**Price Calculation Priority (in `apply_tier_package_pricing()`):** +1. Check for exact package match → Use package price if found +2. Check for tier match → Use tier price if found +3. Fall back to regular product price + +#### 7. `WC_TPP_Template_Loader` +- **Location:** `includes/class-wc-tpp-template-loader.php` +- **Pattern:** Singleton +- **Responsibilities:** + - Initializes Twig environment with proper paths + - Renders Twig templates from both admin and frontend directories + - Handles template caching and error handling + +## Important Implementation Details + +### Price Calculation Logic + +**Package Pricing** (exact match): +```php +// In cart: if quantity == 10 and package exists for 10, use package price +if ($quantity == $package['qty']) { + $unit_price = $package['price'] / $quantity; // Total price divided by quantity + $product->set_price($unit_price); // WooCommerce expects unit price +} +``` + +**Tier Pricing** (range-based): +```php +// In cart: if quantity >= 10, use tier price for quantities 10+ +foreach ($tiers as $tier) { + if ($quantity >= $tier['min_qty']) { + $applicable_price = $tier['price']; // This is already unit price + } +} +$product->set_price($applicable_price); +``` + +### Quantity Restriction Feature + +Products can be configured to ONLY allow purchase in package quantities: + +- **Global setting:** `wc_tpp_restrict_package_quantities` +- **Per-product setting:** `_wc_tpp_restrict_to_packages` +- **When enabled:** + - Quantity inputs are hidden on product page, cart, and mini-cart + - Customers must use package selection buttons + - Validation prevents arbitrary quantities from being added + - Catalog buttons change to "View Options" instead of "Add to Cart" + +### WooCommerce Blocks Compatibility + +**CRITICAL BUG FIXED in v1.1.20:** +- Filter `woocommerce_store_api_product_quantity_editable` passes `WC_Product` object, NOT cart item array +- Previous code tried to use product object as array → fatal error +- Fixed by accepting product object and using `$product->get_id()` + +### Cart Item Metadata + +The plugin stores additional data in cart items for display purposes: + +```php +WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'package' | 'tier'; +WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = 99.99; // For packages +WC()->cart->cart_contents[$cart_item_key]['wc_tpp_unit_price'] = 9.99; // For tiers +``` + +This metadata is used by display filters to show "(Package price)" or "(Volume discount)" labels. + +## Common Patterns & Conventions + +### Class Instantiation Pattern +All classes auto-instantiate at the end of their file: +```php +if (!class_exists('WC_TPP_Frontend')) { + class WC_TPP_Frontend { + // class code + } +} +new WC_TPP_Frontend(); // Auto-instantiate +``` + +**Exception:** Admin and Settings classes use singleton pattern to prevent duplicates. + +### Security Best Practices +- All user inputs are sanitized (integers for quantities/prices) +- Nonce verification on form submissions +- Capability checks (`edit_products`) before saving +- Output escaping in templates (`esc_attr`, `esc_html`, `esc_js`) +- Direct file access prevention via `ABSPATH` check + +### Translation Ready +All user-facing strings use: +```php +__('Text to translate', 'wc-tier-package-prices') +_e('Text to translate', 'wc-tier-package-prices') +``` + +Text domain: `wc-tier-package-prices` + +## Known Issues & Historical Context + +### Settings Page Duplication Saga (v1.1.15-1.1.19) +Multiple versions attempted to fix settings page appearing twice: +- **Root cause:** Settings file auto-instantiation + Composer autoloader +- **Solution:** Removed auto-instantiation from settings file, explicit instantiation in admin class +- **Prevention:** Singleton pattern + duplicate detection in array + +### Class Redeclaration Issues (v1.1.8-1.1.14) +Plugin was completely non-functional: +- **Cause:** Incorrect initialization pattern without `class_exists()` guards +- **Solution:** Added guards and restored direct instantiation pattern +- **Lesson:** Always wrap class declarations in `class_exists()` checks + +### WooCommerce Blocks Fatal Error (v1.1.19 → v1.1.20) +``` +Fatal error: Cannot use object of type WC_Product_Simple as array +Location: includes/class-wc-tpp-cart.php:233 +``` +- **Cause:** Filter signature mismatch - expected array, received product object +- **Fix:** Changed method signature to accept `WC_Product $product` instead of `$cart_item` array +- **Status:** FIXED in v1.1.20 + +## Release Process + +### Version Bumping +Update version in 3 places: +1. `wc-tier-and-package-prices.php` - Plugin header comment (line 7) +2. `wc-tier-and-package-prices.php` - `WC_TPP_VERSION` constant (line 26) +3. `composer.json` - version field (optional, not critical) + +### Creating Release Package +```bash +# From project root +cd releases + +# Create zip excluding dev files +zip -r wc-tier-and-package-prices-X.X.X.zip .. \ + -x '*.git*' '*.log' '.claude/*' 'releases/*' 'node_modules/*' \ + '.DS_Store' 'Thumbs.db' '.vscode/*' '.idea/*' '*.sublime-*' \ + 'notes.*' 'logs/*' 'templates/cache/*' 'composer.lock' + +# Generate checksums +md5sum wc-tier-and-package-prices-X.X.X.zip > wc-tier-and-package-prices-X.X.X.zip.md5 +sha256sum wc-tier-and-package-prices-X.X.X.zip > wc-tier-and-package-prices-X.X.X.zip.sha256 +``` + +**IMPORTANT:** The `vendor/` directory MUST be included in releases (Twig dependency required for runtime). + +### What Gets Released +- All plugin source files +- Compiled vendor dependencies +- Translation files (.mo compiled from .po) +- Assets (CSS, JS) +- Documentation (README, CHANGELOG, etc.) + +### What's Excluded +- Git metadata (`.git/`) +- Development files (`.vscode/`, `.idea/`) +- Logs and cache files +- Previous releases +- `composer.lock` (but `vendor/` is included) + +## Testing Checklist + +When making changes, test these critical paths: + +### Admin +- [ ] Settings page appears once under WooCommerce > Tier & Package Prices +- [ ] Settings save correctly +- [ ] Product edit page shows tier/package meta boxes +- [ ] Adding/removing tiers works +- [ ] Adding/removing packages works +- [ ] Data saves when clicking "Update" on product + +### Frontend (Product Page) +- [ ] Pricing tables display when configured +- [ ] Package buttons update quantity selector +- [ ] Price updates dynamically when quantity changes +- [ ] Restricted products hide quantity input +- [ ] "View Options" appears on catalog for restricted products + +### Cart & Checkout +- [ ] Correct prices applied for tier pricing +- [ ] Correct prices applied for package pricing +- [ ] Cart displays pricing type labels +- [ ] Package quantities can't be edited if restricted +- [ ] Prices recalculate if quantity changed (non-restricted products) +- [ ] Checkout totals are correct + +### WooCommerce Blocks (Critical!) +- [ ] Mini cart block doesn't throw fatal errors +- [ ] Cart block works correctly +- [ ] Checkout block processes orders +- [ ] Quantity editable flag works for blocks + +## Development Tips for Future AI Assistants + +### When Debugging Cart Issues +1. Check `includes/class-wc-tpp-cart.php` first +2. The `apply_tier_package_pricing()` method runs on `woocommerce_before_calculate_totals` +3. Always validate product objects with `is_a($product, 'WC_Product')` +4. Remember: WooCommerce expects UNIT prices, not total prices (except for internal calculations) + +### When Working with WooCommerce Hooks +- WooCommerce has both classic and block-based systems +- Classic cart uses different hooks than Store API (blocks) +- Always check filter/action documentation for parameter types +- Don't assume cart item arrays everywhere - sometimes it's product objects! + +### When Adding Features +- Follow the existing pattern: add setting → add UI → add logic → add template +- Use Twig for all new templates (consistency) +- Add translations for all user-facing strings +- Test with both simple products and variable products (if applicable) +- Consider both classic and block-based cart/checkout + +### When Fixing Bugs +1. Check `CHANGELOG.md` for historical context +2. Look for similar issues in past versions +3. Always add detailed changelog entry explaining root cause +4. Consider edge cases (guest checkout, logged-in users, AJAX add-to-cart, etc.) + +## File Locations Quick Reference + +| Task | File(s) | +|------|---------| +| Change version | `wc-tier-and-package-prices.php` (2 places) | +| Add global setting | `includes/class-wc-tpp-settings.php` | +| Modify product meta box | `includes/class-wc-tpp-product-meta.php` + `templates/admin/*.twig` | +| Change product page display | `includes/class-wc-tpp-frontend.php` + `templates/frontend/*.twig` | +| Fix cart pricing | `includes/class-wc-tpp-cart.php` | +| Update styles | `assets/css/frontend.css` or `assets/css/admin.css` | +| Fix JavaScript bugs | `assets/js/frontend.js` or `assets/js/admin.js` | +| Add translations | `languages/*.po` then compile to `.mo` | +| Document changes | `CHANGELOG.md` | + +## Compatibility Notes + +### WordPress +- Minimum: 6.0 +- Tested up to: 6.9.x +- Uses standard plugin API, no deprecated functions + +### WooCommerce +- Minimum: 8.0 +- Tested up to: 10.x +- HPOS compatible (declared via `FeaturesUtil::declare_compatibility`) +- Blocks compatible (with proper filter handling) + +### PHP +- Minimum: 7.4 +- Uses modern PHP features (type hints acceptable in new code) +- Composer autoloader handles namespacing + +### Browsers +- Modern browsers (ES6+ JavaScript) +- Responsive CSS (mobile-friendly) +- jQuery dependency (WooCommerce provides) + +## Support & Resources + +- **Repository:** https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices +- **Documentation:** See `README.md`, `QUICKSTART.md`, `USAGE_EXAMPLES.md`, `INSTALLATION.md` +- **Changelog:** `CHANGELOG.md` (detailed version history) +- **Issue Tracking:** Check fatal-errors-*.log files for production errors + +## Final Notes + +This is a production-quality plugin with real-world usage. Any changes should: +1. Maintain backward compatibility with existing tier/package configurations +2. Not break WooCommerce core functionality +3. Work with both classic and block-based themes +4. Be thoroughly tested before release +5. Include proper error handling and validation +6. Update CHANGELOG.md with detailed explanations + +The plugin architecture is solid and well-tested. Most bugs arise from: +- WooCommerce API changes (especially blocks) +- Filter/action signature changes +- Edge cases in cart calculations +- Settings persistence issues + +Always refer to this document when starting work on this project. Good luck! diff --git a/includes/class-wc-tpp-cart.php b/includes/class-wc-tpp-cart.php index 0a98966..96487fc 100644 --- a/includes/class-wc-tpp-cart.php +++ b/includes/class-wc-tpp-cart.php @@ -226,11 +226,16 @@ if (!class_exists('WC_TPP_Cart')) { * Make quantity non-editable for restricted products in WooCommerce blocks * * @param bool $editable Whether the quantity is editable - * @param array $cart_item Cart item data + * @param WC_Product $product Product object * @return bool */ - public function block_quantity_editable($editable, $cart_item) { - $product_id = $cart_item['id'] ?? ($cart_item['product_id'] ?? 0); + public function block_quantity_editable($editable, $product) { + // Validate product object + if (!$product || !is_a($product, 'WC_Product')) { + return $editable; + } + + $product_id = $product->get_id(); if (!$product_id) { return $editable; diff --git a/releases/wc-tier-and-package-prices-1.1.20.zip b/releases/wc-tier-and-package-prices-1.1.20.zip new file mode 100644 index 0000000..29a64a0 Binary files /dev/null and b/releases/wc-tier-and-package-prices-1.1.20.zip differ diff --git a/releases/wc-tier-and-package-prices-1.1.20.zip.md5 b/releases/wc-tier-and-package-prices-1.1.20.zip.md5 new file mode 100644 index 0000000..caa4033 --- /dev/null +++ b/releases/wc-tier-and-package-prices-1.1.20.zip.md5 @@ -0,0 +1 @@ +bfdeee75bfe3795c9ab9abfe47f12a41 wc-tier-and-package-prices-1.1.20.zip diff --git a/releases/wc-tier-and-package-prices-1.1.20.zip.sha256 b/releases/wc-tier-and-package-prices-1.1.20.zip.sha256 new file mode 100644 index 0000000..152b6be --- /dev/null +++ b/releases/wc-tier-and-package-prices-1.1.20.zip.sha256 @@ -0,0 +1 @@ +953859241d15d76ec4783c72bac851ddd69e5a1f7b119ee4f9ebd30c7fabed17 wc-tier-and-package-prices-1.1.20.zip diff --git a/wc-tier-and-package-prices.php b/wc-tier-and-package-prices.php index 69ea88b..7efe470 100644 --- a/wc-tier-and-package-prices.php +++ b/wc-tier-and-package-prices.php @@ -4,7 +4,7 @@ * Plugin Name: WooCommerce Tier and Package Prices * Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices * Description: Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices - * Version: 1.1.19 + * Version: 1.1.20 * Author: Marco Graetsch * Author URI: https://src.bundespruefstelle.ch/magdev * Text Domain: wc-tier-package-prices @@ -23,7 +23,7 @@ if (!defined('ABSPATH')) { // Define plugin constants if (!defined('WC_TPP_VERSION')) { - define('WC_TPP_VERSION', '1.1.19'); + define('WC_TPP_VERSION', '1.1.20'); } if (!defined('WC_TPP_PLUGIN_DIR')) { define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));