<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Spryker Documentation</title>
        <description>Spryker documentation center.</description>
        <link>https://docs.spryker.com/</link>
        <atom:link href="https://docs.spryker.com/feed.xml" rel="self" type="application/rss+xml"/>
        <lastBuildDate>Wed, 02 Sep 2026 09:04:09 +0000</lastBuildDate>
        <generator>Jekyll v4.2.2</generator>
        
        
        <item>
            <title>Release notes 202608.0</title>
            <description>## B2B Business-Ready Commerce Experiences

### Workflows {% include badge.html type=&quot;feature,early-access&quot; %}

This release introduces a workflows infrastructure that lets teams model and manage workflows for different business entities directly in the Back Office, without requiring code changes or deployment. It provides a reusable, auditable, and versioned workflows foundation built on top of the existing state machine engine.

{% include carousel.html
images=&quot;
https://d2s0ynfc62ej12.cloudfront.net/docs/About/all/releases/release-notes-202608.0.md/workflows_1.jpg||::
https://d2s0ynfc62ej12.cloudfront.net/docs/About/all/releases/release-notes-202608.0.md/workflows_2.jpg||&quot;
%}

**Key capabilities:**
- Create and adjust workflows directly in the Back Office, including states, transitions, events, conditions, and timeouts that define each process
- Manage workflow versions and control which version is active
- Monitor workflow progress across different entities, with support for project-specific requirements

**Business benefits:**
- Enables business teams to create and adjust workflows faster without developer involvement
- Reduces duplication by replacing one-off workflow implementations with one reusable infrastructure
- Improves governance through versioned definitions and controlled building blocks

