commit 7273c9cde75b05b63387e2fa26073dab138392d1 Author: magdev Date: Sun Dec 21 04:56:50 2025 +0100 Release version 1.0.1 - Add Twig template engine integration - Migrate all templates to Twig format - Add German (Switzerland, Informal) translation - Improve template organization and security - Add Composer dependency management - Create comprehensive changelog diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 0000000..673c391 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,11 @@ +{ + "permissions": { + "allow": [ + "Bash(unzip:*)", + "Bash(msgfmt:*)", + "Bash(ls:*)", + "Bash(mkdir:*)", + "Bash(composer init:*)" + ] + } +} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c5b4e88 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# WordPress +.DS_Store +Thumbs.db + +# Composer +/vendor/ +composer.lock + +# Twig cache +/templates/cache/ + +# IDE +.vscode/ +.idea/ +*.sublime-project +*.sublime-workspace + +# Node +node_modules/ +npm-debug.log + +# Logs +*.log + +# OS +.DS_Store +._* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..41b10eb --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ +# Changelog + +All notable changes to WooCommerce Tier and Package Prices will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [1.0.1] - 2025-12-21 + +### Added +- Twig template engine integration for all templates +- Template loader class with WordPress integration +- German (Switzerland, Informal) translation (de_CH_informal) +- Composer dependency management +- Comprehensive translation support in Twig templates +- Template caching support (disabled in debug mode) + +### Changed +- Migrated all PHP templates to Twig format (.twig) +- Improved template organization and separation of concerns +- Enhanced security with automatic HTML escaping in templates +- Updated composer.json with complete package metadata + +### Removed +- Old PHP template files (replaced with Twig) + +### Technical +- Added Twig v3.22.2 dependency +- Created WC_TPP_Template_Loader class for centralized template rendering +- Integrated WordPress functions (__(), _e(), esc_*, wc_price()) into Twig +- Added Swiss German localization with informal address form +- Organized templates into admin/ and frontend/ directories + +## [1.0.0] - 2025-12-21 + +### Added +- Initial release +- Tier pricing functionality (quantity-based discounts) +- Package pricing functionality (fixed-price bundles) +- Admin settings page for plugin configuration +- Product meta boxes for configuring tier and package prices +- Frontend pricing tables display +- Cart integration for automatic price calculation +- WooCommerce HPOS compatibility +- Multilingual support with text domain +- German (Germany) translation (de_DE) +- English (US) translation (en_US) + +### Features +- Volume discounts based on quantity thresholds +- Fixed-price packages with custom labels +- Configurable display positions (before/after cart button, after price) +- Real-time price updates in cart +- Responsive pricing tables +- Admin-friendly interface for price management diff --git a/INSTALLATION.md b/INSTALLATION.md new file mode 100644 index 0000000..af191ba --- /dev/null +++ b/INSTALLATION.md @@ -0,0 +1,188 @@ +# Installation Guide + +## Quick Installation + +### Method 1: WordPress Admin Upload (Recommended) + +1. Download `wc-tier-and-package-prices.zip` from the releases +2. Log in to your WordPress admin dashboard +3. Navigate to **Plugins > Add New** +4. Click **Upload Plugin** button at the top +5. Click **Choose File** and select `wc-tier-and-package-prices.zip` +6. Click **Install Now** +7. After installation, click **Activate Plugin** +8. You're done! The plugin is now active. + +### Method 2: Manual FTP Upload + +1. Download `wc-tier-and-package-prices.zip` +2. Extract the ZIP file to get the `wc-tier-and-package-prices` folder +3. Connect to your server via FTP +4. Upload the entire `wc-tier-and-package-prices` folder to `/wp-content/plugins/` +5. Go to your WordPress admin dashboard +6. Navigate to **Plugins** +7. Find "WooCommerce Tier and Package Prices" and click **Activate** + +### Method 3: WP-CLI + +If you have WP-CLI installed: + +```bash +wp plugin install wc-tier-and-package-prices.zip --activate +``` + +## Post-Installation Setup + +### 1. Verify Installation + +After activation, you should see: +- A new menu item: **WooCommerce > Tier & Package Prices** +- No error messages +- Plugin listed in **Plugins** page + +### 2. Configure Global Settings + +1. Go to **WooCommerce > Tier & Package Prices** +2. Configure the following options: + - **Enable Tier Pricing**: ✓ (checked) + - **Enable Package Pricing**: ✓ (checked) + - **Display Pricing Table**: ✓ (checked) + - **Display Position**: Choose where to show pricing tables + - Before Add to Cart (recommended) + - After Add to Cart + - After Price +3. Click **Save Changes** + +### 3. Configure Your First Product + +1. Go to **Products** and edit any product (or create new one) +2. Scroll to **Product data** panel +3. Click the **General** tab +4. Scroll down to find the pricing sections + +#### Add Tier Pricing (Volume Discounts) + +1. Click **Add Tier** button +2. Example configuration: + - Min Quantity: `10`, Price: `9.50` + - Min Quantity: `25`, Price: `8.50` + - Min Quantity: `50`, Price: `7.50` +3. Click **Update** to save + +#### Add Package Pricing (Fixed Packages) + +1. Click **Add Package** button +2. Example configuration: + - Quantity: `12`, Price: `99.00`, Label: "Dozen Pack" + - Quantity: `24`, Price: `180.00`, Label: "Double Pack" +3. Click **Update** to save + +### 4. Test on Frontend + +1. Visit your product page on the frontend +2. You should see: + - Pricing table showing tiers and/or packages + - Price updates when changing quantity + - Package selection buttons (if packages configured) +3. Add to cart and verify correct pricing + +## Requirements Check + +Before installation, verify: + +- ✓ WordPress 5.8 or higher +- ✓ WooCommerce 5.0 or higher installed and activated +- ✓ PHP 7.4 or higher +- ✓ Write permissions in `/wp-content/plugins/` directory + +## Troubleshooting + +### Plugin Won't Activate + +**Error: "WooCommerce is required"** +- Solution: Install and activate WooCommerce first + +**Error: "Parse error" or "Syntax error"** +- Solution: Your PHP version is too old. Upgrade to PHP 7.4 or higher + +### Pricing Tables Not Showing + +1. Check global settings are enabled (**WooCommerce > Tier & Package Prices**) +2. Verify you added tiers/packages to the specific product +3. Clear browser cache and reload the page +4. Check theme compatibility (try default WordPress theme) + +### Prices Not Calculating Correctly + +1. Make sure tiers are sorted by minimum quantity (plugin does this automatically) +2. Verify package quantities exactly match what customer selects +3. Clear WooCommerce caches (**WooCommerce > Status > Tools > Clear template cache**) + +### Styling Issues + +1. Clear browser cache +2. Check if theme has conflicting CSS +3. Try adding custom CSS in **Appearance > Customize > Additional CSS** + +## Uninstallation + +To completely remove the plugin: + +1. Go to **Plugins** page +2. Deactivate "WooCommerce Tier and Package Prices" +3. Click **Delete** +4. Confirm deletion + +**Note:** Product meta data (tiers and packages) will be preserved in database even after uninstallation. If you reinstall, your configurations will still be there. + +To completely remove all data: +1. Before uninstalling, remove all tiers and packages from products +2. Then proceed with uninstallation + +## File Structure After Installation + +Your WordPress installation should have: + +``` +wp-content/ +└── plugins/ + └── wc-tier-and-package-prices/ + ├── wc-tier-and-package-prices.php (main file) + ├── README.md + ├── INSTALLATION.md + ├── USAGE_EXAMPLES.md + ├── includes/ + │ ├── class-wc-tpp-admin.php + │ ├── class-wc-tpp-cart.php + │ ├── class-wc-tpp-frontend.php + │ └── class-wc-tpp-product-meta.php + └── assets/ + ├── css/ + │ ├── admin.css + │ └── frontend.css + └── js/ + ├── admin.js + └── frontend.js +``` + +## Getting Help + +If you encounter issues: + +1. Check this installation guide +2. Review the [README.md](README.md) for feature documentation +3. Review [USAGE_EXAMPLES.md](USAGE_EXAMPLES.md) for configuration examples +4. Check WordPress error logs +5. Enable WP_DEBUG to see detailed errors + +## Next Steps + +After successful installation: + +1. Read [USAGE_EXAMPLES.md](USAGE_EXAMPLES.md) for practical examples +2. Configure tier pricing on your products +3. Test the complete checkout flow +4. Customize display settings to match your theme +5. Consider creating package deals for popular quantities + +Enjoy your new tier and package pricing capabilities! diff --git a/QUICKSTART.md b/QUICKSTART.md new file mode 100644 index 0000000..5cf3f70 --- /dev/null +++ b/QUICKSTART.md @@ -0,0 +1,97 @@ +# Quick Start Guide + +Get started with WooCommerce Tier and Package Prices in 5 minutes! + +## Step 1: Install (2 minutes) + +1. Go to **Plugins > Add New** in WordPress +2. Click **Upload Plugin** +3. Choose `wc-tier-and-package-prices.zip` +4. Click **Install Now** then **Activate** + +✓ Done! Plugin is active. + +## Step 2: Basic Setup (1 minute) + +1. Go to **WooCommerce > Tier & Package Prices** +2. Make sure all checkboxes are checked: + - ✓ Enable Tier Pricing + - ✓ Enable Package Pricing + - ✓ Display Pricing Table +3. Set Display Position to **Before Add to Cart** +4. Click **Save Changes** + +## Step 3: Add Pricing to a Product (2 minutes) + +### Example: Selling T-Shirts + +1. Edit a product (or create new one) +2. Set regular price: **$20.00** + +#### Add Volume Discounts + +Click **Add Tier** and create: +- Min Qty: `10` → Price: `$18.00` (10% off) +- Min Qty: `25` → Price: `$16.00` (20% off) + +#### Add Package Deals + +Click **Add Package** and create: +- Qty: `12` → Price: `$200` → Label: "Team Pack" +- Qty: `24` → Price: `$360` → Label: "Bulk Order" + +5. Click **Update** to save + +## Step 4: Test It! + +1. Visit your product page +2. See the pricing table appear +3. Change quantity to `12` → price updates automatically +4. Click "Select Package" on Team Pack → quantity changes to 12 +5. Add to cart → verify price is correct ($200) + +## That's It! + +Your tier and package pricing is now working! + +## Common Use Cases + +### Wholesale Store +``` +Tier 1: 10+ items = $X each +Tier 2: 50+ items = $Y each +Tier 3: 100+ items = $Z each +``` + +### Bulk Packages +``` +Package 1: 6 items = $XX +Package 2: 12 items = $YY +Package 3: 24 items = $ZZ +``` + +### Mixed Approach +Use tiers for flexible quantities (10+, 25+, 50+) +Use packages for common bundles (6-pack, dozen, case) + +## Pro Tips + +1. **Round Numbers**: Use 10, 25, 50, 100 for tiers +2. **Meaningful Savings**: Offer at least 10% off per tier +3. **Label Packages**: "Family Pack" sells better than "4-pack" +4. **Test Checkout**: Always complete a test order +5. **Mobile Check**: View on phone to verify responsiveness + +## Need More Help? + +- Full documentation: [README.md](README.md) +- Detailed examples: [USAGE_EXAMPLES.md](USAGE_EXAMPLES.md) +- Installation help: [INSTALLATION.md](INSTALLATION.md) + +## Keyboard Shortcuts (Admin) + +- Add new tier: Click "Add Tier" button +- Remove tier: Click "Remove" button on that tier +- Save: Press Ctrl+S (or Cmd+S on Mac) to save product + +Happy selling! diff --git a/README.md b/README.md new file mode 100644 index 0000000..7cb1bfb --- /dev/null +++ b/README.md @@ -0,0 +1,201 @@ +# WooCommerce Tier and Package Prices + +A powerful WooCommerce plugin that adds tier pricing and package pricing functionality to your products with configurable quantities at fixed prices. + +## Features + +### Tier Pricing (Volume Discounts) +- Set quantity-based pricing tiers +- Automatic discounts when customers buy in larger quantities +- Display savings percentage and amount +- Dynamically updates price based on quantity selected + +### Package Pricing +- Create fixed-price packages with specific quantities +- Example: 10 pieces for $50, 25 pieces for $100 +- Custom labels for packages (e.g., "Starter Pack", "Value Bundle") +- One-click package selection on product pages + +### Admin Features +- Easy-to-use product meta boxes for adding tiers and packages +- Global settings page under WooCommerce menu +- Configure display position (before/after add to cart, after price) +- Enable/disable tier or package pricing independently +- Sortable pricing rules + +### Frontend Features +- Beautiful pricing tables on product pages +- Responsive design for mobile devices +- Visual highlighting of active tier/package +- Real-time price updates when quantity changes +- Cart integration with proper price calculations +- Clear pricing indicators in cart + +## Installation + +1. Upload the `wc-tier-and-package-prices` folder to the `/wp-content/plugins/` directory +2. Activate the plugin through the 'Plugins' menu in WordPress +3. Make sure WooCommerce is installed and activated + +## Configuration + +### Global Settings + +Navigate to **WooCommerce > Tier & Package Prices** to configure: + +- **Enable Tier Pricing**: Turn on/off volume discounts +- **Enable Package Pricing**: Turn on/off fixed-price packages +- **Display Pricing Table**: Show/hide pricing tables on product pages +- **Display Position**: Choose where to show pricing tables + +### Product Settings + +When editing a product, scroll to the **Product data** panel: + +1. Click on the **General** tab +2. Scroll down to find **Tier Pricing** and **Package Pricing** sections + +#### Adding Tier Pricing + +1. Click "Add Tier" button +2. Set minimum quantity (e.g., 10) +3. Set price per unit (e.g., 9.99) +4. Add more tiers as needed +5. Tiers are automatically sorted by quantity + +**Example:** +- 1-9 items: $12.00 each (regular price) +- 10-24 items: $10.00 each (tier 1) +- 25-49 items: $8.50 each (tier 2) +- 50+ items: $7.00 each (tier 3) + +#### Adding Package Pricing + +1. Click "Add Package" button +2. Set exact quantity (e.g., 10) +3. Set fixed price for that quantity (e.g., 99.99) +4. Optionally add a custom label (e.g., "Starter Pack") +5. Add more packages as needed + +**Example:** +- Package 1: 10 pieces for $95 (labeled "Small Bundle") +- Package 2: 25 pieces for $200 (labeled "Value Pack") +- Package 3: 50 pieces for $350 (labeled "Business Pack") + +## How It Works + +### Price Calculation Priority + +1. **Package Price** (highest priority): If the exact quantity matches a package, the package price applies +2. **Tier Price**: If no package match, the highest applicable tier price is used +3. **Regular Price**: If no tier or package applies, the regular product price is used + +### Frontend Display + +- Pricing tables show all available tiers and packages +- Current tier/package is highlighted based on selected quantity +- Prices update dynamically when quantity changes +- Package buttons allow one-click quantity selection + +### Cart Behavior + +- Correct prices are applied in the cart +- Cart shows whether tier or package pricing is applied +- Subtotals are calculated correctly +- Compatible with WooCommerce checkout process + +## File Structure + +``` +wc-tier-and-package-prices/ +├── wc-tier-and-package-prices.php # Main plugin file +├── includes/ +│ ├── class-wc-tpp-admin.php # Admin settings +│ ├── class-wc-tpp-product-meta.php # Product meta boxes +│ ├── class-wc-tpp-frontend.php # Frontend display +│ └── class-wc-tpp-cart.php # Cart price calculations +├── assets/ +│ ├── css/ +│ │ ├── admin.css # Admin styles +│ │ └── frontend.css # Frontend styles +│ └── js/ +│ ├── admin.js # Admin JavaScript +│ └── frontend.js # Frontend JavaScript +└── README.md +``` + +## Requirements + +- WordPress 6.0 or higher (tested up to 6.9.x) +- WooCommerce 8.0 or higher (tested up to 10.x) +- PHP 7.4 or higher + +### Compatibility + +- ✅ **WooCommerce 10.x**: Fully compatible with the latest WooCommerce release +- ✅ **WordPress 6.9.x**: Tested and verified with WordPress 6.9.x +- ✅ **HPOS (High-Performance Order Storage)**: Full support for WooCommerce Custom Order Tables +- ✅ **Block-based Themes**: Compatible with modern WordPress block themes +- ✅ **Multisite**: Works on WordPress multisite installations + +## Frequently Asked Questions + +**Q: Can I use both tier pricing and package pricing on the same product?** +A: Yes! Package pricing takes priority when the exact quantity matches. + +**Q: What happens if a customer changes the quantity in the cart?** +A: The price will automatically recalculate based on the new quantity. + +**Q: Can I set different tiers for different products?** +A: Yes, each product can have its own tier and package pricing configuration. + +**Q: Does this work with variable products?** +A: Currently, this plugin is designed for simple products. Variable product support may be added in future versions. + +## Support + +For bug reports and feature requests, please use the plugin's support forum or contact the developer. + +## License + +This plugin is licensed under the GPL v2 or later. + +## Changelog + +### Version 1.0.0 - 2025-12-21 + +#### Compatibility Updates + +- ✅ Updated for WooCommerce 10.x compatibility +- ✅ Updated for WordPress 6.9.x compatibility +- ✅ Added HPOS (High-Performance Order Storage) support +- ✅ Declared compatibility with WooCommerce Custom Order Tables + +#### Security Enhancements + +- ✅ Added nonce verification for product meta save operations +- ✅ Added capability checks for user permissions +- ✅ Enhanced data escaping and sanitization +- ✅ Implemented autosave prevention + +#### Code Improvements + +- ✅ Enhanced cart object validation +- ✅ Improved product object type checking +- ✅ Better error handling for edge cases +- ✅ Updated data storage methods for cart items +- ✅ Modernized JavaScript localization with proper escaping + +#### Initial Features + +- Initial release with tier pricing functionality +- Package pricing with fixed quantities +- Customizable pricing tables +- Global settings page +- Product-level configuration +- Cart integration with dynamic pricing +- Responsive frontend design + +## Credits + +Developed by Marco Graetsch for WooCommerce store owners who need flexible pricing options. diff --git a/USAGE_EXAMPLES.md b/USAGE_EXAMPLES.md new file mode 100644 index 0000000..91196bf --- /dev/null +++ b/USAGE_EXAMPLES.md @@ -0,0 +1,171 @@ +# Usage Examples + +## Example 1: T-Shirt Store with Volume Discounts + +### Tier Pricing Setup + +For a t-shirt that normally costs $20: + +| Min Quantity | Price per Unit | Customer Saves | +|--------------|----------------|----------------| +| 1 | $20.00 | - | +| 10 | $18.00 | $2.00 (10%) | +| 25 | $16.00 | $4.00 (20%) | +| 50 | $14.00 | $6.00 (30%) | + +**How to configure:** +1. Edit your t-shirt product +2. Set regular price to $20.00 +3. Add tier: Min Qty = 10, Price = $18.00 +4. Add tier: Min Qty = 25, Price = $16.00 +5. Add tier: Min Qty = 50, Price = $14.00 +6. Save product + +**Customer experience:** +- Buys 5 shirts: $20.00 each = $100.00 total +- Buys 15 shirts: $18.00 each = $270.00 total (saves $30) +- Buys 30 shirts: $16.00 each = $480.00 total (saves $120) + +## Example 2: Office Supplies with Package Deals + +### Package Pricing Setup + +For pens that normally cost $2.00 each: + +| Package | Quantity | Fixed Price | Price per Unit | +|--------------|----------|-------------|----------------| +| Single | 1 | $2.00 | $2.00 | +| Small Pack | 12 | $20.00 | $1.67 | +| Value Pack | 24 | $36.00 | $1.50 | +| Bulk Pack | 50 | $65.00 | $1.30 | + +**How to configure:** +1. Edit your pen product +2. Set regular price to $2.00 +3. Add package: Qty = 12, Price = $20.00, Label = "Small Pack" +4. Add package: Qty = 24, Price = $36.00, Label = "Value Pack" +5. Add package: Qty = 50, Price = $65.00, Label = "Bulk Pack" +6. Save product + +**Customer experience:** +- Customer sees package cards on product page +- Clicks "Select Package" for "Value Pack" +- Quantity automatically set to 24 +- Price shows $36.00 (not $48.00) + +## Example 3: Combined Tier and Package Pricing + +### Coffee Beans Store + +For premium coffee beans at $15.00 per bag: + +**Tier Pricing:** +- 1-4 bags: $15.00 each +- 5-9 bags: $14.00 each +- 10+ bags: $13.00 each + +**Package Deals:** +- Monthly subscription: 4 bags for $50.00 ($12.50 each) +- Quarterly bundle: 12 bags for $140.00 ($11.67 each) + +**How to configure:** +1. Set regular price: $15.00 +2. Add tier: Min Qty = 5, Price = $14.00 +3. Add tier: Min Qty = 10, Price = $13.00 +4. Add package: Qty = 4, Price = $50.00, Label = "Monthly Box" +5. Add package: Qty = 12, Price = $140.00, Label = "Quarterly Bundle" + +**Customer scenarios:** +- Buys 3 bags: $15.00 each = $45.00 +- Buys 4 bags: Gets prompted for Monthly Box at $50.00 +- Buys 6 bags: $14.00 each = $84.00 (tier pricing) +- Buys 12 bags: Gets Quarterly Bundle at $140.00 (package beats tier) + +## Example 4: Promotional Packages + +### Seasonal Gift Sets + +Regular mug price: $10.00 + +**Package Deals:** +- Pair Pack: 2 mugs for $18.00 (10% off) +- Family Set: 4 mugs for $32.00 (20% off) +- Office Bundle: 10 mugs for $70.00 (30% off) + +**Configuration:** +- Package 1: Qty = 2, Price = $18.00, Label = "Perfect Pair" +- Package 2: Qty = 4, Price = $32.00, Label = "Family Set" +- Package 3: Qty = 10, Price = $70.00, Label = "Office Bundle" + +## Tips for Best Results + +### Tier Pricing Best Practices + +1. **Progressive Discounts**: Make higher quantities more attractive + - Good: 10 items = 10% off, 25 items = 20% off, 50 items = 30% off + - Bad: 10 items = 20% off, 25 items = 15% off + +2. **Clear Break Points**: Use round numbers for quantities + - Good: 10, 25, 50, 100 + - Avoid: 7, 23, 47, 93 + +3. **Meaningful Savings**: Ensure discounts are worth the extra purchase + - Minimum 5-10% per tier level + - Higher tiers should have progressively better deals + +### Package Pricing Best Practices + +1. **Strategic Quantities**: Match common use cases + - 12-pack (dozen) + - 24-pack (two dozen) + - 6-pack (half dozen) + +2. **Compelling Labels**: Make packages attractive + - "Starter Pack" instead of "10-piece" + - "Family Bundle" instead of "4-pack" + - "Business Value Pack" instead of "50-piece" + +3. **Sweet Spot Pricing**: Price just below psychological barriers + - $99.99 instead of $100.00 + - $49.95 instead of $50.00 + +### Combining Both Strategies + +1. Use **packages** for common quantities (6, 12, 24) +2. Use **tiers** for flexible volume discounts (10+, 25+, 50+) +3. Packages take priority when exact match exists +4. Tiers fill the gaps between packages + +## Display Position Recommendations + +- **Before Add to Cart**: Best for products where pricing is the main decision factor +- **After Add to Cart**: Good for impulse purchases and upsells +- **After Price**: Ideal for clean product pages, keeps info near the price + +## Testing Your Setup + +1. Add a product to your store +2. Configure tier/package pricing +3. View the product page in an incognito window +4. Test different quantities +5. Verify prices update correctly +6. Add to cart and check cart totals +7. Complete a test checkout + +## Common Scenarios + +### Wholesale Store +- Tier 1: 10+ items = wholesale price tier 1 +- Tier 2: 50+ items = wholesale price tier 2 +- Tier 3: 100+ items = wholesale price tier 3 + +### Subscription Box +- Package 1: 1 month (4 items) = $40 +- Package 2: 3 months (12 items) = $108 (10% off) +- Package 3: 6 months (24 items) = $192 (20% off) + +### Event Supplies +- Package 1: Small party (10 pieces) = $50 +- Package 2: Medium party (25 pieces) = $110 +- Package 3: Large party (50 pieces) = $200 +- Package 4: Event package (100 pieces) = $350 diff --git a/assets/css/admin.css b/assets/css/admin.css new file mode 100644 index 0000000..9bdf77f --- /dev/null +++ b/assets/css/admin.css @@ -0,0 +1,91 @@ +/** + * Admin styles for WooCommerce Tier and Package Prices + */ + +.wc-tpp-tier-pricing, +.wc-tpp-package-pricing { + border-top: 1px solid #eee; + padding-top: 15px; + margin-top: 15px; +} + +.wc-tpp-tier-pricing > p:first-child, +.wc-tpp-package-pricing > p:first-child { + font-weight: 600; + margin-bottom: 10px; +} + +.wc-tpp-tier-pricing .description, +.wc-tpp-package-pricing .description { + display: block; + margin-top: 5px; + font-style: italic; + color: #666; +} + +.wc-tpp-tier-row, +.wc-tpp-package-row { + display: flex; + gap: 15px; + align-items: flex-end; + padding: 15px; + background: #f9f9f9; + border: 1px solid #ddd; + border-radius: 4px; + margin-bottom: 10px; +} + +.wc-tpp-tier-row .form-field, +.wc-tpp-package-row .form-field { + margin: 0; + flex: 1; +} + +.wc-tpp-tier-row label, +.wc-tpp-package-row label { + display: block; + font-weight: 600; + margin-bottom: 5px; +} + +.wc-tpp-tier-row input, +.wc-tpp-package-row input { + width: 100%; +} + +.wc-tpp-remove-tier, +.wc-tpp-remove-package { + flex-shrink: 0; + color: #b32d2e; + border-color: #b32d2e; + margin-bottom: 0; +} + +.wc-tpp-remove-tier:hover, +.wc-tpp-remove-package:hover { + background: #b32d2e; + color: #fff; +} + +.wc-tpp-add-tier, +.wc-tpp-add-package { + margin-top: 10px; +} + +.wc-tpp-tiers-container, +.wc-tpp-packages-container { + margin-bottom: 15px; +} + +.wc-tpp-tiers-container:empty::before, +.wc-tpp-packages-container:empty::before { + content: "No items added yet. Click 'Add' button to create pricing rules."; + display: block; + padding: 20px; + background: #f0f0f1; + border: 1px dashed #ccc; + border-radius: 4px; + text-align: center; + color: #666; + font-style: italic; +} diff --git a/assets/css/frontend.css b/assets/css/frontend.css new file mode 100644 index 0000000..c381502 --- /dev/null +++ b/assets/css/frontend.css @@ -0,0 +1,173 @@ +/** + * Frontend styles for WooCommerce Tier and Package Prices + */ + +.wc-tpp-pricing-container { + margin: 20px 0; + padding: 20px; + background: #f9f9f9; + border: 1px solid #e0e0e0; + border-radius: 8px; +} + +.wc-tpp-tier-pricing-table, +.wc-tpp-package-pricing-table { + margin-bottom: 30px; +} + +.wc-tpp-tier-pricing-table:last-child, +.wc-tpp-package-pricing-table:last-child { + margin-bottom: 0; +} + +.wc-tpp-tier-pricing-table h3, +.wc-tpp-package-pricing-table h3 { + margin: 0 0 15px 0; + font-size: 1.2em; + color: #333; +} + +/* Tier pricing table */ +.wc-tpp-table { + width: 100%; + border-collapse: collapse; + background: #fff; + border-radius: 4px; + overflow: hidden; +} + +.wc-tpp-table thead { + background: #333; + color: #fff; +} + +.wc-tpp-table th { + padding: 12px; + text-align: left; + font-weight: 600; + font-size: 0.9em; +} + +.wc-tpp-table tbody tr { + border-bottom: 1px solid #e0e0e0; + transition: background-color 0.2s; +} + +.wc-tpp-table tbody tr:hover { + background: #f5f5f5; +} + +.wc-tpp-table tbody tr.wc-tpp-active-tier { + background: #e7f3e7; + font-weight: 600; +} + +.wc-tpp-table td { + padding: 12px; + font-size: 0.95em; +} + +/* Package pricing */ +.wc-tpp-packages { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 15px; +} + +.wc-tpp-package { + background: #fff; + border: 2px solid #e0e0e0; + border-radius: 8px; + padding: 20px; + text-align: center; + transition: all 0.3s; + cursor: pointer; +} + +.wc-tpp-package:hover { + border-color: #333; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + transform: translateY(-2px); +} + +.wc-tpp-package.wc-tpp-selected { + border-color: #4CAF50; + background: #f0f8f0; +} + +.wc-tpp-package-header h4 { + margin: 0 0 15px 0; + font-size: 1.1em; + color: #333; +} + +.wc-tpp-package-details { + margin-bottom: 15px; +} + +.wc-tpp-package-qty { + font-size: 1.4em; + margin-bottom: 10px; +} + +.wc-tpp-package-qty strong { + color: #333; +} + +.wc-tpp-package-price { + font-size: 1.6em; + font-weight: 700; + color: #4CAF50; + margin-bottom: 5px; +} + +.wc-tpp-package-unit-price { + font-size: 0.85em; + color: #666; +} + +.wc-tpp-select-package { + width: 100%; + padding: 10px 20px; + background: #333; + color: #fff; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 0.95em; + transition: background 0.3s; +} + +.wc-tpp-select-package:hover { + background: #555; +} + +.wc-tpp-package.wc-tpp-selected .wc-tpp-select-package { + background: #4CAF50; +} + +.wc-tpp-package.wc-tpp-selected .wc-tpp-select-package:hover { + background: #45a049; +} + +/* Cart notices */ +.wc-tpp-notice { + color: #4CAF50; + font-style: italic; +} + +/* Responsive design */ +@media (max-width: 768px) { + .wc-tpp-packages { + grid-template-columns: 1fr; + } + + .wc-tpp-table { + font-size: 0.9em; + } + + .wc-tpp-table th, + .wc-tpp-table td { + padding: 8px; + } +} diff --git a/assets/js/admin.js b/assets/js/admin.js new file mode 100644 index 0000000..ba7fab1 --- /dev/null +++ b/assets/js/admin.js @@ -0,0 +1,62 @@ +/** + * Admin JavaScript for WooCommerce Tier and Package Prices + */ + +(function($) { + 'use strict'; + + $(document).ready(function() { + let tierIndex = $('.wc-tpp-tier-row').length; + let packageIndex = $('.wc-tpp-package-row').length; + + // Add tier + $('.wc-tpp-add-tier').on('click', function(e) { + e.preventDefault(); + const template = $('#wc-tpp-tier-row-template').html(); + const newRow = template.replace(/\{\{INDEX\}\}/g, tierIndex); + $('.wc-tpp-tiers-container').append(newRow); + tierIndex++; + }); + + // Remove tier + $(document).on('click', '.wc-tpp-remove-tier', function(e) { + e.preventDefault(); + if (confirm('Are you sure you want to remove this tier?')) { + $(this).closest('.wc-tpp-tier-row').remove(); + } + }); + + // Add package + $('.wc-tpp-add-package').on('click', function(e) { + e.preventDefault(); + const template = $('#wc-tpp-package-row-template').html(); + const newRow = template.replace(/\{\{INDEX\}\}/g, packageIndex); + $('.wc-tpp-packages-container').append(newRow); + packageIndex++; + }); + + // Remove package + $(document).on('click', '.wc-tpp-remove-package', function(e) { + e.preventDefault(); + if (confirm('Are you sure you want to remove this package?')) { + $(this).closest('.wc-tpp-package-row').remove(); + } + }); + + // Validate inputs + $(document).on('input', 'input[name*="[min_qty]"], input[name*="[qty]"]', function() { + const value = parseInt($(this).val()); + if (value < 1) { + $(this).val(1); + } + }); + + $(document).on('input', 'input[name*="[price]"]', function() { + const value = parseFloat($(this).val()); + if (value < 0) { + $(this).val(0); + } + }); + }); + +})(jQuery); diff --git a/assets/js/frontend.js b/assets/js/frontend.js new file mode 100644 index 0000000..06492c2 --- /dev/null +++ b/assets/js/frontend.js @@ -0,0 +1,179 @@ +/** + * Frontend JavaScript for WooCommerce Tier and Package Prices + */ + +(function($) { + 'use strict'; + + $(document).ready(function() { + const $quantityInput = $('input.qty'); + const $priceDisplay = $('.woocommerce-Price-amount.amount').first(); + + if ($quantityInput.length === 0) { + return; + } + + // Get tiers and packages data + const tiers = []; + const packages = []; + + $('.wc-tpp-tier-pricing-table tbody tr').each(function() { + tiers.push({ + minQty: parseInt($(this).data('min-qty')), + price: parseFloat($(this).data('price')) + }); + }); + + $('.wc-tpp-package').each(function() { + packages.push({ + qty: parseInt($(this).data('qty')), + price: parseFloat($(this).data('price')) + }); + }); + + // Update price display based on quantity + function updatePriceDisplay() { + const quantity = parseInt($quantityInput.val()) || 1; + + // Check for exact package match + let matchedPackage = null; + packages.forEach(function(pkg) { + if (pkg.qty === quantity) { + matchedPackage = pkg; + } + }); + + if (matchedPackage) { + const totalPrice = matchedPackage.price; + const unitPrice = totalPrice / quantity; + updatePrice(unitPrice, 'Package price: ' + formatPrice(totalPrice) + ' total'); + highlightPackage(quantity); + return; + } + + // Check for tier pricing + let applicableTier = null; + tiers.forEach(function(tier) { + if (quantity >= tier.minQty) { + applicableTier = tier; + } + }); + + if (applicableTier) { + updatePrice(applicableTier.price, 'Volume discount applied'); + highlightTier(quantity); + removePackageHighlight(); + } else { + removeAllHighlights(); + } + } + + // Update price in the product page + function updatePrice(price, message) { + if ($priceDisplay.length) { + const formattedPrice = formatPrice(price); + $priceDisplay.html(formattedPrice); + + // Add message if provided + if (message) { + if (!$('.wc-tpp-price-message').length) { + $priceDisplay.after('
'); + } + $('.wc-tpp-price-message').html('' + message + ''); + } + } + } + + // Format price according to WooCommerce settings + function formatPrice(price) { + const decimals = wcTppData.price_decimals || 2; + const decimalSep = wcTppData.price_decimal_separator || '.'; + const thousandSep = wcTppData.price_thousand_separator || ','; + const symbol = wcTppData.currency_symbol || '$'; + const position = wcTppData.currency_position || 'left'; + + let priceStr = price.toFixed(decimals); + const parts = priceStr.split('.'); + parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, thousandSep); + priceStr = parts.join(decimalSep); + + switch (position) { + case 'left': + return symbol + priceStr; + case 'right': + return priceStr + symbol; + case 'left_space': + return symbol + ' ' + priceStr; + case 'right_space': + return priceStr + ' ' + symbol; + default: + return symbol + priceStr; + } + } + + // Highlight active tier + function highlightTier(quantity) { + $('.wc-tpp-table tbody tr').removeClass('wc-tpp-active-tier'); + let activeRow = null; + + $('.wc-tpp-table tbody tr').each(function() { + const minQty = parseInt($(this).data('min-qty')); + if (quantity >= minQty) { + activeRow = $(this); + } + }); + + if (activeRow) { + activeRow.addClass('wc-tpp-active-tier'); + } + } + + // Highlight selected package + function highlightPackage(quantity) { + $('.wc-tpp-package').removeClass('wc-tpp-selected'); + $('.wc-tpp-table tbody tr').removeClass('wc-tpp-active-tier'); + + $('.wc-tpp-package').each(function() { + const pkgQty = parseInt($(this).data('qty')); + if (pkgQty === quantity) { + $(this).addClass('wc-tpp-selected'); + } + }); + } + + // Remove package highlight + function removePackageHighlight() { + $('.wc-tpp-package').removeClass('wc-tpp-selected'); + } + + // Remove all highlights + function removeAllHighlights() { + $('.wc-tpp-package').removeClass('wc-tpp-selected'); + $('.wc-tpp-table tbody tr').removeClass('wc-tpp-active-tier'); + $('.wc-tpp-price-message').remove(); + } + + // Handle quantity input changes + $quantityInput.on('input change', function() { + updatePriceDisplay(); + }); + + // Handle package selection + $('.wc-tpp-select-package').on('click', function(e) { + e.preventDefault(); + const $package = $(this).closest('.wc-tpp-package'); + const qty = parseInt($package.data('qty')); + + $quantityInput.val(qty).trigger('change'); + + // Scroll to add to cart button + $('html, body').animate({ + scrollTop: $('.single_add_to_cart_button').offset().top - 100 + }, 500); + }); + + // Initial update + updatePriceDisplay(); + }); + +})(jQuery); diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5ec4957 --- /dev/null +++ b/composer.json @@ -0,0 +1,22 @@ +{ + "name": "magdev/wc-tier-package-prices", + "description": "WooCommerce plugin for tier pricing and package prices with Twig templates", + "version": "1.0.1", + "type": "wordpress-plugin", + "license": "GPL-2.0-or-later", + "authors": [ + { + "name": "Marco Graetsch", + "homepage": "https://src.bundespruefstelle.ch/magdev" + } + ], + "require": { + "php": ">=7.4", + "twig/twig": "^3.0" + }, + "autoload": { + "classmap": [ + "includes/" + ] + } +} diff --git a/includes/class-wc-tpp-admin.php b/includes/class-wc-tpp-admin.php new file mode 100644 index 0000000..c65ed58 --- /dev/null +++ b/includes/class-wc-tpp-admin.php @@ -0,0 +1,48 @@ +display('admin/settings-page.twig'); + } +} + +new WC_TPP_Admin(); diff --git a/includes/class-wc-tpp-cart.php b/includes/class-wc-tpp-cart.php new file mode 100644 index 0000000..5d93a3a --- /dev/null +++ b/includes/class-wc-tpp-cart.php @@ -0,0 +1,94 @@ += 2) { + return; + } + + // Check if cart object is valid + if (!$cart || !is_a($cart, 'WC_Cart')) { + return; + } + + foreach ($cart->get_cart() as $cart_item_key => $cart_item) { + $product_id = $cart_item['product_id']; + $quantity = $cart_item['quantity']; + $product = $cart_item['data']; + + // Validate product object + if (!$product || !is_a($product, 'WC_Product')) { + continue; + } + + // Check for exact package match first + $package_price = null; + if (get_option('wc_tpp_enable_package_pricing') === 'yes') { + $package_price = WC_TPP_Frontend::get_package_price($product_id, $quantity); + } + + if ($package_price !== null) { + // Apply package pricing (total price divided by quantity) + $unit_price = $package_price / $quantity; + $product->set_price($unit_price); + // Store pricing information in cart item for display + WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'package'; + WC()->cart->cart_contents[$cart_item_key]['wc_tpp_total_price'] = $package_price; + } else { + // Apply tier pricing if no package match + if (get_option('wc_tpp_enable_tier_pricing') === 'yes') { + $tier_price = WC_TPP_Frontend::get_tier_price($product_id, $quantity); + if ($tier_price !== null) { + $product->set_price($tier_price); + // Store pricing information in cart item for display + WC()->cart->cart_contents[$cart_item_key]['wc_tpp_pricing_type'] = 'tier'; + WC()->cart->cart_contents[$cart_item_key]['wc_tpp_unit_price'] = $tier_price; + } + } + } + } + } + + public function display_cart_item_price($price, $cart_item, $cart_item_key) { + if (isset($cart_item['wc_tpp_pricing_type'])) { + if ($cart_item['wc_tpp_pricing_type'] === 'package') { + $total_price = isset($cart_item['wc_tpp_total_price']) ? $cart_item['wc_tpp_total_price'] : $cart_item['line_total']; + $unit_price = $total_price / $cart_item['quantity']; + return wc_price($unit_price) . ' (' . __('Package price', 'wc-tier-package-prices') . ')'; + } elseif ($cart_item['wc_tpp_pricing_type'] === 'tier') { + $unit_price = isset($cart_item['wc_tpp_unit_price']) ? $cart_item['wc_tpp_unit_price'] : $cart_item['data']->get_price(); + return wc_price($unit_price) . ' (' . __('Volume discount', 'wc-tier-package-prices') . ')'; + } + } + return $price; + } + + public function display_cart_item_subtotal($subtotal, $cart_item, $cart_item_key) { + if (isset($cart_item['wc_tpp_pricing_type']) && $cart_item['wc_tpp_pricing_type'] === 'package') { + $total_price = isset($cart_item['wc_tpp_total_price']) ? $cart_item['wc_tpp_total_price'] : $cart_item['line_total']; + return wc_price($total_price); + } + return $subtotal; + } +} + +new WC_TPP_Cart(); diff --git a/includes/class-wc-tpp-frontend.php b/includes/class-wc-tpp-frontend.php new file mode 100644 index 0000000..f906d09 --- /dev/null +++ b/includes/class-wc-tpp-frontend.php @@ -0,0 +1,109 @@ + esc_js(get_woocommerce_currency_symbol()), + 'currency_position' => esc_js(get_option('woocommerce_currency_pos', 'left')), + 'price_decimals' => absint(wc_get_price_decimals()), + 'price_decimal_separator' => esc_js(wc_get_price_decimal_separator()), + 'price_thousand_separator' => esc_js(wc_get_price_thousand_separator()) + )); + } + } + + public function display_pricing_table_before() { + if (get_option('wc_tpp_display_position') === 'before_add_to_cart') { + $this->display_pricing_table(); + } + } + + public function display_pricing_table_after() { + if (get_option('wc_tpp_display_position') === 'after_add_to_cart') { + $this->display_pricing_table(); + } + } + + public function display_pricing_table_after_price() { + if (get_option('wc_tpp_display_position') === 'after_price') { + $this->display_pricing_table(); + } + } + + public function display_pricing_table() { + global $product; + + if (!$product || !is_a($product, 'WC_Product') || get_option('wc_tpp_display_table') !== 'yes') { + return; + } + + $product_id = $product->get_id(); + $tiers = get_post_meta($product_id, '_wc_tpp_tiers', true); + $packages = get_post_meta($product_id, '_wc_tpp_packages', true); + + if (empty($tiers) && empty($packages)) { + return; + } + + WC_TPP_Template_Loader::get_instance()->display('frontend/pricing-table.twig', array( + 'product' => $product, + 'tiers' => $tiers, + 'packages' => $packages + )); + } + + public static function get_tier_price($product_id, $quantity) { + $tiers = get_post_meta($product_id, '_wc_tpp_tiers', true); + + if (empty($tiers) || !is_array($tiers)) { + return null; + } + + $applicable_price = null; + foreach ($tiers as $tier) { + if ($quantity >= $tier['min_qty']) { + $applicable_price = $tier['price']; + } + } + + return $applicable_price; + } + + public static function get_package_price($product_id, $quantity) { + $packages = get_post_meta($product_id, '_wc_tpp_packages', true); + + if (empty($packages) || !is_array($packages)) { + return null; + } + + foreach ($packages as $package) { + if ($quantity == $package['qty']) { + return $package['price']; + } + } + + return null; + } +} + +new WC_TPP_Frontend(); diff --git a/includes/class-wc-tpp-product-meta.php b/includes/class-wc-tpp-product-meta.php new file mode 100644 index 0000000..2e7e004 --- /dev/null +++ b/includes/class-wc-tpp-product-meta.php @@ -0,0 +1,157 @@ + +
+

