14 Commits

Author SHA1 Message Date
a73ff4926f Merge branch 'dev' 2025-12-30 00:38:33 +01:00
78101baf88 Release version 1.2.6 - Critical bugfixes for v1.2.5 features
Fixed two critical issues that prevented v1.2.5 features from working:

1. Parent Product Pricing Forms Not Visible (Critical)
   - Variable products were missing admin UI for parent pricing configuration
   - v1.2.5 implemented backend logic but forgot the admin forms
   - Added add_variable_parent_pricing_fields() method
   - Modified existing methods to skip variable products (simple only)
   - Parent pricing now fully functional with matching UI

2. Table Headers Not Hiding When Empty
   - CSS sibling selector ~ doesn't work when thead comes before tbody
   - Removed incorrect selector, kept only :has() pseudo-class
   - Added !important flag for proper specificity
   - Modern browser support (Chrome 105+, Firefox 121+, Safari 15.4+)

Changed files:
- includes/class-wc-tpp-product-meta.php - Added parent pricing forms
- assets/css/admin.css - Fixed header hiding CSS
- wc-tier-and-package-prices.php - Version 1.2.6
- composer.json - Version 1.2.6
- CHANGELOG.md - Detailed release notes

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:38:29 +01:00
d99ece71e4 Update CLAUDE.md with v1.2.5 implementation learnings
- Marked both v1.2.5 enhancements as completed
- Added implementation details for CSS :has() pseudo-class for hiding empty table headers
- Documented parent product default pricing fallback pattern
- Added helper method documentation (get_packages_with_fallback, is_restriction_enabled)
- Reorganized roadmap section (completed v1.2.5 enhancements moved from planned)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:28:00 +01:00
e4747130e4 Add release package for version 1.2.5
Release package includes:
- Parent product default pricing for variable products
- Hide empty table headers in admin area
- Package size: 433KB (383 files)
- Checksums: MD5 and SHA256 generated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:25:14 +01:00
e4ecc2c0be Release version 1.2.5 - Parent product default pricing and UI enhancements
Added:
- Parent product default pricing for variable products - set tier/package pricing once on parent, variations inherit unless overridden
- Hide empty table headers in admin until pricing rules are defined

Technical:
- Added parent fallback logic to get_tier_price() and get_package_price() methods
- Created helper methods get_packages_with_fallback() and is_restriction_enabled() in cart class
- Updated all cart methods to support parent product defaults
- Added CSS :has() selectors to hide table headers when tbody is empty
- Fixed cart pricing calls to pass correct product ID for fallback resolution

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:23:56 +01:00
e2e40538df removed /finish-session in favor of the global /end-session 2025-12-30 00:16:09 +01:00
67e11d3030 permitted more commands 2025-12-30 00:15:42 +01:00
2de6a92784 Update CLAUDE.md with v1.2.4 learnings and CSS troubleshooting guide
Added comprehensive CSS specificity documentation from v1.2.4 bugfixes:
- CSS Specificity Issues section documenting table border and help-tip positioning problems
- Detailed troubleshooting guide for WooCommerce CSS overrides
- Solution patterns for table styling and float-based layouts
- General rules and diagnostic steps for CSS issues

Updated:
- Last Updated date to 2025-12-30
- Roadmap section to mark v1.2.4 bugs as completed
- Moved future enhancements to v1.2.5+ section

Key learnings documented:
- WooCommerce core CSS requires !important flags for overrides
- Float-based layouts should be replaced with flexbox for precise control
- Table borders require comprehensive targeting of all structural elements
- border-collapse: collapse is essential for borderless tables

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:14:16 +01:00
47f2ed771b Add release package for version 1.2.4
Release package includes:
- Fixed admin table borders with !important flags
- Fixed checkbox and help icon layout with flexbox
- Package size: 432KB (383 files)
- Checksums: MD5 and SHA256 generated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:04:19 +01:00
880983a879 Merge branch 'dev' 2025-12-30 00:02:38 +01:00
937e80fce3 Release version 1.2.4 - Fix admin table borders and checkbox layout
Fixed:
- Admin table borders still visible despite v1.2.3 fix - added !important flags
- Help icon positioning at right edge instead of next to label - changed to flexbox layout
- Increased checkbox margin from 8px to 12px for better spacing

Technical:
- Added border-collapse: collapse !important to force borderless tables
- Changed label layout from float to inline-flex for proper help-tip positioning
- Added comprehensive border removal with !important on all table elements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:02:34 +01:00
9c5e3c85ae Add release packages for v1.2.1, v1.2.2, and v1.2.3
Release packages with checksums:
- v1.2.1: Admin UI fixes and frontend display improvements
- v1.2.2: Variation UI styling, translations, checkbox rendering
- v1.2.3: Borderless tables and checkbox tooltip improvements

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 21:09:45 +01:00
3d47ee63d8 Update CLAUDE.md with v1.2.2 and v1.2.3 release learnings
Added detailed documentation for release package creation process:
- Critical exclusion patterns (especially wordpress symlink)
- Expected package size and verification steps
- Complete zip command with all necessary exclusions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 21:09:31 +01:00
04eba21521 Release version 1.2.3 - Admin UI styling improvements
Fixed two admin UI bugs:
- Applied borderless table styling to all tier/package tables for consistency
- Fixed checkbox tooltip display and improved checkbox-label spacing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-29 21:06:25 +01:00
29 changed files with 600 additions and 75 deletions

0
.claude/commands/.gitignore vendored Normal file
View File

View File

@@ -1,5 +0,0 @@
# Finish the current session
Update the CLAUDE.md according to your needs and what you've learned in this session.
Create a commit and push this file to branch `dev`

View File

@@ -21,7 +21,25 @@
"Bash(php -l:*)", "Bash(php -l:*)",
"Bash(git push:*)", "Bash(git push:*)",
"Bash(git checkout:*)", "Bash(git checkout:*)",
"Bash(git rebase:*)" "Bash(git rebase:*)",
"Bash(git merge:*)",
"Bash(git stash:*)",
"Bash(for po in languages/*.po)",
"Bash('wc-tier-and-package-prices/*.log' )",
"Bash('wc-tier-and-package-prices/.claude/*' )",
"Bash('wc-tier-and-package-prices/CLAUDE.md' )",
"Bash('wc-tier-and-package-prices/releases/*' )",
"Bash('wc-tier-and-package-prices/node_modules/*' )",
"Bash('wc-tier-and-package-prices/.DS_Store' )",
"Bash('wc-tier-and-package-prices/Thumbs.db' )",
"Bash('wc-tier-and-package-prices/.vscode/*' )",
"Bash('wc-tier-and-package-prices/.idea/*' )",
"Bash('wc-tier-and-package-prices/*.sublime-*' )",
"Bash('wc-tier-and-package-prices/notes.*' )",
"Bash('wc-tier-and-package-prices/logs/*' )",
"Bash('wc-tier-and-package-prices/templates/cache/*' )",
"Bash('wc-tier-and-package-prices/composer.lock' )",
"Bash('*/wordpress/*')"
] ]
} }
} }

View File

