Merge branch 'dev'

This commit is contained in:
2025-12-30 01:28:09 +01:00
8 changed files with 91 additions and 23 deletions

View File

@@ -39,7 +39,8 @@
"Bash('wc-tier-and-package-prices/logs/*' )", "Bash('wc-tier-and-package-prices/logs/*' )",
"Bash('wc-tier-and-package-prices/templates/cache/*' )", "Bash('wc-tier-and-package-prices/templates/cache/*' )",
"Bash('wc-tier-and-package-prices/composer.lock' )", "Bash('wc-tier-and-package-prices/composer.lock' )",
"Bash('*/wordpress/*')" "Bash('*/wordpress/*')",
"Bash(echo:*)"
] ]
} }
} }

View File

@@ -5,6 +5,43 @@ 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.8] - 2025-12-30
### Fixed
- **Currency Symbol Missing in Admin Headers and Placeholders**: Table headers in the admin pricing configuration now display "Price (CURRENCY)" instead of just "Price", making it immediately clear which currency is being used. Price input placeholders now show currency symbol (e.g., "e.g., 9.99 $" instead of "e.g., 9.99"), providing better UX for administrators configuring pricing in different currencies.
- **Variation Pricing Data Not Deleted Properly (Critical)**: When administrators deleted all tier or package pricing entries from a variation (or simple/parent product) and saved, the empty pricing data was still stored in the database instead of being deleted. This caused variations to retain deleted pricing rules. The save logic now properly detects when the filtered pricing arrays are empty after removing invalid entries and deletes the post meta instead of saving empty arrays.
### Technical Details
**Currency Symbol Enhancement**:
- Updated all table headers to use `printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol())`
- Modified `render_tier_row()` and `render_package_row()` methods to pass `currency_symbol` to Twig templates
- Updated `render_variation_tier_row()` and `render_variation_package_row()` with same currency symbol parameter
- Changed Twig template placeholders from `'e.g., 9.99'` to `('e.g., 9.99 ' ~ currency_symbol)`
- Affects all pricing contexts: simple products, variable product parents, and variations
**Pricing Deletion Fix**:
- Modified `save_tier_package_fields()` method (simple/parent products) to check `if (!empty($tiers))` before saving
- Modified `save_variation_pricing_fields()` method (variations) with same empty check logic
- Changed logic from "save on isset, delete otherwise" to "filter entries, then save if not empty, delete if empty"
- Applies to both tier pricing and package pricing for all product types
- Root cause was filtering out empty entries but still calling `update_post_meta()` with an empty array
**User Impact**:
- Administrators see currency symbol in all pricing configuration interfaces
- Clear indication of which currency prices should be entered in
- Deleting all pricing rules now properly removes them from the database
- No orphaned pricing data remains after deletion
- Works correctly for simple products, variable product parents, and variations
### Changed Files
- `includes/class-wc-tpp-product-meta.php` - Added currency symbol to all table headers; updated all render methods to pass currency symbol; fixed empty array deletion logic in both save methods
- `templates/admin/tier-row.twig` - Updated placeholder to include currency symbol
- `templates/admin/package-row.twig` - Updated placeholder to include currency symbol
## [1.2.7] - 2025-12-30 ## [1.2.7] - 2025-12-30
### Fixed ### Fixed

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-30 **Last Updated:** 2025-12-30
**Current Version:** 1.2.7 **Current Version:** 1.2.8
**Author:** Marco Graetsch **Author:** Marco Graetsch
**Project Status:** Production-ready WordPress plugin **Project Status:** Production-ready WordPress plugin
@@ -692,7 +692,7 @@ zip -r wc-tier-and-package-prices/releases/wc-tier-and-package-prices-X.X.X.zip
**Critical Exclusions:** **Critical Exclusions:**
- `*/wordpress/*` - MUST be excluded! The project has a symlink to WordPress installation that zip will follow, creating 129MB+ packages instead of ~430KB - `*/wordpress/*` and `*/core/*` - 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) - `.git/*` - All git metadata (multiple patterns needed for reliability)
- `.claude/*` and `CLAUDE.md` - Development documentation - `.claude/*` and `CLAUDE.md` - Development documentation
- `releases/*` - Prevents including previous releases in new ones - `releases/*` - Prevents including previous releases in new ones
@@ -775,7 +775,13 @@ Roadmap for the upcoming development.
1.**COMPLETED** - Updated all translation files (.pot, .po, .mo) with new strings from v1.2.6 and v1.2.7 for variable product parent pricing features. All 7 language variants updated with translations for "Default Tier & Package Pricing for All Variations" and related strings. 1.**COMPLETED** - Updated all translation files (.pot, .po, .mo) with new strings from v1.2.6 and v1.2.7 for variable product parent pricing features. All 7 language variants updated with translations for "Default Tier & Package Pricing for All Variations" and related strings.
##### Planned Enhancements for v1.2.8+ ##### Bugfixes (Completed in v1.2.8)
1. ~~Add a Suffix with the current configured default currency to the table-header and form placeholder. Use the common currency notation in placeholder~~**FIXED in v1.2.8** - Updated all table headers in admin to display "Price (€)" format using `printf(__('Price (%s)'), get_woocommerce_currency_symbol())`. Modified all template render methods (tier_row, package_row, variation_tier_row, variation_package_row) to pass currency_symbol to Twig templates. Updated admin/tier-row.twig and admin/package-row.twig to concatenate currency symbol in price input placeholders (e.g., "e.g., 9.99 €"). Applied to simple products, variable parent products, and all variations.
2. ~~Already stored tier and package prices on the children of a variable product are still available after deletion. Looks like the storage mechanism has an error. This occurs only on the child product, not on the parent product.~~**FIXED in v1.2.8** - Fixed save logic in both `save_tier_package_fields()` and `save_variation_pricing_fields()` methods. Root cause: Empty arrays were being saved via `update_post_meta()` instead of being deleted. Changed logic from "save on isset, delete otherwise" to "filter entries, then save if not empty, delete if empty". Added `if (!empty($tiers))` and `if (!empty($packages))` checks before calling `update_post_meta()`. Now properly calls `delete_post_meta()` when all pricing entries are removed, preventing empty arrays from persisting in database.
##### Planned Enhancements for v1.2.9+
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.

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.7", "version": "1.2.8",
"type": "wordpress-plugin", "type": "wordpress-plugin",
"license": "GPL-2.0-or-later", "license": "GPL-2.0-or-later",
"authors": [ "authors": [

View File

@@ -55,7 +55,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
<thead> <thead>
<tr> <tr>
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th> <th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th> <th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th> <th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th> <th></th>
</tr> </tr>
@@ -85,7 +85,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
<thead> <thead>
<tr> <tr>
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th> <th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th> <th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th> <th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th> <th></th>
</tr> </tr>
@@ -149,7 +149,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
<thead> <thead>
<tr> <tr>
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th> <th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th> <th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th> <th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th> <th></th>
</tr> </tr>
@@ -195,7 +195,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
<thead> <thead>
<tr> <tr>
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th> <th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th> <th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th> <th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th> <th></th>
</tr> </tr>
@@ -241,14 +241,16 @@ if (!class_exists('WC_TPP_Product_Meta')) {
private function render_tier_row($index, $tier) { private function render_tier_row($index, $tier) {
WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array( WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array(
'index' => $index, 'index' => $index,
'tier' => $tier 'tier' => $tier,
'currency_symbol' => get_woocommerce_currency_symbol()
)); ));
} }
private function render_package_row($index, $package) { private function render_package_row($index, $package) {
WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array( WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array(
'index' => $index, 'index' => $index,
'package' => $package 'package' => $package,
'currency_symbol' => get_woocommerce_currency_symbol()
)); ));
} }
@@ -285,7 +287,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
<thead> <thead>
<tr> <tr>
<th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th> <th><?php _e('Min Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th> <th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th> <th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th> <th></th>
</tr> </tr>
@@ -308,7 +310,7 @@ if (!class_exists('WC_TPP_Product_Meta')) {
<thead> <thead>
<tr> <tr>
<th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th> <th><?php _e('Quantity', 'wc-tier-package-prices'); ?></th>
<th><?php _e('Price', 'wc-tier-package-prices'); ?></th> <th><?php printf(__('Price (%s)', 'wc-tier-package-prices'), get_woocommerce_currency_symbol()); ?></th>
<th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th> <th><?php _e('Label (optional)', 'wc-tier-package-prices'); ?></th>
<th></th> <th></th>
</tr> </tr>
@@ -363,7 +365,8 @@ if (!class_exists('WC_TPP_Product_Meta')) {
WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array( WC_TPP_Template_Loader::get_instance()->display('admin/tier-row.twig', array(
'index' => $index, 'index' => $index,
'tier' => $tier, 'tier' => $tier,
'field_prefix' => 'wc_tpp_tiers[' . $loop . ']' 'field_prefix' => 'wc_tpp_tiers[' . $loop . ']',
'currency_symbol' => get_woocommerce_currency_symbol()
)); ));
} }
@@ -378,7 +381,8 @@ if (!class_exists('WC_TPP_Product_Meta')) {
WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array( WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array(
'index' => $index, 'index' => $index,
'package' => $package, 'package' => $package,
'field_prefix' => 'wc_tpp_packages[' . $loop . ']' 'field_prefix' => 'wc_tpp_packages[' . $loop . ']',
'currency_symbol' => get_woocommerce_currency_symbol()
)); ));
} }
@@ -414,7 +418,12 @@ if (!class_exists('WC_TPP_Product_Meta')) {
usort($tiers, function($a, $b) { usort($tiers, function($a, $b) {
return $a['min_qty'] - $b['min_qty']; return $a['min_qty'] - $b['min_qty'];
}); });
update_post_meta($post_id, '_wc_tpp_tiers', $tiers); // Only save if we have valid tiers, otherwise delete
if (!empty($tiers)) {
update_post_meta($post_id, '_wc_tpp_tiers', $tiers);
} else {
delete_post_meta($post_id, '_wc_tpp_tiers');
}
} else { } else {
delete_post_meta($post_id, '_wc_tpp_tiers'); delete_post_meta($post_id, '_wc_tpp_tiers');
} }
@@ -435,7 +444,12 @@ if (!class_exists('WC_TPP_Product_Meta')) {
usort($packages, function($a, $b) { usort($packages, function($a, $b) {
return $a['qty'] - $b['qty']; return $a['qty'] - $b['qty'];
}); });
update_post_meta($post_id, '_wc_tpp_packages', $packages); // Only save if we have valid packages, otherwise delete
if (!empty($packages)) {
update_post_meta($post_id, '_wc_tpp_packages', $packages);
} else {
delete_post_meta($post_id, '_wc_tpp_packages');
}
} else { } else {
delete_post_meta($post_id, '_wc_tpp_packages'); delete_post_meta($post_id, '_wc_tpp_packages');
} }
@@ -473,7 +487,12 @@ if (!class_exists('WC_TPP_Product_Meta')) {
usort($tiers, function($a, $b) { usort($tiers, function($a, $b) {
return $a['min_qty'] - $b['min_qty']; return $a['min_qty'] - $b['min_qty'];
}); });
update_post_meta($variation_id, '_wc_tpp_tiers', $tiers); // Only save if we have valid tiers, otherwise delete
if (!empty($tiers)) {
update_post_meta($variation_id, '_wc_tpp_tiers', $tiers);
} else {
delete_post_meta($variation_id, '_wc_tpp_tiers');
}
} else { } else {
delete_post_meta($variation_id, '_wc_tpp_tiers'); delete_post_meta($variation_id, '_wc_tpp_tiers');
} }
@@ -494,7 +513,12 @@ if (!class_exists('WC_TPP_Product_Meta')) {
usort($packages, function($a, $b) { usort($packages, function($a, $b) {
return $a['qty'] - $b['qty']; return $a['qty'] - $b['qty'];
}); });
update_post_meta($variation_id, '_wc_tpp_packages', $packages); // Only save if we have valid packages, otherwise delete
if (!empty($packages)) {
update_post_meta($variation_id, '_wc_tpp_packages', $packages);
} else {
delete_post_meta($variation_id, '_wc_tpp_packages');
}
} else { } else {
delete_post_meta($variation_id, '_wc_tpp_packages'); delete_post_meta($variation_id, '_wc_tpp_packages');
} }

