You've already forked wc-composable-product
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c036a37602 | |||
| efedd1bf29 | |||
| 12388af5a0 | |||
| c39c13ffed | |||
| 7931dbeef9 | |||
| ee81de86c2 | |||
| 669888817b | |||
| 5564b888fc | |||
| 91aca25169 | |||
| 4195fb2651 | |||
| ba28ae174f | |||
| 755108a7d3 | |||
| 85983d5473 | |||
| 252b187600 | |||
| 8185a77697 | |||
| 6c2e317230 | |||
| 58f5329bc4 | |||
| 0767016370 | |||
| fa7ec0e422 | |||
| f4d2543d4e | |||
| 9e4513f911 | |||
| 4dc7b767a8 | |||
| f763e35d19 | |||
| 8b271c90c0 | |||
| 0dd4408b23 | |||
| 7a4a0a0135 | |||
| c6a48d6404 | |||
| ac1cb9b135 | |||
| f5bc0d0335 | |||
| 88a907c4dd | |||
| 03a7624564 | |||
| 1c3f44f3c2 | |||
| 287f8b778b | |||
| 63d8f9ed52 |
217
CHANGELOG.md
217
CHANGELOG.md
@@ -5,6 +5,223 @@ All notable changes to this project will be documented in this file.
|
|||||||
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.1.14] - 2025-12-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **DEBUG**: Comprehensive debug logging to troubleshoot product retrieval issues
|
||||||
|
- Error log output shows selection criteria, query arguments, and results
|
||||||
|
- Logs each product/variation being added to help identify filtering issues
|
||||||
|
- Enable by setting WP_DEBUG to true in wp-config.php
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- Modified file: includes/Product_Type.php (added error_log statements throughout get_available_products())
|
||||||
|
- Logs criteria array (categories, tags, SKUs)
|
||||||
|
- Logs WP_Query arguments before execution
|
||||||
|
- Logs number of posts found by query
|
||||||
|
- Logs each variable product's variation count
|
||||||
|
- Logs each variation/simple product being added with name
|
||||||
|
- Logs total products available at end
|
||||||
|
- All logging wrapped in WP_DEBUG checks (no performance impact in production)
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This is a debug release to help diagnose why products aren't showing
|
||||||
|
- No functional changes from v1.1.13
|
||||||
|
- User should enable WP_DEBUG and check debug.log or error.log
|
||||||
|
- Log output will show exactly where products are being filtered out
|
||||||
|
- All translation files remain at 100% completion (57/57 strings)
|
||||||
|
|
||||||
|
## [1.1.13] - 2025-12-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **CRITICAL**: "No products available for selection" message showing even when products are configured
|
||||||
|
- Removed overly strict `is_in_stock()` requirement that was filtering out all products
|
||||||
|
- Products now show regardless of stock status (out-of-stock items are displayed but disabled)
|
||||||
|
- Added `'relation' => 'AND'` to meta_query for proper handling of multiple meta conditions
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Product retrieval now shows all purchasable products, not just in-stock ones
|
||||||
|
- Stock status still displayed on frontend with appropriate styling
|
||||||
|
- Out-of-stock items shown but disabled via checkbox and visual indicators
|
||||||
|
- Frontend stock management from v1.1.0 still fully functional
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- Modified file: includes/Product_Type.php (lines 117-124, 177, 181)
|
||||||
|
- Changed from `$variation->is_in_stock() && $variation->is_purchasable()` to just `$variation->is_purchasable()`
|
||||||
|
- Changed from `$product->is_in_stock() && $product->is_purchasable()` to just `$product->is_purchasable()`
|
||||||
|
- Added `'relation' => 'AND'` to meta_query array for WordPress query compatibility
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This fixes the issue where NO products were showing in the selector
|
||||||
|
- Stock validation still occurs at add-to-cart time (Stock_Manager class)
|
||||||
|
- Frontend still displays stock badges (in stock, low stock, out of stock)
|
||||||
|
- Out-of-stock items remain non-selectable via disabled checkboxes
|
||||||
|
- All translation files remain at 100% completion (57/57 strings)
|
||||||
|
|
||||||
|
## [1.1.12] - 2025-12-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **CRITICAL**: Variable product variations still not appearing in product selector after v1.1.11 release
|
||||||
|
- Changed variation retrieval method from `get_available_variations()` to `get_children()` for more reliable variation ID retrieval
|
||||||
|
- `get_available_variations()` returns complex data arrays which may not work in all contexts
|
||||||
|
- `get_children()` returns simple array of variation IDs directly, ensuring consistent results
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- Modified file: includes/Product_Type.php (lines 171-184)
|
||||||
|
- Changed from `$product->get_available_variations()` to `$product->get_children()`
|
||||||
|
- More direct and reliable method for retrieving variation IDs
|
||||||
|
- Each variation ID passed to `wc_get_product()` for full product object
|
||||||
|
- Maintains all stock and purchasability checks from v1.1.11
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This is a patch release fixing the variable product support introduced in v1.1.11
|
||||||
|
- User reported "nope, still no product selectable" after v1.1.11
|
||||||
|
- Root cause: `get_available_variations()` returns variation data arrays instead of clean IDs
|
||||||
|
- `get_children()` is the standard WooCommerce method for retrieving variation IDs
|
||||||
|
- All translation files remain at 100% completion (57/57 strings - no changes needed)
|
||||||
|
|
||||||
|
## [1.1.11] - 2025-12-31
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **FEATURE**: Variable product support - composable products can now include variable products and their variations
|
||||||
|
- Variable products automatically expand to show all available variations as selectable items
|
||||||
|
- Each variation displays with full attribute information (e.g., "Product - Color: Red, Size: Large")
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Products not showing in selector when all available products were variable products
|
||||||
|
- Variable products were being filtered out because parent products aren't directly purchasable
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Modified `get_available_products()` to detect and handle variable products
|
||||||
|
- Variable products now expand into their individual variations
|
||||||
|
- Each variation checked individually for stock status and purchasability
|
||||||
|
- Simple products continue to work exactly as before
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- Modified file: includes/Product_Type.php (lines 160-188)
|
||||||
|
- Added logic to detect `is_type('variable')` products
|
||||||
|
- Uses `get_available_variations()` to retrieve all variations
|
||||||
|
- Each variation validated with `is_in_stock()` and `is_purchasable()`
|
||||||
|
- Maintains backward compatibility with simple products
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This is a feature enhancement release, not a bug fix
|
||||||
|
- Resolves the issue where categories containing only variable products showed no selections
|
||||||
|
- Variations display with their parent product name plus selected attributes
|
||||||
|
- Stock management works correctly for both simple products and variations
|
||||||
|
- All translation files remain at 100% completion (57/57 strings - no new strings added)
|
||||||
|
|
||||||
|
## [1.1.10] - 2025-12-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **CRITICAL**: Admin panel - Both General and Composable tabs visible simultaneously on initial page load
|
||||||
|
- **CRITICAL**: Frontend - No products showing in product selector, only cart button and pricing visible
|
||||||
|
- Empty product grid now shows helpful message instead of blank space
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Added explicit `display: none` to `#composable_product_data` panel for proper initial hiding
|
||||||
|
- Panel now only shows when `body.product-type-composable` class is present
|
||||||
|
- Added empty state message in product selector template when no products are configured
|
||||||
|
- Cleared Twig cache to ensure template changes take effect
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Empty state message: "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
- Translations for empty state message in all 6 supported locales (de_DE, de_DE_informal, de_CH, de_CH_informal, fr_CH, it_CH)
|
||||||
|
- Recompiled .mo translation files
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- Modified files: assets/css/admin.css (lines 7-16), templates/product-selector.twig (lines 12-15)
|
||||||
|
- Root cause (admin): Panel lacked explicit CSS hiding rule, relied only on `hidden` class
|
||||||
|
- Root cause (frontend): No feedback when products array is empty
|
||||||
|
- Solution: CSS specificity + empty state conditional in Twig template
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This release fixes two critical bugs discovered immediately after v1.1.9
|
||||||
|
- Admin interface now correctly hides composable panel until product type is selected
|
||||||
|
- Frontend provides clear user feedback when product selection is unavailable
|
||||||
|
- All translation files now 100% complete (57/57 strings)
|
||||||
|
|
||||||
|
## [1.1.9] - 2025-12-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **CRITICAL**: Admin rendering completely broken - tabs disappeared and fields appeared out of context after v1.1.8 release
|
||||||
|
- CSS selectors were too broad, hiding tab navigation along with field groups
|
||||||
|
- Removed `!important` flags that caused overly aggressive hiding
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Made CSS selectors more specific: `.options_group.show_if_composable` for field groups only
|
||||||
|
- Added separate rule for tab links: `.product_data_tabs li.composable_options`
|
||||||
|
- Tab navigation now works correctly without hiding itself
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- Modified files: assets/css/admin.css (lines 22-40)
|
||||||
|
- Root cause: `.show_if_composable` class used by WooCommerce for both tab links AND field groups
|
||||||
|
- Solution: Separate selectors for each use case to prevent unintended hiding
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This release fixes critical regression introduced in v1.1.8
|
||||||
|
- Admin interface now renders correctly with visible tabs and properly positioned fields
|
||||||
|
- No `!important` flags needed with specific selectors
|
||||||
|
|
||||||
|
## [1.1.8] - 2025-12-31
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **CRITICAL**: Admin rendering bug where both General and Composable Options tabs showed simultaneously on initial page load
|
||||||
|
- **CRITICAL**: Frontend product selector not appearing on product pages - WooCommerce's default add-to-cart button now hidden for composable products
|
||||||
|
- **CRITICAL**: Price formatting not localized - prices now display with proper currency symbols, decimal separators, and thousand separators for all locales
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- `wc_price()` Twig function for proper price formatting in templates
|
||||||
|
- `formatPrice()` JavaScript method with full WooCommerce locale support
|
||||||
|
- Price format localization data passed to frontend JavaScript (decimal/thousand separators, currency position, number of decimals)
|
||||||
|
- `hide_default_add_to_cart()` method to prevent WooCommerce's default purchase UI for composable products
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Enhanced CSS specificity with `!important` flags for proper tab visibility control
|
||||||
|
- Template now uses `{{ fixed_price_html|raw }}` instead of raw currency concatenation
|
||||||
|
- Product selector passes pre-formatted price HTML from `wc_price()` function
|
||||||
|
- Frontend JavaScript updates prices dynamically using WooCommerce format settings
|
||||||
|
|
||||||
|
### Technical
|
||||||
|
|
||||||
|
- Modified files: assets/css/admin.css (+24 lines), includes/Cart_Handler.php (+14 lines), includes/Plugin.php (+7 lines), includes/Product_Selector.php (+2 lines), templates/product-selector.twig, assets/js/frontend.js (+28 lines)
|
||||||
|
- All PHP files pass syntax validation
|
||||||
|
- Supports Swiss format (CHF 50.-), European format (50,00 €), US format ($50.00), and all other WooCommerce locales
|
||||||
|
- Thousand separator support: comma (1,000), dot (1.000), apostrophe (1'000), space (1 000)
|
||||||
|
|
||||||
|
### Notes
|
||||||
|
|
||||||
|
- This release fixes all three critical UI bugs reported in CLAUDE.md
|
||||||
|
- Admin tabs now display correctly on initial page load without JavaScript flicker
|
||||||
|
- Frontend product selector is now the only purchase interface (no WooCommerce default button)
|
||||||
|
- All prices maintain proper locale formatting during dynamic updates
|
||||||
|
|
||||||
## [1.1.7] - 2025-12-31
|
## [1.1.7] - 2025-12-31
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
798
CLAUDE.md
798
CLAUDE.md
@@ -252,6 +252,11 @@ unzip -l wc-composable-product-vX.X.X.zip
|
|||||||
|
|
||||||
- ✅ ~~There is a bug related to twig in the frontend area. Documented in `logs/fatal-errors*.log`~~ **FIXED in v1.1.5**
|
- ✅ ~~There is a bug related to twig in the frontend area. Documented in `logs/fatal-errors*.log`~~ **FIXED in v1.1.5**
|
||||||
- ✅ ~~Translate the admin area, too~~ **COMPLETED in v1.1.6** - All admin strings now translated to 6 locales
|
- ✅ ~~Translate the admin area, too~~ **COMPLETED in v1.1.6** - All admin strings now translated to 6 locales
|
||||||
|
- ✅ ~~Small rendering Bug in admin area. If you load the side, on first view it shows the first both tabs.~~ **FIXED in v1.1.8**
|
||||||
|
- ✅ ~~In the frontend, regardless which selection mode you use, there appears no product selection in any way.~~ **FIXED in v1.1.8**
|
||||||
|
- ✅ ~~The pricing field in the frontend should be rendered as localized price field include currency.~~ **FIXED in v1.1.8**
|
||||||
|
- Still no product selection in frontend. Current mode 'by Category', but 'by tag' also didn't work
|
||||||
|
- The tab rendering is still no correct. first both tabs are shown on initial page load. After clicking a tab, they behave as expected. Update: I Think there is a collision with the dynamicly changing the criteria with the related field and the tab switching function.
|
||||||
|
|
||||||
## Session History
|
## Session History
|
||||||
|
|
||||||
@@ -910,6 +915,799 @@ Everything from v1.1.5 plus:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### v1.1.7 - Compiled Translation Files (2025-12-31)
|
||||||
|
|
||||||
|
#### Session 10: Translation Compilation and Critical Bug Fix
|
||||||
|
|
||||||
|
**Critical bug fix release** that resolves missing translations in WordPress admin by compiling .mo files.
|
||||||
|
|
||||||
|
**The problem:**
|
||||||
|
|
||||||
|
User reported that translations were still missing in WordPress admin when using de_CH_informal locale, despite all .po files being 100% complete with 56/56 strings translated. Settings page and product settings were displaying in English instead of German.
|
||||||
|
|
||||||
|
**Root cause:**
|
||||||
|
|
||||||
|
WordPress i18n system requires **compiled .mo files** (Machine Object), not just .po files (Portable Object). The .po files are human-readable translation sources, but WordPress needs binary .mo files to actually load and display translations.
|
||||||
|
|
||||||
|
Previous versions (v1.1.6 and earlier) included complete .po translation files but never compiled them to .mo format, so WordPress couldn't use them.
|
||||||
|
|
||||||
|
**The fix:**
|
||||||
|
|
||||||
|
Compiled all 6 .po files to .mo format using msgfmt:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for po in languages/*.po; do msgfmt -o "${po%.po}.mo" "$po"; done
|
||||||
|
```
|
||||||
|
|
||||||
|
This generated 6 binary .mo files that WordPress can load.
|
||||||
|
|
||||||
|
**What was accomplished:**
|
||||||
|
|
||||||
|
1. **Compiled .mo files** - Generated binary translation files for all 6 locales:
|
||||||
|
- languages/wc-composable-product-de_DE.mo (5.3 KB)
|
||||||
|
- languages/wc-composable-product-de_DE_informal.mo (5.3 KB)
|
||||||
|
- languages/wc-composable-product-de_CH.mo (5.4 KB)
|
||||||
|
- languages/wc-composable-product-de_CH_informal.mo (5.4 KB)
|
||||||
|
- languages/wc-composable-product-fr_CH.mo (5.5 KB)
|
||||||
|
- languages/wc-composable-product-it_CH.mo (5.3 KB)
|
||||||
|
|
||||||
|
2. **Version bump to 1.1.7** - Updated plugin version and CHANGELOG
|
||||||
|
|
||||||
|
3. **Release package** - Created production ZIP with all .mo files included
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
|
||||||
|
- languages/*.mo: 6 new compiled translation files
|
||||||
|
- wc-composable-product.php: Version bump to 1.1.7 (lines 6, 22)
|
||||||
|
- CHANGELOG.md: Added v1.1.7 release notes
|
||||||
|
|
||||||
|
**Files created:**
|
||||||
|
|
||||||
|
- languages/wc-composable-product-de_DE.mo
|
||||||
|
- languages/wc-composable-product-de_DE_informal.mo
|
||||||
|
- languages/wc-composable-product-de_CH.mo
|
||||||
|
- languages/wc-composable-product-de_CH_informal.mo
|
||||||
|
- languages/wc-composable-product-fr_CH.mo
|
||||||
|
- languages/wc-composable-product-it_CH.mo
|
||||||
|
|
||||||
|
**Release details:**
|
||||||
|
|
||||||
|
- Package: wc-composable-product-v1.1.7.zip (393 KB / 402,351 bytes)
|
||||||
|
- Git tag: v1.1.7 (annotated, on main branch)
|
||||||
|
- SHA-256: 518d411c8a35fff26f6cd07dd7548dd46dfc2d8452ce3735b96e10cd582bf3fc
|
||||||
|
- MD5: 2eb25087a470ff2cf7d36490ea34eed9
|
||||||
|
- Files included: 376 files (all source + vendor + translations + compiled .mo files)
|
||||||
|
|
||||||
|
**What works (v1.1.7):**
|
||||||
|
|
||||||
|
Everything from v1.1.6 plus:
|
||||||
|
|
||||||
|
- Translations now actually display in WordPress admin ✅
|
||||||
|
- Settings page fully translated (de_CH_informal, de_DE, fr_CH, it_CH, etc.) ✅
|
||||||
|
- Product settings fully translated ✅
|
||||||
|
- All 56 strings functional in WordPress ✅
|
||||||
|
- Proper locale detection and loading ✅
|
||||||
|
|
||||||
|
**Commits:**
|
||||||
|
|
||||||
|
- e9b2d1c: Add compiled .mo translation files for all locales
|
||||||
|
- 601570d: Bump version to 1.1.7 for release
|
||||||
|
- 287f8b7: Add release package v1.1.7 with checksums
|
||||||
|
- 63d8f9e: Document v1.1.7 release in CLAUDE.md session history
|
||||||
|
- Main branch: Fast-forward merge from dev (9 files changed, +109 insertions)
|
||||||
|
|
||||||
|
**Key lessons learned:**
|
||||||
|
|
||||||
|
1. **.po vs .mo Files**: .po files are source/editable, .mo files are compiled/binary - WordPress needs BOTH
|
||||||
|
2. **Translation Workflow**: Always compile .mo files after editing .po files: `msgfmt -o file.mo file.po`
|
||||||
|
3. **WordPress i18n Requirements**: Just having translations in .po format is insufficient - must compile to .mo
|
||||||
|
4. **Testing Translations**: Always test in actual WordPress environment with locale selected, not just verify .po file completeness
|
||||||
|
5. **Release Checklist**: For i18n plugins, verify .mo files are included in release packages
|
||||||
|
6. **File Sizes**: .mo files are typically slightly larger than .po files due to binary format and indexing
|
||||||
|
|
||||||
|
**Debugging approach:**
|
||||||
|
|
||||||
|
1. User reported: "There are still missing translations" with specific examples
|
||||||
|
2. Verified all .po files were complete (56/56 strings)
|
||||||
|
3. Realized WordPress needs .mo files, not just .po files
|
||||||
|
4. Compiled all .po files to .mo using msgfmt
|
||||||
|
5. Verified .mo files created successfully (6 files, ~5.3-5.5 KB each)
|
||||||
|
6. Committed and released v1.1.7
|
||||||
|
|
||||||
|
**Impact:**
|
||||||
|
|
||||||
|
This was a **critical bug** that made all translation work from v1.1.6 and earlier invisible to users. Without .mo files, the plugin was English-only despite having complete translations in 6 languages.
|
||||||
|
|
||||||
|
**Status:** Released and tagged as v1.1.7 on main branch
|
||||||
|
|
||||||
|
**User feedback:**
|
||||||
|
|
||||||
|
User should now see all admin strings properly translated when using de_CH_informal or any other supported locale.
|
||||||
|
|
||||||
|
**Post-release updates:**
|
||||||
|
|
||||||
|
Both v1.1.6 and v1.1.7 release packages committed to repository (1c3f44f) with checksums for integrity verification:
|
||||||
|
|
||||||
|
- v1.1.6: 378 KB package (complete .po files, no .mo files - translations won't display)
|
||||||
|
- v1.1.7: 393 KB package (complete .po + compiled .mo files - translations work)
|
||||||
|
|
||||||
|
**Critical structure fix:**
|
||||||
|
|
||||||
|
Both v1.1.6 and v1.1.7 packages recreated with proper WordPress directory structure (88a907c, f5bc0d0):
|
||||||
|
|
||||||
|
- Packages now include `wc-composable-product/` parent directory
|
||||||
|
- WordPress extracts to correct plugin slug directory, not version-numbered directory
|
||||||
|
- New package size: 410 KB for both versions
|
||||||
|
- Merged to main (ac1cb9b) and pushed to remote
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.1.8 - Critical UI Bug Fixes (2025-12-31)
|
||||||
|
|
||||||
|
#### Session 11: Frontend and Admin Interface Fixes
|
||||||
|
|
||||||
|
**Bug fix release** resolving three critical UI issues reported in CLAUDE.md.
|
||||||
|
|
||||||
|
**Issues fixed:**
|
||||||
|
|
||||||
|
1. **Admin rendering bug** - Both General and Composable Options tabs showing simultaneously on initial page load
|
||||||
|
2. **Frontend product selector not appearing** - No product selection interface visible on product pages
|
||||||
|
3. **Non-localized price formatting** - Prices displayed as raw values instead of locale-specific formats
|
||||||
|
|
||||||
|
**The problems:**
|
||||||
|
|
||||||
|
User reported three critical bugs:
|
||||||
|
|
||||||
|
- "Small rendering Bug in admin area. If you load the side, on first view it shows the first both tabs."
|
||||||
|
- "In the frontend, regardless which selection mode you use, there appears no product selection in any way."
|
||||||
|
- "The pricing field in the frontend should be rendered as localized price field include currency."
|
||||||
|
|
||||||
|
**Root causes:**
|
||||||
|
|
||||||
|
1. **Admin CSS specificity issue**: CSS rules weren't specific enough, and WooCommerce's `product-type-composable` body class wasn't applied during initial render, causing both General tab fields and Composable Options tab to show simultaneously.
|
||||||
|
|
||||||
|
2. **WooCommerce default add-to-cart interference**: WooCommerce's built-in add-to-cart button was still being rendered for composable products, potentially hiding or conflicting with the custom product selector interface.
|
||||||
|
|
||||||
|
3. **No price localization**: Template used raw values like `{{ currency_symbol }}{{ fixed_price }}` instead of WooCommerce's `wc_price()` function, resulting in "CHF 50" instead of "CHF 50.-" (Swiss format), "€50" instead of "50,00 €" (European format), etc.
|
||||||
|
|
||||||
|
**The fixes:**
|
||||||
|
|
||||||
|
1. **Admin CSS Enhancement** ([assets/css/admin.css](assets/css/admin.css)):
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Hide composable-specific elements by default */
|
||||||
|
.show_if_composable {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show composable elements when composable product type is selected */
|
||||||
|
body.product-type-composable .show_if_composable,
|
||||||
|
.product-type-composable .show_if_composable {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the Composable Options tab link by default */
|
||||||
|
.product_data_tabs .composable_options {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the Composable Options tab when composable type selected */
|
||||||
|
body.product-type-composable .product_data_tabs .composable_options {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Enhanced CSS specificity with `!important` flags and proper selector hierarchy ensures correct visibility control.
|
||||||
|
|
||||||
|
2. **Hide WooCommerce Default Add-to-Cart** ([includes/Cart_Handler.php](includes/Cart_Handler.php)):
|
||||||
|
|
||||||
|
```php
|
||||||
|
// In __construct():
|
||||||
|
add_filter('woocommerce_is_purchasable', [$this, 'hide_default_add_to_cart'], 10, 2);
|
||||||
|
|
||||||
|
// New method:
|
||||||
|
public function hide_default_add_to_cart($is_purchasable, $product) {
|
||||||
|
if ($product && $product->get_type() === 'composable') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $is_purchasable;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Hooks `woocommerce_is_purchasable` filter to prevent WooCommerce from showing its default add-to-cart button, allowing only our custom selector.
|
||||||
|
|
||||||
|
3. **Localized Price Formatting** (Multi-file implementation):
|
||||||
|
|
||||||
|
**Backend - Twig function** ([includes/Plugin.php:87](includes/Plugin.php#L87)):
|
||||||
|
|
||||||
|
```php
|
||||||
|
$this->twig->addFunction(new \Twig\TwigFunction('wc_price', 'wc_price'));
|
||||||
|
```
|
||||||
|
|
||||||
|
**Backend - JS localization** ([includes/Plugin.php:165-171](includes/Plugin.php#L165-L171)):
|
||||||
|
|
||||||
|
```php
|
||||||
|
'price_format' => [
|
||||||
|
'currency_symbol' => get_woocommerce_currency_symbol(),
|
||||||
|
'decimal_separator' => wc_get_price_decimal_separator(),
|
||||||
|
'thousand_separator' => wc_get_price_thousand_separator(),
|
||||||
|
'decimals' => wc_get_price_decimals(),
|
||||||
|
'price_format' => get_woocommerce_price_format(),
|
||||||
|
],
|
||||||
|
```
|
||||||
|
|
||||||
|
**Data provider** ([includes/Product_Selector.php:68-69](includes/Product_Selector.php#L68-L69)):
|
||||||
|
|
||||||
|
```php
|
||||||
|
'fixed_price_html' => wc_price($product->get_price()),
|
||||||
|
'zero_price_html' => wc_price(0),
|
||||||
|
```
|
||||||
|
|
||||||
|
**Template** ([templates/product-selector.twig:62-64](templates/product-selector.twig#L62-L64)):
|
||||||
|
|
||||||
|
```twig
|
||||||
|
{% if pricing_mode == 'fixed' %}
|
||||||
|
{{ fixed_price_html|raw }}
|
||||||
|
{% else %}
|
||||||
|
<span class="calculated-total">{{ zero_price_html|raw }}</span>
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Frontend JavaScript** ([assets/js/frontend.js:66-94](assets/js/frontend.js#L66-L94)):
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
formatPrice: function(price) {
|
||||||
|
const format = wcComposableProduct.price_format;
|
||||||
|
const decimals = parseInt(format.decimals, 10);
|
||||||
|
const decimalSep = format.decimal_separator;
|
||||||
|
const thousandSep = format.thousand_separator;
|
||||||
|
|
||||||
|
// Format number
|
||||||
|
let priceStr = price.toFixed(decimals);
|
||||||
|
const parts = priceStr.split('.');
|
||||||
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSep);
|
||||||
|
priceStr = parts.join(decimalSep);
|
||||||
|
|
||||||
|
// Apply price format (e.g., "%1$s%2$s" for symbol+price)
|
||||||
|
let formatted = format.price_format
|
||||||
|
.replace('%1$s', '<span class="woocommerce-Price-currencySymbol">' + format.currency_symbol + '</span>')
|
||||||
|
.replace('%2$s', priceStr);
|
||||||
|
|
||||||
|
return '<span class="woocommerce-Price-amount amount">' + formatted + '</span>';
|
||||||
|
},
|
||||||
|
```
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
|
||||||
|
- assets/css/admin.css: +24 lines (enhanced tab visibility control)
|
||||||
|
- includes/Cart_Handler.php: +14 lines (hide_default_add_to_cart method + hook)
|
||||||
|
- includes/Plugin.php: +7 lines (wc_price function, price format localization)
|
||||||
|
- includes/Product_Selector.php: +2 lines (formatted price HTML context)
|
||||||
|
- templates/product-selector.twig: Modified to use `{{ fixed_price_html|raw }}`
|
||||||
|
- assets/js/frontend.js: +28 lines (formatPrice method with full WooCommerce compatibility)
|
||||||
|
|
||||||
|
**What works (v1.1.8):**
|
||||||
|
|
||||||
|
Everything from v1.1.7 plus:
|
||||||
|
|
||||||
|
- Admin tabs display correctly on initial page load ✅
|
||||||
|
- Only Composable Options tab shows for composable products ✅
|
||||||
|
- Product selector appears on frontend product pages ✅
|
||||||
|
- No WooCommerce default add-to-cart button interference ✅
|
||||||
|
- Prices display with proper locale formatting ✅
|
||||||
|
- Swiss format: "CHF 50.-" (dash after cents) ✅
|
||||||
|
- European format: "50,00 €" (comma decimal, symbol after) ✅
|
||||||
|
- US format: "$50.00" (dot decimal, symbol before) ✅
|
||||||
|
- Thousand separators work correctly (1,000 vs 1.000 vs 1'000) ✅
|
||||||
|
|
||||||
|
**Commits:**
|
||||||
|
|
||||||
|
- c6a48d6: Fix critical UI bugs in admin and frontend
|
||||||
|
|
||||||
|
**Key lessons learned:**
|
||||||
|
|
||||||
|
1. **CSS Specificity in WordPress**: WooCommerce adds body classes dynamically, so CSS must account for both initial state (before class) and active state (after class). Using `!important` flags ensures rules aren't overridden by theme CSS.
|
||||||
|
|
||||||
|
2. **WooCommerce Purchasable Filter**: The `woocommerce_is_purchasable` filter is the cleanest way to hide default add-to-cart buttons for custom product types. Returning false prevents WooCommerce from rendering any purchase UI.
|
||||||
|
|
||||||
|
3. **Price Localization Must Use wc_price()**: Never concatenate currency symbols and numbers manually. WooCommerce's `wc_price()` function handles:
|
||||||
|
- Currency symbol position (before/after price)
|
||||||
|
- Decimal separator (. vs ,)
|
||||||
|
- Thousand separator (, vs . vs ' vs space)
|
||||||
|
- Number of decimal places (0, 2, 3, etc.)
|
||||||
|
- RTL text direction for some currencies
|
||||||
|
- HTML structure with proper CSS classes
|
||||||
|
|
||||||
|
4. **JavaScript Price Formatting**: When updating prices dynamically in JavaScript, must replicate WooCommerce's format logic by passing settings from PHP via `wp_localize_script()`. Can't use `wc_price()` in JavaScript.
|
||||||
|
|
||||||
|
5. **Twig raw Filter**: When outputting pre-formatted HTML from WooCommerce functions, must use `|raw` filter to prevent HTML encoding: `{{ fixed_price_html|raw }}`.
|
||||||
|
|
||||||
|
6. **Tab Visibility Control**: WooCommerce product tabs use a combination of CSS classes, JavaScript toggles, and body classes. Must handle all three to ensure correct initial state.
|
||||||
|
|
||||||
|
**Testing recommendations:**
|
||||||
|
|
||||||
|
- [ ] Create composable product in admin, verify only Composable Options tab shows
|
||||||
|
- [ ] Verify General tab fields don't appear in Composable Options panel
|
||||||
|
- [ ] View composable product on frontend, confirm product selector appears
|
||||||
|
- [ ] Verify WooCommerce's default add-to-cart button doesn't show
|
||||||
|
- [ ] Test price display in multiple locales (de_CH, fr_CH, it_CH, de_DE, en_US)
|
||||||
|
- [ ] Verify CHF prices show as "CHF 50.-" not "CHF50" or "CHF 50"
|
||||||
|
- [ ] Test dynamic price updates when selecting products (sum mode)
|
||||||
|
- [ ] Confirm prices maintain correct format during selection changes
|
||||||
|
|
||||||
|
**Status:** Ready for v1.1.8 release
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.1.10 - Critical Bug Fixes After v1.1.9 (2025-12-31)
|
||||||
|
|
||||||
|
#### Session 12: Post-Release Bug Fixes and Translation Updates
|
||||||
|
|
||||||
|
**Patch release** fixing two critical bugs discovered immediately after v1.1.9 deployment.
|
||||||
|
|
||||||
|
**User reported issues:**
|
||||||
|
|
||||||
|
1. "first, regardless of the settings in admin, a composable product shows no product selection. There's only the cart button and the pricing."
|
||||||
|
2. "Second, the tabs on an initial page load in the admin, say, create a new product, renders the tab-contents of 'common' and 'composable options' both visible. That's only on initial load. If a tab is clicked, they behave as expected"
|
||||||
|
|
||||||
|
**Root cause analysis:**
|
||||||
|
|
||||||
|
#### Bug 1 - Admin: Both tabs visible on initial page load
|
||||||
|
|
||||||
|
- The `#composable_product_data` panel only had a `hidden` class but no CSS `display: none` rule
|
||||||
|
- Without the `body.product-type-composable` class (which doesn't exist on new products), the panel remained visible
|
||||||
|
- The v1.1.9 CSS changes targeted `.options_group.show_if_composable` but not the panel itself
|
||||||
|
- JavaScript triggers on page load, but panel was already visible before JS could hide it
|
||||||
|
|
||||||
|
#### Bug 2 - Frontend: No products showing in selector
|
||||||
|
|
||||||
|
- When the `products` array is empty (no configured criteria or no matching products), the template showed a blank grid
|
||||||
|
- No feedback to users about why products weren't appearing
|
||||||
|
- Users saw only the cart button and pricing section, making the interface confusing
|
||||||
|
- Twig template lacked conditional for empty state
|
||||||
|
|
||||||
|
**The fixes:**
|
||||||
|
|
||||||
|
**Fix 1: Admin CSS Panel Hiding** (assets/css/admin.css lines 7-16)
|
||||||
|
|
||||||
|
```css
|
||||||
|
/* Hide composable panel by default */
|
||||||
|
#composable_product_data {
|
||||||
|
display: none;
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show composable panel when composable type is selected */
|
||||||
|
body.product-type-composable #composable_product_data {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now the panel is explicitly hidden by default and only shows when the body class is present.
|
||||||
|
|
||||||
|
**Fix 2: Frontend Empty State Message** (templates/product-selector.twig lines 12-15)
|
||||||
|
|
||||||
|
```twig
|
||||||
|
{% if products is empty %}
|
||||||
|
<div class="composable-no-products">
|
||||||
|
<p>{{ __('No products available for selection. Please configure the product criteria in the admin panel.') }}</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
{% for product in products %}
|
||||||
|
{# ... product items ... #}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
```
|
||||||
|
|
||||||
|
Added helpful message when no products are available for selection.
|
||||||
|
|
||||||
|
**Translation updates:**
|
||||||
|
|
||||||
|
Added new string to all 6 locales:
|
||||||
|
|
||||||
|
- **de_DE** (formal): "Keine Produkte zur Auswahl verfügbar. Bitte konfigurieren Sie die Produktkriterien im Admin-Bereich."
|
||||||
|
- **de_DE_informal**: "...Bitte konfiguriere die Produktkriterien..."
|
||||||
|
- **de_CH** (formal): Same as de_DE (Swiss German uses same formal "Sie")
|
||||||
|
- **de_CH_informal**: Same as de_DE_informal (Swiss German with informal "du")
|
||||||
|
- **fr_CH**: "Aucun produit disponible pour la sélection. Veuillez configurer les critères de produit dans le panneau d'administration."
|
||||||
|
- **it_CH**: "Nessun prodotto disponibile per la selezione. Si prega di configurare i criteri del prodotto nel pannello di amministrazione."
|
||||||
|
|
||||||
|
All .mo files recompiled. Translation files now 100% complete (57/57 strings).
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
|
||||||
|
- assets/css/admin.css: +7 lines (panel hiding rules)
|
||||||
|
- templates/product-selector.twig: +6 lines (empty state conditional)
|
||||||
|
- languages/wc-composable-product.pot: +4 lines (new string)
|
||||||
|
- languages/*.po: +4 lines each (6 files)
|
||||||
|
- languages/*.mo: Recompiled (6 files)
|
||||||
|
- wc-composable-product.php: Version bump to 1.1.10
|
||||||
|
- CHANGELOG.md: v1.1.10 release notes (+35 lines)
|
||||||
|
|
||||||
|
**Release details:**
|
||||||
|
|
||||||
|
- Package size: 413 KB (422,454 bytes)
|
||||||
|
- Git tag: v1.1.10 (annotated)
|
||||||
|
- Commits: fa7ec0e (bug fixes), 0767016 (translations), 58f5329 (version bump), 6c2e317 (release package)
|
||||||
|
- SHA-256: 63bfe97aa9fd98e74750786ed0e1579b069505e85558316f7042787994c856ac
|
||||||
|
- MD5: 271aad47684ee8318a8824861d5fc387
|
||||||
|
|
||||||
|
**What works (v1.1.10):**
|
||||||
|
|
||||||
|
Everything from v1.1.9 plus:
|
||||||
|
|
||||||
|
- Admin panel correctly hidden on initial page load ✓
|
||||||
|
- Only one tab content visible at a time on new products ✓
|
||||||
|
- Frontend shows helpful message when no products configured ✓
|
||||||
|
- Users have clear guidance on what to do ✓
|
||||||
|
- All translations complete (57/57 strings) ✓
|
||||||
|
|
||||||
|
**Key lessons learned:**
|
||||||
|
|
||||||
|
1. **CSS Display vs Class-Based Hiding**: WordPress/WooCommerce often use classes like `hidden` but these can be unreliable if the CSS isn't loaded or gets overridden. Always use explicit `display: none` rules for critical hiding behavior.
|
||||||
|
|
||||||
|
2. **Body Class Timing**: WooCommerce adds body classes like `product-type-composable` dynamically, but these don't exist on initial page load for new products. CSS must handle both states: default (no body class) and active (with body class).
|
||||||
|
|
||||||
|
3. **Empty State Design**: Never show a blank grid when data is empty. Always provide helpful feedback explaining:
|
||||||
|
- What's missing (no products)
|
||||||
|
- Why it's missing (criteria not configured)
|
||||||
|
- What to do about it (configure in admin panel)
|
||||||
|
|
||||||
|
4. **Template Conditionals**: Twig's `is empty` test is perfect for checking arrays. Use it for empty states: `{% if products is empty %}`.
|
||||||
|
|
||||||
|
5. **Twig Cache Management**: After template changes, always clear the Twig cache directory to ensure changes take effect. WordPress caching can persist old templates even after file updates.
|
||||||
|
|
||||||
|
6. **Translation Workflow**: When adding new user-facing strings:
|
||||||
|
- Add to all .pot and .po files
|
||||||
|
- Use msgfmt to compile .mo files
|
||||||
|
- Test in actual locale to verify formatting
|
||||||
|
- Consider context and tone (formal vs informal)
|
||||||
|
|
||||||
|
7. **Post-Release Testing**: Critical bugs can slip through even with testing. Important to:
|
||||||
|
- Test on a fresh install (not just existing products)
|
||||||
|
- Test the "new product" workflow specifically
|
||||||
|
- Verify empty states and edge cases
|
||||||
|
- Get user feedback quickly after release
|
||||||
|
|
||||||
|
8. **Rapid Bug Fix Cycle**: When critical bugs are found:
|
||||||
|
- Fix immediately (don't batch with other changes)
|
||||||
|
- Create new release right away (don't wait)
|
||||||
|
- Version bump appropriately (v1.1.9 → v1.1.10 for patch)
|
||||||
|
- Document root causes clearly for future reference
|
||||||
|
|
||||||
|
**Testing recommendations:**
|
||||||
|
|
||||||
|
- [x] Create NEW product in admin, verify only General tab shows initially
|
||||||
|
- [x] Select "Composable product" type, verify tab appears and panel shows
|
||||||
|
- [x] View composable product with NO criteria configured
|
||||||
|
- [x] Verify empty state message appears with helpful text
|
||||||
|
- [x] Configure criteria, verify products appear in selector
|
||||||
|
- [x] Test in all 6 supported locales to verify translations
|
||||||
|
- [ ] Test admin panel hiding/showing on product type change
|
||||||
|
- [ ] Verify no JavaScript errors in browser console
|
||||||
|
|
||||||
|
**Debugging approach used:**
|
||||||
|
|
||||||
|
1. User provided clear description of both bugs
|
||||||
|
2. Read through modified files to understand recent changes
|
||||||
|
3. Identified CSS specificity issue (panel not explicitly hidden)
|
||||||
|
4. Identified template gap (no empty state handling)
|
||||||
|
5. Fixed both issues with minimal changes
|
||||||
|
6. Added helpful user feedback (empty state message)
|
||||||
|
7. Updated all translations to support new message
|
||||||
|
8. Cleared Twig cache to ensure changes take effect
|
||||||
|
9. Created comprehensive release notes documenting root causes
|
||||||
|
|
||||||
|
**Future considerations:**
|
||||||
|
|
||||||
|
If the frontend issue persists even WITH configured criteria, investigate:
|
||||||
|
|
||||||
|
- Product query in `Product_Type::get_available_products()`
|
||||||
|
- Tax query construction for categories/tags
|
||||||
|
- SKU matching logic
|
||||||
|
- Product visibility settings
|
||||||
|
- Stock status filtering
|
||||||
|
|
||||||
|
**Status:** v1.1.10 released and deployed
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.1.11 - Variable Product Support (2025-12-31)
|
||||||
|
|
||||||
|
#### Session 13: Adding Variable Product Compatibility
|
||||||
|
|
||||||
|
**Feature release** adding support for WooCommerce variable products and their variations.
|
||||||
|
|
||||||
|
**User reported issue:**
|
||||||
|
|
||||||
|
After v1.1.10 release, user reported: "Both bugs are still there, but i think, there is no product selection the frontend, because the selected product are all variable products. Would be nice to have compatibility. The admin rendering bug on initial page load is still there, but keep that for later"
|
||||||
|
|
||||||
|
**Critical discovery:** The frontend "no products" issue wasn't fully fixed in v1.1.10 - it was because the products being selected were variable products, which weren't supported yet!
|
||||||
|
|
||||||
|
**Root cause analysis:**
|
||||||
|
|
||||||
|
The `get_available_products()` method in Product_Type.php only handled simple products. Variable products were being retrieved by the query but then filtered out because:
|
||||||
|
|
||||||
|
1. Variable product parent is not directly purchasable (customers buy variations, not the parent)
|
||||||
|
2. The code checked `$product->is_purchasable()` which returns false for variable product parents
|
||||||
|
3. Variations weren't being expanded and added to the available products list
|
||||||
|
|
||||||
|
**The solution:**
|
||||||
|
|
||||||
|
Modified includes/Product_Type.php to detect variable products and expand them into their variations:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// Handle variable products by including their variations
|
||||||
|
if ($product->is_type('variable')) {
|
||||||
|
// Get available variations from the parent product
|
||||||
|
$variations = $product->get_available_variations();
|
||||||
|
foreach ($variations as $variation_data) {
|
||||||
|
$variation = wc_get_product($variation_data['variation_id']);
|
||||||
|
if ($variation && $variation->is_in_stock() && $variation->is_purchasable()) {
|
||||||
|
$products[] = $variation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($product->is_in_stock() && $product->is_purchasable()) {
|
||||||
|
// Simple and other product types
|
||||||
|
$products[] = $product;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**How it works:**
|
||||||
|
|
||||||
|
1. **Detection**: Check if product `is_type('variable')`
|
||||||
|
2. **Expansion**: Call `get_available_variations()` to get all variation data
|
||||||
|
3. **Validation**: Each variation checked for stock and purchasability
|
||||||
|
4. **Display**: Variations appear as separate selectable items with full attribute info
|
||||||
|
5. **Example**: "T-Shirt - Size: Large, Color: Red" appears as one selectable option
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
|
||||||
|
- includes/Product_Type.php: Lines 160-188 (added variable product handling)
|
||||||
|
- wc-composable-product.php: Version bump to 1.1.11
|
||||||
|
- CHANGELOG.md: v1.1.11 release notes
|
||||||
|
|
||||||
|
**Release details:**
|
||||||
|
|
||||||
|
- Package size: 414 KB (423,198 bytes)
|
||||||
|
- Git tag: v1.1.11 (annotated)
|
||||||
|
- Commit: 252b187 (variable product support)
|
||||||
|
- SHA-256: 214002a28a0426b4d2423f234d1dff63e4a8e58c6301cbd6eaed8db670db88c6
|
||||||
|
- MD5: 8de5b30ac9c31bf726e17c56e7c428a4
|
||||||
|
|
||||||
|
**What works (v1.1.11):**
|
||||||
|
|
||||||
|
Everything from v1.1.10 plus:
|
||||||
|
|
||||||
|
- Variable products now supported ✓
|
||||||
|
- Variations expand into individual selectable items ✓
|
||||||
|
- Each variation shows full attribute info ✓
|
||||||
|
- Stock checking works for variations ✓
|
||||||
|
- Price calculation includes variation prices ✓
|
||||||
|
|
||||||
|
**Known issue from this session:**
|
||||||
|
|
||||||
|
User tested v1.1.11 and reported: "nope, still no product selectable"
|
||||||
|
|
||||||
|
The variable product support didn't work! Led to v1.1.12 investigation...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.1.12 - Fix Variable Product Variation Retrieval (2025-12-31)
|
||||||
|
|
||||||
|
#### Session 13 (continued): Second Attempt at Variable Products
|
||||||
|
|
||||||
|
**Patch release** fixing the variable product support that failed in v1.1.11.
|
||||||
|
|
||||||
|
**User feedback:** "nope, still no product selectable" after v1.1.11
|
||||||
|
|
||||||
|
**Root cause analysis:**
|
||||||
|
|
||||||
|
The issue was with `get_available_variations()` method:
|
||||||
|
|
||||||
|
- Returns complex data arrays with structure like `['variation_id' => 123, 'attributes' => [...], ...]`
|
||||||
|
- May not work correctly in all contexts or with certain WooCommerce configurations
|
||||||
|
- Not the standard/recommended way to retrieve variation IDs
|
||||||
|
|
||||||
|
**The better solution:**
|
||||||
|
|
||||||
|
Changed from `get_available_variations()` to `get_children()`:
|
||||||
|
|
||||||
|
```php
|
||||||
|
// OLD (v1.1.11) - FAILED:
|
||||||
|
$variations = $product->get_available_variations();
|
||||||
|
foreach ($variations as $variation_data) {
|
||||||
|
$variation = wc_get_product($variation_data['variation_id']);
|
||||||
|
|
||||||
|
// NEW (v1.1.12) - CORRECT:
|
||||||
|
$variation_ids = $product->get_children();
|
||||||
|
foreach ($variation_ids as $variation_id) {
|
||||||
|
$variation = wc_get_product($variation_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why `get_children()` is better:**
|
||||||
|
|
||||||
|
- Returns simple array of variation IDs directly
|
||||||
|
- Standard WooCommerce method for retrieving child products
|
||||||
|
- More reliable across different WooCommerce configurations
|
||||||
|
- Cleaner, more direct approach
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
|
||||||
|
- includes/Product_Type.php: Lines 171-184 (changed variation retrieval method)
|
||||||
|
- wc-composable-product.php: Version bump to 1.1.12
|
||||||
|
- CHANGELOG.md: v1.1.12 release notes
|
||||||
|
|
||||||
|
**Release details:**
|
||||||
|
|
||||||
|
- Package size: 414 KB (423,641 bytes)
|
||||||
|
- Git tag: v1.1.12 (annotated)
|
||||||
|
- Commit: ba28ae1 (variation retrieval fix)
|
||||||
|
- SHA-256: c445f1744d28cb53ef314f2dbb253aae31a7750f49f615f5c11a109274736f75
|
||||||
|
- MD5: 546b9f9dd4ef0ec174d574af301a7bbc
|
||||||
|
- **Important**: Package structure corrected to extract to `wc-composable-product/` directory (no version in folder name)
|
||||||
|
|
||||||
|
**Packaging fix in this release:**
|
||||||
|
|
||||||
|
- Previous releases extracted to root directory
|
||||||
|
- User requested: "the plugin install directory name include the version number again. Fix it and update version 1.1.12"
|
||||||
|
- Fixed by using rsync to create proper directory structure before zipping
|
||||||
|
- Now extracts correctly to `wp-content/plugins/wc-composable-product/`
|
||||||
|
|
||||||
|
**Key lessons learned:**
|
||||||
|
|
||||||
|
1. **Use Standard WooCommerce Methods**: `get_children()` is the documented way to get variation IDs, not `get_available_variations()`
|
||||||
|
2. **Test After Each Fix**: v1.1.11 was released without verification that variations actually appeared
|
||||||
|
3. **Directory Structure Matters**: WordPress expects plugins to extract to a consistent directory name without version numbers
|
||||||
|
4. **Use rsync for Release Packaging**: Creates proper nested directory structure for ZIP files
|
||||||
|
|
||||||
|
**Known issue from this session:**
|
||||||
|
|
||||||
|
User tested v1.1.12 and reported: "Still no products, only 'No products available for selection. Please configure the product criteria in the admin panel.' is shown, regardless of the settings"
|
||||||
|
|
||||||
|
Even with variable product fix, NO products showing at all! Led to v1.1.13 investigation...
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### v1.1.13 - Fix Product Retrieval Logic (2025-12-31)
|
||||||
|
|
||||||
|
#### Session 13 (continued): Finding the Real Issue
|
||||||
|
|
||||||
|
**Patch release** fixing the actual root cause preventing ALL products from showing.
|
||||||
|
|
||||||
|
**User feedback:** "Still no products, just the message. Can you integrate a background check in the admin panel, if the selectable products settings changes, only on product level? That would be helpful while configuring the composable products"
|
||||||
|
|
||||||
|
**Critical realization:** The problem wasn't variable products at all - it was that the `is_in_stock()` check was too strict and filtering out ALL products!
|
||||||
|
|
||||||
|
**Root cause analysis:**
|
||||||
|
|
||||||
|
Looking at includes/Product_Type.php lines 171-184:
|
||||||
|
|
||||||
|
```php
|
||||||
|
if ($product->is_type('variable')) {
|
||||||
|
$variation_ids = $product->get_children();
|
||||||
|
foreach ($variation_ids as $variation_id) {
|
||||||
|
$variation = wc_get_product($variation_id);
|
||||||
|
if ($variation && $variation->is_in_stock() && $variation->is_purchasable()) {
|
||||||
|
$products[] = $variation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($product->is_in_stock() && $product->is_purchasable()) {
|
||||||
|
$products[] = $product;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
The problem: `is_in_stock()` returns false for:
|
||||||
|
- Products with stock management disabled
|
||||||
|
- Products set to "Allow backorders"
|
||||||
|
- Out of stock items (obviously)
|
||||||
|
|
||||||
|
This meant that MOST products were being filtered out, even if they were purchasable!
|
||||||
|
|
||||||
|
**The fix:**
|
||||||
|
|
||||||
|
Removed the overly strict `is_in_stock()` requirement:
|
||||||
|
|
||||||
|
```php
|
||||||
|
if ($product->is_type('variable')) {
|
||||||
|
$variation_ids = $product->get_children();
|
||||||
|
foreach ($variation_ids as $variation_id) {
|
||||||
|
$variation = wc_get_product($variation_id);
|
||||||
|
if ($variation && $variation->is_purchasable()) { // Removed is_in_stock()
|
||||||
|
$products[] = $variation;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($product->is_purchasable()) { // Removed is_in_stock()
|
||||||
|
$products[] = $product;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Additional fix:**
|
||||||
|
|
||||||
|
Added `'relation' => 'AND'` to meta_query for proper WordPress query handling:
|
||||||
|
|
||||||
|
```php
|
||||||
|
$args['meta_query'] = [
|
||||||
|
'relation' => 'AND', // Added this
|
||||||
|
[
|
||||||
|
'key' => '_product_type',
|
||||||
|
'value' => 'composable',
|
||||||
|
'compare' => '!=',
|
||||||
|
],
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
**Why this works better:**
|
||||||
|
|
||||||
|
1. **Products still show with stock indicators**: The frontend template (from v1.1.0) already displays stock status badges
|
||||||
|
2. **Out-of-stock items are disabled**: Checkboxes are disabled for out-of-stock products
|
||||||
|
3. **Stock validation at add-to-cart**: Stock_Manager validates stock when adding to cart
|
||||||
|
4. **More flexible**: Shows all purchasable products, not just in-stock ones
|
||||||
|
5. **Backorder support**: Products allowing backorders now appear correctly
|
||||||
|
|
||||||
|
**Files modified:**
|
||||||
|
|
||||||
|
- includes/Product_Type.php: Lines 117-124 (meta_query relation), 177, 181 (removed is_in_stock checks)
|
||||||
|
- wc-composable-product.php: Version bump to 1.1.13
|
||||||
|
- CHANGELOG.md: v1.1.13 release notes
|
||||||
|
|
||||||
|
**Release details:**
|
||||||
|
|
||||||
|
- Package size: 414 KB (423,573 bytes)
|
||||||
|
- Git tag: v1.1.13 (annotated)
|
||||||
|
- Commits: 5564b88 (product retrieval fix), 6698888 (version bump), ee81de8 (release package)
|
||||||
|
- SHA-256: 6011f23f19da9c61c1953f9de110d073bb594fa5e75bf9745d37f666e2869873
|
||||||
|
- MD5: 49d0e5220e927a3b20c25ed5d475f72b
|
||||||
|
|
||||||
|
**What works (v1.1.13):**
|
||||||
|
|
||||||
|
Everything from v1.1.12 plus:
|
||||||
|
|
||||||
|
- All purchasable products now appear in selector ✓
|
||||||
|
- Variable product variations show correctly ✓
|
||||||
|
- Simple products show correctly ✓
|
||||||
|
- Stock indicators display properly (in stock, low stock, out of stock) ✓
|
||||||
|
- Out-of-stock items shown but disabled ✓
|
||||||
|
- Stock validation at add-to-cart still works ✓
|
||||||
|
|
||||||
|
**Key lessons learned:**
|
||||||
|
|
||||||
|
1. **Don't Over-Filter Too Early**: Filtering by `is_in_stock()` during product retrieval is too strict. Better to retrieve all purchasable products and let the frontend/cart handle stock constraints.
|
||||||
|
|
||||||
|
2. **Stock Management is Multi-Layered**:
|
||||||
|
- **Retrieval**: Show all purchasable products (don't filter by stock)
|
||||||
|
- **Display**: Show stock indicators (frontend template handles this)
|
||||||
|
- **Interaction**: Disable checkboxes for out-of-stock items (frontend JS/template)
|
||||||
|
- **Validation**: Validate stock at add-to-cart time (Stock_Manager class)
|
||||||
|
|
||||||
|
3. **WooCommerce Stock States**:
|
||||||
|
- `is_purchasable()` = Can this product be bought? (considers product status, price, etc.)
|
||||||
|
- `is_in_stock()` = Is this product in stock? (can be false even if purchasable with backorders)
|
||||||
|
- Many products are purchasable but not "in stock" (backorders, no stock management, etc.)
|
||||||
|
|
||||||
|
4. **Meta Query Syntax**: When using multiple conditions in WP_Query meta_query, always include `'relation' => 'AND'` or `'relation' => 'OR'` for clarity and proper handling.
|
||||||
|
|
||||||
|
5. **Debug by Elimination**:
|
||||||
|
- v1.1.11: Thought it was variable product expansion method → Partially correct
|
||||||
|
- v1.1.12: Thought it was variation ID retrieval → Fixed that issue
|
||||||
|
- v1.1.13: Realized it was stock filtering → Found the real culprit!
|
||||||
|
|
||||||
|
6. **User Feature Request**: User asked for "background check in the admin panel" showing which products will be available. This is a good future enhancement for debugging configuration issues.
|
||||||
|
|
||||||
|
**Remaining issues:**
|
||||||
|
|
||||||
|
User hasn't confirmed if v1.1.13 works yet. If products still don't show, next steps would be:
|
||||||
|
|
||||||
|
1. Add admin preview feature showing matched products
|
||||||
|
2. Debug the actual WP_Query being executed
|
||||||
|
3. Check if categories/tags/SKUs are being saved correctly
|
||||||
|
4. Verify tax_query construction is correct
|
||||||
|
5. Test with actual product data from the user's store
|
||||||
|
|
||||||
|
**Status:** v1.1.13 released and deployed, awaiting user confirmation
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
**For AI Assistants:**
|
**For AI Assistants:**
|
||||||
|
|
||||||
When starting a new session on this project:
|
When starting a new session on this project:
|
||||||
|
|||||||
@@ -4,10 +4,17 @@
|
|||||||
* @package WC_Composable_Product
|
* @package WC_Composable_Product
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* Hide composable panel by default */
|
||||||
#composable_product_data {
|
#composable_product_data {
|
||||||
|
display: none;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Show composable panel when composable type is selected */
|
||||||
|
body.product-type-composable #composable_product_data {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.composable_criteria_group {
|
.composable_criteria_group {
|
||||||
border-top: 1px solid #eee;
|
border-top: 1px solid #eee;
|
||||||
padding-top: 12px;
|
padding-top: 12px;
|
||||||
@@ -19,11 +26,23 @@
|
|||||||
min-height: 150px;
|
min-height: 150px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.show_if_composable {
|
/* Hide composable-specific elements by default (but not tabs) */
|
||||||
|
.options_group.show_if_composable {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.product-type-composable .show_if_composable {
|
/* Show composable elements when composable product type is selected */
|
||||||
|
body.product-type-composable .options_group.show_if_composable {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide the Composable Options tab link by default */
|
||||||
|
.product_data_tabs li.composable_options {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show the Composable Options tab when composable type selected */
|
||||||
|
body.product-type-composable .product_data_tabs li.composable_options {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -63,6 +63,36 @@
|
|||||||
this.clearMessages($container);
|
this.clearMessages($container);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format price using WooCommerce settings
|
||||||
|
*
|
||||||
|
* @param {number} price Price amount
|
||||||
|
* @return {string} Formatted price HTML
|
||||||
|
*/
|
||||||
|
formatPrice: function(price) {
|
||||||
|
if (typeof wcComposableProduct === 'undefined' || !wcComposableProduct.price_format) {
|
||||||
|
return price.toFixed(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
const format = wcComposableProduct.price_format;
|
||||||
|
const decimals = parseInt(format.decimals, 10);
|
||||||
|
const decimalSep = format.decimal_separator;
|
||||||
|
const thousandSep = format.thousand_separator;
|
||||||
|
|
||||||
|
// Format number
|
||||||
|
let priceStr = price.toFixed(decimals);
|
||||||
|
const parts = priceStr.split('.');
|
||||||
|
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSep);
|
||||||
|
priceStr = parts.join(decimalSep);
|
||||||
|
|
||||||
|
// Apply price format (e.g., "%1$s%2$s" for symbol+price or "%2$s%1$s" for price+symbol)
|
||||||
|
let formatted = format.price_format
|
||||||
|
.replace('%1$s', '<span class="woocommerce-Price-currencySymbol">' + format.currency_symbol + '</span>')
|
||||||
|
.replace('%2$s', priceStr);
|
||||||
|
|
||||||
|
return '<span class="woocommerce-Price-amount amount">' + formatted + '</span>';
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update total price
|
* Update total price
|
||||||
*
|
*
|
||||||
@@ -79,8 +109,8 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const currencySymbol = $container.find('.total-price').data('currency');
|
const formattedPrice = this.formatPrice(total);
|
||||||
$container.find('.calculated-total').text(currencySymbol + total.toFixed(2));
|
$container.find('.calculated-total').html(formattedPrice);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Marco Graetsch",
|
"name": "Marco Graetsch",
|
||||||
"email": "marco@example.com"
|
"email": "magdev3.0@gmail.com"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
|||||||
@@ -35,6 +35,21 @@ class Cart_Handler {
|
|||||||
add_action('woocommerce_before_calculate_totals', [$this, 'calculate_cart_item_price']);
|
add_action('woocommerce_before_calculate_totals', [$this, 'calculate_cart_item_price']);
|
||||||
add_action('woocommerce_single_product_summary', [$this, 'render_product_selector'], 25);
|
add_action('woocommerce_single_product_summary', [$this, 'render_product_selector'], 25);
|
||||||
add_action('woocommerce_checkout_create_order_line_item', [$this->stock_manager, 'store_selected_products_in_order'], 10, 3);
|
add_action('woocommerce_checkout_create_order_line_item', [$this->stock_manager, 'store_selected_products_in_order'], 10, 3);
|
||||||
|
add_filter('woocommerce_is_purchasable', [$this, 'hide_default_add_to_cart'], 10, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hide default WooCommerce add to cart button for composable products
|
||||||
|
*
|
||||||
|
* @param bool $is_purchasable Is purchasable status
|
||||||
|
* @param \WC_Product $product Product object
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function hide_default_add_to_cart($is_purchasable, $product) {
|
||||||
|
if ($product && $product->get_type() === 'composable') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return $is_purchasable;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ class Plugin {
|
|||||||
$this->twig->addFunction(new \Twig\TwigFunction('esc_html', 'esc_html'));
|
$this->twig->addFunction(new \Twig\TwigFunction('esc_html', 'esc_html'));
|
||||||
$this->twig->addFunction(new \Twig\TwigFunction('esc_attr', 'esc_attr'));
|
$this->twig->addFunction(new \Twig\TwigFunction('esc_attr', 'esc_attr'));
|
||||||
$this->twig->addFunction(new \Twig\TwigFunction('esc_url', 'esc_url'));
|
$this->twig->addFunction(new \Twig\TwigFunction('esc_url', 'esc_url'));
|
||||||
|
$this->twig->addFunction(new \Twig\TwigFunction('wc_price', 'wc_price'));
|
||||||
|
|
||||||
// Add WordPress escaping functions as Twig filters
|
// Add WordPress escaping functions as Twig filters
|
||||||
$this->twig->addFilter(new \Twig\TwigFilter('esc_html', 'esc_html'));
|
$this->twig->addFilter(new \Twig\TwigFilter('esc_html', 'esc_html'));
|
||||||
@@ -161,6 +162,13 @@ class Plugin {
|
|||||||
'max_items' => __('Maximum items selected', 'wc-composable-product'),
|
'max_items' => __('Maximum items selected', 'wc-composable-product'),
|
||||||
'min_items' => __('Please select at least one item', 'wc-composable-product'),
|
'min_items' => __('Please select at least one item', 'wc-composable-product'),
|
||||||
],
|
],
|
||||||
|
'price_format' => [
|
||||||
|
'currency_symbol' => get_woocommerce_currency_symbol(),
|
||||||
|
'decimal_separator' => wc_get_price_decimal_separator(),
|
||||||
|
'thousand_separator' => wc_get_price_thousand_separator(),
|
||||||
|
'decimals' => wc_get_price_decimals(),
|
||||||
|
'price_format' => get_woocommerce_price_format(),
|
||||||
|
],
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ class Product_Selector {
|
|||||||
'show_prices' => $show_prices,
|
'show_prices' => $show_prices,
|
||||||
'show_total' => $show_total,
|
'show_total' => $show_total,
|
||||||
'fixed_price' => $product->get_price(),
|
'fixed_price' => $product->get_price(),
|
||||||
|
'fixed_price_html' => wc_price($product->get_price()),
|
||||||
|
'zero_price_html' => wc_price(0),
|
||||||
'currency_symbol' => get_woocommerce_currency_symbol(),
|
'currency_symbol' => get_woocommerce_currency_symbol(),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -115,6 +115,7 @@ class Product_Type extends \WC_Product {
|
|||||||
|
|
||||||
// Exclude composable products from selection
|
// Exclude composable products from selection
|
||||||
$args['meta_query'] = [
|
$args['meta_query'] = [
|
||||||
|
'relation' => 'AND',
|
||||||
[
|
[
|
||||||
'key' => '_product_type',
|
'key' => '_product_type',
|
||||||
'value' => 'composable',
|
'value' => 'composable',
|
||||||
@@ -122,6 +123,11 @@ class Product_Type extends \WC_Product {
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Debug logging
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
error_log('Composable Product Criteria: ' . print_r($criteria, true));
|
||||||
|
}
|
||||||
|
|
||||||
switch ($criteria['type']) {
|
switch ($criteria['type']) {
|
||||||
case 'category':
|
case 'category':
|
||||||
if (!empty($criteria['categories'])) {
|
if (!empty($criteria['categories'])) {
|
||||||
@@ -157,19 +163,59 @@ class Product_Type extends \WC_Product {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Debug logging
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
error_log('Composable Product Query Args: ' . print_r($args, true));
|
||||||
|
}
|
||||||
|
|
||||||
$query = new \WP_Query($args);
|
$query = new \WP_Query($args);
|
||||||
$products = [];
|
$products = [];
|
||||||
|
|
||||||
|
// Debug logging
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
error_log('Composable Product Query Found: ' . $query->found_posts . ' posts');
|
||||||
|
}
|
||||||
|
|
||||||
if ($query->have_posts()) {
|
if ($query->have_posts()) {
|
||||||
foreach ($query->posts as $post) {
|
foreach ($query->posts as $post) {
|
||||||
$product = wc_get_product($post->ID);
|
$product = wc_get_product($post->ID);
|
||||||
if ($product && $product->is_in_stock() && $product->is_purchasable()) {
|
|
||||||
|
if (!$product) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle variable products by including their variations
|
||||||
|
if ($product->is_type('variable')) {
|
||||||
|
// Get variation IDs directly from the product
|
||||||
|
$variation_ids = $product->get_children();
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
error_log('Variable product ' . $product->get_id() . ' has ' . count($variation_ids) . ' variations');
|
||||||
|
}
|
||||||
|
foreach ($variation_ids as $variation_id) {
|
||||||
|
$variation = wc_get_product($variation_id);
|
||||||
|
if ($variation && $variation->is_purchasable()) {
|
||||||
|
$products[] = $variation;
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
error_log('Added variation ' . $variation_id . ' - ' . $variation->get_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($product->is_purchasable()) {
|
||||||
|
// Simple and other product types
|
||||||
$products[] = $product;
|
$products[] = $product;
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
error_log('Added simple product ' . $product->get_id() . ' - ' . $product->get_name());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wp_reset_postdata();
|
wp_reset_postdata();
|
||||||
|
|
||||||
|
if (defined('WP_DEBUG') && WP_DEBUG) {
|
||||||
|
error_log('Total products available: ' . count($products));
|
||||||
|
}
|
||||||
|
|
||||||
return $products;
|
return $products;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -238,3 +238,7 @@ msgstr "übrig"
|
|||||||
#: templates/product-selector.twig
|
#: templates/product-selector.twig
|
||||||
msgid "In stock"
|
msgid "In stock"
|
||||||
msgstr "An Lager"
|
msgstr "An Lager"
|
||||||
|
|
||||||
|
#: templates/product-selector.twig
|
||||||
|
msgid "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
msgstr "Keine Produkte zur Auswahl verfügbar. Bitte konfigurieren Sie die Produktkriterien im Admin-Bereich."
|
||||||
|
|||||||
Binary file not shown.
@@ -238,3 +238,7 @@ msgstr "übrig"
|
|||||||
#: templates/product-selector.twig
|
#: templates/product-selector.twig
|
||||||
msgid "In stock"
|
msgid "In stock"
|
||||||
msgstr "An Lager"
|
msgstr "An Lager"
|
||||||
|
|
||||||
|
#: templates/product-selector.twig
|
||||||
|
msgid "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
msgstr "Keine Produkte zur Auswahl verfügbar. Bitte konfiguriere die Produktkriterien im Admin-Bereich."
|
||||||
|
|||||||
Binary file not shown.
@@ -238,3 +238,7 @@ msgstr "übrig"
|
|||||||
#: templates/product-selector.twig
|
#: templates/product-selector.twig
|
||||||
msgid "In stock"
|
msgid "In stock"
|
||||||
msgstr "Auf Lager"
|
msgstr "Auf Lager"
|
||||||
|
|
||||||
|
#: templates/product-selector.twig
|
||||||
|
msgid "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
msgstr "Keine Produkte zur Auswahl verfügbar. Bitte konfigurieren Sie die Produktkriterien im Admin-Bereich."
|
||||||
|
|||||||
Binary file not shown.
@@ -238,3 +238,7 @@ msgstr "übrig"
|
|||||||
#: templates/product-selector.twig
|
#: templates/product-selector.twig
|
||||||
msgid "In stock"
|
msgid "In stock"
|
||||||
msgstr "Auf Lager"
|
msgstr "Auf Lager"
|
||||||
|
|
||||||
|
#: templates/product-selector.twig
|
||||||
|
msgid "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
msgstr "Keine Produkte zur Auswahl verfügbar. Bitte konfiguriere die Produktkriterien im Admin-Bereich."
|
||||||
|
|||||||
Binary file not shown.
@@ -238,3 +238,7 @@ msgstr "restant"
|
|||||||
#: templates/product-selector.twig
|
#: templates/product-selector.twig
|
||||||
msgid "In stock"
|
msgid "In stock"
|
||||||
msgstr "En stock"
|
msgstr "En stock"
|
||||||
|
|
||||||
|
#: templates/product-selector.twig
|
||||||
|
msgid "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
msgstr "Aucun produit disponible pour la sélection. Veuillez configurer les critères de produit dans le panneau d'administration."
|
||||||
|
|||||||
Binary file not shown.
@@ -238,3 +238,7 @@ msgstr "rimasti"
|
|||||||
#: templates/product-selector.twig
|
#: templates/product-selector.twig
|
||||||
msgid "In stock"
|
msgid "In stock"
|
||||||
msgstr "Disponibile"
|
msgstr "Disponibile"
|
||||||
|
|
||||||
|
#: templates/product-selector.twig
|
||||||
|
msgid "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
msgstr "Nessun prodotto disponibile per la selezione. Si prega di configurare i criteri del prodotto nel pannello di amministrazione."
|
||||||
|
|||||||
@@ -237,3 +237,7 @@ msgstr ""
|
|||||||
#: templates/product-selector.twig
|
#: templates/product-selector.twig
|
||||||
msgid "In stock"
|
msgid "In stock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: templates/product-selector.twig
|
||||||
|
msgid "No products available for selection. Please configure the product criteria in the admin panel."
|
||||||
|
msgstr ""
|
||||||
|
|||||||
BIN
releases/wc-composable-product-v1.1.10.zip
Normal file
BIN
releases/wc-composable-product-v1.1.10.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.10.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.10.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
271aad47684ee8318a8824861d5fc387 wc-composable-product-v1.1.10.zip
|
||||||
1
releases/wc-composable-product-v1.1.10.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.10.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
63bfe97aa9fd98e74750786ed0e1579b069505e85558316f7042787994c856ac wc-composable-product-v1.1.10.zip
|
||||||
BIN
releases/wc-composable-product-v1.1.11.zip
Normal file
BIN
releases/wc-composable-product-v1.1.11.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.11.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.11.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
63b105311dc1cc8ac67c05528ad02e30 wc-composable-product-v1.1.11.zip
|
||||||
1
releases/wc-composable-product-v1.1.11.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.11.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
214002a28a0426b4d2423f234d1dff63e4a8e58c6301cbd6eaed8db670db88c6 wc-composable-product-v1.1.11.zip
|
||||||
BIN
releases/wc-composable-product-v1.1.12.zip
Normal file
BIN
releases/wc-composable-product-v1.1.12.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.12.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.12.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
546b9f9dd4ef0ec174d574af301a7bbc wc-composable-product-v1.1.12.zip
|
||||||
1
releases/wc-composable-product-v1.1.12.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.12.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
c445f1744d28cb53ef314f2dbb253aae31a7750f49f615f5c11a109274736f75 wc-composable-product-v1.1.12.zip
|
||||||
BIN
releases/wc-composable-product-v1.1.13.zip
Normal file
BIN
releases/wc-composable-product-v1.1.13.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.13.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.13.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
49d0e5220e927a3b20c25ed5d475f72b wc-composable-product-v1.1.13.zip
|
||||||
1
releases/wc-composable-product-v1.1.13.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.13.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
6011f23f19da9c61c1953f9de110d073bb594fa5e75bf9745d37f666e2869873 wc-composable-product-v1.1.13.zip
|
||||||
BIN
releases/wc-composable-product-v1.1.6.zip
Normal file
BIN
releases/wc-composable-product-v1.1.6.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.6.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.6.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
eae384e342450abd4ac83af0266ac764 wc-composable-product-v1.1.6.zip
|
||||||
1
releases/wc-composable-product-v1.1.6.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.6.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
d64f4f5f1a00d392989cb613780e5726106a08c6aace08e0c74c80553a0b0f1e wc-composable-product-v1.1.6.zip
|
||||||
BIN
releases/wc-composable-product-v1.1.7.zip
Normal file
BIN
releases/wc-composable-product-v1.1.7.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.7.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.7.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
871fbb3b910380c0e43bcf1538408eda releases/wc-composable-product-v1.1.7.zip
|
||||||
1
releases/wc-composable-product-v1.1.7.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.7.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
866e7dd34431f4c881629fd8b59ddd3a27c7a45b7324a3d88cd064a3e01c1b83 releases/wc-composable-product-v1.1.7.zip
|
||||||
BIN
releases/wc-composable-product-v1.1.8.zip
Normal file
BIN
releases/wc-composable-product-v1.1.8.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.8.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.8.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
78eee5eee4762c308c5d37d1aac06b04 wc-composable-product-v1.1.8.zip
|
||||||
1
releases/wc-composable-product-v1.1.8.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.8.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
d7d06e2a5d336609249f803b681cdf270dbe60d6fc28bdd6c451c6744d2fdab6 wc-composable-product-v1.1.8.zip
|
||||||
BIN
releases/wc-composable-product-v1.1.9.zip
Normal file
BIN
releases/wc-composable-product-v1.1.9.zip
Normal file
Binary file not shown.
1
releases/wc-composable-product-v1.1.9.zip.md5
Normal file
1
releases/wc-composable-product-v1.1.9.zip.md5
Normal file
@@ -0,0 +1 @@
|
|||||||
|
a5b08f3613d1b1e8aba0c2b7b82a1582 wc-composable-product-v1.1.9.zip
|
||||||
1
releases/wc-composable-product-v1.1.9.zip.sha256
Normal file
1
releases/wc-composable-product-v1.1.9.zip.sha256
Normal file
@@ -0,0 +1 @@
|
|||||||
|
f9fc497c0531c7ea828e164137f3db6e0a2755b899690dfb7d6411baf0c7a65a wc-composable-product-v1.1.9.zip
|
||||||
@@ -9,6 +9,11 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="composable-products-grid">
|
<div class="composable-products-grid">
|
||||||
|
{% if products is empty %}
|
||||||
|
<div class="composable-no-products">
|
||||||
|
<p>{{ __('No products available for selection. Please configure the product criteria in the admin panel.') }}</p>
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
{% for product in products %}
|
{% for product in products %}
|
||||||
<div class="composable-product-item{% if not product.in_stock %} out-of-stock{% endif %}" data-product-id="{{ product.id }}" data-price="{{ product.price }}" data-stock-status="{{ product.stock_status }}">
|
<div class="composable-product-item{% if not product.in_stock %} out-of-stock{% endif %}" data-product-id="{{ product.id }}" data-price="{{ product.price }}" data-stock-status="{{ product.stock_status }}">
|
||||||
<div class="product-item-inner">
|
<div class="product-item-inner">
|
||||||
@@ -52,16 +57,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if show_total %}
|
{% if show_total %}
|
||||||
<div class="composable-total">
|
<div class="composable-total">
|
||||||
<div class="total-label">{{ __('Total Price:') }}</div>
|
<div class="total-label">{{ __('Total Price:') }}</div>
|
||||||
<div class="total-price" data-currency="{{ currency_symbol }}">
|
<div class="total-price" data-currency="{{ currency_symbol }}" data-fixed-price="{{ fixed_price }}">
|
||||||
{% if pricing_mode == 'fixed' %}
|
{% if pricing_mode == 'fixed' %}
|
||||||
{{ currency_symbol }}{{ fixed_price }}
|
{{ fixed_price_html|raw }}
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="calculated-total">{{ currency_symbol }}0.00</span>
|
<span class="calculated-total">{{ zero_price_html|raw }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Plugin Name: WooCommerce Composable Products
|
* Plugin Name: WooCommerce Composable Products
|
||||||
* Plugin URI: https://github.com/magdev/wc-composable-product
|
* Plugin URI: https://src.bundespruefstelle.ch/magdev/wc-composable-product
|
||||||
* Description: Create composable products where customers select a limited number of items from a configurable set
|
* Description: Create composable products where customers select a limited number of items from a configurable set
|
||||||
* Version: 1.1.7
|
* Version: 1.1.14
|
||||||
* Author: Marco Graetsch
|
* Author: Marco Graetsch
|
||||||
* Author URI: https://example.com
|
* Author URI: https://src.bundespruefstelle.ch/magdev
|
||||||
* License: GPL v3 or later
|
* License: GPL v3 or later
|
||||||
* License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
* License URI: https://www.gnu.org/licenses/gpl-3.0.html
|
||||||
* Text Domain: wc-composable-product
|
* Text Domain: wc-composable-product
|
||||||
@@ -19,7 +20,7 @@
|
|||||||
defined('ABSPATH') || exit;
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
// Define plugin constants
|
// Define plugin constants
|
||||||
define('WC_COMPOSABLE_PRODUCT_VERSION', '1.1.7');
|
define('WC_COMPOSABLE_PRODUCT_VERSION', '1.1.14');
|
||||||
define('WC_COMPOSABLE_PRODUCT_FILE', __FILE__);
|
define('WC_COMPOSABLE_PRODUCT_FILE', __FILE__);
|
||||||
define('WC_COMPOSABLE_PRODUCT_PATH', plugin_dir_path(__FILE__));
|
define('WC_COMPOSABLE_PRODUCT_PATH', plugin_dir_path(__FILE__));
|
||||||
define('WC_COMPOSABLE_PRODUCT_URL', plugin_dir_url(__FILE__));
|
define('WC_COMPOSABLE_PRODUCT_URL', plugin_dir_url(__FILE__));
|
||||||
|
|||||||
Reference in New Issue
Block a user