@@ -5,6 +5,108 @@ All notable changes to WooCommerce Tier and Package Prices will be documented in
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), 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). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.2.6] - 2025-12-30
### Fixed
- **Parent Product Pricing Forms Not Visible (Critical)**: Variable products were missing the pricing configuration forms on the parent product edit page. The v1.2.5 feature for parent product default pricing was implemented in the backend logic (cart calculations and frontend display) but the admin UI to configure these defaults was not added. Now variable product parents have a dedicated "Default Tier & Package Pricing for All Variations" section in the product edit page where administrators can configure default pricing that applies to all variations unless a specific variation overrides it.
- **Table Headers Not Hiding When Empty**: The CSS selector for hiding table headers when no pricing rules exist was using an incorrect approach. The sibling selector `~` doesn't work when `<thead>` comes before `<tbody>` in the HTML structure. Removed the sibling selector and kept only the `:has()` pseudo-class approach with `!important` flag for proper specificity.
### Technical Details
**Parent Product Forms Fix**:
- Added new method `add_variable_parent_pricing_fields()` in `WC_TPP_Product_Meta` class
- Hooked to `woocommerce_product_options_pricing` action but only displays for variable products
- Modified existing `add_tier_pricing_fields()` and `add_package_pricing_fields()` to skip variable products (they now only show for simple products)
- Parent product forms use same field names as simple products (`_wc_tpp_tiers`, `_wc_tpp_packages`, `_wc_tpp_restrict_to_packages`)
- Data is saved to parent product post meta using existing `save_tier_package_fields()` method
- Backend fallback logic from v1.2.5 now has matching admin UI for configuration
**CSS Selector Fix**:
- Removed incorrect `.wc-tpp-tiers-container:empty ~ thead` selector (sibling selector can't target previous elements)
- Kept only `.wc-tpp-tiers-table:has(tbody.wc-tpp-tiers-container:empty) thead` with `!important` flag
- `:has()` pseudo-class is supported in modern browsers (Chrome 105+, Firefox 121+, Safari 15.4+)
**User Impact**:
- Administrators can now configure default tier/package pricing on variable product parents (feature was non-functional in v1.2.5)
- Table headers properly hide when no pricing rules exist, creating cleaner admin interface
- No data migration needed - existing configurations remain intact
### Changed Files
- `includes/class-wc-tpp-product-meta.php` - Added `add_variable_parent_pricing_fields()` method; modified `add_tier_pricing_fields()` and `add_package_pricing_fields()` to skip variable products
- `assets/css/admin.css` - Fixed table header hiding CSS selector; removed incorrect sibling selector; added `!important` flag
## [1.2.5] - 2025-12-30
### Added
- **Parent Product Default Pricing**: Variable products can now define tier and package pricing at the parent product level that applies as defaults to all variations. Individual variations can override these defaults with their own specific pricing. This makes it much easier to set up pricing for products with many variations - set defaults once on the parent, then only customize the variations that need different pricing.
- **Hide Empty Table Headers**: Table headers for tier and package pricing in the admin area now automatically hide when no pricing rules are defined. This creates a cleaner interface when starting to configure a product, showing only the helpful empty state message and "Add" button.
### Technical Details
**Parent Fallback Implementation**:
- Modified `WC_TPP_Frontend::get_tier_price()` and `WC_TPP_Frontend::get_package_price()` to fall back to parent product pricing when variation doesn't have its own pricing
- Updated `WC_TPP_Cart` to use helper methods `get_packages_with_fallback()` and `is_restriction_enabled()` for consistent parent fallback behavior
- All cart validation, quantity restriction, and display methods now support parent product defaults
- Fixed cart pricing calls to pass parent `$product_id` instead of `$effective_id` for proper fallback resolution
**CSS Enhancement**:
- Added `:has()` pseudo-class selectors to hide table headers when tbody is empty
- Leverages existing empty state message styling for consistent UX
**Backward Compatibility**:
- 100% backward compatible - existing products continue working as before
- No database migrations required
- Variations with specific pricing take precedence over parent defaults
### Changed Files
- `includes/class-wc-tpp-frontend.php` - Added parent fallback logic to `get_tier_price()` and `get_package_price()` methods
- `includes/class-wc-tpp-cart.php` - Added helper methods `get_packages_with_fallback()` and `is_restriction_enabled()`; updated all cart methods to support parent fallback; fixed pricing calls to use correct product ID
- `assets/css/admin.css` - Added CSS rules to hide table headers when no pricing rules exist
## [1.2.4] - 2025-12-30
### Fixed
- **Admin Table Borders (Critical)**: Fixed table borders still appearing in v1.2.3 despite borderless styling attempt. WooCommerce's default CSS was overriding `border: none` declarations. Added `!important` flags to all border removal rules and `border-collapse: collapse !important` to force borderless styling. Now all tier/package pricing tables (simple and variable products) display correctly without borders, matching WooCommerce's clean admin UI.
- **Checkbox and Help Icon Layout**: Fixed help icon positioning and checkbox spacing issues from v1.2.3. The help icon was appearing at the right edge of the container instead of next to the label text. Increased checkbox-label margin from 8px to 12px for better spacing. Changed label layout from float positioning to flexbox (`inline-flex`) to keep help icon directly adjacent to label text. Added inline description hiding when tooltip is shown.
### Technical Details
**Root Cause - Table Borders**: WooCommerce's core admin CSS has higher specificity border rules that override simple `border: none` declarations. The solution required:
- Adding `!important` to all `border: none` rules targeting tables, th, td, thead, tbody, and tr elements
- Adding `border-collapse: collapse !important` to prevent cell borders from being visible
- Comprehensive targeting of all table structural elements for complete border removal
**Root Cause - Help Icon Position**: WooCommerce's default `.woocommerce-help-tip` styling uses `float: right` which positions the icon at the container's right edge. The fix:
- Removed float positioning with `float: none`
- Changed to `display: inline-block` with `vertical-align: middle`
- Wrapped label and help-tip in flexbox container (`display: inline-flex; align-items: center`)
- Controlled spacing with precise margins (checkbox: 12px right, help-tip: 6px left)
### Changed Files
- `assets/css/admin.css` - Added `!important` flags to all border removal rules; added `border-collapse: collapse`; increased checkbox margin to 12px; converted label layout to flexbox; positioned help-tip with inline-block; added inline description hiding
## [1.2.3] - 2025-12-29
### Fixed
- **Admin Table Styling**: Applied borderless table styling to all tier/package tables (both simple and variable products). Previously only variation tables had borders removed in v1.2.2. Now all pricing tables in the admin have a consistent borderless appearance matching WooCommerce's clean admin UI style.
- **Checkbox Styling and Tooltip**: Fixed checkbox styling issues where the help text was displayed inline instead of as a tooltip, and the margin between checkbox and label was too small. Added `desc_tip => true` to the variation restriction checkbox to enable tooltip display. Added CSS rules to increase checkbox-label margin to 8px and hide inline description text when tooltips are used.
### Changed Files
- `assets/css/admin.css` - Applied `border: none` to all tier/package table elements; added checkbox margin and description hiding rules
- `includes/class-wc-tpp-product-meta.php` - Added `desc_tip => true` parameter to variation checkbox (line 213)
## [1.2.2] - 2025-12-29 ## [1.2.2] - 2025-12-29
### Fixed ### Fixed

221
CLAUDE.md
View File

@@ -1,7 +1,7 @@
# WooCommerce Tier and Package Prices - AI Context Document # WooCommerce Tier and Package Prices - AI Context Document
**Last Updated:** 2025-12-29 **Last Updated:** 2025-12-30
**Current Version:** 1.2.2 **Current Version:** 1.2.5
**Author:** Marco Graetsch **Author:** Marco Graetsch
**Project Status:** Production-ready WordPress plugin **Project Status:** Production-ready WordPress plugin
@@ -13,6 +13,7 @@ This is a WooCommerce plugin that adds flexible pricing capabilities to products
2. **Package Pricing (Fixed Bundles)**: Exact quantity packages at fixed prices (e.g., exactly 10 items for $95, exactly 25 for $200) 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 ### Key Fact: 100% AI-Generated
This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase was created through AI assistance. This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase was created through AI assistance.
## Technical Stack ## Technical Stack
@@ -27,6 +28,7 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
- **Internationalization:** WordPress i18n (.pot/.po/.mo files) - **Internationalization:** WordPress i18n (.pot/.po/.mo files)
### Dependencies ### Dependencies
```json ```json
{ {
"twig/twig": "^3.0", "twig/twig": "^3.0",
@@ -38,7 +40,8 @@ This project is proudly **"vibe-coded"** using Claude.AI - the entire codebase w
## Architecture ## Architecture
### Directory Structure ### Directory Structure
```
```txt
wc-tier-and-package-prices/ wc-tier-and-package-prices/
├── wc-tier-and-package-prices.php # Main plugin file (entry point) ├── wc-tier-and-package-prices.php # Main plugin file (entry point)
├── includes/ # PHP classes ├── includes/ # PHP classes
@@ -172,6 +175,7 @@ wc-tier-and-package-prices/
### Price Calculation Logic ### Price Calculation Logic
**Package Pricing** (exact match): **Package Pricing** (exact match):
```php ```php
// In cart: if quantity == 10 and package exists for 10, use package price // In cart: if quantity == 10 and package exists for 10, use package price
if ($quantity == $package['qty']) { if ($quantity == $package['qty']) {
@@ -181,6 +185,7 @@ if ($quantity == $package['qty']) {
``` ```
**Tier Pricing** (range-based): **Tier Pricing** (range-based):
```php ```php
// In cart: if quantity >= 10, use tier price for quantities 10+ // In cart: if quantity >= 10, use tier price for quantities 10+
foreach ($tiers as $tier) { foreach ($tiers as $tier) {
@@ -226,7 +231,9 @@ This metadata is used by display filters to show "(Package price)" or "(Volume d
## Common Patterns & Conventions ## Common Patterns & Conventions
### Class Instantiation Pattern ### Class Instantiation Pattern
All classes auto-instantiate at the end of their file: All classes auto-instantiate at the end of their file:
```php ```php
if (!class_exists('WC_TPP_Frontend')) { if (!class_exists('WC_TPP_Frontend')) {
class WC_TPP_Frontend { class WC_TPP_Frontend {
@@ -247,7 +254,9 @@ new WC_TPP_Frontend(); // Auto-instantiate
- Direct file access prevention via `ABSPATH` check - Direct file access prevention via `ABSPATH` check
### Translation Ready ### Translation Ready
All user-facing strings use: All user-facing strings use:
```php ```php
__('Text to translate', 'wc-tier-package-prices') __('Text to translate', 'wc-tier-package-prices')
_e('Text to translate', 'wc-tier-package-prices') _e('Text to translate', 'wc-tier-package-prices')
@@ -270,6 +279,7 @@ Note: Swiss locales use CHF currency formatting in examples (e.g., "CHF 50.-")
## Known Issues & Historical Context ## Known Issues & Historical Context
### Settings Page Duplication Saga (v1.1.15-1.1.19) ### Settings Page Duplication Saga (v1.1.15-1.1.19)
Multiple versions attempted to fix settings page appearing twice: Multiple versions attempted to fix settings page appearing twice:
- **Root cause:** Settings file auto-instantiation + Composer autoloader - **Root cause:** Settings file auto-instantiation + Composer autoloader
@@ -277,6 +287,7 @@ Multiple versions attempted to fix settings page appearing twice:
- **Prevention:** Singleton pattern + duplicate detection in array - **Prevention:** Singleton pattern + duplicate detection in array
### Class Redeclaration Issues (v1.1.8-1.1.14) ### Class Redeclaration Issues (v1.1.8-1.1.14)
Plugin was completely non-functional: Plugin was completely non-functional:
- **Cause:** Incorrect initialization pattern without `class_exists()` guards - **Cause:** Incorrect initialization pattern without `class_exists()` guards
@@ -284,7 +295,8 @@ Plugin was completely non-functional:
- **Lesson:** Always wrap class declarations in `class_exists()` checks - **Lesson:** Always wrap class declarations in `class_exists()` checks
### WooCommerce Blocks Fatal Error (v1.1.19 → v1.1.20) ### WooCommerce Blocks Fatal Error (v1.1.19 → v1.1.20)
```
```txt
Fatal error: Cannot use object of type WC_Product_Simple as array Fatal error: Cannot use object of type WC_Product_Simple as array
Location: includes/class-wc-tpp-cart.php:233 Location: includes/class-wc-tpp-cart.php:233
``` ```
@@ -293,9 +305,45 @@ Location: includes/class-wc-tpp-cart.php:233
- **Fix:** Changed method signature to accept `WC_Product $product` instead of `$cart_item` array - **Fix:** Changed method signature to accept `WC_Product $product` instead of `$cart_item` array
- **Status:** FIXED in v1.1.20 - **Status:** FIXED in v1.1.20
### CSS Specificity Issues (v1.2.3 → v1.2.4)
**Problem:** Admin table borders still visible despite `border: none` declarations in v1.2.3
```txt
Issue: WooCommerce's core admin CSS has higher specificity border rules
Location: assets/css/admin.css
Symptom: Tables still showing borders in product edit screens
```
- **Root Cause:** WooCommerce's default admin CSS uses highly specific selectors that override simple `border: none` declarations
- **Failed Approach (v1.2.3):** Adding `border: none` to table elements without `!important`
- **Successful Fix (v1.2.4):**
- Added `!important` flags to ALL border removal rules
- Added `border-collapse: collapse !important` to force borderless styling
- Targeted all table structural elements: `table`, `th`, `td`, `thead`, `tbody`, `tr`
- **Lesson:** When overriding WooCommerce core CSS, `!important` is often necessary due to high specificity in core styles
**Problem:** Help icon positioned at right edge instead of next to label text
```txt
Issue: WooCommerce help-tip uses float: right positioning
Location: assets/css/admin.css (checkbox/label layout)
Symptom: Help icon appearing far from label text at container edge
```
- **Root Cause:** WooCommerce's default `.woocommerce-help-tip` styling uses `float: right`
- **Failed Approach (v1.2.3):** Simple margin adjustments without changing positioning model
- **Successful Fix (v1.2.4):**
- Removed float positioning: `float: none`
- Changed to `display: inline-block` with `vertical-align: middle`
- Wrapped label and help-tip in flexbox container: `display: inline-flex; align-items: center`
- Controlled precise spacing with margins (checkbox: 12px, help-tip: 6px)
- **Lesson:** Overriding float-based layouts often requires changing to flexbox for proper control
## Release Process ## Release Process
### Version Bumping ### Version Bumping
Update version in 3 places: Update version in 3 places:
1. `wc-tier-and-package-prices.php` - Plugin header comment (line 7) 1. `wc-tier-and-package-prices.php` - Plugin header comment (line 7)
@@ -468,6 +516,7 @@ When making changes, test these critical paths:
### Common Pitfalls and Solutions ### Common Pitfalls and Solutions
#### Release Package Creation #### Release Package Creation
**Problem:** Empty or corrupted zip files (0 bytes or wrong structure) **Problem:** Empty or corrupted zip files (0 bytes or wrong structure)
**Cause:** Running zip command from wrong directory or incorrect path patterns **Cause:** Running zip command from wrong directory or incorrect path patterns
**Solution:** Always run from parent directory (`/home/magdev/workspaces/node`) and use full relative paths in exclusions **Solution:** Always run from parent directory (`/home/magdev/workspaces/node`) and use full relative paths in exclusions
@@ -481,7 +530,9 @@ When making changes, test these critical paths:
**Solution:** Follow verification steps and check package size immediately after creation **Solution:** Follow verification steps and check package size immediately after creation
#### UI Changes in Admin #### UI Changes in Admin
**WooCommerce CSS Classes:** **WooCommerce CSS Classes:**
- `short` - Small input fields (~60px width) - `short` - Small input fields (~60px width)
- `regular` - Medium input fields (~120px width) - `regular` - Medium input fields (~120px width)
- `long` - Large input fields (~200px+ width) - `long` - Large input fields (~200px+ width)
@@ -490,7 +541,85 @@ When modifying admin input fields in Twig templates, use WooCommerce's standard
**Location:** `templates/admin/*.twig` for admin UI changes **Location:** `templates/admin/*.twig` for admin UI changes
#### CSS Specificity and WooCommerce Overrides
**CRITICAL LESSON from v1.2.4:** WooCommerce's core admin CSS uses high-specificity selectors that require `!important` to override.
**Problem Symptoms:**
- CSS rules not applying despite correct selectors
- Styles work in simple cases but fail with WooCommerce elements
- Browser DevTools shows rule crossed out or overridden
**Diagnostic Steps:**
1. Inspect element in browser DevTools
2. Check "Computed" tab to see which styles are actually applied
3. Look for crossed-out rules in "Styles" tab (indicates override)
4. Check selector specificity - WooCommerce often uses complex selectors
**Solution Patterns:**
**For Table Styling:**
```css
/* ❌ This will likely be overridden */
.wc-tpp-tiers-table {
border: none;
}
/* ✅ Use !important for core WooCommerce overrides */
.wc-tpp-tiers-table,
.wc-tpp-tiers-table th,
.wc-tpp-tiers-table td,
.wc-tpp-tiers-table thead,
.wc-tpp-tiers-table tbody,
.wc-tpp-tiers-table tr {
border: none !important;
}
/* ✅ Also use border-collapse to prevent cell borders */
.wc-tpp-tiers-table {
border-collapse: collapse !important;
}
```
**For Float-Based Layouts:**
```css
/* ❌ Float positioning is hard to control precisely */
.woocommerce-help-tip {
float: right;
margin-left: 10px;
}
/* ✅ Use flexbox for precise control */
label[for="_wc_tpp_restrict_to_packages"] {
display: inline-flex;
align-items: center;
gap: 0;
}
.woocommerce-help-tip {
float: none;
display: inline-block;
vertical-align: middle;
margin-left: 6px;
margin-right: 0;
}
```
**General Rules:**
1. **Always test in actual WordPress admin** - browser preview may not show WooCommerce's CSS
2. **Target all related elements** - tables require styling on `table`, `thead`, `tbody`, `tr`, `th`, `td`
3. **Use `!important` sparingly but don't fear it** - sometimes it's the only way to override WooCommerce core
4. **Prefer flexbox over floats** - gives better control over alignment and spacing
5. **Check across browsers** - table rendering can vary between Chrome/Firefox/Safari
**When Styles Don't Apply:**
- First verify selector is correct (DevTools should show rule, even if crossed out)
- If selector is correct but crossed out, increase specificity or add `!important`
- If selector doesn't appear at all, check file is enqueued and cache is cleared
- Use browser's "Inspect" right-click to see exact element structure
#### Git Workflow Issues #### Git Workflow Issues
**Problem:** Cannot rebase due to uncommitted changes **Problem:** Cannot rebase due to uncommitted changes
**Solution:** Stash local config files (`.claude/settings.local.json`) before git operations **Solution:** Stash local config files (`.claude/settings.local.json`) before git operations
@@ -519,7 +648,7 @@ The plugin uses Twig 3.0 for templating. Key files:
### Complete Release Workflow Summary ### Complete Release Workflow Summary
Based on v1.1.22 release experience, here's the complete workflow: Based on v1.1.22, v1.2.2, and v1.2.3 release experience, here's the complete workflow:
1. **Fix bugs/add features** on `dev` branch 1. **Fix bugs/add features** on `dev` branch
2. **Update version numbers** (3 files: main plugin file header, constant, composer.json) 2. **Update version numbers** (3 files: main plugin file header, constant, composer.json)
@@ -544,6 +673,52 @@ Based on v1.1.22 release experience, here's the complete workflow:
- `releases/wc-tier-and-package-prices-X.X.X.zip*` - Package and checksums - `releases/wc-tier-and-package-prices-X.X.X.zip*` - Package and checksums
- Feature-specific files (templates, PHP classes, etc.) - Feature-specific files (templates, PHP classes, etc.)
### Release Package Creation - Critical Notes
**IMPORTANT:** The zip command must be run from the **parent directory** to create proper archive structure.
**Correct command (from `/home/magdev/workspaces/php`):**
```bash
cd /home/magdev/workspaces/php
zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/ \
-x '*/\.git/*' '*/.git/*' 'wc-tier-and-package-prices/.git/*' \
'*.gitignore' '*.log' '*/.claude/*' '*/CLAUDE.md' \
'*/releases/*' '*/wordpress/*' '*/node_modules/*' \
'*/.DS_Store' '*/Thumbs.db' '*/.vscode/*' '*/.idea/*' \
'*.sublime-*' '*/notes.*' '*/logs/*' '*/templates/cache/*' \
'*/composer.lock'
```
**Critical Exclusions:**
- `*/wordpress/*` - MUST be excluded! The project has a symlink to WordPress installation that zip will follow, creating 129MB+ packages instead of ~430KB
- `.git/*` - All git metadata (multiple patterns needed for reliability)
- `.claude/*` and `CLAUDE.md` - Development documentation
- `releases/*` - Prevents including previous releases in new ones
- `composer.lock` - Not needed in production (vendor/ is included)
**Expected Package Size:** ~430-431KB (383 files)
**Package Size Alert:** If >1MB, exclusions failed (likely wordpress symlink included)
**Verification Steps:**
```bash
# 1. Check size (should be ~430KB)
ls -lh releases/wc-tier-and-package-prices-X.X.X.zip
# 2. Verify file count (should be 383 files)
unzip -l releases/wc-tier-and-package-prices-X.X.X.zip | tail -1
# 3. Check for excluded files
unzip -l releases/wc-tier-and-package-prices-X.X.X.zip | grep -E "CLAUDE\.md|\.claude/|\.git/|wordpress/"
# Should return nothing (exit code 1)
# 4. Verify version in package
unzip -p releases/wc-tier-and-package-prices-X.X.X.zip wc-tier-and-package-prices/wc-tier-and-package-prices.php | head -30 | grep -E "Version:|WC_TPP_VERSION"
```
### Future Features and Roadmap ### Future Features and Roadmap
The is a hierarchical list for upcoming features and can be considered as a The is a hierarchical list for upcoming features and can be considered as a
@@ -566,13 +741,37 @@ Roadmap for the upcoming development.
##### Bugfixes (Completed in v1.2.2) ##### Bugfixes (Completed in v1.2.2)
3. ~~Remove the table borders in admin on variable product to better fit the surrounding element styles.~~**FIXED in v1.2.2** - Added CSS rules to remove table borders specifically for variation pricing tables (`.wc-tpp-variation-pricing`), matching WooCommerce's borderless variation UI style. 1. ~~Remove the table borders in admin on variable product to better fit the surrounding element styles.~~**FIXED in v1.2.2** - Added CSS rules to remove table borders specifically for variation pricing tables (`.wc-tpp-variation-pricing`), matching WooCommerce's borderless variation UI style.
4. ~~Add missing translations in admin templates ("Price", "Tier & Package Pricing", "Min Quantity") for all languages used in this project.~~**FIXED in v1.2.2** - Added missing translation entries for "Min Quantity", "Price", and "Label (optional)" to all .po files (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH, en_US) and recompiled .mo files. 2. ~~Add missing translations in admin templates ("Price", "Tier & Package Pricing", "Min Quantity") for all languages used in this project.~~**FIXED in v1.2.2** - Added missing translation entries for "Min Quantity", "Price", and "Label (optional)" to all .po files (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH, en_US) and recompiled .mo files.
5. ~~Check the template for wc_tpp_restrict_to_packages[] checkbox elements in admin on variable products and fix the rendering.~~**FIXED in v1.2.2** - Fixed checkbox value parameter in variation pricing fields. Changed from ternary expression to direct value assignment, allowing WooCommerce's `woocommerce_wp_checkbox()` to properly handle the checked state. 3. ~~Check the template for wc_tpp_restrict_to_packages[] checkbox elements in admin on variable products and fix the rendering.~~**FIXED in v1.2.2** - Fixed checkbox value parameter in variation pricing fields. Changed from ternary expression to direct value assignment, allowing WooCommerce's `woocommerce_wp_checkbox()` to properly handle the checked state.
##### New Features ##### Bugfixes (Completed in v1.2.3)
1. ~~Style the tier and packages tables in admin on simple products according to the styles on variable products.~~**FIXED in v1.2.3** - Applied borderless table styling to all tier/package tables (both simple and variable products). Removed borders from table, th, and td elements to match WooCommerce's clean admin UI style.
2. ~~The checkbox styles from 1.2.2 bug 3 are still not looking correct. The helptext is written instead of hidden after the help icon and the margin between checkbox and label are to small.~~**FIXED in v1.2.3** - Added `desc_tip => true` to variation checkbox to show tooltip instead of inline text. Added CSS rules to increase checkbox-label margin (8px) and hide inline description text when tooltip is used.
##### Bugfixes (Completed in v1.2.4)
1. ~~Bug 1 in v1.2.3 is not fixed. Now both table display have border again. they shouldn't have border.~~**FIXED in v1.2.4** - Added `!important` flags and `border-collapse: collapse` to table CSS to override WooCommerce's default table styling. Added comprehensive border removal for all table elements (table, thead, tbody, tr, th, td) to ensure truly borderless tables across all browsers.
2. ~~Bug 2 in v1.2.3: Increase the margin between checkbox and label and put the help icon right next to the label, not at the right border~~**FIXED in v1.2.4** - Increased checkbox right margin from 8px to 12px. Repositioned help tip icon to display inline right next to the label text using flexbox layout with `display: inline-flex`, removing float positioning that caused it to appear at the right edge.
##### Enhancements (Completed in v1.2.5)
1. ~~Hide the table-headers in admin area until a tier or respectivly a package price is defined.~~**COMPLETED in v1.2.5** - Added CSS `:has()` pseudo-class selectors to automatically hide table headers when tbody is empty. Creates a cleaner interface showing only the helpful empty state message and "Add" button when no pricing rules are configured.
2. ~~Make it possible to define tier or package prices on variable products in the parent product as a default for that product and all variants of it unless a variant has its own tier or package prices.~~**COMPLETED in v1.2.5** - Implemented parent product default pricing with automatic fallback. Variable products can define tier/package pricing once at parent level; variations inherit these defaults unless they have their own specific pricing. Added helper methods in cart class and updated all pricing/restriction checks to support parent fallback.
##### Fixes for v1.2.5
1. The table headers in admin are still visible.
2. The parent product fallback on variable product is also not visible.
##### Planned Enhancements for v1.2.6+
1. Create different, selectable templates for tierprices and packages to use in the frontend. Make the new templates selectable globally on the settings-page, not per product. 1. Create different, selectable templates for tierprices and packages to use in the frontend. Make the new templates selectable globally on the settings-page, not per product.
@@ -608,7 +807,7 @@ Roadmap for the upcoming development.
## File Locations Quick Reference ## File Locations Quick Reference
| Task | File(s) | | Task | File(s) |
|------|---------| | ------ | --------- |
| Change version | `wc-tier-and-package-prices.php` (2 places) | | Change version | `wc-tier-and-package-prices.php` (2 places) |
| Add global setting | `includes/class-wc-tpp-settings.php` | | Add global setting | `includes/class-wc-tpp-settings.php` |
| Modify product meta box | `includes/class-wc-tpp-product-meta.php` + `templates/admin/*.twig` | | Modify product meta box | `includes/class-wc-tpp-product-meta.php` + `templates/admin/*.twig` |
@@ -648,7 +847,7 @@ Roadmap for the upcoming development.
## Support & Resources ## Support & Resources
- **Repository:** https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices - **Repository:** <https://src.bundespruefstelle.ch/magdev/wc-tier-package-prices>
- **Documentation:** See `README.md`, `QUICKSTART.md`, `USAGE_EXAMPLES.md`, `INSTALLATION.md` - **Documentation:** See `README.md`, `QUICKSTART.md`, `USAGE_EXAMPLES.md`, `INSTALLATION.md`
- **Changelog:** `CHANGELOG.md` (detailed version history) - **Changelog:** `CHANGELOG.md` (detailed version history)
- **Issue Tracking:** Check fatal-errors-*.log files for production errors - **Issue Tracking:** Check fatal-errors-*.log files for production errors

View File

@@ -23,30 +23,39 @@
color: #666; color: #666;
} }
/* Table styling */ /* Table styling - borderless design for all tier/package tables */
.wc-tpp-tiers-table, .wc-tpp-tiers-table,
.wc-tpp-packages-table { .wc-tpp-packages-table {
margin-top: 15px; margin-top: 15px;
margin-bottom: 15px; margin-bottom: 15px;
border: none !important;
border-collapse: collapse !important;
} }
.wc-tpp-tiers-table th, .wc-tpp-tiers-table th,
.wc-tpp-packages-table th,
.wc-tpp-tiers-table td,
.wc-tpp-packages-table td {
border: none !important;
}
.wc-tpp-tiers-table th {
font-weight: 600;
text-align: left;
}
.wc-tpp-packages-table th { .wc-tpp-packages-table th {
font-weight: 600; font-weight: 600;
text-align: left; text-align: left;
} }
/* Remove borders for variation pricing tables to match WooCommerce variation UI style */ .wc-tpp-tiers-table thead,
.wc-tpp-variation-pricing .wc-tpp-tiers-table, .wc-tpp-packages-table thead,
.wc-tpp-variation-pricing .wc-tpp-packages-table { .wc-tpp-tiers-table tbody,
border: none; .wc-tpp-packages-table tbody,
} .wc-tpp-tiers-table tr,
.wc-tpp-packages-table tr {
.wc-tpp-variation-pricing .wc-tpp-tiers-table th, border: none !important;
.wc-tpp-variation-pricing .wc-tpp-packages-table th,
.wc-tpp-variation-pricing .wc-tpp-tiers-table td,
.wc-tpp-variation-pricing .wc-tpp-packages-table td {
border: none;
} }
/* Table row styling - rows are now <tr> elements in a table */ /* Table row styling - rows are now <tr> elements in a table */
@@ -101,3 +110,41 @@
color: #666; color: #666;
font-style: italic; font-style: italic;
} }
/* Hide table headers when there are no pricing rules */
/* Use :has() pseudo-class to check if tbody is empty */
.wc-tpp-tiers-table:has(tbody.wc-tpp-tiers-container:empty) thead,
.wc-tpp-packages-table:has(tbody.wc-tpp-packages-container:empty) thead {
display: none !important;
}
/* Checkbox styling improvements */
#_wc_tpp_restrict_to_packages,
input[id^="wc_tpp_restrict_to_packages_"] {
margin-right: 12px !important;
}
/* Position help tip icon right next to the label text */
.wc-tpp-tier-pricing .woocommerce-help-tip,
.wc-tpp-package-pricing .woocommerce-help-tip,
.wc-tpp-variation-pricing .woocommerce-help-tip {
margin-left: 6px;
margin-right: 0;
float: none;
display: inline-block;
vertical-align: middle;
}
/* Fix WooCommerce checkbox label layout for our checkboxes */
label[for="_wc_tpp_restrict_to_packages"],
label[for^="wc_tpp_restrict_to_packages_"] {
display: inline-flex;
align-items: center;
gap: 0;
}
/* Hide inline description text when tooltip is shown */
#_wc_tpp_restrict_to_packages + .description,
input[id^="wc_tpp_restrict_to_packages_"] + .description {
display: none;
}

View File

@@ -1,7 +1,7 @@
{ {
"name": "magdev/wc-tier-package-prices", "name": "magdev/wc-tier-package-prices",
"description": "WooCommerce plugin for tier pricing and package prices with Twig templates", "description": "WooCommerce plugin for tier pricing and package prices with Twig templates",
"version": "1.2.2", "version": "1.2.6",
"type": "wordpress-plugin", "type": "wordpress-plugin",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"authors": [ "authors": [

View File

@@ -50,10 +50,10 @@ if (!class_exists('WC_TPP_Cart')) {
continue; continue;
} }
// Check for exact package match first (use effective ID for variations) // Check for exact package match first (pass product_id for parent fallback support)
$package_price = null; $package_price = null;
if (get_option('wc_tpp_enable_package_pricing') === 'yes') { if (get_option('wc_tpp_enable_package_pricing') === 'yes') {
$package_price = WC_TPP_Frontend::get_package_price($effective_id, $quantity, $variation_id); $package_price = WC_TPP_Frontend::get_package_price($product_id, $quantity, $variation_id);
} }
if ($package_price !== null) { if ($package_price !== null) {
@@ -64,9 +64,9 @@ if (!class_exists('WC_TPP_Cart')) {
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'package'; WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'package';
WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = $package_price; WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = $package_price;
} else { } else {
// Apply tier pricing if no package match (use effective ID for variations) // Apply tier pricing if no package match (pass product_id for parent fallback support)
if (get_option('wc_tpp_enable_tier_pricing') === 'yes') { if (get_option('wc_tpp_enable_tier_pricing') === 'yes') {
$tier_price = WC_TPP_Frontend::get_tier_price($effective_id, $quantity, $variation_id); $tier_price = WC_TPP_Frontend::get_tier_price($product_id, $quantity, $variation_id);
if ($tier_price !== null) { if ($tier_price !== null) {
$product->set_price($tier_price); $product->set_price($tier_price);
// Store pricing information in cart item for display // Store pricing information in cart item for display
@@ -103,20 +103,16 @@ if (!class_exists('WC_TPP_Cart')) {
public function validate_package_quantity($passed, $product_id, $quantity) { public function validate_package_quantity($passed, $product_id, $quantity) {
// Check for variation ID in request (for variable products) // Check for variation ID in request (for variable products)
$variation_id = isset($_REQUEST['variation_id']) ? absint($_REQUEST['variation_id']) : 0; $variation_id = isset($_REQUEST['variation_id']) ? absint($_REQUEST['variation_id']) : 0;
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
// Check if restriction is enabled globally or for this product/variation // Check if restriction is enabled (with parent fallback for variations)
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; if (!$this->is_restriction_enabled($product_id, $variation_id)) {
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
if (!$global_restrict && !$product_restrict) {
return $passed; return $passed;
} }
// Get packages for this product/variation // Get packages for this product/variation (with parent fallback)
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true); $packages = $this->get_packages_with_fallback($product_id, $variation_id);
if (empty($packages) || !is_array($packages)) { if (!$packages) {
return $passed; return $passed;
} }
@@ -156,15 +152,8 @@ if (!class_exists('WC_TPP_Cart')) {
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0; $variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
$effective_id = $variation_id > 0 ? $variation_id : $product_id; $effective_id = $variation_id > 0 ? $variation_id : $product_id;
// Check if restriction is enabled globally or for this product/variation // Check if restriction is enabled (with parent fallback) and packages exist
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
// Get packages for this product/variation
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
// If restriction is enabled and packages exist, show quantity as text only
if (($global_restrict || $product_restrict) && !empty($packages)) {
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s</span>', return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s</span>',
$effective_id, $effective_id,
$cart_item['quantity'] $cart_item['quantity']
@@ -179,15 +168,8 @@ if (!class_exists('WC_TPP_Cart')) {
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0; $variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
$effective_id = $variation_id > 0 ? $variation_id : $product_id; $effective_id = $variation_id > 0 ? $variation_id : $product_id;
// Check if restriction is enabled globally or for this product/variation // Check if restriction is enabled (with parent fallback) and packages exist
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
// Get packages for this product/variation
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
// If restriction is enabled and packages exist, show quantity as text only
if (($global_restrict || $product_restrict) && !empty($packages)) {
return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s &times;</span>', return sprintf('<span class="wc-tpp-cart-quantity wc-tpp-restricted-qty" data-product-id="%d">%s &times;</span>',
$effective_id, $effective_id,
$cart_item['quantity'] $cart_item['quantity']
@@ -209,11 +191,8 @@ if (!class_exists('WC_TPP_Cart')) {
$variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0; $variation_id = isset($cart_item['variation_id']) ? absint($cart_item['variation_id']) : 0;
$effective_id = $variation_id > 0 ? $variation_id : $product_id; $effective_id = $variation_id > 0 ? $variation_id : $product_id;
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; // Check if restriction is enabled (with parent fallback) and packages exist
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes'; if ($this->is_restriction_enabled($product_id, $variation_id) && $this->get_packages_with_fallback($product_id, $variation_id)) {
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
if (($global_restrict || $product_restrict) && !empty($packages)) {
$restricted_products[] = $effective_id; $restricted_products[] = $effective_id;
} }
} }
@@ -254,20 +233,61 @@ if (!class_exists('WC_TPP_Cart')) {
return $editable; return $editable;
} }
// For variations, use the variation ID directly (get_id() returns variation ID for WC_Product_Variation) // For variations, get parent product ID and variation ID
$effective_id = $product_id; $variation_id = 0;
$parent_id = $product_id;
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes'; if ($product->is_type('variation')) {
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes'; $variation_id = $product_id;
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true); $parent_id = $product->get_parent_id();
}
// If restriction is enabled and packages exist, make quantity non-editable // Check if restriction is enabled (with parent fallback) and packages exist
if (($global_restrict || $product_restrict) && !empty($packages)) { if ($this->is_restriction_enabled($parent_id, $variation_id) && $this->get_packages_with_fallback($parent_id, $variation_id)) {
return false; return false;
} }
return $editable; return $editable;
} }
/**
* Get packages with parent fallback for variations
*
* @param int $product_id Parent product ID
* @param int $variation_id Variation ID (0 for simple products)
* @return array|false Packages array or false if none found
*/
private function get_packages_with_fallback($product_id, $variation_id = 0) {
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
// Fall back to parent pricing if variation doesn't have its own pricing
if ((empty($packages) || !is_array($packages)) && $variation_id > 0) {
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
}
return (!empty($packages) && is_array($packages)) ? $packages : false;
}
/**
* Check if restriction is enabled for a product/variation with parent fallback
*
* @param int $product_id Parent product ID
* @param int $variation_id Variation ID (0 for simple products)
* @return bool Whether restriction is enabled
*/
private function is_restriction_enabled($product_id, $variation_id = 0) {
$effective_id = $variation_id > 0 ? $variation_id : $product_id;
$global_restrict = get_option('wc_tpp_restrict_package_quantities', 'no') === 'yes';
$product_restrict = get_post_meta($effective_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
// Fall back to parent restriction setting if variation doesn't have its own
if (!$product_restrict && $variation_id > 0) {
$product_restrict = get_post_meta($product_id, '_wc_tpp_restrict_to_packages', true) === 'yes';
}
return $global_restrict || $product_restrict;
}
} }
new WC_TPP_Cart(); new WC_TPP_Cart();

View File

@@ -133,6 +133,11 @@ if (!class_exists('WC_TPP_Frontend')) {
$effective_id = $variation_id > 0 ? $variation_id : $product_id; $effective_id = $variation_id > 0 ? $variation_id : $product_id;
$tiers = get_post_meta($effective_id, '_wc_tpp_tiers', true); $tiers = get_post_meta($effective_id, '_wc_tpp_tiers', true);
// Fall back to parent pricing if variation doesn't have its own pricing
if ((empty($tiers) || !is_array($tiers)) && $variation_id > 0) {
$tiers = get_post_meta($product_id, '_wc_tpp_tiers', true);
}
if (empty($tiers) || !is_array($tiers)) { if (empty($tiers) || !is_array($tiers)) {
return null; return null;
} }
@@ -159,6 +164,11 @@ if (!class_exists('WC_TPP_Frontend')) {
$effective_id = $variation_id > 0 ? $variation_id : $product_id; $effective_id = $variation_id > 0 ? $variation_id : $product_id;
$packages = get_post_meta($effective_id, '_wc_tpp_packages', true); $packages = get_post_meta($effective_id, '_wc_tpp_packages', true);
// Fall back to parent pricing if variation doesn't have its own pricing
if ((empty($packages) || !is_array($packages)) && $variation_id > 0) {
$packages = get_post_meta($product_id, '_wc_tpp_packages', true);
}
if (empty($packages) || !is_array($packages)) { if (empty($packages) || !is_array($packages)) {
return null; return null;
} }

View File

@@ -16,13 +16,127 @@ if (!class_exists('WC_TPP_Product_Meta')) {
add_action('woocommerce_product_options_pricing', array($this, 'add_package_pricing_fields')); add_action('woocommerce_product_options_pricing', array($this, 'add_package_pricing_fields'));
add_action('woocommerce_process_product_meta', array($this, 'save_tier_package_fields')); add_action('woocommerce_process_product_meta', array($this, 'save_tier_package_fields'));
// Variable product parent hooks (for default pricing)
add_action('woocommerce_product_options_pricing', array($this, 'add_variable_parent_pricing_fields'));
// Variable product variation hooks // Variable product variation hooks
add_action('woocommerce_variation_options_pricing', array($this, 'add_variation_pricing_fields'), 10, 3); add_action('woocommerce_variation_options_pricing', array($this, 'add_variation_pricing_fields'), 10, 3);
add_action('woocommerce_save_product_variation', array($this, 'save_variation_pricing_fields'), 10, 2); add_action('woocommerce_save_product_variation', array($this, 'save_variation_pricing_fields'), 10, 2);
} }
/**
* Add tier and package pricing fields for variable product parents
* These serve as defaults for all variations unless overridden
*/
public function add_variable_parent_pricing_fields() {
global $post;
// Only show for variable products, not simple products
$product = wc_get_product($post->ID);
if (!$product || !$product->is_type('variable')) {
return;
}
?>
<div class="options_group wc-tpp-variable-parent-pricing">
<p class="form-field">
<strong><?php _e('Default Tier & Package Pricing for All Variations', 'wc-tier-package-prices'); ?></strong>
<span class="woocommerce-help-tip" data-tip="<?php esc_attr_e('Set default pricing for all variations. Individual variations can override these defaults with their own specific pricing.', 'wc-tier-package-prices'); ?>"></span>
</p>
<p class="description" style="margin-left: 12px; margin-bottom: 15px;">
<?php _e('Configure default tier and package pricing here. All variations will inherit these settings unless they define their own specific pricing.', 'wc-tier-package-prices'); ?>
</p>
<!-- Tier Pricing Section -->
<div class="wc-tpp-parent-tier-pricing" style="margin-left: 12px;">
<p><strong><?php _e('Tier Pricing', 'wc-tier-package-prices'); ?></strong></p>
<table class="widefat wc-tpp-tiers-table">
<thead>
<tr>
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th>
</tr>
</thead>
<tbody class="wc-tpp-tiers-container">
<?php
$tiers = get_post_meta($post->ID, '_wc_tpp_tiers', true);
if (!is_array($tiers)) {
$tiers = array();
}
foreach ($tiers as $index => $tier) {
$this->render_tier_row($index, $tier);
}
?>
</tbody>
</table>
<p class="form-field">
<button type="button" class="button wc-tpp-add-tier"><?php _e('Add Tier', 'wc-tier-package-prices'); ?></button>
</p>
</div>
<!-- Package Pricing Section -->
<div class="wc-tpp-parent-package-pricing" style="margin-left: 12px; margin-top: 20px;">
<p><strong><?php _e('Package Pricing', 'wc-tier-package-prices'); ?></strong></p>
<table class="widefat wc-tpp-packages-table">
<thead>
<tr>
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th>
</tr>
</thead>
<tbody class="wc-tpp-packages-container">
<?php
$packages = get_post_meta($post->ID, '_wc_tpp_packages', true);
if (!is_array($packages)) {
$packages = array();
}
foreach ($packages as $index => $package) {
$this->render_package_row($index, $package);
}
?>
</tbody>
</table>
<p class="form-field">
<button type="button" class="button wc-tpp-add-package"><?php _e('Add Package', 'wc-tier-package-prices'); ?></button>
</p>
<?php
woocommerce_wp_checkbox(array(
'id' => '_wc_tpp_restrict_to_packages',
'label' => __('Restrict to Package Quantities (Default)', 'wc-tier-package-prices'),
'description' => __('Default restriction setting for all variations. Only allow quantities defined in packages above.', 'wc-tier-package-prices'),
'desc_tip' => true,
));
?>
</div>
<!-- Templates for JavaScript (shared between simple and variable parent) -->
<script type="text/html" id="wc-tpp-tier-row-template">
<?php $this->render_tier_row('{{INDEX}}', array('min_qty' => '', 'price' => '', 'label' => '')); ?>
</script>
<script type="text/html" id="wc-tpp-package-row-template">
<?php $this->render_package_row('{{INDEX}}', array('qty' => '', 'price' => '', 'label' => '')); ?>
</script>
</div>
<?php
}
public function add_tier_pricing_fields() { public function add_tier_pricing_fields() {
global $post; global $post;
// Only show for simple products (variable products use add_variable_parent_pricing_fields)
$product = wc_get_product($post->ID);
if ($product && $product->is_type('variable')) {
return;
}
?> ?>
<div class="options_group wc-tpp-tier-pricing"> <div class="options_group wc-tpp-tier-pricing">
<p class="form-field"> <p class="form-field">
@@ -62,6 +176,13 @@ if (!class_exists('WC_TPP_Product_Meta')) {
public function add_package_pricing_fields() { public function add_package_pricing_fields() {
global $post; global $post;
// Only show for simple products (variable products use add_variable_parent_pricing_fields)
$product = wc_get_product($post->ID);
if ($product && $product->is_type('variable')) {
return;
}
?> ?>
<div class="options_group wc-tpp-package-pricing"> <div class="options_group wc-tpp-package-pricing">
<p class="form-field"> <p class="form-field">
@@ -210,6 +331,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
'name' => 'wc_tpp_restrict_to_packages[' . $loop . ']', 'name' => 'wc_tpp_restrict_to_packages[' . $loop . ']',
'label' => __('Restrict to Package Quantities', 'wc-tier-package-prices'), 'label' => __('Restrict to Package Quantities', 'wc-tier-package-prices'),
'description' => __('Only allow quantities defined in packages above', 'wc-tier-package-prices'), 'description' => __('Only allow quantities defined in packages above', 'wc-tier-package-prices'),
'desc_tip' => true,
'value' => $restrict, 'value' => $restrict,
'cbvalue' => 'yes', 'cbvalue' => 'yes',
'wrapper_class' => 'form-row form-row-full' 'wrapper_class' => 'form-row form-row-full'

Binary file not shown.

View File

@@ -0,0 +1 @@
470f3d87ce3ab9eb5a4ddc4a7262d282 wc-tier-and-package-prices-1.2.1.zip

View File

@@ -0,0 +1 @@
23e814583b71299f1d9e77c32ec0b9e88ce31c2ddf89a729282234c7f916ed2b wc-tier-and-package-prices-1.2.1.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
6c1bace109d1401832774668d85cf2c8 wc-tier-and-package-prices-1.2.2.zip

View File

@@ -0,0 +1 @@
a8674027621bd62ae63c2b63ec33da177f514816c35b45b9788d8dd5edd2c6ec wc-tier-and-package-prices-1.2.2.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
2ec8f0cbd01f1e77e0e61d84587025bd wc-tier-and-package-prices-1.2.3.zip

View File

@@ -0,0 +1 @@
ff4e7bd3db5de90e1481e3699cc2e69324046dd01d59c091e19540e3eb6cd0bd wc-tier-and-package-prices-1.2.3.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
abd9cb934494f7053c19a0a022d5717f wc-tier-and-package-prices-1.2.4.zip

View File

@@ -0,0 +1 @@
976bd9bf2cdc5bbf34a65160bc1b42f74cbff71d39c634a042efd59aa90a13d0 wc-tier-and-package-prices-1.2.4.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
61eafd6c2fdd1468c486efe0c6e3bfa4 wc-tier-and-package-prices-1.2.5.zip

View File

@@ -0,0 +1 @@
4f6d637713511db061dd2db5f948b846eb48c343d9b8823299b85038ccca6a6e wc-tier-and-package-prices-1.2.5.zip

Binary file not shown.

View File

@@ -0,0 +1 @@
b2c862fbad94f42f0756fb496a1dd920 wc-tier-and-package-prices-1.2.6.zip

View File

@@ -0,0 +1 @@
19a00c7fc2f42326f52d2da063766abd1c0b47d1701b9050c018bfb2bafae6f6 wc-tier-and-package-prices-1.2.6.zip

View File

@@ -4,7 +4,7 @@
* Plugin Name: WooCommerce Tier and Package Prices * Plugin Name: WooCommerce Tier and Package Prices
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-tier-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 * Description: Add tier pricing and package prices to WooCommerce products with configurable quantities at fixed prices
* Version: 1.2.2 * Version: 1.2.6
* Author: Marco Graetsch * Author: Marco Graetsch
* Author URI: https://src.bundespruefstelle.ch/magdev * Author URI: https://src.bundespruefstelle.ch/magdev
* Text Domain: wc-tier-package-prices * Text Domain: wc-tier-package-prices
@@ -23,7 +23,7 @@ if (!defined('ABSPATH')) {
// Define plugin constants // Define plugin constants
if (!defined('WC_TPP_VERSION')) { if (!defined('WC_TPP_VERSION')) {
define('WC_TPP_VERSION', '1.2.2'); define('WC_TPP_VERSION', '1.2.6');
} }
if (!defined('WC_TPP_PLUGIN_DIR')) { if (!defined('WC_TPP_PLUGIN_DIR')) {
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__)); define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));