View File

@@ -21,7 +21,7 @@
<input type="text" <input type="text"
name="{{ name_prefix }}[{{ index|esc_attr }}][price]" name="{{ name_prefix }}[{{ index|esc_attr }}][price]"
value="{{ package.price|default('')|esc_attr }}" value="{{ package.price|default('')|esc_attr }}"
placeholder="{{ 'e.g., 99.99'|__('wc-tier-package-prices') }}" placeholder="{{ ('e.g., 99.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}"
class="short wc_input_price"> class="short wc_input_price">
</td> </td>
<td> <td>

View File

@@ -21,7 +21,7 @@
<input type="text" <input type="text"
name="{{ name_prefix }}[{{ index|esc_attr }}][price]" name="{{ name_prefix }}[{{ index|esc_attr }}][price]"
value="{{ tier.price|default('')|esc_attr }}" value="{{ tier.price|default('')|esc_attr }}"
placeholder="{{ 'e.g., 9.99'|__('wc-tier-package-prices') }}" placeholder="{{ ('e.g., 9.99 ' ~ currency_symbol)|__('wc-tier-package-prices') }}"
class="short wc_input_price"> class="short wc_input_price">
</td> </td>
<td> <td>

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.7 * Version: 1.2.8
* 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.7'); define('WC_TPP_VERSION', '1.2.8');
} }
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__));