+ + +

+ +
+ ID, '_wc_tpp_tiers', true); + if (!is_array($tiers)) { + $tiers = array(); + } + + foreach ($tiers as $index => $tier) { + $this->render_tier_row($index, $tier); + } + ?> +
+ +

+ +

+
+ +
+

+ + +

+ +
+ ID, '_wc_tpp_packages', true); + if (!is_array($packages)) { + $packages = array(); + } + + foreach ($packages as $index => $package) { + $this->render_package_row($index, $package); + } + ?> +
+ +

+ +

+
+ + + + + display('admin/tier-row.twig', array( + 'index' => $index, + 'tier' => $tier + )); + } + + private function render_package_row($index, $package) { + WC_TPP_Template_Loader::get_instance()->display('admin/package-row.twig', array( + 'index' => $index, + 'package' => $package + )); + } + + public function save_tier_package_fields($post_id) { + // Verify nonce for security + if (!isset($_POST['woocommerce_meta_nonce']) || !wp_verify_nonce($_POST['woocommerce_meta_nonce'], 'woocommerce_save_data')) { + return; + } + + // Check user permissions + if (!current_user_can('edit_post', $post_id)) { + return; + } + + // Avoid auto-save + if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { + return; + } + + // Save tier pricing + if (isset($_POST['_wc_tpp_tiers'])) { + $tiers = array(); + foreach ($_POST['_wc_tpp_tiers'] as $tier) { + if (!empty($tier['min_qty']) && !empty($tier['price'])) { + $tiers[] = array( + 'min_qty' => absint($tier['min_qty']), + 'price' => wc_format_decimal($tier['price']) + ); + } + } + // Sort by minimum quantity + usort($tiers, function($a, $b) { + return $a['min_qty'] - $b['min_qty']; + }); + update_post_meta($post_id, '_wc_tpp_tiers', $tiers); + } else { + delete_post_meta($post_id, '_wc_tpp_tiers'); + } + + // Save package pricing + if (isset($_POST['_wc_tpp_packages'])) { + $packages = array(); + foreach ($_POST['_wc_tpp_packages'] as $package) { + if (!empty($package['qty']) && !empty($package['price'])) { + $packages[] = array( + 'qty' => absint($package['qty']), + 'price' => wc_format_decimal($package['price']), + 'label' => sanitize_text_field($package['label']) + ); + } + } + // Sort by quantity + usort($packages, function($a, $b) { + return $a['qty'] - $b['qty']; + }); + update_post_meta($post_id, '_wc_tpp_packages', $packages); + } else { + delete_post_meta($post_id, '_wc_tpp_packages'); + } + } +} + +new WC_TPP_Product_Meta(); diff --git a/includes/class-wc-tpp-template-loader.php b/includes/class-wc-tpp-template-loader.php new file mode 100644 index 0000000..83a7bd4 --- /dev/null +++ b/includes/class-wc-tpp-template-loader.php @@ -0,0 +1,103 @@ +init_twig(); + } + + private function init_twig() { + $loader = new FilesystemLoader(WC_TPP_PLUGIN_DIR . 'templates'); + + $this->twig = new Environment($loader, array( + 'cache' => WP_DEBUG ? false : WC_TPP_PLUGIN_DIR . 'templates/cache', + 'auto_reload' => true, + 'autoescape' => 'html', + )); + + // Add WordPress translation filter + $this->twig->addFilter(new TwigFilter('__', function ($string, $domain = 'wc-tier-package-prices') { + return __($string, $domain); + })); + + $this->twig->addFilter(new TwigFilter('_e', function ($string, $domain = 'wc-tier-package-prices') { + _e($string, $domain); + })); + + $this->twig->addFilter(new TwigFilter('esc_html', 'esc_html')); + $this->twig->addFilter(new TwigFilter('esc_attr', 'esc_attr')); + $this->twig->addFilter(new TwigFilter('esc_url', 'esc_url')); + + // Add WordPress functions + $this->twig->addFunction(new TwigFunction('get_option', 'get_option')); + $this->twig->addFunction(new TwigFunction('checked', function($checked, $current = true, $echo = false) { + return checked($checked, $current, $echo); + })); + $this->twig->addFunction(new TwigFunction('selected', function($selected, $current = true, $echo = false) { + return selected($selected, $current, $echo); + })); + $this->twig->addFunction(new TwigFunction('settings_fields', function($option_group) { + settings_fields($option_group); + })); + $this->twig->addFunction(new TwigFunction('submit_button', function($text = null) { + submit_button($text); + })); + $this->twig->addFunction(new TwigFunction('get_admin_page_title', 'get_admin_page_title')); + $this->twig->addFunction(new TwigFunction('wc_price', 'wc_price')); + } + + /** + * Render a Twig template + * + * @param string $template Template file name (e.g., 'admin/settings-page.twig') + * @param array $context Variables to pass to the template + * @return string Rendered template + */ + public function render($template, $context = array()) { + try { + return $this->twig->render($template, $context); + } catch (Exception $e) { + if (WP_DEBUG) { + return sprintf( + '

Twig Error: %s

', + esc_html($e->getMessage()) + ); + } + return ''; + } + } + + /** + * Display a Twig template + * + * @param string $template Template file name + * @param array $context Variables to pass to the template + */ + public function display($template, $context = array()) { + echo $this->render($template, $context); + } +} diff --git a/languages/README.md b/languages/README.md new file mode 100644 index 0000000..7f6fc07 --- /dev/null +++ b/languages/README.md @@ -0,0 +1,223 @@ +# WooCommerce Tier and Package Prices - Translations + +This directory contains translation files for the WooCommerce Tier and Package Prices plugin. + +## Available Languages + +- **English (US)** - `en_US` - Default language +- **German (Germany)** - `de_DE` - Deutsch + +## File Structure + +``` +languages/ +├── README.md # This file +├── wc-tier-package-prices.pot # Translation template (3.1 KB) +├── wc-tier-package-prices-en_US.po # English source file +├── wc-tier-package-prices-en_US.mo # English compiled file (2.9 KB) +├── wc-tier-package-prices-de_DE.po # German source file +└── wc-tier-package-prices-de_DE.mo # German compiled file (3.0 KB) +``` + +## File Types + +- **`.pot`** - Portable Object Template - Master template file for creating new translations +- **`.po`** - Portable Object - Human-readable translation source file +- **`.mo`** - Machine Object - Compiled binary file used by WordPress + +## Adding a New Translation + +### Method 1: Using Poedit (Recommended) + +1. Download and install [Poedit](https://poedit.net/) +2. Open Poedit and click "Create new translation" +3. Select the `wc-tier-package-prices.pot` file +4. Choose your language +5. Translate all strings +6. Save the file (this creates both `.po` and `.mo` files automatically) +7. Name the files as `wc-tier-package-prices-{locale}.po` and `wc-tier-package-prices-{locale}.mo` + - Example for French: `wc-tier-package-prices-fr_FR.po` and `wc-tier-package-prices-fr_FR.mo` + +### Method 2: Using Command Line + +1. Copy the POT file to create a new PO file: + ```bash + cp wc-tier-package-prices.pot wc-tier-package-prices-fr_FR.po + ``` + +2. Edit the PO file header: + ``` + "Language: fr_FR\n" + "Language-Team: French\n" + "Plural-Forms: nplurals=2; plural=(n > 1);\n" + ``` + +3. Translate all `msgstr` entries in the PO file + +4. Compile the PO file to MO: + ```bash + msgfmt -o wc-tier-package-prices-fr_FR.mo wc-tier-package-prices-fr_FR.po + ``` + +## WordPress Locale Codes + +Common locale codes for WordPress: + +- `en_US` - English (United States) +- `en_GB` - English (United Kingdom) +- `de_DE` - German (Germany) +- `de_CH` - German (Switzerland) +- `fr_FR` - French (France) +- `fr_CH` - French (Switzerland) +- `es_ES` - Spanish (Spain) +- `it_IT` - Italian (Italy) +- `nl_NL` - Dutch (Netherlands) +- `pt_BR` - Portuguese (Brazil) +- `ru_RU` - Russian +- `zh_CN` - Chinese (Simplified) +- `ja` - Japanese + +For a complete list, see: https://translate.wordpress.org/ + +## Translation Statistics + +### English (en_US) +- **Total strings**: 30 +- **Translated**: 30 (100%) +- **Status**: ✅ Complete + +### German (de_DE) +- **Total strings**: 30 +- **Translated**: 30 (100%) +- **Status**: ✅ Complete + +## Translatable Strings + +The plugin contains translations for: + +### Admin Interface +- Settings page titles and descriptions +- Product meta box labels +- Field labels and placeholders +- Button text + +### Frontend +- Pricing table headers +- Package selection buttons +- Cart notifications +- Discount displays + +## Testing Translations + +### 1. Install Translation Files + +Upload the `.mo` file to one of these locations: +- Plugin directory: `wp-content/plugins/wc-tier-and-package-prices/languages/` +- WordPress languages directory: `wp-content/languages/plugins/` + +### 2. Change WordPress Language + +1. Go to **Settings > General** +2. Set **Site Language** to your desired language +3. Save changes + +### 3. Verify Translation + +1. Navigate to **WooCommerce > Tier & Package Prices** +2. Edit a product and check the meta boxes +3. View a product on the frontend +4. Check the cart page + +All text should appear in the selected language. + +## Updating Existing Translations + +When the plugin is updated with new strings: + +1. Update the POT template file +2. Open your PO file in Poedit +3. Click **Catalog > Update from POT file** +4. Select the new `wc-tier-package-prices.pot` file +5. Translate any new strings +6. Save (automatically compiles to MO) + +## Command Line Compilation + +If you prefer command-line tools: + +```bash +# Compile a single language +msgfmt -o wc-tier-package-prices-de_DE.mo wc-tier-package-prices-de_DE.po + +# Compile all languages at once +for po in *.po; do + msgfmt -o "${po%.po}.mo" "$po" +done + +# Verify MO file +msgunfmt wc-tier-package-prices-de_DE.mo | less +``` + +## Translation Guidelines + +### Style Guide + +1. **Consistency**: Use consistent terminology throughout +2. **Context**: Consider the context where text appears (admin vs frontend) +3. **Length**: Keep translations similar in length to the original +4. **Formality**: Maintain appropriate level of formality for your language +5. **Technical Terms**: Don't translate technical terms like "WooCommerce" + +### German Translation Notes + +- Uses formal "Sie" form (not informal "du") +- Currency examples adjusted (€ instead of $) +- Decimal separator uses comma (9,99 instead of 9.99) +- Date format: DD.MM.YYYY + +### Best Practices + +- ✅ Translate user-facing strings +- ✅ Keep HTML tags intact +- ✅ Preserve placeholders like `%s`, `%d` +- ✅ Maintain the same number of placeholders +- ❌ Don't translate variable names +- ❌ Don't translate code or CSS classes +- ❌ Don't remove or add HTML tags + +## Contributing Translations + +To contribute a new translation: + +1. Create the PO and MO files following the guidelines above +2. Test thoroughly in a WordPress installation +3. Submit via pull request or contact the developer +4. Include: + - Both PO and MO files + - Your name/credits for attribution + - Screenshot showing the translation in use + +## Support + +For translation-related questions or to request a new language: + +- **Plugin URI**: https://src.bundespruefstelle.ch/wc-tier-package-prices +- **Author**: Marco Graetsch +- **Author URI**: https://src.bundespruefstelle.ch/magdev + +## Credits + +### Translators + +- **English (en_US)**: Marco Graetsch +- **German (de_DE)**: Marco Graetsch + +### Translation Tools + +- [Poedit](https://poedit.net/) - Cross-platform PO editor +- [Loco Translate](https://wordpress.org/plugins/loco-translate/) - WordPress plugin for translations +- [GNU gettext](https://www.gnu.org/software/gettext/) - Command-line tools + +## License + +Translation files are distributed under the same GPL v2 or later license as the plugin. diff --git a/languages/wc-tier-package-prices-de_CH_informal.mo b/languages/wc-tier-package-prices-de_CH_informal.mo new file mode 100644 index 0000000..7f68f92 Binary files /dev/null and b/languages/wc-tier-package-prices-de_CH_informal.mo differ diff --git a/languages/wc-tier-package-prices-de_CH_informal.po b/languages/wc-tier-package-prices-de_CH_informal.po new file mode 100644 index 0000000..40ea62b --- /dev/null +++ b/languages/wc-tier-package-prices-de_CH_informal.po @@ -0,0 +1,163 @@ +# German (Switzerland, Informal) translation for WooCommerce Tier and Package Prices +# Copyright (C) 2025 Marco Graetsch +# This file is distributed under the GPL v2 or later. +msgid "" +msgstr "" +"Project-Id-Version: WooCommerce Tier and Package Prices 1.0.0\n" +"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n" +"POT-Creation-Date: 2025-12-21 00:00+0000\n" +"PO-Revision-Date: 2025-12-21 00:00+0000\n" +"Last-Translator: Marco Graetsch\n" +"Language-Team: German (Switzerland)\n" +"Language: de_CH_informal\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.0\n" +"X-Domain: wc-tier-package-prices\n" + +#: wc-tier-and-package-prices.php:41 +msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active." +msgstr "WooCommerce Staffel- und Paketpreise benötigt eine installierte und aktive WooCommerce-Installation." + +#: includes/class-wc-tpp-admin.php:21 +#: includes/class-wc-tpp-admin.php:22 +msgid "Tier & Package Prices" +msgstr "Staffel- & Paketpreise" + +#: includes/class-wc-tpp-admin.php:54 +msgid "Enable Tier Pricing" +msgstr "Staffelpreise aktivieren" + +#: includes/class-wc-tpp-admin.php:58 +msgid "Enable tier pricing for products" +msgstr "Staffelpreise für Produkte aktivieren" + +#: includes/class-wc-tpp-admin.php:63 +msgid "Enable Package Pricing" +msgstr "Paketpreise aktivieren" + +#: includes/class-wc-tpp-admin.php:67 +msgid "Enable fixed-price packages for products" +msgstr "Festpreis-Pakete für Produkte aktivieren" + +#: includes/class-wc-tpp-admin.php:72 +msgid "Display Pricing Table" +msgstr "Preistabelle anzeigen" + +#: includes/class-wc-tpp-admin.php:76 +msgid "Show tier and package pricing table on product pages" +msgstr "Staffel- und Paketpreis-Tabelle auf Produktseiten anzeigen" + +#: includes/class-wc-tpp-admin.php:81 +msgid "Display Position" +msgstr "Anzeigeposition" + +#: includes/class-wc-tpp-admin.php:85 +msgid "Before Add to Cart" +msgstr "Vor \"In den Warenkorb\"" + +#: includes/class-wc-tpp-admin.php:86 +msgid "After Add to Cart" +msgstr "Nach \"In den Warenkorb\"" + +#: includes/class-wc-tpp-admin.php:87 +msgid "After Price" +msgstr "Nach dem Preis" + +#: includes/class-wc-tpp-product-meta.php:23 +msgid "Tier Pricing" +msgstr "Staffelpreise" + +#: includes/class-wc-tpp-product-meta.php:24 +msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities." +msgstr "Mengenbasierte Preisstaffeln festlegen. Kunden erhalten günstigere Preise beim Kauf grösserer Mengen." + +#: includes/class-wc-tpp-product-meta.php:41 +msgid "Add Tier" +msgstr "Staffel hinzufügen" + +#: includes/class-wc-tpp-product-meta.php:52 +msgid "Package Pricing" +msgstr "Paketpreise" + +#: includes/class-wc-tpp-product-meta.php:53 +msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100." +msgstr "Festpreis-Pakete mit bestimmten Mengen festlegen. Zum Beispiel: 10 Stück für CHF 50.-, 25 Stück für CHF 100.-." + +#: includes/class-wc-tpp-product-meta.php:70 +msgid "Add Package" +msgstr "Paket hinzufügen" + +#: includes/class-wc-tpp-product-meta.php:90 +msgid "Minimum Quantity" +msgstr "Mindestmenge" + +#: includes/class-wc-tpp-product-meta.php:91 +msgid "e.g., 10" +msgstr "z.B. 10" + +#: includes/class-wc-tpp-product-meta.php:95 +#: includes/class-wc-tpp-product-meta.php:114 +msgid "e.g., 9.99" +msgstr "z.B. 9.90" + +#: includes/class-wc-tpp-product-meta.php:97 +#: includes/class-wc-tpp-product-meta.php:120 +msgid "Remove" +msgstr "Entfernen" + +#: includes/class-wc-tpp-product-meta.php:109 +#: includes/class-wc-tpp-frontend.php:75 +msgid "Quantity" +msgstr "Menge" + +#: includes/class-wc-tpp-product-meta.php:113 +msgid "Fixed Price" +msgstr "Festpreis" + +#: includes/class-wc-tpp-product-meta.php:117 +msgid "Label (Optional)" +msgstr "Bezeichnung (Optional)" + +#: includes/class-wc-tpp-product-meta.php:118 +msgid "e.g., Starter Pack" +msgstr "z.B. Starter-Paket" + +#: includes/class-wc-tpp-frontend.php:71 +msgid "Volume Discounts" +msgstr "Mengenrabatte" + +#: includes/class-wc-tpp-product-meta.php:94 +#: includes/class-wc-tpp-frontend.php:76 +msgid "Price per Unit" +msgstr "Preis pro Einheit" + +#: includes/class-wc-tpp-frontend.php:77 +msgid "You Save" +msgstr "Du sparst" + +#: includes/class-wc-tpp-frontend.php:110 +msgid "Package Deals" +msgstr "Paketangebote" + +#: includes/class-wc-tpp-frontend.php:123 +msgid "pieces" +msgstr "Stück" + +#: includes/class-wc-tpp-frontend.php:129 +msgid "per unit" +msgstr "pro Einheit" + +#: includes/class-wc-tpp-frontend.php:133 +msgid "Select Package" +msgstr "Paket auswählen" + +#: includes/class-wc-tpp-cart.php:63 +msgid "Package price" +msgstr "Paketpreis" + +#: includes/class-wc-tpp-cart.php:66 +msgid "Volume discount" +msgstr "Mengenrabatt" diff --git a/languages/wc-tier-package-prices-de_DE.mo b/languages/wc-tier-package-prices-de_DE.mo new file mode 100644 index 0000000..e9d73a7 Binary files /dev/null and b/languages/wc-tier-package-prices-de_DE.mo differ diff --git a/languages/wc-tier-package-prices-de_DE.po b/languages/wc-tier-package-prices-de_DE.po new file mode 100644 index 0000000..66ec22c --- /dev/null +++ b/languages/wc-tier-package-prices-de_DE.po @@ -0,0 +1,163 @@ +# German (Germany) translation for WooCommerce Tier and Package Prices +# Copyright (C) 2025 Marco Graetsch +# This file is distributed under the GPL v2 or later. +msgid "" +msgstr "" +"Project-Id-Version: WooCommerce Tier and Package Prices 1.0.0\n" +"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n" +"POT-Creation-Date: 2025-12-21 00:00+0000\n" +"PO-Revision-Date: 2025-12-21 00:00+0000\n" +"Last-Translator: Marco Graetsch\n" +"Language-Team: German\n" +"Language: de_DE\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.0\n" +"X-Domain: wc-tier-package-prices\n" + +#: wc-tier-and-package-prices.php:41 +msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active." +msgstr "WooCommerce Staffel- und Paketpreise erfordert, dass WooCommerce installiert und aktiviert ist." + +#: includes/class-wc-tpp-admin.php:21 +#: includes/class-wc-tpp-admin.php:22 +msgid "Tier & Package Prices" +msgstr "Staffel- & Paketpreise" + +#: includes/class-wc-tpp-admin.php:54 +msgid "Enable Tier Pricing" +msgstr "Staffelpreise aktivieren" + +#: includes/class-wc-tpp-admin.php:58 +msgid "Enable tier pricing for products" +msgstr "Staffelpreise für Produkte aktivieren" + +#: includes/class-wc-tpp-admin.php:63 +msgid "Enable Package Pricing" +msgstr "Paketpreise aktivieren" + +#: includes/class-wc-tpp-admin.php:67 +msgid "Enable fixed-price packages for products" +msgstr "Festpreis-Pakete für Produkte aktivieren" + +#: includes/class-wc-tpp-admin.php:72 +msgid "Display Pricing Table" +msgstr "Preistabelle anzeigen" + +#: includes/class-wc-tpp-admin.php:76 +msgid "Show tier and package pricing table on product pages" +msgstr "Staffel- und Paketpreis-Tabelle auf Produktseiten anzeigen" + +#: includes/class-wc-tpp-admin.php:81 +msgid "Display Position" +msgstr "Anzeigeposition" + +#: includes/class-wc-tpp-admin.php:85 +msgid "Before Add to Cart" +msgstr "Vor \"In den Warenkorb\"" + +#: includes/class-wc-tpp-admin.php:86 +msgid "After Add to Cart" +msgstr "Nach \"In den Warenkorb\"" + +#: includes/class-wc-tpp-admin.php:87 +msgid "After Price" +msgstr "Nach dem Preis" + +#: includes/class-wc-tpp-product-meta.php:23 +msgid "Tier Pricing" +msgstr "Staffelpreise" + +#: includes/class-wc-tpp-product-meta.php:24 +msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities." +msgstr "Mengenbasierte Preisstaffeln festlegen. Kunden erhalten Rabatte beim Kauf größerer Mengen." + +#: includes/class-wc-tpp-product-meta.php:41 +msgid "Add Tier" +msgstr "Staffel hinzufügen" + +#: includes/class-wc-tpp-product-meta.php:52 +msgid "Package Pricing" +msgstr "Paketpreise" + +#: includes/class-wc-tpp-product-meta.php:53 +msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100." +msgstr "Festpreis-Pakete mit bestimmten Mengen festlegen. Zum Beispiel: 10 Stück für 50€, 25 Stück für 100€." + +#: includes/class-wc-tpp-product-meta.php:70 +msgid "Add Package" +msgstr "Paket hinzufügen" + +#: includes/class-wc-tpp-product-meta.php:90 +msgid "Minimum Quantity" +msgstr "Mindestmenge" + +#: includes/class-wc-tpp-product-meta.php:91 +msgid "e.g., 10" +msgstr "z.B. 10" + +#: includes/class-wc-tpp-product-meta.php:95 +#: includes/class-wc-tpp-product-meta.php:114 +msgid "e.g., 9.99" +msgstr "z.B. 9,99" + +#: includes/class-wc-tpp-product-meta.php:97 +#: includes/class-wc-tpp-product-meta.php:120 +msgid "Remove" +msgstr "Entfernen" + +#: includes/class-wc-tpp-product-meta.php:109 +#: includes/class-wc-tpp-frontend.php:75 +msgid "Quantity" +msgstr "Menge" + +#: includes/class-wc-tpp-product-meta.php:113 +msgid "Fixed Price" +msgstr "Festpreis" + +#: includes/class-wc-tpp-product-meta.php:117 +msgid "Label (Optional)" +msgstr "Bezeichnung (Optional)" + +#: includes/class-wc-tpp-product-meta.php:118 +msgid "e.g., Starter Pack" +msgstr "z.B. Starter-Paket" + +#: includes/class-wc-tpp-frontend.php:71 +msgid "Volume Discounts" +msgstr "Mengenrabatte" + +#: includes/class-wc-tpp-product-meta.php:94 +#: includes/class-wc-tpp-frontend.php:76 +msgid "Price per Unit" +msgstr "Preis pro Einheit" + +#: includes/class-wc-tpp-frontend.php:77 +msgid "You Save" +msgstr "Sie sparen" + +#: includes/class-wc-tpp-frontend.php:110 +msgid "Package Deals" +msgstr "Paketangebote" + +#: includes/class-wc-tpp-frontend.php:123 +msgid "pieces" +msgstr "Stück" + +#: includes/class-wc-tpp-frontend.php:129 +msgid "per unit" +msgstr "pro Einheit" + +#: includes/class-wc-tpp-frontend.php:133 +msgid "Select Package" +msgstr "Paket auswählen" + +#: includes/class-wc-tpp-cart.php:63 +msgid "Package price" +msgstr "Paketpreis" + +#: includes/class-wc-tpp-cart.php:66 +msgid "Volume discount" +msgstr "Mengenrabatt" diff --git a/languages/wc-tier-package-prices-en_US.mo b/languages/wc-tier-package-prices-en_US.mo new file mode 100644 index 0000000..2010611 Binary files /dev/null and b/languages/wc-tier-package-prices-en_US.mo differ diff --git a/languages/wc-tier-package-prices-en_US.po b/languages/wc-tier-package-prices-en_US.po new file mode 100644 index 0000000..d765932 --- /dev/null +++ b/languages/wc-tier-package-prices-en_US.po @@ -0,0 +1,163 @@ +# English (US) translation for WooCommerce Tier and Package Prices +# Copyright (C) 2025 Marco Graetsch +# This file is distributed under the GPL v2 or later. +msgid "" +msgstr "" +"Project-Id-Version: WooCommerce Tier and Package Prices 1.0.0\n" +"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n" +"POT-Creation-Date: 2025-12-21 00:00+0000\n" +"PO-Revision-Date: 2025-12-21 00:00+0000\n" +"Last-Translator: Marco Graetsch\n" +"Language-Team: English\n" +"Language: en_US\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 3.0\n" +"X-Domain: wc-tier-package-prices\n" + +#: wc-tier-and-package-prices.php:41 +msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active." +msgstr "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active." + +#: includes/class-wc-tpp-admin.php:21 +#: includes/class-wc-tpp-admin.php:22 +msgid "Tier & Package Prices" +msgstr "Tier & Package Prices" + +#: includes/class-wc-tpp-admin.php:54 +msgid "Enable Tier Pricing" +msgstr "Enable Tier Pricing" + +#: includes/class-wc-tpp-admin.php:58 +msgid "Enable tier pricing for products" +msgstr "Enable tier pricing for products" + +#: includes/class-wc-tpp-admin.php:63 +msgid "Enable Package Pricing" +msgstr "Enable Package Pricing" + +#: includes/class-wc-tpp-admin.php:67 +msgid "Enable fixed-price packages for products" +msgstr "Enable fixed-price packages for products" + +#: includes/class-wc-tpp-admin.php:72 +msgid "Display Pricing Table" +msgstr "Display Pricing Table" + +#: includes/class-wc-tpp-admin.php:76 +msgid "Show tier and package pricing table on product pages" +msgstr "Show tier and package pricing table on product pages" + +#: includes/class-wc-tpp-admin.php:81 +msgid "Display Position" +msgstr "Display Position" + +#: includes/class-wc-tpp-admin.php:85 +msgid "Before Add to Cart" +msgstr "Before Add to Cart" + +#: includes/class-wc-tpp-admin.php:86 +msgid "After Add to Cart" +msgstr "After Add to Cart" + +#: includes/class-wc-tpp-admin.php:87 +msgid "After Price" +msgstr "After Price" + +#: includes/class-wc-tpp-product-meta.php:23 +msgid "Tier Pricing" +msgstr "Tier Pricing" + +#: includes/class-wc-tpp-product-meta.php:24 +msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities." +msgstr "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities." + +#: includes/class-wc-tpp-product-meta.php:41 +msgid "Add Tier" +msgstr "Add Tier" + +#: includes/class-wc-tpp-product-meta.php:52 +msgid "Package Pricing" +msgstr "Package Pricing" + +#: includes/class-wc-tpp-product-meta.php:53 +msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100." +msgstr "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100." + +#: includes/class-wc-tpp-product-meta.php:70 +msgid "Add Package" +msgstr "Add Package" + +#: includes/class-wc-tpp-product-meta.php:90 +msgid "Minimum Quantity" +msgstr "Minimum Quantity" + +#: includes/class-wc-tpp-product-meta.php:91 +msgid "e.g., 10" +msgstr "e.g., 10" + +#: includes/class-wc-tpp-product-meta.php:95 +#: includes/class-wc-tpp-product-meta.php:114 +msgid "e.g., 9.99" +msgstr "e.g., 9.99" + +#: includes/class-wc-tpp-product-meta.php:97 +#: includes/class-wc-tpp-product-meta.php:120 +msgid "Remove" +msgstr "Remove" + +#: includes/class-wc-tpp-product-meta.php:109 +#: includes/class-wc-tpp-frontend.php:75 +msgid "Quantity" +msgstr "Quantity" + +#: includes/class-wc-tpp-product-meta.php:113 +msgid "Fixed Price" +msgstr "Fixed Price" + +#: includes/class-wc-tpp-product-meta.php:117 +msgid "Label (Optional)" +msgstr "Label (Optional)" + +#: includes/class-wc-tpp-product-meta.php:118 +msgid "e.g., Starter Pack" +msgstr "e.g., Starter Pack" + +#: includes/class-wc-tpp-frontend.php:71 +msgid "Volume Discounts" +msgstr "Volume Discounts" + +#: includes/class-wc-tpp-product-meta.php:94 +#: includes/class-wc-tpp-frontend.php:76 +msgid "Price per Unit" +msgstr "Price per Unit" + +#: includes/class-wc-tpp-frontend.php:77 +msgid "You Save" +msgstr "You Save" + +#: includes/class-wc-tpp-frontend.php:110 +msgid "Package Deals" +msgstr "Package Deals" + +#: includes/class-wc-tpp-frontend.php:123 +msgid "pieces" +msgstr "pieces" + +#: includes/class-wc-tpp-frontend.php:129 +msgid "per unit" +msgstr "per unit" + +#: includes/class-wc-tpp-frontend.php:133 +msgid "Select Package" +msgstr "Select Package" + +#: includes/class-wc-tpp-cart.php:63 +msgid "Package price" +msgstr "Package price" + +#: includes/class-wc-tpp-cart.php:66 +msgid "Volume discount" +msgstr "Volume discount" diff --git a/languages/wc-tier-package-prices.pot b/languages/wc-tier-package-prices.pot new file mode 100644 index 0000000..020291a --- /dev/null +++ b/languages/wc-tier-package-prices.pot @@ -0,0 +1,166 @@ +# Copyright (C) 2025 Marco Graetsch +# This file is distributed under the GPL v2 or later. +msgid "" +msgstr "" +"Project-Id-Version: WooCommerce Tier and Package Prices 1.0.0\n" +"Report-Msgid-Bugs-To: https://src.bundespruefstelle.ch/wc-tier-package-prices\n" +"POT-Creation-Date: 2025-12-21 00:00+0000\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"PO-Revision-Date: 2025-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"X-Generator: Poedit 3.0\n" +"X-Domain: wc-tier-package-prices\n" + +#: wc-tier-and-package-prices.php:41 +msgid "WooCommerce Tier and Package Prices requires WooCommerce to be installed and active." +msgstr "" + +#: includes/class-wc-tpp-admin.php:21 +#: includes/class-wc-tpp-admin.php:22 +msgid "Tier & Package Prices" +msgstr "" + +#: includes/class-wc-tpp-admin.php:54 +msgid "Enable Tier Pricing" +msgstr "" + +#: includes/class-wc-tpp-admin.php:58 +msgid "Enable tier pricing for products" +msgstr "" + +#: includes/class-wc-tpp-admin.php:63 +msgid "Enable Package Pricing" +msgstr "" + +#: includes/class-wc-tpp-admin.php:67 +msgid "Enable fixed-price packages for products" +msgstr "" + +#: includes/class-wc-tpp-admin.php:72 +msgid "Display Pricing Table" +msgstr "" + +#: includes/class-wc-tpp-admin.php:76 +msgid "Show tier and package pricing table on product pages" +msgstr "" + +#: includes/class-wc-tpp-admin.php:81 +msgid "Display Position" +msgstr "" + +#: includes/class-wc-tpp-admin.php:85 +msgid "Before Add to Cart" +msgstr "" + +#: includes/class-wc-tpp-admin.php:86 +msgid "After Add to Cart" +msgstr "" + +#: includes/class-wc-tpp-admin.php:87 +msgid "After Price" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:23 +msgid "Tier Pricing" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:24 +msgid "Set quantity-based pricing tiers. Customers get discounted prices when buying in larger quantities." +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:41 +msgid "Add Tier" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:52 +msgid "Package Pricing" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:53 +msgid "Set fixed-price packages with specific quantities. For example: 10 pieces for $50, 25 pieces for $100." +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:70 +msgid "Add Package" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:90 +msgid "Minimum Quantity" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:91 +msgid "e.g., 10" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:94 +msgid "Price per Unit" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:95 +#: includes/class-wc-tpp-product-meta.php:114 +msgid "e.g., 9.99" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:97 +#: includes/class-wc-tpp-product-meta.php:120 +msgid "Remove" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:109 +msgid "Quantity" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:113 +msgid "Fixed Price" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:117 +msgid "Label (Optional)" +msgstr "" + +#: includes/class-wc-tpp-product-meta.php:118 +msgid "e.g., Starter Pack" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:71 +msgid "Volume Discounts" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:75 +msgid "Quantity" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:76 +msgid "Price per Unit" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:77 +msgid "You Save" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:110 +msgid "Package Deals" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:123 +msgid "pieces" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:129 +msgid "per unit" +msgstr "" + +#: includes/class-wc-tpp-frontend.php:133 +msgid "Select Package" +msgstr "" + +#: includes/class-wc-tpp-cart.php:63 +msgid "Package price" +msgstr "" + +#: includes/class-wc-tpp-cart.php:66 +msgid "Volume discount" +msgstr "" diff --git a/templates/admin/package-row.twig b/templates/admin/package-row.twig new file mode 100644 index 0000000..3f11a6c --- /dev/null +++ b/templates/admin/package-row.twig @@ -0,0 +1,36 @@ +{# + # Admin Package Row Template + # + # @package WC_Tier_Package_Prices + # @var int index + # @var array package + #} +
+

+ + +

+

+ + +

+

+ + +

+ +
diff --git a/templates/admin/settings-page.twig b/templates/admin/settings-page.twig new file mode 100644 index 0000000..e2525cf --- /dev/null +++ b/templates/admin/settings-page.twig @@ -0,0 +1,53 @@ +{# + # Admin Settings Page Template + # + # @package WC_Tier_Package_Prices + #} +
+

{{ get_admin_page_title()|esc_html }}

+
+ {{ settings_fields('wc_tpp_settings') }} + + + + + + + + + + + + + + + + + +
+ + + +

{{ 'Enable tier pricing for products'|__('wc-tier-package-prices') }}

+
+ + + +

{{ 'Enable fixed-price packages for products'|__('wc-tier-package-prices') }}

+
+ + + +

{{ 'Show tier and package pricing table on product pages'|__('wc-tier-package-prices') }}

+
+ + + +
+ {{ submit_button() }} +
+
diff --git a/templates/admin/tier-row.twig b/templates/admin/tier-row.twig new file mode 100644 index 0000000..dbd3c6d --- /dev/null +++ b/templates/admin/tier-row.twig @@ -0,0 +1,28 @@ +{# + # Admin Tier Row Template + # + # @package WC_Tier_Package_Prices + # @var int index + # @var array tier + #} +
+

+ + +

+

+ + +

+ +
diff --git a/templates/frontend/package-pricing-display.twig b/templates/frontend/package-pricing-display.twig new file mode 100644 index 0000000..5511537 --- /dev/null +++ b/templates/frontend/package-pricing-display.twig @@ -0,0 +1,35 @@ +{# + # Frontend Package Pricing Display Template + # + # @package WC_Tier_Package_Prices + # @var array packages + #} +
+

{{ 'Package Deals'|__('wc-tier-package-prices') }}

+
+ {% for index, package in packages %} + {% set price_per_unit = package.qty > 0 ? package.price / package.qty : 0 %} +
+
+ {% if package.label is not empty %} +

{{ package.label|esc_html }}

+ {% endif %} +
+
+
+ {{ package.qty|esc_html }} {{ 'pieces'|__('wc-tier-package-prices') }} +
+
+ {{ wc_price(package.price)|raw }} +
+
+ {{ wc_price(price_per_unit)|raw }} {{ 'per unit'|__('wc-tier-package-prices') }} +
+
+ +
+ {% endfor %} +
+
diff --git a/templates/frontend/pricing-table.twig b/templates/frontend/pricing-table.twig new file mode 100644 index 0000000..036c2da --- /dev/null +++ b/templates/frontend/pricing-table.twig @@ -0,0 +1,17 @@ +{# + # Frontend Pricing Table Template + # + # @package WC_Tier_Package_Prices + # @var object product + # @var array tiers + # @var array packages + #} +
+ {% if tiers is not empty and get_option('wc_tpp_enable_tier_pricing') == 'yes' %} + {% include 'frontend/tier-pricing-table.twig' with {'product': product, 'tiers': tiers} %} + {% endif %} + + {% if packages is not empty and get_option('wc_tpp_enable_package_pricing') == 'yes' %} + {% include 'frontend/package-pricing-display.twig' with {'packages': packages} %} + {% endif %} +
diff --git a/templates/frontend/tier-pricing-table.twig b/templates/frontend/tier-pricing-table.twig new file mode 100644 index 0000000..0c25719 --- /dev/null +++ b/templates/frontend/tier-pricing-table.twig @@ -0,0 +1,41 @@ +{# + # Frontend Tier Pricing Table Template + # + # @package WC_Tier_Package_Prices + # @var object product + # @var array tiers + #} +
+

{{ 'Volume Discounts'|__('wc-tier-package-prices') }}

+ + + + + + + + + + {% set regular_price = product.get_regular_price() %} + {% for tier in tiers %} + {% set savings = 0 %} + {% set savings_percent = 0 %} + {% if regular_price > 0 %} + {% set savings = regular_price - tier.price %} + {% set savings_percent = (savings / regular_price) * 100 %} + {% endif %} + + + + + + {% endfor %} + +
{{ 'Quantity'|__('wc-tier-package-prices') }}{{ 'Price per Unit'|__('wc-tier-package-prices') }}{{ 'You Save'|__('wc-tier-package-prices') }}
{{ tier.min_qty|esc_html }}+{{ wc_price(tier.price)|raw }} + {% if savings > 0 %} + {{ wc_price(savings)|raw }} ({{ savings_percent|round(2) }}%) + {% else %} + - + {% endif %} +
+
diff --git a/wc-tier-and-package-prices.php b/wc-tier-and-package-prices.php new file mode 100644 index 0000000..56732ef --- /dev/null +++ b/wc-tier-and-package-prices.php @@ -0,0 +1,111 @@ + +
+

+
+ init_hooks(); + $this->includes(); + } + + private function init_hooks() { + add_action('plugins_loaded', array($this, 'load_textdomain')); + add_action('before_woocommerce_init', array($this, 'declare_hpos_compatibility')); + register_activation_hook(__FILE__, array($this, 'activate')); + register_deactivation_hook(__FILE__, array($this, 'deactivate')); + } + + private function includes() { + require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-template-loader.php'; + require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-admin.php'; + require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-product-meta.php'; + require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-frontend.php'; + require_once WC_TPP_PLUGIN_DIR . 'includes/class-wc-tpp-cart.php'; + } + + public function declare_hpos_compatibility() { + if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) { + \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); + } + } + + public function load_textdomain() { + load_plugin_textdomain('wc-tier-package-prices', false, dirname(WC_TPP_PLUGIN_BASENAME) . '/languages'); + } + + public function activate() { + // Add default options + add_option('wc_tpp_enable_tier_pricing', 'yes'); + add_option('wc_tpp_enable_package_pricing', 'yes'); + add_option('wc_tpp_display_table', 'yes'); + flush_rewrite_rules(); + } + + public function deactivate() { + flush_rewrite_rules(); + } +} + +// Initialize the plugin +function wc_tpp_init() { + return WC_Tier_Package_Prices::get_instance(); +} + +add_action('plugins_loaded', 'wc_tpp_init', 11);