Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .wordlist.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ ArrayFacade
AssociationField
AsyncPaymentTransactionStruct
AsynchronousPaymentHandlerInterface
AudienceContext
AudienceContextResolver
AuditLogValueEntity
AuthenticationIdentityLoader
AutoIncrementField
Expand Down Expand Up @@ -470,6 +472,15 @@ Iframe
ImportTranslationsTrait
Inclusivity
IndexerService
IndividualPricingApplyExtension
IndividualPricingBuildCacheSingleRuleMessage
IndividualPricingCacheEntryUpdaterMessage
IndividualPricingComputedCacheEntity
IndividualPricingIndexerEvent
IndividualPricingIndexingMessage
IndividualPricingLookupBatchCriteriaEvent
IndividualPricingLookupCriteriaEvent
IndividualPricingProductSubscriber
Init
Initialisms
IntField
Expand Down Expand Up @@ -761,6 +772,7 @@ ProductController
ProductCountRouteResponse
ProductDataSelection
ProductDataSet
ProductEntity
ProductListRoute
ProductManufacturerDefinition
ProductMedia
Expand Down Expand Up @@ -1105,6 +1117,8 @@ YamlFileLoader
ZSH
accel
acl
actionAmount
actionType
actionability
activateShopwareTheme
adr
Expand All @@ -1122,6 +1136,7 @@ api
apiKey
apk
appVersion
applyToAllProducts
args
arrayfacade
async
Expand Down Expand Up @@ -1273,6 +1288,7 @@ customerAware
customerGroup
customerGroupAware
customerHasFeature
customerId
customerRecovery
customizability
customizable
Expand Down Expand Up @@ -1491,6 +1507,7 @@ inclusivity
incrementer
incrementing
indexActions
individualPricingId
infoIt
ini
init
Expand Down Expand Up @@ -1657,6 +1674,7 @@ orderIds
orderTransaction
orderTransactionCaptureRefund
org's
organizationUnitIds
otel
otlp
overrideComponentSetup
Expand Down Expand Up @@ -1723,6 +1741,7 @@ pricefactory
productCountRoute
productId
productNumber
productStreamId
productproxy
productsfacade
profiler
Expand All @@ -1737,6 +1756,8 @@ pyspelling
qa
qa@shopware.com
qty's
qtyFrom
qtyTo
quantityBefore
quantityDelta
quickstart
Expand Down Expand Up @@ -1845,6 +1866,7 @@ shopware's
shopwarelabs
shopwarepartners
shorthands
showStrikeThrough
simples
simplexml
skipOnFeature
Expand Down Expand Up @@ -1974,11 +1996,14 @@ upsert
uri
url
urls
useValidityRange
userAware
userRecovery
userland
utils
uuid
validFrom
validUntil
validator
validators
varchar
Expand Down
8 changes: 5 additions & 3 deletions products/extensions/b2b-components/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ In the world of digital B2B commerce, where businesses engage with other compani

* **Order Approval** allows for a more controlled buying process by introducing an approval workflow.

* **Individual Pricing** enables merchants to define catalog-wide discounts and special pricing based on flexible conditions for B2B scenarios, including volume pricing and company-specific pricing agreements.

* **Quick Order and Shopping List** takes care of distinctive B2B buying behaviors.

* **Organization Unit** allows for the configuration of more differentiated and specific access rights to meet the needs of businesses with complex structures.
Expand Down Expand Up @@ -84,7 +86,7 @@ We will place this check before every route, controller or API as follows:

