6 Commits

Author SHA1 Message Date
71ea40598f Merge branch 'dev' 2025-12-30 00:48:46 +01:00
e5aca708cc Release version 1.2.7 - Fix v1.2.6 regressions (both issues still not working)
Fixed both critical issues that were not resolved in v1.2.6:

1. Variable Product Forms Still Not Showing (Critical)
   - v1.2.6 used wrong hook (woocommerce_product_options_pricing)
   - That hook only fires for simple products, not variable products
   - Changed to woocommerce_product_options_general_product_data
   - This hook fires for all product types after general tab
   - Forms now appear correctly for variable product parents

2. Table Headers Still Visible When Empty (Critical)
   - CSS :has() pseudo-class wasn't working reliably
   - Implemented JavaScript + CSS class approach instead
   - Added updateTableHeaders() function that toggles has-rows class
   - Headers hide by default, show only when table has rows
   - Function called on page load and after all add/remove operations
   - Works across all browsers without modern CSS requirements

Changed files:
- includes/class-wc-tpp-product-meta.php - Fixed WooCommerce hook
- assets/css/admin.css - Class-based header visibility
- assets/js/admin.js - Added updateTableHeaders() and parent handlers

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

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-30 00:48:46 +01:00
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
16 changed files with 270 additions and 12 deletions

View File

@@ -5,6 +5,73 @@ 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/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [1.2.7] - 2025-12-30
### Fixed
- **Variable Product Forms Still Not Showing (Critical)**: The v1.2.6 fix used the wrong WooCommerce hook. The `woocommerce_product_options_pricing` hook only fires for simple products, not variable products. Changed to use `woocommerce_product_options_general_product_data` hook which fires for all product types after the general tab, allowing the code to check product type and conditionally display the parent pricing fields.
- **Table Headers Still Visible When Empty (Critical)**: The CSS `:has()` pseudo-class approach from v1.2.6 wasn't working reliably across all browsers. Implemented a JavaScript-based solution that adds/removes a `has-rows` class on tables based on whether they contain pricing rules. Headers now hide by default and show only when the table has rows, with JavaScript updating the state when rows are added or removed.
### Technical Details
**Variable Product Hook Fix**:
- Changed from `woocommerce_product_options_pricing` to `woocommerce_product_options_general_product_data`
- The general product data hook fires for all product types
- Method still checks `$product->is_type('variable')` to only show for variable products
- This ensures forms appear in the correct location in the WordPress admin
**Table Header Visibility Fix**:
- Replaced CSS-only `:has()` solution with JavaScript + CSS class approach
- CSS now uses `.wc-tpp-tiers-table.has-rows thead` to show headers
- Added `updateTableHeaders()` JavaScript function that checks row count and toggles class
- Function is called on page load and after any add/remove row operation
- Works reliably across all browsers without requiring modern CSS features
**User Impact**:
- Variable product parent pricing forms now actually appear in the WordPress admin
- Table headers properly hide when empty and show when populated
- No browser compatibility issues - works in all modern browsers
### Changed Files
- `includes/class-wc-tpp-product-meta.php` - Changed hook from `woocommerce_product_options_pricing` to `woocommerce_product_options_general_product_data`
- `assets/css/admin.css` - Replaced `:has()` pseudo-class with class-based approach
- `assets/js/admin.js` - Added `updateTableHeaders()` function and calls after all row operations; added handlers for variable product parent forms
## [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

View File

@@ -759,11 +759,21 @@ Roadmap for the upcoming development.
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.
##### Planned Enhancements for v1.2.5+
##### Enhancements (Completed in v1.2.5)
1. Hide the table-headers in admin area until a tier or respectivly a package price is defined.
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.
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.
### When Debugging Cart Issues

View File