**Documentation:**
- [Workflows feature overview](https://docs.spryker.com/docs/pbc/all/back-office/latest/base-shop/workflows-feature-overview)
- [Workflows installation guide](https://docs.spryker.com/docs/dg/dev/integrate-and-configure/integrate-workflow-feature)

### Spryker Design System Storefront {% include badge.html type=&quot;improvement&quot; %}

This release extends the Spryker Design System in the storefront by modernizing the product listing page and merchant profile page. It helps create a more consistent buying journey for B2B customers while giving teams a scalable foundation for future storefront enhancements.

{% include carousel.html
images=&quot;
https://d2s0ynfc62ej12.cloudfront.net/docs/About/all/releases/release-notes-202608.0.md/Design_System_PLP.mp4||&quot;
%}

**Key capabilities:**
- Modernized product listing page based on the B2B design system
- Added a reusable merchant page template for consistent merchant content
- Expanded the storefront design foundation introduced in earlier design-system phases

**Business benefits:**
- Improves product discovery with a more consistent and efficient browsing experience
- Increases storefront visual consistency for B2B buyers
- Reduces future design and development effort through reusable page structures

### Recurring Orders: general availability {% include badge.html type=&quot;feature&quot; %}

Recurring Orders is now generally available. This release gives buyers real control over an upcoming order before it is placed. They can handle the exceptions that used to break a schedule f.e. swapping in a replacement when a product is discontinued, adjusting quantities, adding products that were missed and decide whether each change applies once or permanently. Budget problems can be corrected by the buyer directly rather than raised as a ticket

**Key capabilities:**
- Adjust an upcoming order before it is placed: replace an item that is no longer available, add or remove lines, and change quantities.
- Choose whether a change applies to the next delivery only or to every delivery from now on.
- Move an order to a different budget or cost center without cancelling the schedule.
- Resolve an order that is in review because of an exceeded budget.
- Proactively editing for Schedule Name, Frequency, Budget &amp; Cost Center, Next Execution date
- Run schedules with up to 200 line items, at the speed buyers expect across list, overview, and review screens.

**Business benefits:**
- Supply continuity. A missed replenishment can stop a production line. Schedules that survive real-world exceptions keep materials arriving without anyone watching the calendar.
- Procurement capacity freed up. Repeat orders are already-made decisions. Automating them lets buyers spend their time on sourcing, not re-keying the same basket.
- Spend stays inside the guardrails. Budget and cost center governance travels with every scheduled order, so automation never becomes a route around approval control.
- Stickier customers. A buyer who runs replenishment through your storefront has embedded you in their production workflow, switching becomes a procurement project, not a price comparison.


**Documentation:**
- [Recurring Orders feature overview](https://docs.spryker.com/docs/pbc/all/order-experience-management/latest/base-shop/feature-overviews/recurring-orders-feature-overview)
- [Install the Recurring Orders feature](https://docs.spryker.com/docs/pbc/all/order-experience-management/latest/base-shop/install-and-upgrade/install-features/install-the-recurring-orders-feature)
- [Recurring Orders GA release](https://api.release.spryker.com/release-group/6689)

### Algolia API update {% include badge.html type=&quot;improvement&quot; %}

The Algolia integration has been updated to use the newer PHP client version. This change helps keep the integration aligned with the supported Algolia SDK lifecycle and reduces risk related to the older client reaching the end of SLA.

**Key capabilities:**
- Updates the integration to the newer Algolia PHP client version
- Aligns the integration with Algolia&apos;s current supported SDK direction
- Prepares customers for continued use without relying on an outdated client

**Business benefits:**
- Reduces support and lifecycle risk for customers using the Algolia integration
- Improves confidence in long-term maintainability
- Helps customers stay current with less ambiguity around SDK support status

**Documentation:**
- [Algolia module update](https://github.com/spryker-eco/algolia/releases/tag/2.0.0)
- [Migration guide](https://docs.spryker.com/docs/pbc/all/search/latest/base-shop/third-party-integrations/algolia/upgrade-the-algolia-module)


### Vertex Back Office Configuration {% include badge.html type=&quot;feature&quot; %}

The Vertex configuration page in the Back Office has been updated to reflect the new integration and replace legacy App Composition Platform-oriented fields. This makes the setup clearer and better aligned with the current Vertex integration experience.

**Key capabilities:**
- Updates Vertex configuration fields in the Back Office
- Replaces legacy field structure inherited from the previous ACP-based setup
- Aligns configuration with the new integration model

**Business benefits:**
- Simplifies Vertex setup for business and implementation teams
- Reduces confusion caused by outdated configuration fields
- Helps shorten implementation and configuration time

**Documentation:**
- [Configure Vertex in the Back Office](https://docs.spryker.com/docs/pbc/all/tax-management/latest/base-shop/third-party-integrations/vertex/install-vertex/configure-vertex-in-the-back-office)

## Connected, and AI-Enabled Platform

### AI Dev SDK: Introduce Project Setup Wizard {% include badge.html type=&quot;feature,early-access&quot; %}

The AI Dev SDK now provides a guided setup wizard for new Spryker B2B Marketplace projects based on a fresh demoshop clone. It captures project decisions once and applies them through a resumable setup workflow. This helps teams move faster from initial clone to a verified, project-shaped shop.

&lt;figure class=&quot;video_container&quot;&gt;
    &lt;video width=&quot;100%&quot; height=&quot;auto&quot; controls&gt;
      &lt;source src=&quot;https://d2s0ynfc62ej12.cloudfront.net/docs/About/all/releases/release-notes-202608.0.md/AI_DEV_SDK_project_wizard.mp4&quot; type=&quot;video/mp4&quot;&gt;
  &lt;/video&gt;
&lt;/figure&gt;

**Key capabilities:**
- Runs a guided setup interview covering project identity, services, stores, localization, CI, and run mode
- Applies setup decisions through orchestrated steps with resume support if the process is interrupted
- Produces setup artifacts, verification results, and a summary of remaining manual actions

**Business benefits:**
- Reduces time and effort needed to initialize a new project
- Improves consistency across early project setup decisions
- Gives teams a clearer and more reliable path to a ready-for-scope development environment

**Documentation:**
- [AI Dev SDK Project Starter Wizard](https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev-project-starter-wizard)

### AI Dev SDK: Introduce Project Upgrade Helper {% include badge.html type=&quot;feature,early-access&quot; %}

The AI Dev SDK now includes an AI-assisted upgrade workflow for customized Spryker projects. It carries a project from its current release to the target one, adopting major module releases into existing project code and verifying the result against a running application. Team checkpoints keep release choice and feature adoption under explicit control.

**Key capabilities:**
- Moves a customized project onto the target Spryker release and resolves the dependency work automatically
- Adopts major module releases into project code by applying the official migration guides to existing customizations
- Realigns customizations, including customized storefront and Back Office screens, with the updated platform
- Verifies the upgraded project against a running application, covering artifact regeneration, tests, and key pages
- Keeps release choice and new feature adoption under team control at defined checkpoints
- Records progress and findings so an upgrade can pause, resume, and run as repeatable CI checks

**Business benefits:**
- Turns weeks of manual upgrade investigation into a guided, largely automated run
- Keeps existing customizations working instead of quietly going dead after an upgrade
- Adopts new platform capabilities only when the team chooses them
- Makes upgrades reviewable and resumable across sprints

**Documentation:**
- [AI Dev SDK Upgrade Workflow](https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev-upgrade-workflow)

### AI Dev SDK: Autonomous Bugfixer &amp; Improved Customization Workflow {% include badge.html type=&quot;early-access&quot; %} {% include badge.html type=&quot;feature&quot; %}

The AI Dev SDK introduces a new guided workflow for turning bug reports into validated project changes and improves the existing workflow for feature requests. Teams can start with plain-language input and follow an orchestrated path through planning, implementation, verification, and review, while retaining decision-making throughout the workflow and reviewing and approving changes before they are delivered.

**Key capabilities:**
- Improves feature implementation from request intake through acceptance criteria, implementation planning, coding, and verification
- Supports bug fixing from symptom or ticket intake through reproduction, root-cause analysis, minimal fix, testing, QA, and final verification
- Produces traceable reports and supports collaborative or autonomous execution modes

**Business benefits:**
- Speeds up delivery from request or incident to validated change
- Reduces manual handoffs across implementation, QA, and review steps
- Improves consistency and traceability in AI-assisted development workflows

**Documentation:**
- [AI Dev SDK Bugfix Workflow](https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev-bugfix-workflow)
- [AI Dev SDK Customization Workflow](https://docs.spryker.com/docs/dg/dev/ai/ai-dev/ai-dev-customization-workflow)

### AI Commerce: Configurable Instructions Prompt {% include badge.html type=&quot;improvement,early-access&quot; %}

AI Commerce feature prompts can now be configured consistently through the Back Office across the platform, rather than being hardcoded in the codebase. This applies to capabilities such as Search by Image, Visual Add-to-Cart, the Back Office Assistant, and Smart PIM.

Teams can customise prompts more easily to reflect project-specific language, tone, and business requirements, without requiring deployments or support from developer teams.

**Key capabilities:**
- Moves prompt definitions to configurable project-level settings with default values provided out of the box
- Supports prompt customization without changing the codebase
- Enables easier tuning of AI behavior for specific catalogs, industries, and branding needs

**Business benefits:**
- Makes AI Commerce features easier to adapt for different business contexts
- Reduces custom code and upgrade friction in AI Commerce projects
- Shortens iteration cycles for prompt tuning and testing

**Documentation:**
- [AI Commerce Overview](https://docs.spryker.com/docs/dg/dev/ai/ai-commerce/ai-commerce-overview)

## Efficient and Flexible Cloud Foundation

### Enhancements to Symfony Scheduler {% include badge.html type=&quot;improvement&quot; %}

We improved scheduled job management to give teams more control and better visibility in the Back Office and PaaS environment. You can now manage job execution more easily, run multiple schedules in parallel within the same container, and review richer operational logs and status information.

{% include carousel.html
images=&quot;
https://spryker.s3.eu-central-1.amazonaws.com/docs/About/Releases/release-notes-202608/scheduler2.png||::
https://spryker.s3.eu-central-1.amazonaws.com/docs/About/Releases/release-notes-202608/scheduler1.png||&quot;
%}

**Key capabilities:**
- Start, stop, enable, and disable scheduled jobs from the Back Office
- Run multiple scheduled jobs in parallel in the same container to reduce infrastructure overhead
- View job status details such as running, waiting, error state, worker name, timing, priority, and error logs of the last run

**Business benefits:**
- Reduces operational effort for managing scheduled jobs
- Improves transparency for support and business teams through better job visibility
- Helps optimize infrastructure usage and costs

**Release:**
- [Enhancements to Symfony Scheduler](https://api.release.spryker.com/release-group/6718)

### Maintenance and Service Updates {% include badge.html type=&quot;improvement&quot; %}

We delivered routine maintenance and service updates across the cloud foundation to keep environments secure, reliable, and aligned with current technology standards. These updates introduce PHP 8.5 as the default runtime version, add initial support for OpenSearch 2.x and 3.x, and establish the foundation for migrating from AWS OpenSearch 1.x.

**Key capabilities:**
- Added PHP 8.5 support to Docker SDK PHP images and enabled it as the default version. PHP 8.2 images are still available, while discontinuing further image generation for that version [after 2026](https://www.php.net/supported-versions.php)
- Added compatibility for indexing, reading, and writing data with newer OpenSearch versions.
- Support for the newer OpenSearch version in Spryker PaaS comes in the next release.

**Business benefits:**
- Improves security and stability through up-to-date platform components
- Helps teams remain aligned with supported PHP and OpenSearch technology versions
- Reduces operational risk and technical debt

**Documentation:**
- [Upgrade to PHP 8.5](https://docs.spryker.com/docs/dg/dev/upgrade-and-migrate/upgrade-to-php-85)
- [Supported versions of PHP](https://docs.spryker.com/docs/dg/dev/supported-versions-of-php)
- [OpenSearch migration to 3.5](https://docs.spryker.com/docs/pbc/all/search/latest/base-shop/install-and-upgrade/migrate-from-opensearch-1.3-to-3.5)

### Frontend builder for Yves moved into ShopUi {% include badge.html type=&quot;improvement&quot; %}

We moved the Yves builder into ShopUi and introduced a cleaner extension approach for project-level customization. This makes the builder more reusable, configurable, and easier to maintain across projects.

**Key capabilities:**
- Builder logic is now centralized in ShopUi instead of the project layer
- Project teams can extend entry paths, discovery paths, and namespaces through configuration
- Supports project-level overrides without changing module internals

**Business benefits:**
- Reduces customization effort for frontend builds
- Improves maintainability and reuse across projects
- Simplifies frontend setup while preserving existing storefront behavior

**Documentation:**
- [Frontend builder for Yves v2](/docs/dg/dev/frontend-development/latest/yves/frontend-builder-for-yves-v2)
- [Upgrade to frontend builder v2 for Yves](/docs/dg/dev/upgrade-and-migrate/upgrade-to-frontend-builder-v2-for-yves)

### Dynamic multistore locale visibility fixes {% include badge.html type=&quot;improvement&quot; %}

We fixed an issue where adding a new locale via Dynamic Multistore could result in products, category trees, and navigation not being visible as expected.

**Key capabilities:**
- Correctly populates entities with data for newly added locales
- Restores product visibility for newly added locales
- Ensures category trees and navigation are displayed correctly after locale addition


**Business benefits:**
- Reduces setup issues when expanding into new locales
- Helps teams launch localized storefronts with fewer manual corrections
- Ensures consistent and complete entity data across stores and locales

**Documentation:**
- [Adjusted editing navigation for new locale](https://api.release.spryker.com/release-group/6658)

### Customer login consistency improvements {% include badge.html type=&quot;improvement&quot; %}

We fixed an issue that could break login behavior when a customer signed in from different places.

**Key capabilities:**
- Improves login handling across multiple customer sessions
- Prevents disruptions caused by parallel or distributed login activity
- Stabilizes customer authentication behavior

**Business benefits:**
- Reduces customer login friction
- Improves reliability of the customer account experience
- Lowers support effort related to authentication issues

**Documentation:**
- [Fixed session handler to avoid behavior inconsistency](https://api.release.spryker.com/release-group/6656)

### Reservation aggregation across multiple plugins {% include badge.html type=&quot;improvement&quot; %}

We introduced a new, more flexible plugin stack for reservation calculation. The new mechanism executes all configured reservation aggregation plugins, ensuring that reservation data is processed completely and accurately.

**Key capabilities:**
- Executes multiple reservation aggregation plugins in sequence
- Prevents incomplete aggregation when reservations span multiple stores or warehouses
- Avoids incorrect merging behavior during reservation processing
- Provides a more flexible foundation for extending reservation calculation logic

**Business benefits:**
- Improves inventory and reservation accuracy
- Supports more complex multi-store and warehouse setups
- Reduces risk of stock inconsistencies

**Documentation:**
- [Install the Packaging Units feature](https://docs.spryker.com/docs/pbc/all/product-information-management/latest/base-shop/install-and-upgrade/install-features/install-the-packaging-units-feature#set-up-behavior)
- [Install the Marketplace Inventory Management + Order Management feature](https://docs.spryker.com/docs/pbc/all/warehouse-management-system/latest/marketplace/install-features/install-the-marketplace-inventory-management-order-management-feature#prerequisites)
- [Install the Marketplace Inventory Management + Packaging Units feature](https://docs.spryker.com/docs/pbc/all/warehouse-management-system/latest/marketplace/install-features/install-the-marketplace-inventory-management-packaging-units-feature#set-up-behavior)
- [Multistore reservation aggregation](https://api.release.spryker.com/release-group/6611)
- [Fix reservation aggregation request expander](https://api.release.spryker.com/release-group/6676)

### API Platform nested object schema support {% include badge.html type=&quot;improvement&quot; %}

We extended API Platform resource YAML so a list of objects can declare its element shape. A `type: array` property with a sibling items block now publishes the element as a referenced component schema, instead of a bare untyped array. Single nested objects were already typed; this closes the gap for collections.

Key capabilities:
- Declare `items: {type: object, properties: …}` alongside `type: array` in a resource schema
- The generated OpenAPI document references a generated element schema and registers it in the same document
- Lists of scalars and canonical (shared) object shapes are supported
- Existing providers keep working — assigning raw arrays is still valid; no provider rewrite required

Adoption is per field, and opt-in for a reason: a typed element is a closed shape. Adding items to a list that is already part of a released response silently drops every payload key you did not declare. Before adopting an existing field, diff a real response payload against your intended items.properties. New fields are unaffected. Storefront APIs are unchanged.

Note: openapiContext.items is documentation passthrough only — it generates nothing. Only a sibling of type: array produces a typed element schema.

Documentation:
- [Typed collections in the published contract](/docs/integrations/spryker-api/api-platform/typed-collections.html)
- [Object collections](/docs/integrations/spryker-api/api-platform/resource-schemas.html#object-collections)

### Graceful handling of email delivery failures {% include badge.html type=&quot;improvement&quot; %}

We improved email sending mechanisms across Storefront, Back Office, Merchant Portal, and API to handle delivery failures gracefully without causing system errors or disrupting application workflows.

**Key capabilities:**
- Prevents email delivery failures from interrupting or causing the failure of primary business processes, including cases where email addresses or domains are not whitelisted in AWS SES sandbox mode.
- Displays clear, non-disruptive error messages with error codes in the Storefront, Merchant Portal, and Back Office, while allowing the primary user action to complete successfully.
- Records comprehensive details for every email delivery failure, including the email type, sender, recipients, and relevant exception details.
- Logs email delivery failures that occur during Order Management System (OMS) processing without halting execution or leaving orders stuck.
- Standardizes REST/Glue API responses to return a 422 Unprocessable Entity status code instead of a 500 Internal Server Error

**Business benefits:**
- Delivers a smooth user experience by preventing app crashes during email failure scenarios
- Reduces support overhead and troubleshooting effort for technical and operations teams

**Documentation:**
- [Email service](/docs/ca/dev/email-service/email-service)
- [Email deliverability](/docs/ca/dev/email-service/email-deliverability)

### Community contributions {% include badge.html type=&quot;improvement&quot; %}

We included several community-driven improvements in this release. These contributions help improve compatibility and code quality in commonly used modules.

**Key capabilities:**
- Made QuoteApproval compatible with PropelBehavior
- Replaced docblocks with typed class constants in Transfer
- Added support for sensitive properties in Transfer

**Business benefits:**
- Brings practical enhancements contributed by the community
- Improves maintainability, security, and compatibility in key modules
- Strengthens collaboration across the Spryker ecosystem

**Documentation:**
- [New community PR: Make QuoteApproval compatible to PropelBehavior](https://api.release.spryker.com/release-group/6664)
- [New community PR: Add support of sensitive properties](https://api.release.spryker.com/release-group/6657)
</description>
            <pubDate>Wed, 02 Sep 2026 08:38:25 +0000</pubDate>
            <link>https://docs.spryker.com/docs/about/all/releases/release-notes-202608.0.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/about/all/releases/release-notes-202608.0.html</guid>
            
            
        </item>
        
        <item>
            <title>Packaged Business Capabilities</title>
            <description>Packaged Business Capabilities (PBCs) are capabilities that enclose a certain functionality with the Spryker system. PBCs provide a good foundation for decision makers throughout multiple business entities.

This section is in beta because not all the PBCs are covered. Marketplace functionalities will also be added at a later stage. You are welcome to use the docs that are already here, and we will keep adding the docs for the rest of the PBCs.

## Spryker PBCs

| NAME | DESCRIPTION | BENEFITS |
| --- | --- | --- |
| [AI Foundation](/docs/pbc/all/ai-foundation/{{site.version}}/ai-foundation.html) | Provider agnostic AI layer that connects Spryker Commerce OS to leading AI providers through one common interface. | Accelerates delivery of AI features and keeps freedom to choose and change AI providers and models per use case. |
| [AI Commerce](/docs/pbc/all/ai-commerce/{{site.version}}/ai-commerce.html) | Brings AI assisted capabilities to the Spryker storefront and the Back Office to help buyers and operators complete commerce tasks faster and more consistently, reducing manual effort with human review and enterprise governance. | Speeds up key commerce workflows while improving consistency and reducing errors, enabling scalable adoption of assisted capabilities across teams and markets. |
| [Business Intelligence](/docs/pbc/all/business-intelligence/{{site.version}}/business-intelligence.html) | Analyze data to understand how your shop performs and areas for improvement. | Make informed decisions. |
| [Carrier Management](/docs/pbc/all/carrier-management/{{site.version}}/carrier-management.html) | The Spryker Cloud Commerce OS integrates with several shipping carriers and methods and lets you define their availability, price, and tax set. During the checkout process, customers have the option to select their preferred shipment method and relevant carrier. | Ensures quick and cost-effective delivery. |
| [Cart and Checkout](/docs/pbc/all/cart-and-checkout/{{site.version}}/cart-and-checkout.html) | The online shopping cart and checkout process act as a gateway for customer and order management. It lets your customers organize and manage their purchases, apply vouchers and coupon codes. Based on their roles and permissions, your B2B customers can add or remove products, share the cart, and manage their purchases. | Increases conversion rates and reduces drop-off rates. Offers additional B2B specific, permission-related functionalities. |
| [Content Management System (CMS)](/docs/pbc/all/content-management-system/{{site.version}}/content-management-system.html) | The CMS features let you customize your store, enrich it with information, stories, or other content, and make it easily findable in search engines. Several SEO features enable you to add customized meta information to all your content and create search engine-friendly URLs. | Provides compelling content and stories where your customers need it. |
| [Customer Relationship Management (CRM)](/docs/pbc/all/customer-relationship-management/{{site.version}}/customer-relationship-management.html) | The customer management tool lets B2B and B2C businesses manage customer accounts and efficiently monitor shopping habits. It provides your B2B Customers with a way to map their business hierarchies, permissions, and role management. With the creation of distinctive Business Units, the internal hierarchy can easily be mapped out, and each Unit can operate independently. The Roles and Permissions System lets your customer&apos;s buyers define the purchase and approval process. | Increases conversion rates and average order values with a compact Customer Relationship Management tool. |
| [Data Exchange](/docs/pbc/all/data-exchange/{{site.version}}/data-exchange.html) | You can import your business logic and data, including product information, customer base, categories, and more, into the Spryker Cloud Commerce OS. You can use the out-of-the-box export data functionality as a generic blueprint for any data set that you need to export. | Lets you import and export specific data points quickly and easily. |
| [Discount Management](/docs/pbc/all/discount-management/{{site.version}}/discount-management.html) | You can define several types of discounts based on a brand, the overall cart value, specific product ranges, or unique customer groups. You can also offer discount vouchers or incentivize certain products through coupon codes. | Lets you run effective promotional campaigns to boost conversion rates. |
| [Dynamic Multistore](/docs/pbc/all/dynamic-multistore/{{site.version}}/dynamic-multistore.html) | Manage stores in the Back Office | Make changes to your stores setup quickly and easily. |
| [Emails](/docs/pbc/all/emails/{{site.version}}/emails.html) | You can send automated account e-mails and confirmations or offer different types of newsletter subscriptions. | Keep in touch with your customers. |
| [Gift Cards](/docs/pbc/all/gift-cards/{{site.version}}/gift-cards.html) | Lets your customers purchase and redeem gift cards. Enabling gift card purchases can boost your brand awareness and help you reach new customers. | Lets you acquire new customers through gift card payment options. |
| [Identity Access Management (IAM)](/docs/pbc/all/identity-access-management/{{site.version}}/identity-access-management.html) | Enables the creation of new accounts for end customers and B2B customers. It also allows users to define password settings and utilize multi-login blockers for security purposes. Moreover, a third-party access management function is integrated. | Allows for quick and easy authorization and authentication of customers |
| [Merchant Management](/docs/pbc/all/merchant-management/{{site.version}}/merchant-management.html) | For efficient Merchant Management, two parts are important. One, the overview and management on the Operator side, like approvals, edits, etc. And two, the self-service management of the Merchants, where they can take care of their daily business, like order or product management. | Gives you an overview of all your Merchants&apos; activities. |
| Miscellaneous | This category holds the documents that are not related to any PBC.  |  |
| [Offer Management](/docs/pbc/all/offer-management/{{site.version}}/offer-management.html) | Lets your Merchants create Offers on existing products in your Marketplace. By doing so, duplicates in the product catalog can be avoided and the management of Merchants, Products, and Offers becomes much more convenient. | Saves time because of a good overview of Merchant&apos;s Offers. |
| [Order Management System (OMS)](/docs/pbc/all/order-management-system/{{site.version}}/order-management-system.html) | Helps you keep track of your order processing from your B2B, B2C, or Marketplace, and ensure quick fulfillment. You can manage incoming orders in the Back Office, view and edit orders, track their progress, or contact customers who make open orders directly. With the compact Order Management features, you can keep your order processing running smoothly. | Lets you pProcess orders smoothly to fulfill them quickly. |
| [Payment Service Provider (PSP)](/docs/pbc/all/payment-service-provider/{{site.version}}/payment-service-provider.html) | Provides integration of payment methods. You can integrate multiple payment gateways, define their availability, and customize how they appear on your site. | Lets you provide an excellent shopping experience and integrate your customers&apos; preferred payment methods. |
| [Price Management](/docs/pbc/all/price-management/{{site.version}}/price-management.html) | The Spryker Cloud Commerce OS supports multiple currencies and automatically detects the payment currency based on a customer&apos;s preference. You can manage gross and net prices per product and per country. You can also offer volume discounts to encourage customers to purchase products in larger quantities. | Saves you time by letting you implement your pricing strategy in one place and catering it to your business needs. |
| [Product Information Management (PIM)](/docs/pbc/all/product-information-management/{{site.version}}/product-information-management.html) | Encompasses all functionality that is needed to set up your product catalog. With PIM, you can create and extend the product catalog to match your business needs. | Helps you expand your business by organizing your products in a fast and efficient way. |
| [Product Relationship Management](/docs/pbc/all/product-relationship-management/{{site.version}}/product-relationship-management.html) | Helps you enhance your shop with cross- and up-selling capabilities to increase sales. | Increases average order values with product relations. |
| [Ratings and Reviews](/docs/pbc/all/ratings-reviews/{{site.version}}/ratings-and-reviews.html) | Lets you incorporate user reviews and ratings. You can receive and moderate feedback in the Back Office. The Ratings and Reviews feature also comes with the functionality to add text-free reviews and star ratings. | Inspires trust among customers with ratings and reviews. |
| [Request for Quote (RFQ)](/docs/pbc/all/request-for-quote/{{site.version}}/request-for-quote.html) | Your customers can request a quote for products and services that you sell. The Request for Quote feature supports all functionalities of the price engine and product capabilities, such as Volume Prices, Customer Specific Prices, Measuring and Packaging units, Shipping costs, Product Options, etc. | Enhances customer loyalty and increase conversion rates. |
| [Return Management](/docs/pbc/all/return-management/{{site.version}}/marketplace/marketplace-return-management-feature-overview.html) | Lets you establish a return policy and execute returns. | Increase customer satisfaction and loyalty. |
| [Search](/docs/pbc/all/search/{{site.version}}/base-shop/search-feature-overview/search-feature-overview.html) | The out-of-the-box Elasticsearch technology lets you include full-text search, auto-suggestions, and auto-completion. You can set individual search preferences for multiple stores and categorize your products by adding dynamic filters and facets to help your customers further refine the search results. You can also add more advanced filters that use the product&apos;s metadata or promote a brand&apos;s top-sellers or highly rated products. | Helps you increase conversion rates by providing an excellent Search and Filter experience. |
| [Service Points Management](/docs/pbc/all/service-point-management/{{site.version}}/service-point-management.html) | Set up offline service points where customers can interact with your business. | Creates touch points to connect a shop with offline locations. |
| [Shopping List and Wishlist](/docs/pbc/all/shopping-list-and-wishlist/{{site.version}}/shopping-list-and-wishlist.html) | Your B2B customers can save the products they wish to purchase, in shopping lists. Different roles and permission systems ensure smooth sharing and contribution management amongst company users. This PBC encompasses additional features like printing, barcode generation, and direct-to-cart. Enabling your B2C customers to track and save the products they wish to purchase through a wish list function effectively reduces cart abandonment, boosts your sales, and allows you to keep track of which products are of interest to your customers. | Lets you increase conversion rates and loyalty by offering rich Shopping and B2B Wish Lists. |
| [Tax Management](/docs/pbc/all/tax-management/{{site.version}}/tax-management.html) | Lets you adhere to respective tax regulations in the countries you sell by configuring and managing tax rates for products, shipments, and additional services. You can define tax rates for different countries and apply integrations to manage US taxes. | Lets you comply with fiscal regulations. |
| [User Management](/docs/pbc/all/user-management/{{site.version}}/user-management.html) | Lets the Back Office users manage user access, set rights, and onboard customers. | Ensures high security and compliance through managed user flows. |
| [Warehouse Management System (WMS)](/docs/pbc/all/warehouse-management-system/{{site.version}}/warehouse-management-system.html) | Lets you keep an overview of your stock levels in the Back Office to determine accurate availabilities on your store&apos;s website. Any open orders or reserved items are taken into consideration when stock availabilities are displayed. | Helps you save your time by keeping an eye on your stock levels. |
| [Workflows](/docs/pbc/all/back-office/latest/base-shop/workflows-feature-overview.html) | Workflows lets operations teams design, run, and improve multi-step processes for different entities directly in the Back Office, without relying on developers for every change. Teams can manage process definitions and their versions, so they can work on process improvements without disrupting processes that are currently running. | Speeds up process changes and reduces developer dependency by letting business teams design and adjust workflows directly in the Back Office. |
&lt;!--
| Digital Asset Management (DAM) | The DAM system provides impactful visuals while simultaneously maintaining fast response times, thus helping you reduce your bounce rate effectively and create an enhanced shopping experience. With the DAM, you can add images and videos to any of your pages. | Offers an exceptional brand experience with impactful visuals, banners, and media assets. |
--&gt;


### Application PBCs

| NAME | DESCRIPTION | BENEFITS |
| --- | --- | --- |
| Back Office | The administration interface that allows you to manage all back-office tasks. In the Back Office, you can manage and create customer accounts and define who can access the Back Office. You can also keep track of all your internal processes including the management of your products, orders, customers and many more. | Keeps your back-end processes running efficiently, protects your data and administers all accounts. |
| Storefront | The out-of-the-box online shop application that includes all regular functionalities and workflows. You can use the Storefront as a boilerplate to kick-start your project. | Lets you easily start your online shop from our boilerplate solution. |

## App Composition Platform apps

&lt;div class=&quot;width-100&quot;&gt;

| APP | CATEGORY |
| --- | --- |
| [Vertex](/docs/pbc/all/tax-management/{{site.version}}/base-shop/third-party-integrations/vertex/vertex.html) | Tax compliance. |
| [Algolia](/docs/pbc/all/search/{{site.version}}/base-shop/third-party-integrations/algolia/integrate-algolia.html) | Search engine. |
| [Payone](/docs/pbc/all/payment-service-providers/payone/integrate-payone.html) | Payment service provider. |
| [Bazaarvoice](/docs/pbc/all/ratings-reviews/{{site.version}}/third-party-integrations/integrate-bazaarvoice.html) | Platform for user-generated content. |
| [Stripe](/docs/pbc/all/payment-service-provider/{{site.version}}/base-shop/third-party-integrations/stripe/stripe.html) |  Financial infrastructure platform. |

&lt;/div&gt;
</description>
            <pubDate>Tue, 01 Sep 2026 08:26:04 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/pbc.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/pbc.html</guid>
            
            
        </item>
        
        <item>
            <title>View service points</title>
            <description>This topic describes how to view [service points](/docs/pbc/all/service-point-management/latest/unified-commerce/service-points-feature-overview.html#service-point) in the Back Office.

To start working with service points, go to **Customer Portal&amp;nbsp;&lt;span aria-label=&quot;and then&quot;&gt;&gt;&lt;/span&gt; Service Points**.

The Back Office presents service points as read-only information. To create or change them, use the [Glue API](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-points/glue-api-add-service-points.html) or [data import](/docs/pbc/all/service-point-management/latest/unified-commerce/import-and-export-data/import-file-details-service-point.csv.html).

## Prerequisites

Import service points, or add them using Glue API. Both active and inactive service points are displayed.

Each section contains reference information. Review it before you start, or look up the necessary information as you go through the process.

## View the list of service points

To view all the service points of your project:
1. Go to **Customer Portal&amp;nbsp;&lt;span aria-label=&quot;and then&quot;&gt;&gt;&lt;/span&gt; Service Points**.
    This opens the **Service Points** page.
2. Optional: To find a specific service point, enter its name, key, city, zip code, or country in **Search**.
3. Optional: To change the order of the records, click a column header.

![Service points list in the Back Office](https://spryker.s3.eu-central-1.amazonaws.com/docs/pbc/all/service-point-management/unified-commerce/manage-in-the-back-office/service-points-list.png)

**Tips and tricks**
&lt;br&gt;Inactive service points remain in the list and are marked as **Inactive**. A service point that has no address yet is displayed with **Not set** in the **Address** column.

### Reference information: View the list of service points

The following table describes the columns of the **Service Points** page:

| COLUMN | DESCRIPTION |
| --- | --- |
| Name | Name of the service point. |
| Key | Unique service point identifier. |
| Address | Zip code, city, and country of the service point. Displays **Not set** if the service point has no address. |
| Stores | All the stores the service point is assigned to. |
| Service Types | Service types of all the services provided at the service point. |
| Status | Defines if the service point is active. Inactive service points are not offered on the Storefront. |
| Actions | Opens the details of the service point. |

## View the details of a service point

To view the details of a service point:
1. Go to **Customer Portal&amp;nbsp;&lt;span aria-label=&quot;and then&quot;&gt;&gt;&lt;/span&gt; Service Points**.
2. Next to the service point you want to view, click **View**.
    This opens the **View Service Point** page.

![Service point details in the Back Office](https://spryker.s3.eu-central-1.amazonaws.com/docs/pbc/all/service-point-management/unified-commerce/manage-in-the-back-office/service-point-details.png)

### Reference information: View the details of a service point

The following table describes the sections of the **View Service Point** page:

| SECTION | DESCRIPTION |
| --- | --- |
| Service Point | Name, key, status, and assigned stores of the service point. |
| Address | Full address of the service point in one line: street, zip code, city, region, and country. Parts that are not defined are omitted. Displays **Not set** if the service point has no address. |
| Services | All the services provided at the service point, with their service type, key, and status. |
| Connected Offers | All the product offers that are provided through the services of the service point. An offer connected through several services of the same service point is displayed once. |

The following table describes the columns of the **Connected Offers** section:

| COLUMN | DESCRIPTION |
| --- | --- |
| Offer Reference | Unique product offer identifier. |
| SKU | SKU of the concrete product the offer belongs to. |
| Stores | All the stores the product offer is assigned to. |
| Approval Status | Approval status of the product offer: **Approved**, **Waiting for Approval**, or **Denied**. |
| Status | Defines if the product offer is active. |
| Actions | Opens or edits the product offer. The actions are displayed only if the corresponding pages are configured in your project. |
</description>
            <pubDate>Mon, 31 Aug 2026 13:10:15 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/service-point-management/latest/unified-commerce/manage-in-the-back-office/view-service-points.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/service-point-management/latest/unified-commerce/manage-in-the-back-office/view-service-points.html</guid>
            
            
        </item>
        
        <item>
            <title>Service Points feature overview</title>
            <description>The *Service Points* feature lets you create and manage service points, service types, and associated services.

## Service point

A *service point* is a physical location where services are provided. Depending on the services provided, there can be different kinds of service points, like a warehouse or a physical store. The definition of a service point ultimately depends on the services it provides.

To add service points using Glue API, see [Add service points](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-points/glue-api-add-service-points.html). To import service points, see [Import file details: service_point.csv](/docs/pbc/all/service-point-management/latest/unified-commerce/import-and-export-data/import-file-details-service-point.csv.html).

To add service point addresses using Glue API, see [Add service point addresses](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-point-addresses/glue-api-add-service-point-addresses.html). To import service point addresses, see [Import file details: service_point_address.csv](/docs/pbc/all/service-point-management/latest/unified-commerce/import-and-export-data/import-file-details-service-point-address.csv.html)

## Service type

A *service type* is a classification of services that a business offers to its customers. Service types are determined by the nature of the business. Service type examples:
- Pickup service
- Return service
- Rental service
- Repair service

To add service types using Glue API, see [Add service types](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-types/glue-api-add-service-types.html).

To import service types, see [Import file details: service_type.csv](/docs/pbc/all/service-point-management/latest/unified-commerce/import-and-export-data/import-file-details-service-type.csv.html).


## Service

A *service* represents a specific service type that is provided at a specific service point. Because each service is unique, if two service points provide services with the same service type, like pickup, those services are represented as two separate entities and are managed accordingly. For example, a pickup service at a retail location at Julie-Wolfthorn-Straße 1, 10115, Berlin is a unique service.

To add services using Glue API, see [Add services](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-services/glue-api-add-services.html).

To import services, see [Import file details: service.csv](/docs/pbc/all/service-point-management/latest/unified-commerce/import-and-export-data/import-file-details-service.csv.html).


## Service points use cases


With the help of service points, types, and services, a store operator can model different use cases depending on their business needs. Here are some examples of services that can be implemented at the project level:
- Ship from store
- Car maintenance or installations services
- Product demonstration at a retail location
- Repair service at a retail location


## Service points in the Back Office

Back Office users can review all the service points of a project, including the inactive ones, in **Customer Portal&amp;nbsp;&lt;span aria-label=&quot;and then&quot;&gt;&gt;&lt;/span&gt; Service Points**. The list shows the name, key, address, assigned stores, service types, and status of every service point. Opening a service point displays its address, the services provided at it, and the product offers connected to it through those services.

Service points are read-only in the Back Office. To create or change them, use Glue API or data import.

For more details, see [View service points](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-in-the-back-office/view-service-points.html).

## Service points on the Storefront

When checking out, customers select a service point they want the order to be processes at. The feature is shipped with a search widget that lets them search service points by the following:
- Service point name
- Zip code
- City

By default, search results are sorted by city.

![service point search widget](https://spryker.s3.eu-central-1.amazonaws.com/docs/pbc/all/service-point-management/unified-commerce/service-points-feature-overview.md/service-point-search.png)

You can add only predefined service points by default. But developers can configure customers to be able to enter custom addresses for service points.

After placing an order, the customer can see the selected service point on the Order Details page.

![Storefront order with a service point](https://spryker.s3.eu-central-1.amazonaws.com/docs/pbc/all/service-point-management/unified-commerce/service-points-feature-overview.md/storefront-order-service-point.png)


## Current constraints

- Services can be configured only for product offers.
- Product catalog can&apos;t be filtered by a service type or a service provided in a specific service point.
- The product offer widget on the product details page is not supported. It doesn&apos;t show the differences between product offers based on the services assigned to them. As a result, differnt product offers are displayed as duplicates.
- Customers can&apos;t add products with preselected service points to cart. They can select service points only during checkout.
- If a product is added to cart without a product offer attached to it, this product can be purchased only with the *Delivery* shipment type.


## Related Business User documents

| FEATURE OVERVIEWS | MERCHANT PORTAL GUIDES |
| - | - |
| [Shipment feature overview](/docs/pbc/all/carrier-management/latest/base-shop/shipment-feature-overview.html) | [Create and edit product offers](/docs/pbc/all/offer-management/latest/unified-commerce/unified-commerce-create-and-edit-product-offers.html) |



## Related Developer documents

| INSTALLATION GUIDES | GLUE API GUIDES   |
| - | - |
| [Install the Service Points feature](/docs/pbc/all/service-point-management/latest/unified-commerce/install-features/install-the-service-points-feature.html) | [Add service points](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-points/glue-api-add-service-points.html) |
| [Install the Service Points + Shipment feature](/docs/pbc/all/service-point-management/latest/unified-commerce/install-features/install-the-service-points-shipment-feature.html) |  [Retrieve service points](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-points/glue-api-retrieve-service-points.html)  |
| [Install the Service Points + Customer Account Management feature](/docs/pbc/all/service-point-management/latest/unified-commerce/install-features/install-the-service-points-customer-account-management-feature.html) | [Update service points](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-points/glue-api-update-service-points.html) |
| [Install the Service Points + Order Management feature](/docs/pbc/all/service-point-management/latest/unified-commerce/install-features/install-the-service-points-order-management-feature.html) | [Add service types](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-types/glue-api-add-service-types.html) |
| [Install the Product Offer Shipment feature](/docs/pbc/all/offer-management/latest/marketplace/install-and-upgrade/install-features/install-the-product-offer-shipment-feature.html) | [Retrieve service types](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-types/glue-api-retrieve-service-types.html) |
| [Install the Shipment + Customer Account Management feature](/docs/pbc/all/carrier-management/latest/base-shop/install-and-upgrade/install-features/install-the-shipment-customer-account-management-feature.html) | [Update service types](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-types/glue-api-update-service-types.html) |
| |  [Add service point addresses](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-point-addresses/glue-api-add-service-point-addresses.html) |
| |  [Retrieve service point addresses](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-point-addresses/glue-api-retrieve-service-point-addresses.html) |
| |  [Add service point addresses](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-service-point-addresses/glue-api-update-service-point-addresses.html) |
| | [Add services](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-services/glue-api-add-services.html) |
| |  [Retrieve services](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-services/glue-api-retrieve-services.html) |
| | [Update services](/docs/pbc/all/service-point-management/latest/unified-commerce/manage-using-glue-api/manage-services/glue-api-update-services.html) |
</description>
            <pubDate>Mon, 31 Aug 2026 13:10:15 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/service-point-management/latest/unified-commerce/service-points-feature-overview.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/service-point-management/latest/unified-commerce/service-points-feature-overview.html</guid>
            
            
        </item>
        
        <item>
            <title>System requirements</title>
            <description>## System requirements (apps based on Spryker Framework)


| REQUIREMENT                                | VALUE                                                                                                                                                                                                                                                                                                                                                                                   |
|--------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| OS                                         | Native: Linux                                                                                                                                                                                                                                                                                                                                                                           |
| Web server                                 | NginX or any web server supporting PHP, like lighttpd, Apache, or Cherokee.                                                                                                                                                                                                                                                                                                             |
| Databases                                  | For cloud environments, MariaDB &gt;= 11.8 preferred. For on-premise environments, PostgreSQL &gt;=17  or MySQL &gt;=8.4.                                                                                                                                                                                                                                                                        |
| PHP                                        | PHP &gt;=8.3 with the following extensions: `curl`, `json`, `mysql`, `pdo-sqlite`, `sqlite3`, `gd`, `intl`, `mysqli`, `pgsql`, `ssh2`, `gmp`, `mcrypt`, `pdo-mysql`, `readline`, `twig`, `imagick`, `memcache`, `pdo-pgsql`, `redis`, `xml`, `bz2`, and `mbstring`. For details about supported PHP versions, see [Supported Versions of PHP](/docs/dg/dev/supported-versions-of-php.html) |
| SSL                                        | For production environments, a valid security certificate is required for HTTPS.                                                                                                                                                                                                                                                                                                        |
| Key-value store (Redis or Valkey)          | Redis versions 5.0 and 6.2; Valkey version &gt;=7.2                                                                                                                                                                                                                                                                                                                                        |
| Search engines                             | Elasticsearch 7.x, OpenSearch [1.3, 2.19, 3.5](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/what-is.html#end-of-support)                                                                                                                                                                                                                                        |
| RabbitMQ                                   | Version &gt;=4.1                                                                                                                                                                                                                                                                                                                                                                           |
| Jenkins (for cronjob management)           | Version &gt;=2.0                                                                                                                                                                                                                                                                                                                                                                           |
| Graphviz (for state machine visualization) | Version &gt;=2.0                                                                                                                                                                                                                                                                                                                                                                           |
| Node.js                                    | Version &gt;= 24.14.0. For details about upgrading, see [Upgrade Node.js and npm](/docs/dg/dev/upgrade-and-migrate/upgrade-nodejs.html)                                                                                                                                                                                                                                                    |
| npm                                        | Version &gt;= 10.0.0                                                                                                                                                                                                                                                                                                                                                                       |
| Intranet                                   | Back Office application must be secured in an Intranet using VPN, Basic Auth, IP allowlist, or DMZ.                                                                                                                                                                                                                                                                                     |
| Available languages                        | German and English. UTF-8 left-to-right languages are fully supported.                                                                                                                                                                                                                                                                                                                  |


## Marketplace system requirements

| OPERATING SYSTEM | NATIVE: LINUX-ONLY THROUGH VM: MACOS AND MS WINDOWS                                                                                                                                                                                                                                                                                                                                  |
|---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Web server                                | NginX or any web server supporting PHP, like lighttpd, Apache, or Cherokee.                                                                                                                                                                                                                                                                                                          |
| Databases                               | MariaDB &gt;= 11.8 preferred, PostgreSQL &gt;=17, or MySQL &gt;=8.4.                                                                                                                                                                                                                                                                                                                          |
| PHP                                       | PHP &gt;=8.3 with the following extensions: `curl`, `json`, `mysql`, `pdo-sqlite`, `sqlite3`, `gd`, `intl`, `mysqli`, `pgsql`, `ssh2`, `gmp`, `mcrypt`, `pdo-mysql`, `readline`, `twig`, `imagick`, `memcache`, `pdo-pgsql`, `redis`, `xml`, `bz2`, `mbstring`. For details about supported PHP versions, see [Supported Versions of PHP](/docs/dg/dev/supported-versions-of-php.html). |
| SSL                                       | For production systems, a valid security certificate is required for HTTPS.                                                                                                                                                                                                                                                                                                          |
| Key-value store (Redis or Valkey)         | Redis versions 5.0 and 6.2; Valkey version &gt;=7.2                                                                                                                                                                                                                                                                                                                                     |
| Search engines                             | Elasticsearch &gt;=7.0, OpenSearch &gt;=1.3                                                                                                                                                                                                                                                                                                                                                |
| RabbitMQ                                  | Version &gt;=4.1                                                                                                                                                                                                                                                                                                                                                                        |
| Jenkins (for cronjob management)          | Version &gt;=2.0                                                                                                                                                                                                                                                                                                                                                                        |
| Graphviz (for state machine visualization) | Version &gt;=2.0                                                                                                                                                                                                                                                                                                                                                                        |
| Symfony                                   | Versions 5.0, and 6.0                                                                                                                                                                                                                                                                                                                                                                |
| Node.js                                   | Version &gt;= 24.14.0. For details about upgrading, see [Upgrade Node.js and npm](/docs/dg/dev/upgrade-and-migrate/upgrade-nodejs.html)                                                                                                                                                                                                                                                  |
| npm                                       | Version &gt;= 10.0.0                                                                                                                                                                                                                                                                                                                                                                    |
| Intranet                                  | Back Office application must be secured in an Intranet using VPN, Basic Auth, IP allowlist, or DMZ.                                                                                                                                                                                                                                                                                  |
| Spryker Commerce OS                       | Version &gt;= {{page.release_tag}}                                                                                                                                                                                                                                                                                                                                                          |
</description>
            <pubDate>Mon, 31 Aug 2026 06:49:34 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/system-requirements/latest/system-requirements.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/system-requirements/latest/system-requirements.html</guid>
            
            
        </item>
        
        <item>
            <title>Migrate from OpenSearch 1.3 to 3.5</title>
            <description>This document describes how to migrate an OpenSearch cluster used by a Spryker project from version 1.3 to 3.5.

Between major OpenSearch versions, an index or a cluster setting can become incompatible with the target version, for example because of an outdated Lucene index format, a deprecated setting, or a breaking plugin change. OpenSearch does not support skipping major versions and cannot be upgraded in place while such incompatibilities exist, so the upgrade is blocked until you resolve them. To resolve this, you upgrade the cluster incrementally, from 1.3 to 2.19, and then from 2.19 to 3.5, fixing every incompatibility before each upgrade.

{% info_block warningBox &quot;Verification&quot; %}

Before you start, test the migration in a non-production environment. Blocking writes to an index makes it read-only until the reindexing is complete, so plan the migration for a maintenance window.

{% endinfo_block %}

## 1. Update the required modules

You only need to update the packages for the features used in your project. Check the following table and update the packages that are installed in your project to at least the specified versions:

| Package | Minimum version |
| --- | --- |
| `spryker/sales-return-search` | 1.4.1 |
| `spryker/merchant-search` | 1.3.1 |
| `spryker/product-review` | 2.15.1 |
| `spryker/search-elasticsearch` | 1.23.1 |
| `spryker/service-point-search` | 1.4.1 |
| `spryker-feature/self-service-portal` | 20.9.1 |

Update the packages installed in your project using Composer:

```bash
composer update spryker/merchant-search:&quot;^1.3.1&quot; spryker/product-review:&quot;^2.15.1&quot; spryker/sales-return-search:&quot;^1.4.1&quot; spryker/search-elasticsearch:&quot;^1.23.1&quot; spryker/service-point-search:&quot;^1.4.1&quot; spryker-feature/self-service-portal:&quot;^20.9.1&quot;
```

If your project overrides the search schema of these modules, apply the equivalent changes to your project-level search schema as well.

## 2. Check the upgrade eligibility

Consult the breaking changes and deprecation notices for OpenSearch 2.19, and check your indexes and cluster settings against them to determine whether the cluster is eligible for the upgrade.

- If the cluster is eligible, upgrade it to 2.19 and continue to [4. Check the upgrade eligibility for 3.5](#4-check-the-upgrade-eligibility-for-35).
- If the cluster is not eligible, one or more incompatibilities block the upgrade.
  - For an index with an incompatible index format, unblock the upgrade by reindexing it as described in the following section.
  - For any other incompatibility, for example a deprecated index setting or a breaking plugin change, fix it first. Only continue with the upgrade after all the incompatibilities are resolved.

## 3. Reindex an index to unblock the upgrade

Repeat the following steps for every index with an incompatible index format. Replace `&lt;index&gt;` with the name of the index you are migrating.

### 3.1. Block writes to the index

To prevent data from changing while the index is being cloned, block write operations:

```json
PUT /&lt;index&gt;/_settings
{
  &quot;settings&quot;: {
    &quot;index.blocks.write&quot;: true
  }
}
```

### 3.2. Clone the index

Clone the index into a temporary index:

```json
PUT /&lt;index&gt;/_clone/&lt;index&gt;_tmp
```

### 3.3. Verify the document count

Compare the document count of `&lt;index&gt;` and `&lt;index&gt;_tmp`. The counts must match before you continue.

### 3.4. Delete the original index

```json
DELETE /&lt;index&gt;
```

### 3.5. Re-create the index and its schema

Re-create the index and install its schema using the following console command:

```bash
console search:setup:sources
```

### 3.6. Reindex the documents

Reindex the documents from the temporary index back into the newly created `&lt;index&gt;`:

```json
POST /_reindex?slices=2&amp;wait_for_completion=false
{
  &quot;source&quot;: {
    &quot;index&quot;: &quot;&lt;index&gt;_tmp&quot;
  },
  &quot;dest&quot;: {
    &quot;index&quot;: &quot;&lt;index&gt;&quot;
  }
}
```

### 3.7. Verify the document count again

Compare the document count of `&lt;index&gt;` and `&lt;index&gt;_tmp` again. The counts must match before you continue.

### 3.8. Delete the temporary index

```json
DELETE /&lt;index&gt;_tmp
```

After you reindex all the blocking indexes, upgrade the cluster to version 2.19.

## 4. Check the upgrade eligibility for 3.5

Consult the breaking changes and deprecation notices for OpenSearch 3.5, and check your indexes and cluster settings on version 2.19 against them to determine whether the cluster is eligible for the upgrade.

- If the cluster is eligible, upgrade it to 3.5.
- If the cluster is not eligible, incompatibilities again block the upgrade. Resolve them the same way as in step 2: reindex every index with an incompatible index format by repeating the steps in [3. Reindex an index to unblock the upgrade](#3-reindex-an-index-to-unblock-the-upgrade), and fix any other incompatibility. Then upgrade the cluster to 3.5.
</description>
            <pubDate>Wed, 26 Aug 2026 12:12:26 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/search/latest/base-shop/install-and-upgrade/migrate-from-opensearch-1.3-to-3.5.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/search/latest/base-shop/install-and-upgrade/migrate-from-opensearch-1.3-to-3.5.html</guid>
            
            
        </item>
        
        <item>
            <title>Upgrade the PurchasingControl module</title>
            <description>{% include pbc/all/upgrade-modules/upgrade-the-purchasingcontrol-module.md %} &lt;!-- To edit, see /_includes/pbc/all/upgrade-modules/upgrade-the-purchasingcontrol-module.md --&gt;
</description>
            <pubDate>Tue, 25 Aug 2026 08:40:54 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/upgrade-modules/upgrade-the-purchasingcontrol-module.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/upgrade-modules/upgrade-the-purchasingcontrol-module.html</guid>
            
            
        </item>
        
        <item>
            <title>Purchasing Control feature overview</title>
            <description>The *Purchasing Control* feature lets B2B companies track and control procurement spending by assigning orders to cost centers and enforcing configurable budget rules. It extends the existing [Approval Process](/docs/pbc/all/cart-and-checkout/latest/base-shop/feature-overviews/approval-process-feature-overview.html) with a second dimension of spending governance: per-department or per-project budget limits that work alongside the existing per-person permission limits.

Buyers assign a cost center and a budget to a cart at checkout or to a quote request, and agents can do the same on a customer&apos;s behalf.

{% info_block infoBox &quot;Info&quot; %}

This feature is available in the Back Office and on the Storefront.

{% endinfo_block %}

## Cost centers

A *cost center* is an organizational unit within a company that incurs costs but does not directly generate revenue. Companies use cost centers to track and control spending by department, project, location, or function.

**Common examples:**

- **Departmental:** Marketing, Engineering, HR, Facilities
- **Project-based:** Office Renovation Q2 2026, Trade Show Berlin
- **Location-based:** Warehouse Berlin, Office London

Every purchase a buyer makes is charged to a cost center so the company can track where money is being spent. In ERP systems such as SAP, Oracle, and Microsoft Dynamics, cost centers are a foundational accounting concept - orders flow into the ERP tagged with a cost center code, enabling financial reporting and cost allocation.

### Deactivated cost centers and budgets

A cost center or budget can be deactivated instead of deleted, which preserves the history of the orders already charged to it. Deactivated records are not offered in the cost center and budget selectors, and a deactivated cost center is no longer shown where the assignment of a cart or quote is displayed. If every cost center available to a buyer is deactivated, or if the selected cost center has no active budget left, no choices are rendered and the buyer cannot assign one until an active record is available again.

## Budgets

A *budget* is a spending limit assigned to a cost center for a defined period - monthly, quarterly, or annually. It represents the maximum amount that a department or project is authorized to spend in that period.

**Example:** The Marketing department has a quarterly procurement budget of €50,000 for office supplies and event materials. Once that budget is consumed, further purchases are either blocked, flagged for review, or escalated for approval.

### Budget enforcement rules

Each budget is configured with one of three enforcement rules:

| RULE | DESCRIPTION |
| --- | --- |
| Block | The order is rejected outright when the budget is exceeded. The buyer cannot proceed to checkout. |
| Warn | A warning is displayed to the buyer, but they can proceed. |
| Require Approval | The order is sent for approval when the budget is exceeded. The buyer cannot complete checkout until an approver accepts the order. |

### Budgets in recurring orders

With the [Recurring Orders feature](/docs/pbc/all/order-experience-management/latest/base-shop/feature-overviews/recurring-orders-feature-overview.html) installed, a recurring order carries a cost center and a budget, selected on the recurring order forms and changeable when the buyer edits the schedule or approves a review.

Budgets with the **Require Approval** enforcement rule cannot be used for a recurring order. A recurring order places its follow-up orders unattended, so no approver can accept them at placement time. The restriction applies in two places:

- Such budgets are not offered in the recurring order budget selector.
- Checkout is blocked if a quote being set up as a recurring order carries one. This check applies regardless of the quote grand total and of the remaining budget amount, so the budget does not have to be exceeded for the block to take effect.

Regular, non-recurring checkouts are not affected—there, the **Require Approval** rule behaves as described above.

## Relationship to the Approval Process

Spryker&apos;s existing Approval Process triggers a workflow when a buyer&apos;s order exceeds their *Buy up to grand total* permission. The Purchasing Control feature adds a parallel check: an order might be within a buyer&apos;s personal permission limit but still exceed the cost center&apos;s remaining budget.

Both checks run independently at checkout. If either the permission limit or the budget rule is triggered, the configured action - block, warn, or require approval - is applied. This gives companies layered spending governance: per-person limits *and* per-department or per-project limits.

{% info_block infoBox &quot;Permissions required for the Require Approval enforcement rule&quot; %}

To use budgets with the **Require Approval** enforcement rule, the following [Approval Process](/docs/pbc/all/cart-and-checkout/latest/base-shop/feature-overviews/approval-process-feature-overview.html) permissions must be assigned to the relevant company roles:

| PERMISSION | REQUIRES |
| --- | --- |
| Buy up to grand total | Send cart for approval |
| Send cart for approval | Buy up to grand total |
| Approve up to grand total | None |

{% endinfo_block %}

{% info_block warningBox &quot;Approvals within a business unit&quot; %}

Approvers can only approve orders of employees within their own business unit. This constraint applies to both permission-based and budget-based approval requests.

{% endinfo_block %}

## Cost centers and budgets in the procurement workflow

The typical B2B procurement flow involving cost centers and budgets:

1. **Finance sets budgets.** At the start of a fiscal period, finance allocates budgets to each cost center.
2. **Buyers are assigned to cost centers.** Buyers are linked to one or more cost centers they are authorized to purchase against. Cost centers are linked to company business units, so all users in a business unit are automatically assigned to the corresponding cost centers.
3. **Orders are tagged.** At checkout, the buyer selects the cost center the purchase is charged to and the budget it is drawn from. Both fields are saved together in a single step:
   - The budget dropdown offers the budgets of every cost center available to the buyer, narrowed to the selected cost center.
   - Changing the cost center narrows the budget list immediately, without reloading the page. If the previously selected budget does not belong to the new cost center, the budget field is cleared.
   - If the selected cost center has no active budgets, the budget field is hidden and a message states that no budgets are available.
   - A budget that is selected must belong to the selected cost center. The pairing is validated on the server, so a mismatched combination is rejected even when the browser-side filtering is bypassed.
   - The selector form does not enforce a budget selection by itself, but the field is marked as required in the browser whenever the selected cost center has active budgets. An order cannot be placed without an active budget: if the buyer&apos;s business unit has active cost centers and no active budget is resolved, checkout fails and the buyer is asked to select a cost center and a budget.
4. **Budget is validated.** The system checks whether the order total fits within the remaining budget for the selected cost center.
5. **Enforcement rules apply.** Based on the configured rule, the order is blocked, a warning is shown, or approval is required.
6. **Budget is consumed.** Once the order is confirmed, the budget balance is reduced by the order amount.
7. **Budget is restored.** If the order is cancelled or refunded, the budget balance is restored by the amount corresponding to the cancelled or refunded items. For partial cancellations or refunds, only the amounts of the affected items are restored.

## Checkout validation outcomes

| SCENARIO | OUTCOME |
| --- | --- |
| Within budget and within permission limit | Buyer completes checkout without additional steps. |
| Exceeds budget  -  Warn rule | A warning is displayed; the buyer can proceed to checkout. |
| Exceeds budget  -  Require Approval rule | The order is sent for approval; the buyer cannot complete checkout until approved. |
| Exceeds Buy up to grand total permission limit | The order is sent for approval, same as the standard Approval Process. |
| Exceeds budget  -  Block rule | Checkout is blocked; no approval option is available. |
| Recurring order with a Require Approval budget | Checkout is blocked, whether or not the budget is exceeded. The buyer must select a budget bound to the Block or Warn rule. |
| No active budget resolved while the business unit has active cost centers | Checkout fails; the buyer must select a cost center and a budget before placing the order. |

## Quote lock

When an order is sent for approval - whether triggered by a budget rule or a permission limit - the quote is locked to preserve the order state during the approval review. Neither the buyer nor the approver can modify the quote while it is pending approval. For details, see [Quote lock functionality](/docs/pbc/all/cart-and-checkout/latest/base-shop/feature-overviews/approval-process-feature-overview.html#quote-lock-functionality).

## Cost centers and budgets on quote requests

Buyers do not have to wait until checkout to attribute spending. The same cost center and budget selection is available on quote requests, so a purchase is assigned to a cost center while it is still being negotiated.

{% info_block infoBox &quot;Info&quot; %}

Cost center selection on quote requests requires the [Quotation Process](/docs/pbc/all/request-for-quote/latest/install-and-upgrade/install-features/install-the-quotation-process-feature.html) feature.

{% endinfo_block %}

| CONTEXT | WHERE THE SELECTION IS AVAILABLE |
| --- | --- |
| Buyer (Storefront) | Quote request details page and quote request edit page in the company area. |
| Agent (Storefront) | Agent quote request details page and agent quote request edit page. |

### Cost centers follow the quote request owner

Cost centers are scoped per company business unit. On a quote request, the applicable cost centers are those of the business unit that owns the request - the business unit of the company user who created it - and not those of the person currently viewing it. An agent working on a customer&apos;s quote request therefore sees the customer&apos;s cost centers rather than their own.

Buyers can only change the cost center on their own quote requests. A quote request reference that belongs to another company user is treated as not found, so an unauthorized reference is indistinguishable from a reference that does not exist.

### Editability

On a quote request, selecting a budget is optional: there is no checkout to pass, so a quote request is saved with a cost center and no budget. A budget that is selected must still belong to the selected cost center.

A cost center and a budget can be changed only while the quote request is in an editable status. The status check runs on the server, so a quote request that is no longer editable is rejected even when the form is submitted directly. Quote requests that are not editable still display the assigned cost center and budget as read-only.

## Roles and capabilities

| ROLE | CAPABILITIES |
| --- | --- |
| Site Operator (Back Office) | Create, update, activate, and deactivate cost centers. Assign cost centers to business units. Create and manage budgets with amount, period, currency, and enforcement rule. View the **Cost Center** column in the orders table. Filter and search orders by cost center and budget. View spend-vs-budget reports. Export reports to CSV. Review the audit log. Import cost centers, budgets, and business unit assignments in bulk using data import. |
| Cost Center Manager (Storefront) | Create, update, activate, and deactivate cost centers and budgets from the company area. Requires the **Manage Cost Centers** permission assigned to their company role. |
| Buyer (Storefront) | Select a cost center and budget at checkout. Assign a cost center and budget to their own quote requests. View the remaining budget for the selected cost center. Submit orders for approval when required. Filter order history by cost center and budget. View the assigned cost center and budget on order detail pages. |
| Approver (Storefront) | Review locked quotes pending approval. Approve or reject orders, including those triggered by budget rules. |
| Agent (Storefront) | Assign a cost center and budget to a customer&apos;s quote request on the customer&apos;s behalf, using the cost centers of the quote request owner&apos;s business unit. |

## Related Developer documents

| INSTALLATION GUIDES |
| --- |
| [Install the Purchasing Control feature](/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/install-features/install-the-purchasing-control-feature.html) |

| MIGRATION GUIDES |
| --- |
| [Upgrade the PurchasingControl module](/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/upgrade-modules/upgrade-the-purchasingcontrol-module.html) |
</description>
            <pubDate>Tue, 25 Aug 2026 08:40:54 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/base-shop/feature-overviews/purchasing-control-feature-overview.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/base-shop/feature-overviews/purchasing-control-feature-overview.html</guid>
            
            
        </item>
        
        <item>
            <title>Install the Purchasing Control feature</title>
            <description>This document describes how to install the [Purchasing Control feature](/docs/pbc/all/cart-and-checkout/latest/base-shop/feature-overviews/purchasing-control-feature-overview.html).

## Install feature core

Follow the steps below to install the Purchasing Control feature core.

### Prerequisites

To start feature integration, review and install the necessary features:

| NAME | VERSION | INSTALLATION GUIDE |
| --- | --- | --- |
| Spryker Core | {{page.release_tag}} | [Install the Spryker Core feature](/docs/pbc/all/miscellaneous/latest/install-and-upgrade/install-features/install-the-spryker-core-feature.html) |
| Company Account | {{page.release_tag}} | [Install the Company Account feature](/docs/pbc/all/customer-relationship-management/latest/base-shop/install-and-upgrade/install-features/install-the-company-account-feature.html) |
| Checkout | {{page.release_tag}} | [Install the Checkout feature](/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/install-features/install-the-checkout-feature.html) |
| Approval Process | {{page.release_tag}} | [Install the Approval Process feature](/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/install-features/install-the-approval-process-feature.html) |
| Quotation Process | {{page.release_tag}} | [Install the Quotation Process feature](/docs/pbc/all/request-for-quote/latest/install-and-upgrade/install-features/install-the-quotation-process-feature.html) |

{% info_block infoBox &quot;Quotation Process&quot; %}

The Quotation Process feature is required only if you want buyers and agents to assign cost centers and budgets to quote requests. The `spryker/quote-request` and `spryker/quote-request-agent` modules are installed as dependencies of `spryker-feature/purchasing-control` in either case, but the Storefront quote request pages are only available with the Quotation Process feature installed. If you do not need this part of the feature, skip [Integrate cost centers with quote requests](#integrate-cost-centers-with-quote-requests).

{% endinfo_block %}

### 1) Install the required modules

```bash
composer require spryker-feature/purchasing-control:&quot;^2.0.0&quot; spryker/sales:&quot;^11.83.0&quot; spryker/sales-extension:&quot;^1.15.0&quot; spryker-shop/checkout-page:&quot;^3.40.0&quot; spryker-shop/company-page:&quot;^2.36.0&quot; spryker-shop/customer-page:&quot;^2.77.0&quot; spryker-shop/shop-ui:&quot;^1.108.0&quot; --update-with-dependencies --ignore-platform-req=ext-grpc
```

### 2) Set up database schema and transfer objects

Apply database changes and generate entity and transfer changes:

```bash
console propel:install
console transfer:generate
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure the following changes have been applied in the database:

| DATABASE ENTITY | TYPE | EVENT |
| --- | --- | --- |
| spy_cost_center | table | created |
| spy_cost_center_to_company_business_unit | table | created |
| spy_budget | table | created |
| spy_budget_consumption | table | created |
| spy_quote.fk_cost_center | column | created |
| spy_quote.fk_budget | column | created |
| spy_sales_order.fk_cost_center | column | created |
| spy_sales_order.fk_budget | column | created |

Make sure the following changes have been applied in transfer objects:

| TRANSFER | TYPE | EVENT | PATH |
| --- | --- | --- | --- |
| CostCenter | class | created | src/Generated/Shared/Transfer/CostCenterTransfer.php |
| CostCenterCollection | class | created | src/Generated/Shared/Transfer/CostCenterCollectionTransfer.php |
| CostCenterCriteria | class | created | src/Generated/Shared/Transfer/CostCenterCriteriaTransfer.php |
| CostCenterConditions | class | created | src/Generated/Shared/Transfer/CostCenterConditionsTransfer.php |
| CostCenterCollectionRequest | class | created | src/Generated/Shared/Transfer/CostCenterCollectionRequestTransfer.php |
| CostCenterCollectionResponse | class | created | src/Generated/Shared/Transfer/CostCenterCollectionResponseTransfer.php |
| CostCenterResponse | class | created | src/Generated/Shared/Transfer/CostCenterResponseTransfer.php |
| CostCenterQuoteUpdateRequest | class | created | src/Generated/Shared/Transfer/CostCenterQuoteUpdateRequestTransfer.php |
| CostCenterQuoteUpdateResponse | class | created | src/Generated/Shared/Transfer/CostCenterQuoteUpdateResponseTransfer.php |
| Budget | class | created | src/Generated/Shared/Transfer/BudgetTransfer.php |
| BudgetCollection | class | created | src/Generated/Shared/Transfer/BudgetCollectionTransfer.php |
| BudgetCriteria | class | created | src/Generated/Shared/Transfer/BudgetCriteriaTransfer.php |
| BudgetConditions | class | created | src/Generated/Shared/Transfer/BudgetConditionsTransfer.php |
| BudgetCollectionRequest | class | created | src/Generated/Shared/Transfer/BudgetCollectionRequestTransfer.php |
| BudgetCollectionResponse | class | created | src/Generated/Shared/Transfer/BudgetCollectionResponseTransfer.php |
| BudgetResponse | class | created | src/Generated/Shared/Transfer/BudgetResponseTransfer.php |
| BudgetConsumption | class | created | src/Generated/Shared/Transfer/BudgetConsumptionTransfer.php |
| BudgetConsumptionCollection | class | created | src/Generated/Shared/Transfer/BudgetConsumptionCollectionTransfer.php |
| BudgetConsumptionCriteria | class | created | src/Generated/Shared/Transfer/BudgetConsumptionCriteriaTransfer.php |
| BudgetConsumptionConditions | class | created | src/Generated/Shared/Transfer/BudgetConsumptionConditionsTransfer.php |
| Quote.idCostCenter | property | created | src/Generated/Shared/Transfer/QuoteTransfer.php |
| Quote.idBudget | property | created | src/Generated/Shared/Transfer/QuoteTransfer.php |
| Quote.costCenter | property | created | src/Generated/Shared/Transfer/QuoteTransfer.php |
| Quote.budget | property | created | src/Generated/Shared/Transfer/QuoteTransfer.php |
| Order.fkCostCenter | property | created | src/Generated/Shared/Transfer/OrderTransfer.php |
| Order.fkBudget | property | created | src/Generated/Shared/Transfer/OrderTransfer.php |
| Order.costCenter | property | created | src/Generated/Shared/Transfer/OrderTransfer.php |
| Order.budget | property | created | src/Generated/Shared/Transfer/OrderTransfer.php |
| OrderTableCriteria.costCenterIds | property | created | src/Generated/Shared/Transfer/OrderTableCriteriaTransfer.php |
| OrderTableCriteria.budgetIds | property | created | src/Generated/Shared/Transfer/OrderTableCriteriaTransfer.php |

{% endinfo_block %}

### 3) Set up data import

Register the following data import plugins:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| CostCenterDataImportPlugin | Imports cost centers from `cost_center.csv`. Creates or updates cost centers by key, name, description, and active status. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\DataImport |
| BudgetDataImportPlugin | Imports budgets from `budget.csv`. Resolves the cost center by key and creates or updates budgets by cost center and name. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\DataImport |
| CostCenterToCompanyBusinessUnitDataImportPlugin | Imports cost center to company business unit relations from `cost_center_company_business_unit.csv`. Skips already existing relations. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\DataImport |

**src/Pyz/Zed/DataImport/DataImportDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\DataImport;

use Spryker\Zed\DataImport\DataImportDependencyProvider as SprykerDataImportDependencyProvider;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\DataImport\BudgetDataImportPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\DataImport\CostCenterDataImportPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\DataImport\CostCenterToCompanyBusinessUnitDataImportPlugin;

class DataImportDependencyProvider extends SprykerDataImportDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Zed\DataImport\Dependency\Plugin\DataImportPluginInterface&gt;
     */
    protected function getDataImporterPlugins(): array
    {
        return [
            // ...
            new CostCenterDataImportPlugin(), #PurchasingControlFeature
            new BudgetDataImportPlugin(), #PurchasingControlFeature
            new CostCenterToCompanyBusinessUnitDataImportPlugin(), #PurchasingControlFeature
        ];
    }
}
```

Create the CSV import files:

**data/import/common/common/cost_center.csv**

```csv
key,name,description,is_active
cc-marketing,Marketing,Marketing and communications expenses,1
cc-it,IT &amp; Operations,IT infrastructure and software licenses,1
```

**data/import/common/common/budget.csv**

```csv
cost_center_key,name,amount,currency_iso_code,starts_at,ends_at,enforcement_rule,is_active
cc-marketing,Marketing Q2 2026,50000,EUR,2026-04-01,2026-06-30,warn,1
cc-it,IT Software Licenses 2026,100000,EUR,2026-01-01,2026-12-31,block,1
cc-it,IT Hardware Approvals 2026,200000,EUR,2026-01-01,2026-12-31,require_approval,1
```

**data/import/common/common/cost_center_company_business_unit.csv**

```csv
cost_center_key,business_unit_key
cc-marketing,spryker_systems_berlin
cc-it,spryker_systems_berlin
```

Import the data:

```bash
console data:import purchasing-control-cost-center
console data:import purchasing-control-budget
console data:import purchasing-control-cost-center-to-company-business-unit
```

{% info_block warningBox &quot;Verification&quot; %}

In the Back Office, under **Customers &gt; Cost Centers**, make sure the imported cost centers and budgets are displayed. Make sure the cost centers are assigned to the expected business units.

{% endinfo_block %}

### 4) Set up behavior

Enable the following behaviors by registering the plugins.

#### Set up Zed plugins

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| ManageCostCentersPermissionPlugin | Grants permission to create, update, and manage cost centers. Assign this permission to company roles that should have access to Purchasing Control management pages. | None | SprykerFeature\Shared\PurchasingControl\Plugin\Permission |
| BudgetCheckoutPreConditionPlugin | Validates the cart grand total against the remaining budget before checkout proceeds. Blocks checkout or triggers the approval flow depending on the budget enforcement rule. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Checkout |
| CostCenterOrderSaverPlugin | Saves the selected cost center and budget references to the sales order during checkout. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Checkout |
| ConsumeBudgetCheckoutPostSavePlugin | Records budget consumption immediately after the order is saved so the remaining budget balance is accurate for concurrent buyers. Does nothing when no budget is selected on the quote. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Checkout |
| CostCenterQuoteExpanderPlugin | Expands the quote with the default cost center assigned to the buyer&apos;s business unit when no cost center is already set. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Quote |
| CostCenterQuoteFieldsAllowedForSavingProviderPlugin | Adds `idCostCenter` and `idBudget` to the list of quote fields persisted to the database. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Quote |
| RestoreBudgetOnCancelOmsCommandPlugin | Restores the budget balance by deducting the amount of the canceled order items. Also deducts the shipment group total if all items in the group are canceled. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Oms |
| RestoreBudgetOnRefundOmsCommandPlugin | Restores the budget balance by deducting the refundable amount of the refunded order items. When refund with shipment is enabled, also deducts the shipment group expense refundable amount if all items in the group are refunded. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Oms |
| CostCenterOrderExpanderPlugin | Expands `OrderTransfer` with the assigned cost center and company name, and with the assigned budget when present. Does nothing when no cost center is assigned to the order. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |
| CostCenterSearchOrderExpanderPlugin | Expands each `OrderTransfer` in a list with `CostCenterTransfer` and `BudgetTransfer` when assigned. Used for order search results. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |
| CostCenterOrderSearchQueryExpanderPlugin | Expands `QueryJoinCollectionTransfer` with `WHERE` conditions for `fk_cost_center` and `fk_budget` when filter fields of type `costCenter` or `budget` are present. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |
| CostCenterOrdersTableQueryExpanderPlugin | Adds a `LEFT JOIN` from `spy_sales_order.fk_cost_center` to `spy_cost_center.id_cost_center` and exposes `cost_center_name` as a virtual column on the Back Office orders table query. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |
| CostCenterOrdersTableHeaderExpanderPlugin | Inserts a **Cost Center** column before the **Actions** column in the Back Office orders table. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |
| CostCenterOrdersTableFilterFormExpanderPlugin | Adds cost center and budget multi-select filter fields to the Back Office orders table filter form. Budget choices are loaded via AJAX filtered by the selected cost centers. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |
| CostCenterOrdersTableCriteriaFilterExpanderPlugin | Filters the Back Office orders table by `costCenterIds` and `budgetIds` when present on `OrderTableCriteriaTransfer`. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |
| CostCenterSalesTablePlugin | Normalizes the `cost_center_name` column to `-` for orders that have no cost center assigned. | None | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales |

#### Set up permissions

**src/Pyz/Zed/Permission/PermissionDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Permission;

use Spryker\Zed\Permission\PermissionDependencyProvider as SprykerPermissionDependencyProvider;
use SprykerFeature\Shared\PurchasingControl\Plugin\Permission\ManageCostCentersPermissionPlugin;

class PermissionDependencyProvider extends SprykerPermissionDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Shared\PermissionExtension\Dependency\Plugin\PermissionPluginInterface&gt;
     */
    protected function getPermissionPlugins(): array
    {
        return [
            // ...
            new ManageCostCentersPermissionPlugin(), #PurchasingControlFeature
        ];
    }
}
```

**src/Pyz/Client/Permission/PermissionDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Client\Permission;

use Spryker\Client\Permission\PermissionDependencyProvider as SprykerPermissionDependencyProvider;
use SprykerFeature\Shared\PurchasingControl\Plugin\Permission\ManageCostCentersPermissionPlugin;

class PermissionDependencyProvider extends SprykerPermissionDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Shared\PermissionExtension\Dependency\Plugin\PermissionPluginInterface&gt;
     */
    protected function getPermissionPlugins(): array
    {
        return [
            // ...
            new ManageCostCentersPermissionPlugin(), #PurchasingControlFeature
        ];
    }
}
```

Sync the permission plugins to the database:

```bash
console sync:data permission
```

{% info_block warningBox &quot;Verification&quot; %}

In the Back Office, under **Customers &gt; Company Roles**, assign the **ManageCostCentersPermissionPlugin** permission to a company role. Make sure company users with that role can access the cost center management pages on the Storefront.

{% endinfo_block %}

{% info_block infoBox &quot;Require Approval enforcement rule&quot; %}

If you configure budgets with the **Require Approval** enforcement rule, the following [Approval Process](/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/install-features/install-the-approval-process-feature.html) permissions must be registered and assigned to company roles for the approval workflow to function:

| PERMISSION | REQUIRES |
| --- | --- |
| Buy up to grand total (`PlaceOrderPermissionPlugin`) | Send cart for approval |
| Send cart for approval (`RequestQuoteApprovalPermissionPlugin`) | Buy up to grand total |
| Approve up to grand total (`ApproveQuotePermissionPlugin`) | None |

For plugin registration details, see [Install the Approval Process feature](/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/install-features/install-the-approval-process-feature.html).

{% endinfo_block %}

#### Set up Checkout plugins

**src/Pyz/Zed/Checkout/CheckoutDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Checkout;

use Spryker\Zed\Checkout\CheckoutDependencyProvider as SprykerCheckoutDependencyProvider;
use Spryker\Zed\Kernel\Container;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Checkout\BudgetCheckoutPreConditionPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Checkout\ConsumeBudgetCheckoutPostSavePlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Checkout\CostCenterOrderSaverPlugin;

class CheckoutDependencyProvider extends SprykerCheckoutDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return list&lt;\Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutPreConditionPluginInterface&gt;
     */
    protected function getCheckoutPreConditions(Container $container): array
    {
        return [
            // ...
            new BudgetCheckoutPreConditionPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return list&lt;\Spryker\Zed\Checkout\Dependency\Plugin\CheckoutSaveOrderInterface|\Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutDoSaveOrderInterface&gt;
     */
    protected function getCheckoutOrderSavers(Container $container): array
    {
        return [
            // ...
            new CostCenterOrderSaverPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return list&lt;\Spryker\Zed\CheckoutExtension\Dependency\Plugin\CheckoutPostSaveInterface&gt;
     */
    protected function getCheckoutPostHooks(Container $container): array
    {
        return [
            // ...
            new ConsumeBudgetCheckoutPostSavePlugin(), #PurchasingControlFeature
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

When a buyer places an order with a budget selected, verify the following:
- Checkout is blocked when the order exceeds a budget with the **Block** enforcement rule.
- An approval request is triggered when the order exceeds a budget with the **Require Approval** rule.
- A warning is displayed when the order exceeds a budget with the **Warn** rule.
- A `spy_budget_consumption` record is created after the order is successfully placed.
- The cost center and budget IDs are saved on the `spy_sales_order` record.

{% endinfo_block %}

#### Set up Quote plugins

**src/Pyz/Zed/Quote/QuoteDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Quote;

use Spryker\Zed\Quote\QuoteDependencyProvider as SprykerQuoteDependencyProvider;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Quote\CostCenterQuoteExpanderPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Quote\CostCenterQuoteFieldsAllowedForSavingProviderPlugin;

class QuoteDependencyProvider extends SprykerQuoteDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Zed\QuoteExtension\Dependency\Plugin\QuoteExpanderPluginInterface&gt;
     */
    protected function getQuoteExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterQuoteExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\QuoteExtension\Dependency\Plugin\QuoteFieldsAllowedForSavingProviderPluginInterface&gt;
     */
    protected function getQuoteFieldsAllowedForSavingProviderPlugins(): array
    {
        return [
            // ...
            new CostCenterQuoteFieldsAllowedForSavingProviderPlugin(), #PurchasingControlFeature
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

When a buyer with an assigned business unit opens a cart, make sure the quote is automatically expanded with the default cost center of their business unit.

Make sure `idCostCenter` and `idBudget` are persisted to the `spy_quote` table when the quote is saved.

{% endinfo_block %}

#### Set up Sales plugins

**src/Pyz/Zed/Sales/SalesDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Sales;

use Spryker\Zed\Sales\SalesDependencyProvider as SprykerSalesDependencyProvider;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterOrderExpanderPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterOrderSearchQueryExpanderPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterOrdersTableCriteriaFilterExpanderPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterOrdersTableFilterFormExpanderPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterOrdersTableHeaderExpanderPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterOrdersTableQueryExpanderPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterSalesTablePlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Sales\CostCenterSearchOrderExpanderPlugin;

class SalesDependencyProvider extends SprykerSalesDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Zed\Sales\Dependency\Plugin\OrderExpanderPreSavePluginInterface&gt;
     */
    protected function getOrderHydrationPlugins(): array
    {
        return [
            // ...
            new CostCenterOrderExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\SalesExtension\Dependency\Plugin\SearchOrderExpanderPluginInterface&gt;
     */
    protected function getSearchOrderExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterSearchOrderExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\SalesExtension\Dependency\Plugin\SearchOrderQueryExpanderPluginInterface&gt;
     */
    protected function getOrderSearchQueryExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterOrderSearchQueryExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\SalesExtension\Dependency\Plugin\OrdersTableQueryExpanderPluginInterface&gt;
     */
    protected function getOrdersTableQueryExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterOrdersTableQueryExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\SalesExtension\Dependency\Plugin\OrdersTableHeaderExpanderPluginInterface&gt;
     */
    protected function getOrdersTableHeaderExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterOrdersTableHeaderExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\SalesExtension\Dependency\Plugin\OrdersTableFilterFormExpanderPluginInterface&gt;
     */
    protected function getOrdersTableFilterFormExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterOrdersTableFilterFormExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\SalesExtension\Dependency\Plugin\OrdersTableCriteriaFilterExpanderPluginInterface&gt;
     */
    protected function getOrdersTableCriteriaFilterExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterOrdersTableCriteriaFilterExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\Spryker\Zed\SalesExtension\Dependency\Plugin\SalesTablePluginInterface&gt;
     */
    protected function getSalesTablePlugins(): array
    {
        return [
            // ...
            new CostCenterSalesTablePlugin(), #PurchasingControlFeature
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

- In the Back Office, open **Sales &gt; Orders**. Make sure the **Cost Center** column is displayed in the orders table.
- Make sure the orders table filter form includes cost center and budget multi-select fields.
- Open an individual order. Make sure the cost center and budget names are displayed on the order detail page.
- In the storefront, open **My Account &gt; Orders**. Make sure the cost center and budget data appear on completed orders.

{% endinfo_block %}

#### Set up Recurring Orders plugins

{% info_block infoBox &quot;Recurring Orders feature&quot; %}

This step is only required if your project uses the [Recurring Orders feature](/docs/pbc/all/order-experience-management/latest/base-shop/feature-overviews/recurring-orders-feature-overview.html). The plugins belong to the Purchasing Control module but register in the `OrderExperienceManagement` dependency providers.

{% endinfo_block %}

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| BudgetApprovalRuleRecurringOrderCheckoutValidatorPlugin | Blocks checkout when a quote being set up as a recurring order carries a budget with the `require_approval` enforcement rule. Applies regardless of the quote grand total and the remaining budget amount. Passes when no budget is selected or the budget uses another enforcement rule. | Recurring Orders feature | SprykerFeature\Zed\PurchasingControl\Communication\Plugin\OrderExperienceManagement |
| CostCenterRecurringOrderApproveFormExpanderPlugin | Adds cost center and budget dropdowns to the recurring order review approve form and validates the selected pair server-side. | Recurring Orders feature | SprykerFeature\Yves\PurchasingControl\Plugin\OrderExperienceManagement |
| CostCenterRecurringScheduleEditFormExpanderPlugin | Adds cost center and budget dropdowns to the recurring schedule edit form and validates the selected pair server-side. | Recurring Orders feature | SprykerFeature\Yves\PurchasingControl\Plugin\OrderExperienceManagement |

**src/Pyz/Zed/OrderExperienceManagement/OrderExperienceManagementDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\OrderExperienceManagement;

use SprykerFeature\Zed\OrderExperienceManagement\OrderExperienceManagementDependencyProvider as SprykerOrderExperienceManagementDependencyProvider;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\OrderExperienceManagement\BudgetApprovalRuleRecurringOrderCheckoutValidatorPlugin;

class OrderExperienceManagementDependencyProvider extends SprykerOrderExperienceManagementDependencyProvider
{
    /**
     * @return array&lt;\SprykerFeature\Zed\OrderExperienceManagement\Dependency\Plugin\RecurringOrderCheckoutValidatorPluginInterface&gt;
     */
    protected function getRecurringOrderCheckoutValidatorPlugins(): array
    {
        return [
            new BudgetApprovalRuleRecurringOrderCheckoutValidatorPlugin(), #PurchasingControlFeature
        ];
    }
}
```

For the two Yves form expander plugins and the budget enforcement rules that can be selected on the recurring order forms, see [Install the Recurring Orders feature](/docs/pbc/all/order-experience-management/latest/base-shop/install-and-upgrade/install-features/install-the-recurring-orders-feature.html).

{% info_block warningBox &quot;Verification&quot; %}

Set a budget&apos;s enforcement rule to **Require approval**, assign it to a cart, and set up that cart as a recurring order at checkout. Make sure checkout is blocked with a message stating that the selected budget requires approval and cannot be used for a recurring order.

{% endinfo_block %}

### 5) Configure Back Office navigation

Add the Purchasing Control section to the Back Office navigation:

**config/Zed/navigation.xml**

```xml
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;config&gt;
    &lt;customer&gt;
        ...
        &lt;pages&gt;
            ...
            &lt;purchasing-control&gt;
                &lt;label&gt;Cost Centers&lt;/label&gt;
                &lt;title&gt;Cost Centers&lt;/title&gt;
                &lt;bundle&gt;purchasing-control&lt;/bundle&gt;
                &lt;controller&gt;cost-center&lt;/controller&gt;
                &lt;action&gt;index&lt;/action&gt;
            &lt;/purchasing-control&gt;
        &lt;/pages&gt;
    &lt;/customer&gt;
&lt;/config&gt;
```

Rebuild the navigation cache:

```bash
console navigation:build-cache
```

{% info_block warningBox &quot;Verification&quot; %}

In the Back Office, under **Customers**, make sure the **Cost Centers** menu item is displayed and links to the cost center list page.

{% endinfo_block %}

### 6) Configure the OMS process

Register the OMS command plugins and configure the OMS process XML.

#### Register OMS command plugins

**src/Pyz/Zed/Oms/OmsDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Zed\Oms;

use Spryker\Zed\Kernel\Container;
use Spryker\Zed\Oms\Dependency\Plugin\Command\CommandCollectionInterface;
use Spryker\Zed\Oms\OmsDependencyProvider as SprykerOmsDependencyProvider;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Oms\RestoreBudgetOnCancelOmsCommandPlugin;
use SprykerFeature\Zed\PurchasingControl\Communication\Plugin\Oms\RestoreBudgetOnRefundOmsCommandPlugin;

class OmsDependencyProvider extends SprykerOmsDependencyProvider
{
    /**
     * @param \Spryker\Zed\Kernel\Container $container
     *
     * @return \Spryker\Zed\Kernel\Container
     */
    protected function extendCommandPlugins(Container $container): Container
    {
        $container-&gt;extend(self::COMMAND_PLUGINS, function (CommandCollectionInterface $commandCollection) {
            // ...
            $commandCollection-&gt;add(new RestoreBudgetOnCancelOmsCommandPlugin(), &apos;CostCenter/RestoreBudgetOnCancel&apos;); #PurchasingControlFeature
            $commandCollection-&gt;add(new RestoreBudgetOnRefundOmsCommandPlugin(), &apos;CostCenter/RestoreBudgetOnRefund&apos;); #PurchasingControlFeature

            return $commandCollection;
        });

        return $container;
    }
}
```

#### Configure the OMS process XML

Add the `CostCenter/RestoreBudgetOnCancel` and `CostCenter/RestoreBudgetOnRefund` commands to the relevant events in your OMS process XML. The following example uses `DummyPayment01`:

**config/Zed/oms/DummyPayment01.xml**

```xml
&lt;events&gt;
    ...
    &lt;event name=&quot;cancel&quot; manual=&quot;true&quot; command=&quot;CostCenter/RestoreBudgetOnCancel&quot;/&gt;
    &lt;event name=&quot;refund&quot; manual=&quot;true&quot; command=&quot;CostCenter/RestoreBudgetOnRefund&quot;/&gt;
    ...
&lt;/events&gt;
```

#### Configure budget restoration behavior

By default, shipment costs are not included when restoring the budget on refund. To include shipment costs, override `isRefundWithShipmentEnabled()` in your project config:

**src/Pyz/Zed/PurchasingControl/PurchasingControlConfig.php**

```php
&lt;?php

namespace Pyz\Zed\PurchasingControl;

use SprykerFeature\Zed\PurchasingControl\PurchasingControlConfig as SprykerPurchasingControlConfig;

class PurchasingControlConfig extends SprykerPurchasingControlConfig
{
    protected const bool REFUND_WITH_SHIPMENT_ENABLED = true;
}
```

{% info_block warningBox &quot;Verification&quot; %}

- Place an order with a budget selected. Cancel one item (partial cancel). Make sure the budget balance is increased by the amount of the canceled item only, not the full order total.
- Cancel all items of an order. Make sure the full consumed amount is restored to the budget balance.
- Trigger a refund on an order. Make sure the refunded items&apos; amounts are restored to the budget balance.

{% endinfo_block %}

## Install feature frontend

Follow the steps below to install the Purchasing Control feature frontend.

### 1) Import data

Import the following glossary keys for Storefront translations:

**data/import/common/common/glossary.csv**

```csv
purchasing_control.selector.placeholder,Select cost center,en_US
purchasing_control.selector.placeholder,Kostenstelle wählen,de_DE
purchasing_control.budget.selector.label,Budget,en_US
purchasing_control.budget.selector.label,Budget,de_DE
purchasing_control.budget.selector.placeholder,Select budget,en_US
purchasing_control.budget.selector.placeholder,Budget wählen,de_DE
purchasing_control.budget.remaining,Remaining budget,en_US
purchasing_control.budget.remaining,Verbleibendes Budget,de_DE
purchasing_control.summary.cost_center_label,Cost Center,en_US
purchasing_control.summary.cost_center_label,Kostenstelle,de_DE
purchasing_control.summary.budget_label,Budget,en_US
purchasing_control.summary.budget_label,Budget,de_DE
purchasing_control.summary.budget_remaining,remaining,en_US
purchasing_control.summary.budget_remaining,verbleibend,de_DE
purchasing_control.validation.block,&quot;Your order exceeds the allocated budget. Please adjust your order or contact your manager.&quot;,en_US
purchasing_control.validation.block,&quot;Ihre Bestellung überschreitet das zugewiesene Budget. Bitte passen Sie Ihre Bestellung an oder kontaktieren Sie Ihren Manager.&quot;,de_DE
purchasing_control.validation.warn,Your order exceeds the allocated budget.,en_US
purchasing_control.validation.warn,Ihre Bestellung überschreitet das zugewiesene Budget.,de_DE
purchasing_control.validation.require-approval,This order exceeds the budget. Please send it for approval.,en_US
purchasing_control.validation.require-approval,Diese Bestellung überschreitet das Budget. Bitte senden Sie sie zur Genehmigung.,de_DE
purchasing_control.validation.required,&quot;Please select a cost center and budget before placing your order.&quot;,en_US
purchasing_control.validation.required,&quot;Bitte wählen Sie vor der Bestellung eine Kostenstelle und ein Budget aus.&quot;,de_DE
purchasing_control.budget.validation.cost_center_mismatch,&quot;The selected budget does not belong to the selected cost center.&quot;,en_US
purchasing_control.budget.validation.cost_center_mismatch,&quot;Das ausgewählte Budget gehört nicht zur ausgewählten Kostenstelle.&quot;,de_DE
purchasing_control.quote_request.cost_center_updated,Cost center and budget have been saved.,en_US
purchasing_control.quote_request.cost_center_updated,Kostenstelle und Budget wurden gespeichert.,de_DE
```

If your project also uses the Recurring Orders feature, import the following keys as well. They translate the cost center and budget dropdowns on the recurring order approve and schedule edit forms, and the budget summary rendered by the `recurring-order-budget-summary` molecule:

**data/import/common/common/glossary.csv**

```csv
purchasing_control.recurring_order.budget.total,Total budget,en_US
purchasing_control.recurring_order.budget.total,Gesamtbudget,de_DE
purchasing_control.recurring_order.budget.used,Used,en_US
purchasing_control.recurring_order.budget.used,Verbraucht,de_DE
purchasing_control.recurring_order.budget.remaining,Remaining,en_US
purchasing_control.recurring_order.budget.remaining,Verbleibend,de_DE
purchasing_control.recurring_order.budget.usage,%used% used of %total%,en_US
purchasing_control.recurring_order.budget.usage,%used% von %total% verwendet,de_DE
purchasing_control.recurring_order.cost_center_required,Select cost center,en_US
purchasing_control.recurring_order.cost_center_required,Kostenstelle wählen,de_DE
purchasing_control.recurring_order.budget_required,Select budget,en_US
purchasing_control.recurring_order.budget_required,Budget wählen,de_DE
purchasing_control.recurring_order.budget_cost_center_mismatch,The selected budget does not belong to the selected cost center.,en_US
purchasing_control.recurring_order.budget_cost_center_mismatch,Das ausgewählte Budget gehört nicht zur ausgewählten Kostenstelle.,de_DE
purchasing_control.validation.inactive-budget,The selected budget is no longer available. Please select another budget or contact your manager.,en_US
purchasing_control.validation.inactive-budget,Das ausgewählte Budget ist nicht mehr verfügbar. Bitte wählen Sie ein anderes Budget oder kontaktieren Sie Ihren Manager.,de_DE
purchasing_control.validation.approval-rule-not-supported,&quot;The selected budget requires approval and cannot be used for a recurring order. Please select another budget.&quot;,en_US
purchasing_control.validation.approval-rule-not-supported,&quot;Das ausgewählte Budget erfordert eine Genehmigung und kann nicht für eine wiederkehrende Bestellung verwendet werden. Bitte wählen Sie ein anderes Budget aus.&quot;,de_DE
```

{% info_block infoBox &quot;Recurring order translations&quot; %}

These keys render only on recurring order screens. Register the plugins that display them as described in [Set up Recurring Orders plugins](#set-up-recurring-orders-plugins) and [Install the Recurring Orders feature](/docs/pbc/all/order-experience-management/latest/base-shop/install-and-upgrade/install-features/install-the-recurring-orders-feature.html).

{% endinfo_block %}

Import data:

```bash
console data:import:glossary
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure that, in the database, the configured data has been added to the `spy_glossary_key` and `spy_glossary_translation` tables.

{% endinfo_block %}

### 2) Set up widgets

Register the following global widgets:

| WIDGET | DESCRIPTION | NAMESPACE |
| --- | --- | --- |
| PurchasingControlSummaryWidget | Displays cost center count and budget summaries on the company dashboard. | SprykerFeature\Yves\PurchasingControl\Widget |
| CostCenterSelectorWidget | Renders the cost center and budget selection UI at checkout. | SprykerFeature\Yves\PurchasingControl\Widget |
| CostCenterMenuItemWidget | Renders the Purchasing Control navigation menu item in the storefront company menu. | SprykerFeature\Yves\PurchasingControl\Widget |
| CostCenterBudgetFilterWidget | Renders the cost center and budget filter controls on the order history page. | SprykerFeature\Yves\PurchasingControl\Widget |
| CostCenterOrderDetailWidget | Displays the assigned cost center and budget on the order detail page, taking an `OrderTransfer` as input. | SprykerFeature\Yves\PurchasingControl\Widget |
| CostCenterDetailWidget | Displays the cost center and budget assigned to a quote, taking a `QuoteTransfer` as input and an optional flag that adds a budget usage summary. Renders only where a template calls it—see the note below. Only active cost centers are resolved, so a deactivated cost center makes the widget render nothing. | SprykerFeature\Yves\PurchasingControl\Widget |

**src/Pyz/Yves/ShopApplication/ShopApplicationDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Yves\ShopApplication;

use SprykerFeature\Yves\PurchasingControl\Widget\CostCenterBudgetFilterWidget;
use SprykerFeature\Yves\PurchasingControl\Widget\CostCenterDetailWidget;
use SprykerFeature\Yves\PurchasingControl\Widget\CostCenterMenuItemWidget;
use SprykerFeature\Yves\PurchasingControl\Widget\CostCenterOrderDetailWidget;
use SprykerFeature\Yves\PurchasingControl\Widget\CostCenterSelectorWidget;
use SprykerFeature\Yves\PurchasingControl\Widget\PurchasingControlSummaryWidget;
use SprykerShop\Yves\ShopApplication\ShopApplicationDependencyProvider as SprykerShopApplicationDependencyProvider;

class ShopApplicationDependencyProvider extends SprykerShopApplicationDependencyProvider
{
    /**
     * @return array&lt;string&gt;
     */
    protected function getGlobalWidgets(): array
    {
        return [
            // ...
            CostCenterMenuItemWidget::class, #PurchasingControlFeature
            PurchasingControlSummaryWidget::class, #PurchasingControlFeature
            CostCenterSelectorWidget::class, #PurchasingControlFeature
            CostCenterDetailWidget::class, #PurchasingControlFeature
            CostCenterOrderDetailWidget::class, #PurchasingControlFeature
            CostCenterBudgetFilterWidget::class, #PurchasingControlFeature
        ];
    }
}
```

{% info_block infoBox &quot;Where CostCenterDetailWidget is rendered&quot; %}

Unlike the other widgets in this table, no Purchasing Control template calls `CostCenterDetailWidget`. It renders only where another template calls it explicitly. In the demo shop, its single caller is the recurring order detail sidebar of the [Recurring Orders feature](/docs/pbc/all/order-experience-management/latest/base-shop/feature-overviews/recurring-orders-feature-overview.html). To display the assigned cost center and budget elsewhere—on the cart page or a quote detail page, for example—call the widget from that template:

```twig
{% raw %}{% widget &apos;CostCenterDetailWidget&apos; args [data.quote] only %}{% endwidget %}{% endraw %}
```

Pass a second argument to add a budget usage summary—the total, used, and remaining amounts, plus a used-percentage bar—below the cost center and budget names:

```twig
{% raw %}{% widget &apos;CostCenterDetailWidget&apos; args [data.quote, true] only %}{% endwidget %}{% endraw %}
```

The summary is rendered by the `recurring-order-budget-summary` molecule the module ships. It is omitted when the argument is `false` or not passed, and also when the quote carries no budget or no currency.

{% endinfo_block %}

{% info_block warningBox &quot;Verification&quot; %}

- Make sure all six widgets are available in Twig templates.
- On the storefront company dashboard, make sure the **Purchasing Control** summary widget displays cost center and budget data.
- On the checkout summary page, make sure the cost center and budget selector is displayed.
- On the order detail page, make sure the assigned cost center and budget names are displayed.
- On the order history page, make sure the cost center and budget filter controls are displayed.
- Call `CostCenterDetailWidget` from a template with a quote that has a cost center assigned, and make sure the cost center and budget names are displayed. Deactivate that cost center and make sure the names are no longer displayed.

{% endinfo_block %}

### 3) Extend the ShopUi select component

Extend the ShopUi `select` atom to render HTML attributes for the cost center and budget selectors:

**src/Pyz/Yves/ShopUi/Theme/default/components/atoms/select/select.twig**

```twig
{% raw %}{% block attributes %}
    {%- for attrname, attrvalue in attr | default({}) -%}
        {%- if attrvalue is same as(true) -%} {{ attrname }}=&quot;{{ attrname }}&quot;
        {%- elseif attrvalue is not same as(false) -%} {{ attrname }}=&quot;{{ attrvalue }}&quot;
        {%- endif -%}
    {%- endfor -%}
{% endblock %}{% endraw %}
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure the cost center and budget dropdowns correctly disable unavailable options. Select a cost center and make sure the budget dropdown offers only the budgets of that cost center, and that budgets of other cost centers are both hidden and disabled.

{% endinfo_block %}

### 4) Extend the checkout summary template

Add the `CostCenterSelectorWidget` to the checkout summary page, placing it directly above the `QuoteApprovalWidget` call:

**src/SprykerShop/CheckoutPage/src/SprykerShop/Yves/CheckoutPage/Theme/default/views/summary/summary.twig**

```twig
&lt;div class=&quot;box&quot;&gt;
    {% raw %}{% widget &apos;CostCenterSelectorWidget&apos; args [data.cart] only %}{% endwidget %}{% endraw %}
    {% raw %}{% widget &apos;QuoteApprovalWidget&apos; args [data.cart] only %}{% endwidget %}{% endraw %}
&lt;/div&gt;
```

{% info_block warningBox &quot;Verification&quot; %}

On the checkout summary page, make sure the cost center and budget selector is displayed above the approval widget.

{% endinfo_block %}

### 5) Set up routes

Register the following route provider plugins:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| CostCenterRouteProviderPlugin | Adds storefront routes for cost center list, create, update, and quote update actions. Also adds the POST-only routes that update the cost center and budget of a quote request in the customer and agent contexts. | None | SprykerFeature\Yves\PurchasingControl\Plugin\Router |
| BudgetRouteProviderPlugin | Adds storefront routes for budget list, create, and update actions. | None | SprykerFeature\Yves\PurchasingControl\Plugin\Router |

**src/Pyz/Yves/Router/RouterDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Yves\Router;

use Spryker\Yves\Router\RouterDependencyProvider as SprykerRouterDependencyProvider;
use SprykerFeature\Yves\PurchasingControl\Plugin\Router\BudgetRouteProviderPlugin;
use SprykerFeature\Yves\PurchasingControl\Plugin\Router\CostCenterRouteProviderPlugin;

class RouterDependencyProvider extends SprykerRouterDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Yves\RouterExtension\Dependency\Plugin\RouteProviderPluginInterface&gt;
     */
    protected function getRouteProvider(): array
    {
        return [
            // ...
            new CostCenterRouteProviderPlugin(), #PurchasingControlFeature
            new BudgetRouteProviderPlugin(), #PurchasingControlFeature
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

- Make sure the cost center list, create, and update pages are accessible under `/company/cost-center`.
- Make sure submitting the cost center selector form in the cart updates the quote with the selected cost center and budget.
- Make sure the budget list, create, and update pages are accessible.

{% endinfo_block %}

### 6) Extend the order search form

Register the following plugins to add cost center and budget filter fields to the storefront order history search form:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| CostCenterOrderSearchFormExpanderPlugin | Adds cost center and budget filter dropdowns to the order history search form. Only adds fields when the current customer is a company user. | None | SprykerFeature\Yves\PurchasingControl\Plugin\CustomerPage |
| CostCenterOrderSearchFormHandlerPlugin | Maps selected cost center and budget IDs from the filter form to `FilterFieldTransfer` entries on `OrderListTransfer`. | None | SprykerFeature\Yves\PurchasingControl\Plugin\CustomerPage |

**src/Pyz/Yves/CustomerPage/CustomerPageDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Yves\CustomerPage;

use SprykerShop\Yves\CustomerPage\CustomerPageDependencyProvider as SprykerShopCustomerPageDependencyProvider;
use SprykerFeature\Yves\PurchasingControl\Plugin\CustomerPage\CostCenterOrderSearchFormExpanderPlugin;
use SprykerFeature\Yves\PurchasingControl\Plugin\CustomerPage\CostCenterOrderSearchFormHandlerPlugin;

class CustomerPageDependencyProvider extends SprykerShopCustomerPageDependencyProvider
{
    /**
     * @return array&lt;\SprykerShop\Yves\CustomerPageExtension\Dependency\Plugin\OrderSearchFormExpanderPluginInterface&gt;
     */
    protected function getOrderSearchFormExpanderPlugins(): array
    {
        return [
            // ...
            new CostCenterOrderSearchFormExpanderPlugin(), #PurchasingControlFeature
        ];
    }

    /**
     * @return array&lt;\SprykerShop\Yves\CustomerPageExtension\Dependency\Plugin\OrderSearchFormHandlerPluginInterface&gt;
     */
    protected function getOrderSearchFormHandlerPlugins(): array
    {
        return [
            // ...
            new CostCenterOrderSearchFormHandlerPlugin(), #PurchasingControlFeature
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

On the storefront **My Account &gt; Orders** page, make sure company users see cost center and budget filter dropdowns. Make sure filtering by cost center or budget returns the expected orders.

{% endinfo_block %}

## Integrate cost centers with quote requests

Follow the steps below to let buyers and agents assign a cost center and a budget to a quote request. Skip this section if you do not use quote requests.

{% info_block infoBox &quot;Prerequisites&quot; %}

This part of the feature requires the [Quotation Process](/docs/pbc/all/request-for-quote/latest/install-and-upgrade/install-features/install-the-quotation-process-feature.html) feature.

{% endinfo_block %}

### 1) Install the required modules

```bash
composer require spryker-shop/quote-request-page:&quot;^3.8.0&quot; spryker-shop/quote-request-agent-page:&quot;^3.7.0&quot; --update-with-dependencies
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure the following modules are installed at the specified versions or higher:

| MODULE | EXPECTED VERSION |
| --- | --- |
| QuoteRequestPage | 3.8.0 |
| QuoteRequestAgentPage | 3.7.0 |

{% endinfo_block %}

### 2) Allow the cost center and budget quote fields for saving

Add `ID_COST_CENTER` and `ID_BUDGET` to the quote fields that are persisted with a quote request version. Without this configuration, both fields are stripped when the quote request version is saved: the update appears to succeed, but the selection is lost.

**src/Pyz/Zed/QuoteRequest/QuoteRequestConfig.php**

```php
&lt;?php

namespace Pyz\Zed\QuoteRequest;

use Generated\Shared\Transfer\QuoteTransfer;
use Spryker\Zed\QuoteRequest\QuoteRequestConfig as SprykerQuoteRequestConfig;

class QuoteRequestConfig extends SprykerQuoteRequestConfig
{
    /**
     * @return array&lt;string&gt;
     */
    public function getQuoteFieldsAllowedForSaving(): array
    {
        return array_merge(parent::getQuoteFieldsAllowedForSaving(), [
            // ...
            QuoteTransfer::ID_COST_CENTER, #PurchasingControlFeature
            QuoteTransfer::ID_BUDGET, #PurchasingControlFeature
        ]);
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

Assign a cost center and a budget to a quote request, then reload the quote request details page. Make sure the selection is still displayed. In the database, make sure `spy_quote_request_version.quote` contains the `idCostCenter` and `idBudget` values.

{% endinfo_block %}

### 3) Set up widgets

Register the following global widgets:

| WIDGET | DESCRIPTION | NAMESPACE |
| --- | --- | --- |
| QuoteRequestCostCenterSelectorWidget | Renders the cost center and budget selection UI on the quote request details and quote request edit pages. Takes a `QuoteRequestTransfer` and an optional form action route name. When the route name is omitted, the form posts to `company/cost-center/update-quote-request`. | SprykerFeature\Yves\PurchasingControl\Widget |
| QuoteRequestAgentCostCenterSelectorWidget | Renders the same selection UI on the agent quote request details and agent quote request edit pages. Takes the same two arguments. When the route name is omitted, the form posts to `agent/quote-request/cost-center/update`. | SprykerFeature\Yves\PurchasingControl\Widget |

**src/Pyz/Yves/ShopApplication/ShopApplicationDependencyProvider.php**

```php
&lt;?php

namespace Pyz\Yves\ShopApplication;

use SprykerFeature\Yves\PurchasingControl\Widget\QuoteRequestAgentCostCenterSelectorWidget;
use SprykerFeature\Yves\PurchasingControl\Widget\QuoteRequestCostCenterSelectorWidget;
use SprykerShop\Yves\ShopApplication\ShopApplicationDependencyProvider as SprykerShopApplicationDependencyProvider;

class ShopApplicationDependencyProvider extends SprykerShopApplicationDependencyProvider
{
    /**
     * @return array&lt;string&gt;
     */
    protected function getGlobalWidgets(): array
    {
        return [
            // ...
            QuoteRequestCostCenterSelectorWidget::class, #PurchasingControlFeature
            QuoteRequestAgentCostCenterSelectorWidget::class, #PurchasingControlFeature
        ];
    }
}
```

Both widgets are rendered from a `costCenter` block in the `quote-request-details.twig` and `quote-request-edit.twig` templates of the `QuoteRequestPage` and `QuoteRequestAgentPage` modules. In a project that uses these templates as they are shipped, no template changes are required. To change or remove the placement, override the `costCenter` block.

{% info_block warningBox &quot;Template overrides&quot; %}

If your project overrides any of the four templates, the widget is not rendered on the affected pages, because your override replaces the module template that contains the `costCenter` block. Re-base your overrides against the new module templates, or add the `costCenter` block to them.

{% endinfo_block %}

The second argument controls where the form returns to after saving. The details pages omit it, so the widget falls back to the details route. The edit pages pass it explicitly: the `QuoteRequestPage` edit template passes `company/cost-center/update-quote-request-from-edit`, and the `QuoteRequestAgentPage` edit template passes `agent/quote-request/cost-center/update-from-edit`. Pass a route name of your own only if you also register a route that returns to your page.

{% info_block infoBox &quot;Info&quot; %}

The widgets suppress themselves when there is nothing to show. An editable quote request renders the selection form only if at least one cost center is available to the owner&apos;s business unit. A quote request that is not editable displays the assigned cost center and budget as read-only.

{% endinfo_block %}

{% info_block warningBox &quot;Verification&quot; %}

- On the Storefront, open a quote request in an editable status and make sure the cost center and budget selection is displayed on both the details page and the edit page.
- Select a cost center and a budget, then submit the form. Make sure a success message is displayed and the page returns to the details page or the edit page, depending on where you started.
- Open a quote request that is not editable and make sure the assigned cost center and budget are displayed as read-only, without a submit button.
- As an agent, open a customer&apos;s quote request and make sure the cost center dropdown lists the cost centers of the customer&apos;s business unit, not your own.

{% endinfo_block %}

### 4) Review the quote request routes

The `CostCenterRouteProviderPlugin` that you registered in [Set up routes](#5-set-up-routes) adds the following routes. All of them accept `POST` requests only, and each one determines its own return page, so the redirect target cannot be supplied by the request.

| ROUTE NAME | PATH | RETURNS TO |
| --- | --- | --- |
| `company/cost-center/update-quote-request` | `/company/cost-center/update-quote-request/{quoteRequestReference}` | Quote request details page |
| `company/cost-center/update-quote-request-from-edit` | `/company/cost-center/update-quote-request-from-edit/{quoteRequestReference}` | Quote request edit page |
| `agent/quote-request/cost-center/update` | `/agent/quote-request/cost-center/update/{quoteRequestReference}` | Agent quote request details page |
| `agent/quote-request/cost-center/update-from-edit` | `/agent/quote-request/cost-center/update-from-edit/{quoteRequestReference}` | Agent quote request edit page |

The two `-from-edit` routes are reached only when a widget receives a form action route name as its second argument. For details, see [Set up widgets](#3-set-up-widgets).

The two agent routes are deliberately placed under `/agent/` so that they are covered by the agent firewall.

{% info_block warningBox &quot;Verification&quot; %}

- Make sure a `GET` request to any of the four paths is rejected.
- Make sure a buyer cannot update the cost center of a quote request that belongs to another company user: the request must return `404`, the same response as for a quote request reference that does not exist.
- Make sure an anonymous request to either agent path is rejected by the agent firewall.

{% endinfo_block %}
</description>
            <pubDate>Tue, 25 Aug 2026 08:40:54 +0000</pubDate>
            <link>https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/install-features/install-the-purchasing-control-feature.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/pbc/all/cart-and-checkout/latest/base-shop/install-and-upgrade/install-features/install-the-purchasing-control-feature.html</guid>
            
            
        </item>
        
        <item>
            <title>Install the Configuration Management feature</title>
            <description>This document describes how to install the Configuration Management feature.

## Prerequisites

Update the required modules:

| NAME | VERSION |
|----|---------|
| Kernel | ^3.82   |
| Store | ^1.36   |
| Translator   | ^1.15   |

## Install feature core

### 1) Install the required modules

Install the required modules using Composer:

```bash
composer require spryker/configuration:&quot;^1.0.0&quot; --update-with-dependencies
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure the following modules have been installed:

| MODULE | EXPECTED DIRECTORY |
| --- | --- |
| Configuration | vendor/spryker/configuration |

{% endinfo_block %}

### 2) Set up configuration

Add the following configuration to extend scope support with store-level configuration:

| CONFIGURATION | SPECIFICATION | NAMESPACE |
| --- | --- | --- |
| ConfigurationConfig | Adds `store` scope to available scopes and defines scope hierarchy (store inherits from global). | Pyz\Shared\Configuration |

**src/Pyz/Shared/Configuration/ConfigurationConfig.php**

```php
&lt;?php

/**
 * This file is part of the Spryker Suite.
 * For full license information, please view the LICENSE file that was distributed with this source code.
 */

declare(strict_types = 1);

namespace Pyz\Shared\Configuration;

use Spryker\Shared\Configuration\ConfigurationConfig as SprykerConfigurationConfig;
use Spryker\Shared\Configuration\ConfigurationConstants;

class ConfigurationConfig extends SprykerConfigurationConfig
{
    /**
     * @uses \Spryker\Shared\Store\StoreConstants::SCOPE_STORE
     */
    public const string SCOPE_STORE = &apos;store&apos;;

    public function getAvailableScopes(): array
    {
        $availableScopes = parent::getAvailableScopes();
        $availableScopes[] = static::SCOPE_STORE;

        return $availableScopes;
    }

    public function getScopeHierarchy(): array
    {
        $scopeHierarchy = parent::getScopeHierarchy();
        $scopeHierarchy[static::SCOPE_STORE] = ConfigurationConstants::SCOPE_GLOBAL;

        return $scopeHierarchy;
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure that after reading a configuration value with `store` scope, the system falls back to the `global` scope value if no store-specific value is set.

{% endinfo_block %}

#### 2.1) Set up encryption for secret settings

Add encryption key configuration for encrypting and decrypting secret configuration values:

**config/Shared/config_default.php**

Add the import statement:

```php
use Spryker\Shared\Configuration\ConfigurationConstants;
```

Add the encryption configuration:

```php
// Configuration system
$config[ConfigurationConstants::ENCRYPTION_KEY] = hex2bin(getenv(&apos;SPRYKER_CONFIGURATION_ENCRYPTION_KEY&apos;) ?: &apos;&apos;) ?: null;
$config[ConfigurationConstants::ENCRYPTION_INIT_VECTOR] = hex2bin(getenv(&apos;SPRYKER_CONFIGURATION_ENCRYPTION_INIT_VECTOR&apos;) ?: &apos;&apos;) ?: null;
```

#### 2.2) Provide environment variables


For local development, add the following environment variables to your deploy file (`deploy.dev.yml` or equivalent):

```yaml
image:
    environment:
        SPRYKER_CONFIGURATION_ENCRYPTION_KEY: &apos;&lt;your-64-char-hex-key&gt;&apos;
        SPRYKER_CONFIGURATION_ENCRYPTION_INIT_VECTOR: &apos;&lt;your-32-char-hex-iv&gt;&apos;
```

In Cloud, add environment variables using [Parameter Store](/docs/ca/dev/add-variables-in-the-parameter-store).

To generate new keys, run:

```bash
openssl rand -hex 32  # generates SPRYKER_CONFIGURATION_ENCRYPTION_KEY
openssl rand -hex 16  # generates SPRYKER_CONFIGURATION_ENCRYPTION_INIT_VECTOR
```

{% info_block warningBox &quot;Verification&quot; %}

1. Create a setting with `secret: true` in a YAML schema and run `configuration:sync`.
2. Set a value for the secret setting in the Back Office.
3. Check the `spy_configuration_value` table — the stored value must be encrypted (not plain text).
4. Read the value back in the Back Office — it must be decrypted and displayed correctly.

{% endinfo_block %}

### 3) Set up the database schema and transfer objects

#### 3.1) Set up database schema extension

Add event behavior to the `spy_configuration_value` table to enable Publish &amp; Synchronize:

**src/Pyz/Zed/Configuration/Persistence/Propel/Schema/spy_configuration.schema.xml**

```xml
&lt;?xml version=&quot;1.0&quot;?&gt;
&lt;database xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; name=&quot;zed&quot; xsi:noNamespaceSchemaLocation=&quot;http://static.spryker.com/schema-01.xsd&quot; namespace=&quot;Orm\Zed\Configuration\Persistence&quot; package=&quot;src.Orm.Zed.Configuration.Persistence&quot;&gt;

    &lt;table name=&quot;spy_configuration_value&quot; phpName=&quot;SpyConfigurationValue&quot;&gt;
        &lt;behavior name=&quot;event&quot;&gt;
            &lt;parameter name=&quot;spy_configuration_value_all&quot; column=&quot;*&quot; keep-additional=&quot;true&quot;/&gt;
        &lt;/behavior&gt;
    &lt;/table&gt;

&lt;/database&gt;
```

#### 3.2) Apply database changes and generate transfers

```bash
console propel:install
console transfer:generate
```

{% info_block warningBox &quot;Verification&quot; %}

Make sure that the following changes have been applied by checking your database:

| DATABASE ENTITY | TYPE | EVENT |
| --- | --- | --- |
| spy_configuration_value | table | created |
| spy_configuration_storage | table | created |

{% endinfo_block %}

{% info_block warningBox &quot;Verification&quot; %}

Make sure the following changes in transfer objects have been applied:

| TRANSFER | TYPE | EVENT | PATH |
| --- | --- | --- | --- |
| ConfigurationSetting | class | created | src/Generated/Shared/Transfer/ConfigurationSettingTransfer |
| ConfigurationValue | class | created | src/Generated/Shared/Transfer/ConfigurationValueTransfer |
| ConfigurationScope | class | created | src/Generated/Shared/Transfer/ConfigurationScopeTransfer |
| ConfigurationFeature | class | created | src/Generated/Shared/Transfer/ConfigurationFeatureTransfer |
| ConfigurationTab | class | created | src/Generated/Shared/Transfer/ConfigurationTabTransfer |
| ConfigurationGroup | class | created | src/Generated/Shared/Transfer/ConfigurationGroupTransfer |
| ConfigurationConstraint | class | created | src/Generated/Shared/Transfer/ConfigurationConstraintTransfer |
| ConfigurationDependency | class | created | src/Generated/Shared/Transfer/ConfigurationDependencyTransfer |
| ConfigurationSyncResponse | class | created | src/Generated/Shared/Transfer/ConfigurationSyncResponseTransfer |
| ConfigurationSettingCollection | class | created | src/Generated/Shared/Transfer/ConfigurationSettingCollectionTransfer |
| ConfigurationScopeCollection | class | created | src/Generated/Shared/Transfer/ConfigurationScopeCollectionTransfer |
| ConfigurationValueRequest | class | created | src/Generated/Shared/Transfer/ConfigurationValueRequestTransfer |
| ConfigurationValueResponse | class | created | src/Generated/Shared/Transfer/ConfigurationValueResponseTransfer |
| ConfigurationValidationRequest | class | created | src/Generated/Shared/Transfer/ConfigurationValidationRequestTransfer |
| ConfigurationValidationResponse | class | created | src/Generated/Shared/Transfer/ConfigurationValidationResponseTransfer |
| ConfigurationError | class | created | src/Generated/Shared/Transfer/ConfigurationErrorTransfer |
| ConfigurationValueCollectionRequest | class | created | src/Generated/Shared/Transfer/ConfigurationValueCollectionRequestTransfer |
| ConfigurationValueDeletion | class | created | src/Generated/Shared/Transfer/ConfigurationValueDeletionTransfer |
| ConfigurationValueCollectionResponse | class | created | src/Generated/Shared/Transfer/ConfigurationValueCollectionResponseTransfer |
| ConfigurationSettingValuesCriteria | class | created | src/Generated/Shared/Transfer/ConfigurationSettingValuesCriteriaTransfer |
| ConfigurationSettingValueCollection | class | created | src/Generated/Shared/Transfer/ConfigurationSettingValueCollectionTransfer |
| ConfigurationStorage | class | created | src/Generated/Shared/Transfer/ConfigurationStorageTransfer |
| ConfigurationFileUpload | class | created | src/Generated/Shared/Transfer/ConfigurationFileUploadTransfer |
| ConfigurationFileUploadCollectionRequest | class | created | src/Generated/Shared/Transfer/ConfigurationFileUploadCollectionRequestTransfer |
| ConfigurationFileUploadCollectionResponse | class | created | src/Generated/Shared/Transfer/ConfigurationFileUploadCollectionResponseTransfer |

{% endinfo_block %}

### 4) Add translations

Regenerate the Zed translator cache to pick up the Configuration Management Back Office UI translations:

```bash
console translator:generate-cache
```

{% info_block warningBox &quot;Verification&quot; %}

1. Navigate to the Configuration Management page in the Back Office.
2. Verify that all UI labels, buttons, and messages are displayed in the correct locale.
3. Switch to German locale and verify the German translations appear.

{% endinfo_block %}

### 5) Configure navigation

Add the Configuration Management entry to `config/Zed/navigation.xml`:

```xml
&lt;configuration-management&gt;
    &lt;label&gt;Configuration&lt;/label&gt;
    &lt;title&gt;Configuration Management&lt;/title&gt;
    &lt;icon&gt;settings&lt;/icon&gt;
    &lt;bundle&gt;configuration&lt;/bundle&gt;
    &lt;controller&gt;manage&lt;/controller&gt;
    &lt;action&gt;index&lt;/action&gt;
    &lt;visible&gt;1&lt;/visible&gt;
&lt;/configuration-management&gt;
```

Execute the following command to clear the navigation cache:

```bash
console navigation:cache:remove
```

{% info_block warningBox &quot;Verification&quot; %}

Log in to the Back Office and verify that the **Configuration** menu item appears in the main navigation sidebar.

{% endinfo_block %}

### 6) Set up behavior

#### 6.1) Register console commands and application plugin

Register the configuration sync console command and the application plugin that exposes the Configuration facade as an application service for direct Zed access from the Client layer:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| ConfigurationSyncConsole | Synchronizes configuration schemas from YAML files and generates the merged schema and settings map. | None | Spryker\Zed\Configuration\Communication\Console |
| ConfigurationApplicationPlugin | Registers the Configuration facade as an application service for direct access in Zed applications. | None | Spryker\Zed\Configuration\Communication\Plugin\Application |

**src/Pyz/Zed/Console/ConsoleDependencyProvider.php**

Add the import statements:

```php
use Spryker\Zed\Configuration\Communication\Console\ConfigurationSyncConsole;
use Spryker\Zed\Configuration\Communication\Plugin\Application\ConfigurationApplicationPlugin;
```

Register the console command in `getConsoleCommands()`:

```php
protected function getConsoleCommands(Container $container): array
{
    $commands = [
        // ...
        new ConfigurationSyncConsole(),
        // ...
    ];
}
```

Register the application plugin in `getApplicationPlugins()`:

```php
protected function getApplicationPlugins(Container $container): array
{
    $applicationPlugins = parent::getApplicationPlugins($container);
    // ...
    $applicationPlugins[] = new ConfigurationApplicationPlugin();

    return $applicationPlugins;
}
```

**src/Pyz/Zed/Application/ApplicationDependencyProvider.php**

Add the import statement:

```php
use Spryker\Zed\Configuration\Communication\Plugin\Application\ConfigurationApplicationPlugin;
```

Register the application plugin in each Zed application context (`getBackofficeApplicationPlugins()`, `getBackendGatewayApplicationPlugins()`, `getBackendApiApplicationPlugins()`):

```php
protected function getBackofficeApplicationPlugins(): array
{
    return [
        // ...
        new ConfigurationApplicationPlugin(),
    ];
}

protected function getBackendGatewayApplicationPlugins(): array
{
    return [
        // ...
        new ConfigurationApplicationPlugin(),
    ];
}

protected function getBackendApiApplicationPlugins(): array
{
    return [
        // ...
        new ConfigurationApplicationPlugin(),
    ];
}
```

{% info_block warningBox &quot;Verification&quot; %}

1. Run `console configuration:sync` and verify that it outputs the number of processed settings.
2. Verify that configuration values can be read in Zed context via the Client layer without going through storage.

{% endinfo_block %}

#### 6.2) Set up queue configuration

Register the Configuration storage synchronization queue in both message broker implementations.

**src/Pyz/Client/RabbitMq/RabbitMqConfig.php**

Add the import statement:

```php
use Spryker\Shared\Configuration\ConfigurationConstants;
```

Add the queue name to `getSynchronizationQueueConfiguration()`:

```php
protected function getSynchronizationQueueConfiguration(): array
{
    return [
        // ...
        ConfigurationConstants::QUEUE_NAME_SYNC_CONFIGURATION,
    ];
}
```

**src/Pyz/Client/SymfonyMessenger/SymfonyMessengerConfig.php**

Add the import statement:

```php
use Spryker\Shared\Configuration\ConfigurationConstants;
```

Add the queue name to `getSynchronizationQueueConfiguration()`:

```php
protected function getSynchronizationQueueConfiguration(): array
{
    return [
        // ...
        ConfigurationConstants::QUEUE_NAME_SYNC_CONFIGURATION,
    ];
}
```

{% info_block warningBox &quot;Verification&quot; %}

Run `console queue:setup` and verify that the `sync.storage.configuration` queue is created in RabbitMQ.

{% endinfo_block %}

#### 6.3) Register queue message processor

Register the synchronization storage queue message processor for the Configuration sync queue:

**src/Pyz/Zed/Queue/QueueDependencyProvider.php**

Add the import statements:

```php
use Spryker\Shared\Configuration\ConfigurationConstants;
```

Add the processor to `getProcessorMessagePlugins()`:

```php
protected function getProcessorMessagePlugins(Container $container): array
{
    return [
        // ...
        ConfigurationConstants::QUEUE_NAME_SYNC_CONFIGURATION =&gt; new SynchronizationStorageQueueMessageProcessorPlugin(),
    ];
}
```

{% info_block warningBox &quot;Verification&quot; %}

1. Save a configuration value in the Back Office.
2. Run `console queue:worker:start --stop-when-empty`.
3. Verify that the `sync.storage.configuration` queue is processed without errors.

{% endinfo_block %}

#### 6.4) Register publisher plugins

Enable Publish &amp; Synchronize for configuration values by registering the publisher plugin:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| ConfigurationValueWritePublisherPlugin | Publishes storefront-visible, non-secret configuration values to `spy_configuration_storage` when `spy_configuration_value` entities are created, updated, or deleted. | None | Spryker\Zed\Configuration\Communication\Plugin\Publisher |

**src/Pyz/Zed/Publisher/PublisherDependencyProvider.php**

Add the import statement:

```php
use Spryker\Zed\Configuration\Communication\Plugin\Publisher\ConfigurationValueWritePublisherPlugin;
```

Register the plugin group in `getPublisherPlugins()`:

```php
protected function getPublisherPlugins(): array
{
    return array_merge(
        // ...
        $this-&gt;getConfigurationStoragePlugins(),
    );
}
```

Add the new method:

```php
/**
 * @return list&lt;\Spryker\Zed\PublisherExtension\Dependency\Plugin\PublisherPluginInterface&gt;
 */
protected function getConfigurationStoragePlugins(): array
{
    return [
        new ConfigurationValueWritePublisherPlugin(),
    ];
}
```

{% info_block warningBox &quot;Verification&quot; %}

1. Save a configuration value in the Back Office.
2. Check that the `spy_configuration_storage` table contains the published value.
3. Verify that secret settings are NOT published to storage.

{% endinfo_block %}

#### 6.5) Register scope identifier provider plugins

Register the store scope identifier provider to resolve the current store name as the scope identifier:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| StoreConfigurationScopeIdentifierProviderPlugin | Provides the current store name as the scope identifier for the `store` scope. | Store module installed | Spryker\Zed\Store\Communication\Plugin\Configuration |

**src/Pyz/Zed/Configuration/ConfigurationDependencyProvider.php**

```php
&lt;?php

/**
 * This file is part of the Spryker Suite.
 * For full license information, please view the LICENSE file that was distributed with this source code.
 */

declare(strict_types = 1);

namespace Pyz\Zed\Configuration;

use Spryker\Zed\Configuration\ConfigurationDependencyProvider as SprykerConfigurationDependencyProvider;
use Spryker\Zed\Store\Communication\Plugin\Configuration\StoreConfigurationScopeIdentifierProviderPlugin;

class ConfigurationDependencyProvider extends SprykerConfigurationDependencyProvider
{
    /**
     * @return array&lt;\Spryker\Zed\ConfigurationExtension\Dependency\Plugin\ConfigurationScopeIdentifierProviderPluginInterface&gt;
     */
    protected function getScopeIdentifierProviderPlugins(): array
    {
        return [
            new StoreConfigurationScopeIdentifierProviderPlugin(),
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

1. Navigate to the Configuration Management page in the Back Office.
2. Switch the scope selector to a specific store.
3. Verify that the scope identifier resolves to the store name (for example `DE`, `AT`).

{% endinfo_block %}

#### 6.6) Register Client-level request expander plugins

Register the store scope expander to attach the current store scope to configuration value requests:

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| StoreScopeConfigurationValueRequestExpanderPlugin | Expands the configuration value request with the current store scope and store name as scope identifier. | Store module installed | Spryker\Client\Store\Plugin\Configuration |

**src/Pyz/Client/Configuration/ConfigurationDependencyProvider.php**

```php
&lt;?php

/**
 * This file is part of the Spryker Suite.
 * For full license information, please view the LICENSE file that was distributed with this source code.
 */

declare(strict_types = 1);

namespace Pyz\Client\Configuration;

use Spryker\Client\Configuration\ConfigurationDependencyProvider as SprykerConfigurationDependencyProvider;
use Spryker\Client\Store\Plugin\Configuration\StoreScopeConfigurationValueRequestExpanderPlugin;

class ConfigurationDependencyProvider extends SprykerConfigurationDependencyProvider
{
    protected function getConfigurationValueRequestExpanderPlugins(): array
    {
        return [
            new StoreScopeConfigurationValueRequestExpanderPlugin(),
        ];
    }
}
```

{% info_block warningBox &quot;Verification&quot; %}

1. Read a configuration value via the Client layer.
2. Verify that the request includes the current store as a scope with the store name as scope identifier.

{% endinfo_block %}

#### 6.7) Optional: Set up data import

Enable CLI-based bulk import of configuration values from CSV files.

| PLUGIN | SPECIFICATION | PREREQUISITES | NAMESPACE |
| --- | --- | --- | --- |
| ConfigurationValueDataImportPlugin | Imports configuration values from CSV using the DataImport framework. Validates setting keys, scopes, and constraints. Skips secret settings with a warning. | `configuration:sync` must be run first | Spryker\Zed\Configuration\Communication\Plugin\DataImport |

**src/Pyz/Zed/DataImport/DataImportDependencyProvider.php**

Add the import statement and register the plugin in `getDataImporterPlugins()`:

```php
use Spryker\Zed\Configuration\Communication\Plugin\DataImport\ConfigurationValueDataImportPlugin;
```

```php
protected function getDataImporterPlugins(): array
{
    return [
        // ...
        new ConfigurationValueDataImportPlugin(),
    ];
}
```

**src/Pyz/Zed/DataImport/DataImportConfig.php**

Add the import type to the full import types list:

```php
use Spryker\Zed\Configuration\ConfigurationConfig;
```

```php
public function getFullImportTypes(): array
{
    return [
        // ...
        ConfigurationConfig::IMPORT_TYPE_CONFIGURATION_VALUE,
    ];
}
```

**data/import/common/common/configuration_value.csv**

Create the CSV file with the required columns:

```csv
setting_key,scope,scope_identifier,value
```

**data/import/local/full_EU.yml** (or your region-specific import config)

Add the `configuration-value` data entity:

```yaml
actions:
    # ...
    - data_entity: configuration-value
      source: data/import/common/common/configuration_value.csv
```

{% info_block warningBox &quot;Verification&quot; %}

1. Add a row to `data/import/common/common/configuration_value.csv` with a valid setting key, for example:

   ```csv
   setting_key,scope,scope_identifier,value
   system:general:basic:site_name,store,DE,My German Store
   ```

2. Run `console data:import configuration-value`.
3. Verify the value is saved by checking the Back Office Configuration page.

{% endinfo_block %}

### 7) Add install recipe commands

Add the `configuration:sync` command to the install recipes so that the merged schema and settings map are generated during deployment.

The generated files are written to `data/configuration/` inside the container and are excluded from both the Git repository and the Docker image. A freshly deployed container therefore never has them until `configuration:sync` runs. Without these files, the settings map is empty and every configuration value resolves to `null` at runtime—even though the stored values remain intact in the `spy_configuration_value` table. Add the command to every recipe a deployment invokes, not only to the local build recipe.

#### 7.1) Add the command to the local build recipe

**config/install/docker.yml**

Add the command to the `build` section:

```yaml
    build:
        # ... existing commands ...

        configuration-sync:
            command: &apos;vendor/bin/console configuration:sync&apos;
```

#### 7.2) Add the command to the cloud deployment recipes

Add a `configuration` section to each recipe your deployment pipeline invokes. Place it after the database migration steps and before any `data:import` step: the imported configuration-value rows are validated against the settings map, and the import aborts when the map is missing.

| RECIPE | DEPLOYMENT HOOK |
| --- | --- |
| config/install/production.yml | `SPRYKER_HOOK_INSTALL` |
| config/install/destructive.yml | `SPRYKER_HOOK_DESTRUCTIVE_INSTALL` |
| config/install/dynamic-store.yml | Dynamic Multistore deployments |

Add the following section to each of the preceding recipes:

```yaml
    configuration:
        configuration-sync:
            command: &apos;vendor/bin/console configuration:sync -vvv --no-ansi&apos;
```

If your project clones new environments from a deploy file template, such as `deploy.aws-env-template.yml`, add the section to the template as well so that new environments inherit it.

{% info_block infoBox &quot;pre-deploy recipes&quot; %}

Do not add `configuration:sync` to `config/install/pre-deploy.yml`. This recipe runs before the new code and the database migration are in place, so there is nothing to sync against yet.

{% endinfo_block %}

{% info_block infoBox &quot;Idempotency&quot; %}

Running `configuration:sync` cannot overwrite values set by users. The command only writes the two generated cache files and never opens a database connection, so it is safe to run on every deployment.

{% endinfo_block %}

{% info_block warningBox &quot;Verification&quot; %}

Run each install recipe and verify that the `configuration:sync` step executes without errors.

{% endinfo_block %}

### 8) Configure data directory

Add the `data/configuration/` directory to `.gitignore` exceptions to ensure the generated schema files are tracked:

**.gitignore**

```diff
 /data/*
 !/data/import/
 !/data/export/
+!/data/configuration/
```

Create the directory with a `.gitkeep` file:

```bash
mkdir -p data/configuration
touch data/configuration/.gitkeep
```

Add the `data/configuration/` directory to `.dockerignore` exceptions to ensure the directory is included in the Docker build context and reaches the image:

**.dockerignore**

```diff
 /data
 !/data/import
 !/data/export
+!/data/configuration
```

{% info_block warningBox &quot;Verification&quot; %}

Run `console configuration:sync` and verify that `data/configuration/merged-schema.php` and `data/configuration/settings-map.php` are generated.

{% endinfo_block %}

## Configuration Schema YAML Reference

Configuration settings are defined in YAML files with the `*.configuration.yml` extension. The schema sync command (`configuration:sync`) discovers these files and merges them into a single schema.
</description>
            <pubDate>Tue, 25 Aug 2026 07:19:33 +0000</pubDate>
            <link>https://docs.spryker.com/docs/dg/dev/integrate-and-configure/integrate-confguration-feature.html</link>
            <guid isPermaLink="true">https://docs.spryker.com/docs/dg/dev/integrate-and-configure/integrate-confguration-feature.html</guid>
            
            
        </item>
        
    </channel>
</rss>