```php
use Shopware\Commercial\B2B\QuickOrder\Domain\CustomerSpecificFeature\CustomerSpecificFeatureService;

class ApiController
{
public function __construct(private readonly CustomerSpecificFeatureService $customerSpecificFeatureService)
Expand Down Expand Up @@ -131,11 +133,11 @@ class CustomerSpecificFeatureTwigExtension extends AbstractExtension
if (\array_key_exists('context', $twigContext) && $twigContext['context'] instanceof SalesChannelContext) {
$customerId = $twigContext['context']->getCustomerId();
}

if (!$customerId) {
return false;
}

return $this->customerSpecificFeatureService->isAllowed($customerId, $feature);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
---
nav:
title: Entities and Workflow
position: 10

---

# Entities and Workflow

## Entities

### Individual pricing

Individual Pricing entity is the main configuration entity that defines a pricing rule. It contains all the settings needed to determine which products get special pricing, who receives it, and how the pricing is calculated.

**Key properties:**

- **name**: Human-readable name for the pricing rule
- **description**: Optional description of the pricing rule
- **active**: Boolean flag to enable/disable the rule
- **priority**: Integer value determining evaluation order (higher values = higher priority)
- **target**: Type of audience (companies, tags)
- **actionType**: Type of price modification (by_percent, by_fixed, to_fixed, volume_pricing)
- **actionAmount**: Amount for the pricing action (percentage or fixed value)
- **applyToAllProducts**: If true, applies to all products in the catalog
- **productStreamId**: Reference to a Shopware product stream for filtering specific products
- **useValidityRange**: Boolean indicating if time-based validity is used
- **validFrom**: Start date/time for the pricing rule
- **validUntil**: End date/time for the pricing rule
- **showStrikeThrough**: Whether to show original price with strike-through

**Action Types:**

- `by_percent`: Reduce price by a percentage (e.g., 10% off)
- `by_fixed`: Reduce price by a fixed amount (e.g., $5 off)
- `to_fixed`: Set price to a specific amount (e.g., $99.99)
- `volume_pricing`: Use tiered pricing based on quantity

### Individual pricing tier

The tier entity defines volume-based pricing tiers for quantity discounts. Multiple tiers can be associated with a single Individual Pricing rule when the action type is set to `volume_pricing`.

**Key properties:**

- **individualPricingId**: Reference to the parent pricing rule
- **qtyFrom**: Minimum quantity for this tier (inclusive)
- **qtyTo**: Maximum quantity for this tier (inclusive, null for unlimited)
- **price**: Price collection containing prices for different currencies

**Example tiers:**

- Tier 1: 1-9 units @ $10 each

Check warning on line 52 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L52

If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN[1]) Suggestions: `1–9` URL: https://languagetool.org/insights/post/dashes/#how-to-properly-use-an-en-dash Rule: https://community.languagetool.org/rule/show/HYPHEN_TO_EN?lang=en-US&subId=1 Category: PUNCTUATION
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:52:10: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN[1])
 Suggestions: `1–9`
 URL: https://languagetool.org/insights/post/dashes/#how-to-properly-use-an-en-dash 
 Rule: https://community.languagetool.org/rule/show/HYPHEN_TO_EN?lang=en-US&subId=1
 Category: PUNCTUATION
- Tier 2: 10-49 units @ $9 each

Check warning on line 53 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L53

If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN[1]) Suggestions: `10–49` URL: https://languagetool.org/insights/post/dashes/#how-to-properly-use-an-en-dash Rule: https://community.languagetool.org/rule/show/HYPHEN_TO_EN?lang=en-US&subId=1 Category: PUNCTUATION
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:53:10: If specifying a range, consider using an en dash instead of a hyphen. (HYPHEN_TO_EN[1])
 Suggestions: `10–49`
 URL: https://languagetool.org/insights/post/dashes/#how-to-properly-use-an-en-dash 
 Rule: https://community.languagetool.org/rule/show/HYPHEN_TO_EN?lang=en-US&subId=1
 Category: PUNCTUATION
- Tier 3: 50+ units @ $8 each

### Individual pricing company assignment

This entity links pricing rules to specific business partner companies or organization units. It determines which companies or parts of companies receive the special pricing.

**Key properties:**

- **individualPricingId**: Reference to the pricing rule
- **customerId**: Reference to the business partner customer
- **scope**: Defines assignment scope (whole_company, all_org_units, specific_units)
- **organizationUnitIds**: JSON array of specific organization unit IDs (when scope is specific_units)

**Scopes:**

- `whole_company`: Applies to all employees of the business partner
- `all_org_units`: Applies to all organization units within the company
- `specific_units`: Applies only to specific organization units (requires organizationUnitIds)

### Individual pricing computed cache

The computed cache entity pre-calculates which products are affected by which pricing rules. This significantly improves performance by avoiding repeated filter evaluations.

**Key properties:**

- **individualPricingId**: Reference to the pricing rule
- **productId**: Reference to the affected product (`NULL` if rule applies to all products)

This cache uses a hybrid approach: specific cache entries for targeted rules, and `NULL` entries for catalog-wide rules.

## Schema

```mermaid
erDiagram
b2b_components_individual_pricing {
uuid id PK

Check warning on line 89 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L89

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `ID` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:89:6: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `ID`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
boolean active
boolean show_strike_through
string name
string target
int priority
boolean apply_to_all_products
uuid product_stream_id FK
boolean use_validity_range
datetime valid_from
datetime valid_until
string description
string action_type
float action_amount
uuid created_by_id FK
uuid updated_by_id FK
json custom_fields
}
b2b_components_individual_pricing_tier {
uuid id PK

Check warning on line 108 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L108

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `ID` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:108:6: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `ID`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
uuid individual_pricing_id FK
int qty_from
int qty_to
json price
}
b2b_components_individual_pricing_company_assignment {
uuid id PK

Check warning on line 115 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L115

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `ID` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:115:6: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `ID`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
uuid individual_pricing_id FK
uuid customer_id FK
string scope
json organization_unit_ids
}
b2b_components_individual_pricing_computed_cache {
uuid id PK

Check warning on line 122 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L122

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `ID` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:122:6: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `ID`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
uuid individual_pricing_id FK
uuid product_id FK
}
b2b_components_individual_pricing_tag {
uuid individual_pricing_id FK
uuid tag_id FK
}
product_stream {
uuid id PK

Check warning on line 131 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L131

This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2]) Suggestions: `ID` Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2 Category: CASING
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:131:6: This abbreviation for “identification” is spelled all-uppercase. (ID_CASING[2])
 Suggestions: `ID`
 Rule: https://community.languagetool.org/rule/show/ID_CASING?lang=en-US&subId=2
 Category: CASING