@@ -112,15 +112,16 @@
}
/* Hide table headers when there are no pricing rules */
.wc-tpp-tiers-container:empty ~ thead,
.wc-tpp-packages-container:empty ~ thead {
/* Default: hide headers initially, JavaScript will show them when rows are added */
.wc-tpp-tiers-table thead,
.wc-tpp-packages-table thead {
display: none;
}
/* Alternative approach: hide thead when tbody is empty (more reliable) */
.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;
/* Show headers when table has pricing rows */
.wc-tpp-tiers-table.has-rows thead,
.wc-tpp-packages-table.has-rows thead {
display: table-header-group !important;
}
/* Checkbox styling improvements */

View File

@@ -10,6 +10,28 @@
let tierIndex = $('.wc-tpp-tier-pricing .wc-tpp-tier-row').length;
let packageIndex = $('.wc-tpp-package-pricing .wc-tpp-package-row').length;
// Function to update table header visibility
function updateTableHeaders() {
// Check all tier tables
$('.wc-tpp-tiers-table').each(function() {
const $table = $(this);
const $tbody = $table.find('.wc-tpp-tiers-container');
const hasRows = $tbody.find('tr').length > 0;
$table.toggleClass('has-rows', hasRows);
});
// Check all package tables
$('.wc-tpp-packages-table').each(function() {
const $table = $(this);
const $tbody = $table.find('.wc-tpp-packages-container');
const hasRows = $tbody.find('tr').length > 0;
$table.toggleClass('has-rows', hasRows);
});
}
// Initialize table headers on page load
updateTableHeaders();
// ========================================
// Simple Product Handlers
// ========================================
@@ -21,6 +43,7 @@
const newRow = template.replace(/\{\{INDEX\}\}/g, tierIndex);
$('.wc-tpp-tier-pricing .wc-tpp-tiers-container').append(newRow);
tierIndex++;
updateTableHeaders();
});
// Add package (simple products)
@@ -30,6 +53,31 @@
const newRow = template.replace(/\{\{INDEX\}\}/g, packageIndex);
$('.wc-tpp-package-pricing .wc-tpp-packages-container').append(newRow);
packageIndex++;
updateTableHeaders();
});
// ========================================
// Variable Product Parent Handlers
// ========================================
// Add tier (variable product parent default pricing)
$('.wc-tpp-variable-parent-pricing .wc-tpp-add-tier').on('click', function(e) {
e.preventDefault();
const template = $('#wc-tpp-tier-row-template').html();
const newRow = template.replace(/\{\{INDEX\}\}/g, tierIndex);
$('.wc-tpp-variable-parent-pricing .wc-tpp-tiers-container').append(newRow);
tierIndex++;
updateTableHeaders();
});
// Add package (variable product parent default pricing)
$('.wc-tpp-variable-parent-pricing .wc-tpp-add-package').on('click', function(e) {
e.preventDefault();
const template = $('#wc-tpp-package-row-template').html();
const newRow = template.replace(/\{\{INDEX\}\}/g, packageIndex);
$('.wc-tpp-variable-parent-pricing .wc-tpp-packages-container').append(newRow);
packageIndex++;
updateTableHeaders();
});
// ========================================
@@ -50,6 +98,7 @@
const newRow = template.replace(/\{\{INDEX\}\}/g, currentIndex);
$tbody.append(newRow);
updateTableHeaders();
});
// Add package (variations)
@@ -66,6 +115,7 @@
const newRow = template.replace(/\{\{INDEX\}\}/g, currentIndex);
$tbody.append(newRow);
updateTableHeaders();
});
// ========================================
@@ -77,6 +127,7 @@
e.preventDefault();
if (confirm('Are you sure you want to remove this tier?')) {
$(this).closest('.wc-tpp-tier-row').remove();
updateTableHeaders();
}
});
@@ -85,6 +136,7 @@
e.preventDefault();
if (confirm('Are you sure you want to remove this package?')) {
$(this).closest('.wc-tpp-package-row').remove();
updateTableHeaders();
}
});

View File

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

View File

@@ -16,13 +16,128 @@ if (!class_exists('WC_TPP_Product_Meta')) {
add_action('woocommerce_product_options_pricing', array($this, 'add_package_pricing_fields'));
add_action('woocommerce_process_product_meta', array($this, 'save_tier_package_fields'));
// Variable product parent hooks (for default pricing)
// Use product_options_general_product_data which shows for all product types after the general tab
add_action('woocommerce_product_options_general_product_data', array($this, 'add_variable_parent_pricing_fields'));
// Variable product variation hooks
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 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() {
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">
<p class="form-field">
@@ -62,6 +177,13 @@ if (!class_exists('WC_TPP_Product_Meta')) {
public function add_package_pricing_fields() {
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">
<p class="form-field">

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

Binary file not shown.

View File

@@ -0,0 +1 @@
b52aec2e42c894a1949b5e5898105cf1 wc-tier-and-package-prices-1.2.7.zip

View File

@@ -0,0 +1 @@
7ae87ca85f7799ed716d3a81be8977908fadb33260dbc565f6b0d39188c8b025 wc-tier-and-package-prices-1.2.7.zip

View File

@@ -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.2.5
* Version: 1.2.7
* 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.2.5');
define('WC_TPP_VERSION', '1.2.7');
}
if (!defined('WC_TPP_PLUGIN_DIR')) {
define('WC_TPP_PLUGIN_DIR', plugin_dir_path(__FILE__));