orangecat / module-quickorder
Bulk add-to-cart by SKU for Magento 2 B2B — search, paste, or upload a CSV to fill the cart in one shot
Package info
github.com/olivertar/m2_quickorder
Language:HTML
Type:magento2-module
pkg:composer/orangecat/module-quickorder
Requires
- php: >=8.1
- magento/framework: *
README
Bulk add-to-cart by SKU — search, paste, or upload a CSV to fill the cart in one shot.
Module: Orangecat_QuickOrder
Version: 1.0.0
License: OSL-3.0
Author: Oliverio Gombert olivertar@gmail.com
Table of Contents
- Overview
- Theme Compatibility
- Requirements
- Installation
- What Gets Installed
- Configuration
- Store Admin Guide
- Buyer Guide (Frontend)
- Developer Guide
- REST API
- Frontend Routes Reference
- DevOps & Integrator Notes
Overview
Orangecat_QuickOrder gives logged-in customers a dedicated page to build and submit large orders without browsing the catalog. Three input methods are available in parallel on the same page:
- Live search — type at least 3 characters to search by SKU or product name; results appear in a dropdown with price and type badge.
- Bulk SKU textarea — paste a comma- or newline-separated list of SKUs; the module resolves each SKU to a product and adds it to the staging list.
- CSV upload — upload a file with
SKU,Qtyrows; header row is auto-detected and skipped.
All resolved products land in an editable staging table where the buyer can adjust quantities, select configurable options, and remove individual rows before sending everything to the cart in one request.
Position in the Orangecat B2B Dependency Chain
Orangecat_Core (via composer: orangecat/core)
└── Orangecat_Company
└── ...
Orangecat_QuickOrder ← this module (standalone, no inter-module dependency)
Orangecat_QuickOrder is independent — it does not depend on Orangecat_Company or any other Orangecat module. It integrates with core Magento catalog, checkout, and customer modules only.
Theme Compatibility
| Theme | Status | Notes |
|---|---|---|
| Luma | Supported | quickorder.phtml + RequireJS AMD widget quick-order.js. LESS styles in web/css/source/_module.less. |
| Hyvä | Supported | quickorder_hyva.phtml with inline Alpine.js initQuickOrder() component. CSS utilities in web/css/hyva/module.css. Layout handle hyva_quickorder_index_index.xml swaps the template and loads the Hyvä CSS. |
| Breeze Evolution | Supported | Uses the same Luma template (quickorder.phtml). The JS widget is registered in breeze_default.xml as a Breeze bundle item. LESS styles in web/css/breeze/_default.less. |
Each theme uses its own layout handle. The Hyvä template is at view/frontend/templates/quickorder_hyva.phtml — not in a view/hyva/ subdirectory, following this project's convention.
Requirements
| Dependency | Version / Notes |
|---|---|
| Magento 2 | 2.4.x |
| PHP | >= 8.1 |
magento/module-customer |
Bundled with Magento |
magento/module-catalog |
Bundled with Magento |
magento/module-checkout |
Bundled with Magento |
magento/module-configurable-product |
Bundled with Magento (required for configurable option resolution) |
magento/module-config |
Bundled with Magento |
magento/module-store |
Bundled with Magento |
No dependency on orangecat/core or any other Orangecat module.
Installation
Via Git Submodule (recommended for this project)
# From repo root
git submodule add git@github.com:olivertar/m2_quickorder.git app/code/Orangecat/QuickOrder
git submodule update --init --recursive
Enable the Module
Run inside the PHP container (reward shell):
bin/magento module:enable Orangecat_QuickOrder bin/magento setup:upgrade bin/magento setup:di:compile bin/magento setup:static-content:deploy -f bin/magento cache:flush
What Gets Installed
- No database tables — this module creates no schema changes.
- No EAV attributes — no customer or product attributes are added.
- No data patches — no default records, CMS pages, or roles are created.
setup:upgrade only registers the module version in setup_module. All module state lives in core_config_data (the enabled/disabled flag).
Configuration
Path: Stores > Configuration > Orangecat > Quick Order
General
| Label | Config path | Default | Description |
|---|---|---|---|
| Enable Quick Order | orangecat_quickorder/general/enabled |
Yes | Master switch. When disabled, the frontend page returns 404 and all AJAX endpoints return empty/error responses. The account navigation link is also hidden (via ifconfig). |
orangecat_quickorder/general/enabled
Scope: Default / Website / Store View.
Store Admin Guide
Enabling / Disabling
- Go to Stores > Configuration > Orangecat > Quick Order.
- Set Enable Quick Order to Yes or No.
- Save and flush cache.
No other admin configuration or UI exists. The module has no admin grids, order management screens, or report panels — it is a pure frontend feature.
Verifying the Link Appears
After enabling, logged-in customers should see a Quick Order link in the My Account sidebar. If the link is missing, confirm orangecat_quickorder/general/enabled is 1 for the active store view and that static content has been deployed.
Buyer Guide (Frontend)
Accessing Quick Order
Direct URL: https://your-store.test/quickorder
The Quick Order link appears in the My Account sidebar navigation for all logged-in customers. Guests are redirected to the login page. When the module is disabled, the page returns a 404.
Step 1 — Add Products to Your List
Three methods are available on the same page:
Search by SKU or Name
- Type at least 3 characters in the Search Products input.
- A dropdown appears with matching results showing product name, SKU, price, and type.
- Click a result to add it to the staging list.
For configurable products, the row will show option selectors (e.g. Color, Size) that must be completed before adding to cart.
Bulk SKU Input
- Paste SKUs into the Bulk SKU Input textarea, separated by commas or new lines.
- Click Add to List.
- Each valid SKU resolves to a product row. Invalid or not-found SKUs are reported as errors below the action area.
Only simple products are supported via bulk input. Configurable, bundle, and grouped products are skipped with an explanatory message.
CSV Upload
- Prepare a CSV file:
SKU,Qty PROD-001,5 PROD-002,2 SIMPLE-ABC,10
A header row (SKU,Qty) is optional — if the first cell is literally sku (case-insensitive) it is skipped.
- Click Choose File, select the CSV, then click Upload CSV.
- Valid rows appear in the staging list; invalid SKUs are reported as errors.
Same restriction as bulk input: only simple products are accepted from CSV.
Step 2 — Review and Adjust
The Your Selection table shows all staged products:
| Column | Editable | Notes |
|---|---|---|
| Product | No | Product name |
| SKU | No | Product SKU |
| Options | Yes | Dropdowns for configurable attributes; N/A for simple |
| Price | No | Formatted product price |
| Qty | Yes | Numeric input, minimum 1 |
| Actions | — | × button removes the row |
Use Clear All to empty the entire list.
Step 3 — Add All to Cart
Click Add all to Cart. The module validates that all configurable options are selected before submitting. On success:
- All items are added to the active cart in one request.
- The mini-cart refreshes automatically.
- The staging list clears.
On partial failure (e.g. out-of-stock item), the server returns an error message and no items are added.
Developer Guide
Module Structure
Orangecat/QuickOrder/
├── Controller/
│ ├── Index/
│ │ └── Index.php — GET /quickorder (page; login + enabled check)
│ └── Ajax/
│ ├── Search.php — POST /quickorder/ajax/search
│ ├── BulkSku.php — POST /quickorder/ajax/bulksku
│ ├── Upload.php — POST /quickorder/ajax/upload
│ └── AddToCart.php — POST /quickorder/ajax/addtocart
├── Helper/
│ └── Data.php — isEnabled() helper (legacy; use Model/Config)
├── Model/
│ └── Config.php — isEnabled() via ScopeConfigInterface
├── etc/
│ ├── module.xml — sequence: Customer, Catalog, Checkout
│ ├── acl.xml — Orangecat_QuickOrder::config
│ ├── config.xml — default enabled = 1
│ ├── frontend/routes.xml — frontName: quickorder
│ └── adminhtml/system.xml — Enable Quick Order field
├── registration.php
└── view/frontend/
├── layout/
│ ├── quickorder_index_index.xml — Luma page (2columns-left)
│ ├── hyva_quickorder_index_index.xml — Hyvä: swaps template + loads CSS
│ ├── customer_account.xml — sidebar nav link (all themes)
│ └── breeze_default.xml — Breeze JS bundle registration
├── templates/
│ ├── quickorder.phtml — Luma/Breeze template (data-mage-init)
│ └── quickorder_hyva.phtml — Hyvä template (Alpine.js initQuickOrder())
└── web/
├── js/quick-order.js — Luma/Breeze RequireJS AMD widget
├── css/source/_module.less — Luma LESS styles
├── css/breeze/_default.less — Breeze LESS styles
└── css/hyva/module.css — Hyvä CSS utilities (z-index, animation, etc.)
Key Classes
Orangecat\QuickOrder\Model\Config
Primary config reader. Inject via constructor.
isEnabled(): bool // reads orangecat_quickorder/general/enabled, store scope
Orangecat\QuickOrder\Helper\Data
Extends AbstractHelper. Provides the same isEnabled() check. Kept for compatibility; prefer Model\Config in new code.
Controllers
All AJAX controllers implement HttpPostActionInterface and return JsonFactory responses.
| Class | Method | Route | Key behavior |
|---|---|---|---|
Controller\Index\Index |
execute() |
GET /quickorder |
Redirects to 404 if disabled; redirects to login if not logged in |
Controller\Ajax\Search |
execute() |
POST /quickorder/ajax/search |
query param (min 3 chars); searches SKU and name; returns max 10 results with configurable options array |
Controller\Ajax\BulkSku |
execute() |
POST /quickorder/ajax/bulksku |
skus param; splits on comma/newline; resolves via ProductRepositoryInterface; simple products only |
Controller\Ajax\Upload |
execute() |
POST /quickorder/ajax/upload |
csv_file multipart; parsed with Magento\Framework\File\Csv; auto-detects header row; simple products only |
Controller\Ajax\AddToCart |
execute() |
POST /quickorder/ajax/addtocart |
items JSON param; adds each item via Magento\Checkout\Model\Cart; supports super_attribute for configurables |
Search Response Shape
{
"items": [
{
"id": 42,
"name": "Blue Widget",
"sku": "BW-001",
"price": "$19.99",
"type": "simple",
"options": []
},
{
"id": 55,
"name": "T-Shirt",
"sku": "TS-XL",
"price": "$29.00",
"type": "configurable",
"options": [
{
"id": "93",
"label": "Color",
"code": "color",
"values": [
{ "value": "56", "label": "Blue" },
{ "value": "57", "label": "Red" }
]
}
]
}
]
}
AddToCart Request Shape
POST /quickorder/ajax/addtocart
Content-Type: application/x-www-form-urlencoded
items=%5B%7B%22id%22%3A42%2C%22qty%22%3A3%2C%22super_attribute%22%3A%7B%7D%7D%5D&form_key=abc123
Decoded items value:
[
{ "id": 42, "qty": 3, "super_attribute": {} },
{ "id": 55, "qty": 1, "super_attribute": { "93": "56" } }
]
Observers
This module registers no observers.
Plugins
This module registers no plugins.
JS Components
Frontend — Luma / Breeze
| File | Type | Description |
|---|---|---|
web/js/quick-order.js |
RequireJS AMD widget | Manages all UI state: search debounce (300 ms), result rendering, staging list, qty/option change handlers, CSV upload, bulk SKU, add-to-cart with mini-cart invalidation via Magento_Customer/js/customer-data |
Initialized via data-mage-init in quickorder.phtml:
{
"Orangecat_QuickOrder/js/quick-order": {
"searchUrl": "...",
"uploadUrl": "...",
"bulkSkuUrl": "...",
"addToCartUrl": "...",
"formKey": "..."
}
}
Frontend — Hyvä
| File | Type | Description |
|---|---|---|
quickorder_hyva.phtml |
Inline Alpine.js | initQuickOrder() function returns Alpine data object with handleSearch, handleBulkSku, handleUpload, handleAddToCart, selectProduct, removeItem, clearList. Uses hyva.getFormKey() and window.dispatchMessages for notifications. Cart refresh via CustomEvent('reload-customer-section-data'). |
Email Templates
This module sends no transactional emails.
ACL Resources
| Resource ID | Title | Location |
|---|---|---|
Orangecat_QuickOrder::config |
Orangecat Quick Order Configuration | Stores > Settings > Configuration |
Adding Custom Logic
- Disable for specific customer groups: add a plugin on
Controller\Index\Index::execute()orModel\Config::isEnabled()that checks the customer session's group ID. - Extend search results: add a plugin on
Controller\Ajax\Search::execute()to filter or enrich the$itemsarray before the JSON response is built. - Post-add-to-cart hook: observe
checkout_cart_product_add_afterto trigger custom logic (logging, price override) after each product is added by the Quick Order flow.
REST API
This module exposes no REST API endpoints. There is no etc/webapi.xml.
Frontend Routes Reference
| Route | Controller | Access |
|---|---|---|
GET /quickorder |
Controller\Index\Index |
Logged-in customers; 404 if disabled |
POST /quickorder/ajax/search |
Controller\Ajax\Search |
Any (enabled check only) |
POST /quickorder/ajax/bulksku |
Controller\Ajax\BulkSku |
Any (enabled check only) |
POST /quickorder/ajax/upload |
Controller\Ajax\Upload |
Any (enabled check only) |
POST /quickorder/ajax/addtocart |
Controller\Ajax\AddToCart |
Any (enabled check only) |
The AJAX endpoints do not enforce an active customer session — they rely on the form key for CSRF protection and check the enabled flag. Add a customer-session guard in a plugin if stricter access control is required.
DevOps & Integrator Notes
Deployment Checklist
bin/magento module:enable Orangecat_QuickOrder
bin/magento setup:upgrade # registers module version only (no schema changes)
bin/magento setup:di:compile
bin/magento setup:static-content:deploy -f
bin/magento cache:flush
Integration Token Scope
No REST API. No integration token permissions required for this module.
Disabling Without Uninstalling
bin/magento module:disable Orangecat_QuickOrder bin/magento setup:upgrade bin/magento cache:flush
No dependent Orangecat modules — safe to disable at any time without cascade effects.
Data Integrity
- No database tables are created or modified by this module.
- The only persisted state is
core_config_datarows fororangecat_quickorder/*. - Disabling the module leaves those rows in place; re-enabling restores prior settings.
- No cart data is stored by this module — items written to the cart follow standard Magento quote lifecycle.