string name
}

b2b_components_individual_pricing ||--o{ b2b_components_individual_pricing_tier : "has volume tiers"
b2b_components_individual_pricing ||--o{ b2b_components_individual_pricing_company_assignment : "assigned to companies"
b2b_components_individual_pricing ||--o{ b2b_components_individual_pricing_computed_cache : "cached for products"
b2b_components_individual_pricing ||--o{ b2b_components_individual_pricing_tag : "targets tags"
b2b_components_individual_pricing }o--|| product_stream : "uses"
b2b_components_individual_pricing_company_assignment }o--|| customer : "belongs to"
b2b_components_individual_pricing_computed_cache }o--|| product : "references"
b2b_components_individual_pricing_tag }o--|| tag : "references"
```

## Workflow

The following diagram shows how individual pricing is applied to a product:

```mermaid
flowchart TD
A[Customer views product] --> B{Is logged-in?}
B -->|No| C[Show standard catalog price]
B -->|Yes| D[Load customer context]
D --> E[Query active pricing rules]
E --> F{Any rules found?}
F -->|No| C
F -->|Yes| G[Get all rules at highest priority]
G --> H[Evaluate all rules at this priority]
H --> I{Any rules matched with the product?}
I -->|No| C
I -->|Yes| J{Multiple rules match?}
J -->|No| K[Use single matching rule]
J -->|Yes| L[Calculate price for each rule]
L --> M[Select rule with lowest price]

Check warning on line 164 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L164

A determiner may be missing. (THE_SUPERLATIVE[2]) Suggestions: `the lowest` URL: https://languagetool.org/insights/post/grammar-comparatives-superlatives/#superlative-forms-of-adjectives Rule: https://community.languagetool.org/rule/show/THE_SUPERLATIVE?lang=en-US&subId=2 Category: GRAMMAR
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:164:26: A determiner may be missing. (THE_SUPERLATIVE[2])
 Suggestions: `the lowest`
 URL: https://languagetool.org/insights/post/grammar-comparatives-superlatives/#superlative-forms-of-adjectives 
 Rule: https://community.languagetool.org/rule/show/THE_SUPERLATIVE?lang=en-US&subId=2
 Category: GRAMMAR
K --> Q[Calculate final price]
M --> Q

Check warning on line 166 in products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md

View workflow job for this annotation

GitHub Actions / LanguageTool

[LanguageTool] products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md#L166

Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE) Suggestions: `Q` Rule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US Category: MISC
Raw output
products/extensions/b2b-components/individual-pricing/concepts/entities-and-workflow.md:166:7: Possible typo: you repeated a word (ENGLISH_WORD_REPEAT_RULE)
 Suggestions: `Q`
 Rule: https://community.languagetool.org/rule/show/ENGLISH_WORD_REPEAT_RULE?lang=en-US
 Category: MISC
Q --> R{Show strike-through?}
R -->|Yes| S[Display with original price]
R -->|No| T[Display discounted price]
```

## Target type evaluation

Depending on the target type, different evaluation logic applies:

### Companies target

- Customer must be a business partner or employee
- The company assignment must exist, linking the pricing rule to the customer's company
- Scope is checked (whole company, all units, or specific units)
- If specific units, the customer must belong to one of the specified organization units

### Tags target

- Customer must have at least one of the tags specified in the pricing rule

## Priority and rule selection

When multiple pricing rules could apply to the same product:

1. Only rules at the highest priority level are considered
2. All rules at this priority level are evaluated
3. If multiple rules match, the one resulting in the lowest price is selected
4. If no rules match at the highest priority, standard catalog pricing is used

:::info
Lower priority rules are never evaluated. This ensures the most important pricing rules take precedence, and that when multiple rules compete at the same level, customers always get the best price.
:::
Loading