magendoo / module-base-price
EU Base Price (Grundpreis) display for Magento 2. Shows price per reference unit (kg, litre, m) on product pages, category listings, search results and cart. Compliant with EU Price Indication Directive 98/6/EC and German PAngV.
Package info
github.com/magendooro/module-base-price
Type:magento2-module
pkg:composer/magendoo/module-base-price
Fund package maintenance!
Requires
- php: >=8.1
- magento/framework: >=102.0.0
- magento/module-backend: *
- magento/module-catalog: *
- magento/module-config: *
- magento/module-eav: *
- magento/module-store: *
- magento/module-ui: *
Requires (Dev)
- phpstan/phpstan: ^1.10 || ^2.0
- phpunit/phpunit: ^10.5
This package is auto-updated.
Last update: 2026-04-13 13:29:49 UTC
README
Displays a per-unit reference price (Grundpreis) next to every product's selling price across product pages, category listings, search results, and the shopping cart.
A customer buying a 300 g shampoo at €4.99 also sees "Base price: €16.63 / 1 kilogram", enabling direct price comparison across different pack sizes and brands.
Legal background
Displaying a unit reference price is legally required in several jurisdictions:
- Germany — Preisangabenverordnung (PAngV §4)
- EU member states — Price Indication Directive 98/6/EC, updated by Directive 2019/2161 (Omnibus)
The obligation applies to products sold by weight, volume, length, area, or piece count that are offered in different pack sizes. Consult your legal adviser to determine your specific requirements.
Requirements
| Dependency | Version |
|---|---|
| Magento Open Source / Adobe Commerce | 2.4.x |
| PHP | 8.1 or higher |
Installation
Via Composer (recommended)
composer require magendoo/module-base-price bin/magento setup:upgrade bin/magento setup:di:compile bin/magento cache:flush
Manual
mkdir -p app/code/Magendoo/BasePrice
# Copy all module files into that directory
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush
setup:upgrade creates the magendoo_baseprice_unit table, adds four EAV product attributes, and seeds nine SI measurement units.
Configuration
Navigate to Stores → Configuration → Magendoo Extensions → Base Price.
General
| Field | Default | Description |
|---|---|---|
| Enable Base Price | Yes | Master switch. Disabling this hides base prices everywhere. |
Product Page
| Field | Default | Description |
|---|---|---|
| Enable on Product Pages | Yes | Show base price on individual product pages. |
| Display Template | Base price: {{unit_price}} / {{reference_amount}} {{reference_unit}} |
Output format. Supports all template variables. |
| CSS Class | base-price |
Class applied to the wrapping <div>. |
Category & Search
| Field | Default | Description |
|---|---|---|
| Enable on Category Pages | Yes | Show base price on category listing pages. |
| Category Display Template | ({{unit_price}} / {{reference_amount}} {{reference_unit}}) |
Output format for listing cards. |
| Enable on Search Results | Yes | Show base price on catalogsearch result pages. |
Cart
| Field | Default | Description |
|---|---|---|
| Enable on Cart Page | Yes | Show base price in the shopping cart item rows. |
| Cart Display Template | Base price: {{unit_price}} / {{reference_amount}} {{reference_unit}} |
Output format for cart rows. |
| Enable on Mini-Cart | Yes | Reserved for future Knockout.js integration. |
Template variables
All display templates accept the following placeholders:
| Variable | Description | Example output |
|---|---|---|
{{unit_price}} |
Formatted price per reference unit | €16.63 |
{{reference_amount}} |
Reference quantity | 1 |
{{reference_unit}} |
Reference unit label | kilogram |
{{product_amount}} |
Numeric amount in the product | 300 |
{{measure_unit}} |
Name of the product's measurement unit | gram |
{{product_price}} |
Selling price formatted via store currency | €4.99 |
Unknown placeholders are left intact, so you can safely mix in static text.
Formula
base_price = (selling_price ÷ product_amount) × (reference_amount × rate)
Where rate is the conversion factor from the product unit to the reference unit (e.g. 1 000 for gram → kilogram).
Examples:
| Product | Price | Amount | Unit | Calculation | Base price |
|---|---|---|---|---|---|
| Shampoo | €4.99 | 300 g | gram | (4.99 ÷ 300) × (1 × 1 000) | €16.63 / kg |
| Wine | €9.99 | 0.75 l | litre | (9.99 ÷ 0.75) × (1 × 1) | €13.32 / litre |
| Ribbon | €5.00 | 200 cm | centimetre | (5.00 ÷ 200) × (1 × 100) | €2.50 / metre |
| 6-pack | €5.94 | 6 | piece | (5.94 ÷ 6) × (1 × 1) | €0.99 / piece |
The result is rounded to 4 decimal places before formatting.
Reference amount override
A product can override the unit's default reference amount. Set Reference Amount to 0.1 on a gram-unit product to show the price per 100 g instead of per kilogram:
(4.99 ÷ 300) × (0.1 × 1 000) = €1.66 / 100 g
Unit management
Navigate to Catalog → Base Price Units in the admin panel.
Nine SI units are pre-installed:
| ID | Unit | Reference unit | Rate |
|---|---|---|---|
| 1 | gram | kilogram | 1 000 |
| 2 | milligram | kilogram | 1 000 000 |
| 3 | kilogram | kilogram | 1 |
| 4 | millilitre | litre | 1 000 |
| 5 | centilitre | litre | 100 |
| 6 | litre | litre | 1 |
| 7 | centimetre | metre | 100 |
| 8 | metre | metre | 1 |
| 9 | piece | piece | 1 |
Adding a custom unit
Click Add New Unit and fill in:
| Field | Description | Example |
|---|---|---|
| Name | Display name of the product unit | fluid ounce |
| Reference Unit Label | The reference unit shown in the output | litre |
| Rate | Multiplier to convert to the reference unit | 33.814 |
| Reference Amount | Default quantity shown in the output | 1 |
| Position | Sort order in the admin grid | 100 |
| Status | Active / Inactive | Active |
Configuring products
Admin UI
Open any product in the admin and locate the Base Price panel (below the standard price fields):
| Field | Description |
|---|---|
| Enable Base Price | Override the module-level enable/disable for this specific product. |
| Product Amount | Numeric quantity contained in the package (e.g. 300 for 300 g). |
| Unit | Select from the unit list (gram, millilitre, etc.). |
| Reference Amount | Leave blank to use the unit's default. Set a value to show a custom reference (e.g. 0.1 for "per 100 g"). |
REST API — set a product's base price
TOKEN=$(curl -s -X POST https://your-store.com/rest/V1/integration/admin/token \ -H 'Content-Type: application/json' \ -d '{"username":"admin","password":"your-password"}' | tr -d '"') # Set base price (300 g, gram unit = ID 1) curl -X PUT https://your-store.com/rest/V1/baseprice/products/24-MB01 \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "productBasePrice": { "sku": "24-MB01", "product_amount": 300, "unit_id": 1 } }' # Enable it on the product curl -X PUT https://your-store.com/rest/V1/products/24-MB01 \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "product": { "sku": "24-MB01", "custom_attributes": [ {"attribute_code": "baseprice_is_enabled", "value": "1"} ] } }'
REST API reference
Units (admin token required)
| Method | Endpoint | Description |
|---|---|---|
| GET | /V1/baseprice/units |
List units (supports searchCriteria) |
| GET | /V1/baseprice/units/:unitId |
Get unit by ID |
| POST | /V1/baseprice/units |
Create a unit |
| PUT | /V1/baseprice/units/:unitId |
Update a unit |
| DELETE | /V1/baseprice/units/:unitId |
Delete a unit |
Create a unit:
curl -X POST https://your-store.com/rest/V1/baseprice/units \ -H "Authorization: Bearer $TOKEN" \ -H 'Content-Type: application/json' \ -d '{ "unit": { "name": "fluid ounce", "reference_unit_label": "litre", "rate": 33.814, "reference_amount": 1, "position": 100, "active": true } }'
Note: The active flag is sent as
active, notis_active. Magento's serialiser strips theisprefix from getter method names when building the response body.
Product base price (admin token required)
| Method | Endpoint | Description |
|---|---|---|
| GET | /V1/baseprice/products/:sku |
Get base price config for a product |
| PUT | /V1/baseprice/products/:sku |
Set base price config |
| DELETE | /V1/baseprice/products/:sku |
Remove base price config |
Calculator (anonymous)
GET /V1/baseprice/calculate?price=4.99&productAmount=300&unitId=1
Returns the raw base price as a float. Optional parameter: referenceAmountOverride.
curl 'https://your-store.com/rest/V1/baseprice/calculate?price=4.99&productAmount=300&unitId=1' # → 16.6333
Architecture notes
- EAV attributes — Four product attributes (
baseprice_is_enabled,baseprice_product_amount,baseprice_unit_id,baseprice_reference_amount) are registered withused_in_product_listing = trueso they load in category and search collections. - Listing injection — A plugin on
Magento\Catalog\Block\Product\AbstractProduct::getProductDetailsHtml()appends the base price HTML to every product card, covering all product types without type-specific renderer registration. - Cart injection — The cart block is a child of Magento's
additional.product.infoblock. The cart item template iterates through its children and callssetItem()+toHtml()on each. - Product page — A standard
Templateblock is added viacatalog_product_view.xmland the product block is retrieved directly from layout. - Template rendering —
Model/TemplateRendererdoes a plainstr_replaceover{{variable}}placeholders. HTML escaping is the caller's responsibility;priceCurrency->format()handles currency formatting.
Releases and Packagist
Creating a release
Tag a version and push — the Release workflow runs tests then creates a GitHub release automatically:
git tag v1.2.0 git push origin v1.2.0
Version numbers follow Semantic Versioning. Update CHANGELOG.md and composer.json ("version") before tagging.
Publishing to Packagist
-
Go to packagist.org/packages/submit and enter:
https://github.com/magendooro/module-base-price -
After submission, enable auto-update so Packagist picks up new tags automatically:
- In the Packagist package settings, copy the webhook URL
- In the GitHub repository: Settings → Webhooks → Add webhook → paste the URL
- Content type:
application/json, events: Just the push event
Alternatively, connect your GitHub account to Packagist (Settings → Profile → GitHub) which handles webhooks automatically.
GitHub Secrets required for CI
The workflows install Magento packages from repo.magento.com, which requires Marketplace credentials. Add these to the repository as Actions secrets (Settings → Secrets and variables → Actions):
| Secret | Description |
|---|---|
MAGENTO_MARKETPLACE_PUBLIC_KEY |
Public key from marketplace.magento.com/customer/accessKeys |
MAGENTO_MARKETPLACE_PRIVATE_KEY |
Corresponding private key |
Without these secrets the CI runs will fail at the composer install step.
Running tests
PHPUnit (unit tests)
# From the Magento root vendor/bin/phpunit --filter Magendoo\\BasePrice app/code/Magendoo/BasePrice/Test/Unit
Playwright (functional tests)
The functional test suite lives in dev/tests/functional/playwright/tests/baseprice/ and covers:
baseprice-calculate-combinations.spec.js— All nine seed units,referenceAmountOverride, edge cases, formula consistency (45 tests, no products or admin setup needed — hits the anonymous/V1/baseprice/calculateendpoint directly)baseprice-product-display.spec.js— Product detail page, category listing, cart page, disabled state (22 tests, sets up three sample products inbeforeAlland restores them inafterAll)
cd dev/tests/functional/playwright npx playwright test tests/baseprice/
Uninstall
bin/magento module:uninstall Magendoo_BasePrice bin/magento setup:upgrade bin/magento cache:flush
This removes the magendoo_baseprice_unit table and the four EAV product attributes.
License
MIT — see LICENSE for details.