Appearance
Shelf Code Management
Flow ID: AD-51 | Module(s): eshop | Complexity: Medium Last Updated: 2026-05-12
Business Context
Shelf code management provides CRUD operations for warehouse shelf/location codes. Each product can be assigned a shelf code that indicates its physical location in the warehouse, enabling faster order picking and inventory management. This is primarily used by businesses with physical warehouse operations (e.g., pharmaceutical distributors, large e-commerce warehouses).
The admin interface at /eshop/shelfcodes_admin.htm is a simple form-based CRUD for creating, editing, and deleting shelf code entries. Products reference shelf codes via a plain non-unique shelfcode_id foreign key (no DB-level FK constraint) and the shelf label is surfaced throughout the order picking pipeline -- order detail, packing slips, build summaries, and customer order history -- so warehouse staff can locate each line item.
A complementary barcode-wedge / mobile scanner workflow (Scanaccess) lets warehouse operators scan a product barcode to immediately land on a single-page form for adjusting the product's shelf code, base price, and stock without navigating the full product admin.
API Reference
REST Endpoints
| Method | Endpoint | Actions | Auth | Roles |
|---|---|---|---|---|
| GET | /rest/product/shelfcode | index, item, show | Backend | ADMIN, PRODUCTS |
| POST | /rest/product/shelfcode | store, update | Backend | ADMIN, PRODUCTS |
| DELETE | /rest/product/shelfcode | destroy | Backend | ADMIN, PRODUCTS |
Read routes registered in application/config/rest_routes.php:352-357, write routes in application/config/rest_routes.php:1541-1546. RBAC defaults in application/config/rest_policies.php:518.
Legacy Admin Routes
| Route | Controller | Method | HTTP | Description |
|---|---|---|---|---|
eshop/shelfcodes_admin | Adv_shelfcodes_admin | index() | GET | List all shelf codes |
eshop/shelfcodes_admin/add | Adv_shelfcodes_admin | add() | GET/POST | Create shelf code form |
eshop/shelfcodes_admin/edit/{id} | Adv_shelfcodes_admin | edit($id) | GET/POST | Edit shelf code form |
eshop/shelfcodes_admin/delete/{id} | Adv_shelfcodes_admin | delete($id) | GET | Delete a shelf code (soft-dereferences products first) |
Route definitions at application/config/routes.php:374-377 (default + locale-prefixed variants).
Barcode Scanner Routes
| Route | Controller | Method | HTTP | Description |
|---|---|---|---|---|
scanaccess | Scanaccess | index() | GET | Full-screen autofocused barcode input |
scanaccess/setup | Scanaccess | setup() | POST | Look up product by scanned ID and render edit form |
scanaccess/update | Scanaccess | update() | POST | Commit shelf code / price update |
Registered at application/config/routes.php:124-127.
Code Flow
Admin CRUD
Adv_shelfcodes_admin::add() / edit($id)
|
+--> GET: render form view (create.php / update.php)
+--> POST submit -> $this->validation() at Adv_shelfcodes_admin.php:105-108
| |-- single rule: set_rules('shelfcode_value', ..., 'trim|numeric')
|
+--> Build $data = ['code' => $post['shelfcode_value']]
+--> shelfcodes_model->add_record($data) | edit_record($id, $data)
+--> afterAdd($id) / afterEdit($id) hook (empty stub, lines 110-123)
+--> set_userdata('eshop_success', ...)
\--> redirect('eshop/shelfcodes_admin')Controller methods live at ecommercen/eshop/controllers/Adv_shelfcodes_admin.php:35-103. RBAC enforced in the constructor at lines 17-33: [AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_PRODUCTS].
Soft-Dereference Delete (legacy)
php
// ecommercen/eshop/models/Adv_shelfcodes_model.php:113-118
function delete_record($id) {
$this->db->update($this->tableProduct, ['shelfcode_id' => null], ['shelfcode_id' => $id]);
$this->db->delete($this->table, ['id' => $id]);
$this->session->set_userdata('eshop_success', t('eshop.admin.success.entrydelete'));
}The legacy delete always succeeds: every shop_product.shelfcode_id that references the deleted shelf is first NULLed, then the shelf row is removed. There is no confirmation dialog, no blocking if products still reference the shelf, and no count of affected products shown to the admin.
Product Admin Integration
The product admin controller Adv_products_admin.php wires shelf codes into create/edit/clone/ERP-import forms and the list-view filter/batch action surface:
- Model loaded in constructor at line 32.
- List filter: multiselect
shelvesfilter populated viashelfcodes_model->getCombo()at line 147, rendered inapplication/views/admin/products/list.php:194-200. - Form integration via
shelfcodes_comborender variable at lines 689, 749, 806, and 1815. Consumed by:application/views/admin/products/create.php:583-589application/views/admin/products/update.php:749-762application/views/admin/products/clone.php:662-673application/views/admin/products/add_erp_product.php:571-577application/views/admin/products/new_products_create.php:343-349
- Save logic defaults
shelfcode_idtonull(line 392) and sets it from postedshelfcode_valueonly if non-empty (lines 411-413). - Product-level validation:
set_rules('shelfcode_value', 'Shelfcode', 'trim')at line 959 -- note this drops thenumericconstraint that the shelf codes admin enforces. - Batch action
alter_shelfcode: handler at line 1095, submit at 1583-1587. CallsbatchMasterUpdate($products, ['shelfcode_id' => ..., 'date_changed' => ...]). HookafterBatchActionSubmitAlterShelfCodeat line 2066. UI inapplication/views/admin/products/list.php:549-551andbatch_update.php:312-315. - Search filter:
Adv_product_model::get_records()accepts ashelvescondition and appliesWHERE shop_product.shelfcode_id IN (...)atAdv_product_model.php:2180-2182.
Modern Domain Layer
A full Domain + REST layer was added in 4.99.0 (see docs/changelog/Changelog.4.99.md:769-774). It lives under src/Domains/Product/Shelfcode/ and src/Rest/Product/.
Domain Files
| File | Role | Notes |
|---|---|---|
src/Domains/Product/Shelfcode/Repository/Entity.php | Entity | Fields: int $id, ?string $code |
src/Domains/Product/Shelfcode/Repository/Repository.php | Read repository | table = 'shop_product_shelfcodes' |
src/Domains/Product/Shelfcode/Repository/RepositoryConfigurator.php | Relations config | getRelations(): array { return []; } -- no relations |
src/Domains/Product/Shelfcode/Repository/WriteRepository.php | Write repository | Extends BaseWriteRepository, primary key id; adds nullProductReferences() at lines 18-23 |
src/Domains/Product/Shelfcode/Service.php | Read service | all(), item(), get() |
src/Domains/Product/Shelfcode/WriteService.php | Write service | create(), update(), delete() (lines 47-54; transactional soft-dereference) |
src/Domains/Product/Shelfcode/WriteData.php | Write DTO | Single ?string $code field, OpenAPI schema name Shelfcode |
src/Domains/Product/Shelfcode/Validator.php | Validator | Empty stub -- no length / format / uniqueness rules |
src/Domains/Product/Shelfcode/ListRequest.php | List filter spec | Filters id (Exact), code (Partial); sorts id, code. Comment explicitly notes "Shelfcode does not have MUI translations." |
Modern Delete Behavior
php
// src/Domains/Product/Shelfcode/WriteService.php:47-54
public function delete(int|string $id): bool {
return (bool) $this->writeRepository->transactional(function () use ($id) {
$this->writeRepository->nullProductReferences($id);
return $this->writeRepository->delete($id);
});
}php
// src/Domains/Product/Shelfcode/Repository/WriteRepository.php:18-23
public function nullProductReferences(int|string $id): void {
$this->db
->where('shelfcode_id', $id)
->update('shop_product', ['shelfcode_id' => null]);
}The modern write service now mirrors the legacy soft-dereference: nullProductReferences() NULLs every shop_product.shelfcode_id that references the deleted shelf before removing the shelf row. In addition, both operations are wrapped in transactional(), making the modern path atomically safer than the legacy delete_record(), which issues the two statements without a transaction (ecommercen/eshop/models/Adv_shelfcodes_model.php:113-118).
REST Layer
| File | Role |
|---|---|
src/Rest/Product/Controllers/Shelfcode.php | REST controller (lines 1-150). Extends HandlesRestfulActions, uses HandlesWriteActions. Methods: index, show, item, store, update, destroy. Full OpenAPI annotations. |
src/Rest/Product/Resources/Shelfcode/Resource.php | Resource transformer. OpenAPI schema ProductShelfcodeResource. Fields: id, code. |
src/Rest/Product/Resources/Shelfcode/Collection.php | Collection wrapper. OpenAPI schema ProductShelfcodeCollection. |
Product Relation
The modern Product domain declares shelf code as a BELONGS_TO relation:
php
// src/Domains/Product/Product/Repository/RepositoryConfigurator.php:26
'shelfcode' => new Relation(Relation::BELONGS_TO, ShelfcodeRepository::class, 'shelfcode_id'),src/Domains/Product/Product/Repository/Entity.php:11declares?int $shelfcode_id.src/Domains/Product/Product/WriteData.php:12, 58, 99, 140acceptsshelfcodeIdon create / update.src/Rest/Product/Resources/Product/Resource.php:79-80exposesshelfcodeIdonly when$this->context?->isBackend()is true -- this field is backend-only on the Product REST resource (seedocs/changelog/Changelog.4.99.md:95-96).- Lines 46 and 106 of the Product resource expose an optional embedded
shelfcoderelation. The OA schema name referenced there does not match the actual class declaration (see Known Issues #10).
DI Registration
- Domain:
src/Domains/Product/container.php:318-324 - REST:
src/Rest/Product/container.php:242-247
Tests
| File | Scope |
|---|---|
tests/Integration/Domains/Product/Shelfcode/RepositoryTest.php | Integration -- repository |
tests/Integration/Domains/Product/Shelfcode/ServiceTest.php | Integration -- read service |
tests/Unit/Domains/Product/Shelfcode/ServiceTest.php | Unit -- read service |
tests/Unit/Rest/Product/Resources/Shelfcode/ResourceTest.php | Unit -- Resource transformer |
tests/Unit/Rest/Product/Resources/Shelfcode/CollectionTest.php | Unit -- Collection transformer |
Architecture
| Component | Path | Purpose |
|---|---|---|
Shelfcodes_admin (wrapper) | application/modules/eshop/controllers/Shelfcodes_admin.php:1-13 | Empty client-override shim |
Adv_shelfcodes_admin | ecommercen/eshop/controllers/Adv_shelfcodes_admin.php:1-124 | Legacy admin CRUD, extends Admin_c |
Shelfcodes_model (wrapper) | application/modules/eshop/models/Shelfcodes_model.php:1-7 | Empty client-override shim |
Adv_shelfcodes_model | ecommercen/eshop/models/Adv_shelfcodes_model.php:1-119 | Legacy model, extends Adv_base_model |
Scanaccess | application/controllers/Scanaccess.php | Standalone barcode scanner controller |
| Domain Entity | src/Domains/Product/Shelfcode/Repository/Entity.php | Modern entity |
| Domain Service | src/Domains/Product/Shelfcode/Service.php | Modern read service |
| Domain WriteService | src/Domains/Product/Shelfcode/WriteService.php | Modern write service |
| REST Controller | src/Rest/Product/Controllers/Shelfcode.php | REST endpoints |
| Routes | application/config/routes.php:374-377 | Legacy admin routes |
| REST routes | application/config/rest_routes.php:352-357,1541-1546 | REST read + write routes |
| Admin menu | application/config/admin_menu.php:570-576 | Menu registration |
Legacy Model Method Inventory (Adv_shelfcodes_model.php)
| Method | Lines | Purpose |
|---|---|---|
get_records($cond, $asrow) | 24-40 | List shelf codes with optional conditions |
shelfcode_label($shelfcode_id) | 42-47 | Return the code string for a given PK |
getCombo($emptyFirst = false) | 55-71 | Return [id => code] map for form_dropdown() |
add_record($data) | 73-80 | Insert a shelf code |
edit_record($id, $data) | 82-88 | Update a shelf code |
product_shelfcode($productId) | 90-97 | Join product -> shelfcode, return shelfcode_id |
productShelfCodeLabel($productId) | 99-111 | Join product -> shelfcode, return label string |
delete_record($id) | 113-118 | Soft-dereference then delete |
Deprecated Global Helper
ecommercen/helpers/eshop_helper.php:3-26 defines a global product_shelfcode($productId) function that delegates to productShelfCodeLabel(). It is marked @deprecated but is still called by four marketplace order-ingestion models (see Marketplace Order Imports).
Admin Menu
Registered in application/config/admin_menu.php:570-576 inside the PRODUCTS group with label "Shelves" and icon <i class="mdi mdi-server"></i>. Visible to roles [AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_PRODUCTS].
Views
| View | Purpose |
|---|---|
application/views/admin/shelfcodes/list.php | Simple table: ID, Code, Actions |
application/views/admin/shelfcodes/create.php | Single-field form (shelfcode_value) |
application/views/admin/shelfcodes/update.php | Same form, pre-populated |
Data Model
shop_product_shelfcodes Table
Defined in database/initial/initial.sql:1824-1829. No Phinx migration has ever altered the schema.
| Column | Type | Null | Default | Description |
|---|---|---|---|---|
id | INT(10) AUTO_INCREMENT | No | -- | Primary key |
code | VARCHAR(255) | Yes | '0' | Shelf code string. The DDL has no NOT NULL, so the column is technically nullable, but the default is the literal string "0" rather than NULL. |
Indexes
PRIMARY KEY (id)KEY code (code)-- plain non-unique index
No _mui table. Shelf codes have no translations by design (see comment in ListRequest.php).
Reference from shop_product
database/initial/initial.sql:1521-1574 declares:
shelfcode_id INT(11) DEFAULT NULLKEY shelfcode_id (shelfcode_id)-- plain non-unique index- No
FOREIGN KEYconstraint. Referential integrity is maintained entirely in PHP (legacy soft-dereference on delete).
No other tables in the schema reference shop_product_shelfcodes.
Barcode Scanner Workflow
Scanaccess is a standalone controller for USB barcode-wedge / mobile scanner workflows. There is no AdvBarcodeScanner library and no label-printing infrastructure -- labels are assumed to be printed externally; Scanaccess only covers the scan-in / edit side.
Controller
application/controllers/Scanaccess.php extends Admin_c. It loads eshop/shelfcodes_model and eshop/product_model in the constructor. Notably it makes no allowRole call, so any logged-in admin role can use it -- see Known Issues & Security Gaps.
Actions
| Method | Lines | Behavior |
|---|---|---|
index() | 22-25 | Renders scanaccess/scanview.php -- a minimal full-page autofocused text input named id with form action scanaccess/setup. |
setup() | 27-51 | Looks up the product by ID, joins the current shelf code, passes $this->shelfcodes_model->get_combo() (Scanaccess.php:46) to application/views/scanaccess/setup.php. The snake_case call does not match the actual model method (see Known Issues #9). |
update() | 53-72 | On POST upd, calls product_model->batchMasterUpdate([$productid], ['shelfcode_id' => $shelfcodeid, 'price' => $productPrice, 'date_changed' => date('Y-m-d H:i:s')]). |
Views
application/views/scanaccess/scanview.php:1-50-- single inputbarcodeInput; a JSonloadhandler auto-focuses it so a barcode-wedge scanner immediately submits.application/views/scanaccess/setup.php:1-111-- product image, name, ID, editable base price, shelf code dropdown, vendor code, and an "Αλλαξε στοκ" (change stock) section. A comment at line 50 notes// @TODO drop-old-attrs product code need fix for barcode reference-- barcode display is currently disabled.application/views/scanaccess/update.phpexists but is not touched by any action in the visited controller.
Flow Diagram
Scanner/operator
|
v
GET /scanaccess -- scanview.php (autofocused input)
|
v
POST /scanaccess/setup -- resolves product_id, loads shelfcode combo
| and current shelfcode, renders setup.php
v
POST /scanaccess/update -- batchMasterUpdate: shelfcode_id, price, date_changedPicking Workflow
Shelf codes surface at multiple points in the order fulfillment pipeline via joins in Adv_order_basket_model:
php
// ecommercen/eshop/models/Adv_order_basket_model.php:22
protected $tableShelfCodes = 'shop_product_shelfcodes';Admin Order Detail (lines 381-411)
SELECT ... {$this->tableShelfCodes}.code AS shelf_code ... LEFT JOIN shop_product_shelfcodes ON shop_product_shelfcodes.id = shop_product.shelfcode_id -- joins the line-item product to its current shelf code for display on the admin order detail view.
Packing Slip (lines 470-500)
A similar SELECT with GROUP_CONCAT of barcodes, used by the logistics packing slip.
View Surface Points
| View | Line | Context |
|---|---|---|
application/views/admin/orders/update.php | 232 | Admin order detail -- label eshop.admin.shelfcode.shelfcode_label |
application/views/admin/orders/build_summary.php | 28-30 | Order build summary partial |
application/views/admin/orders/invoice_logistics.php | 13-15 | Logistics packing slip |
application/views/admin/customers/order_history.php | 20 | Customer order history panel |
Marketplace Order Imports
Marketplace order ingestion models snapshot the shelf code label at order time -- storing a string copy rather than a foreign key -- via the deprecated global product_shelfcode() helper:
| File | Line |
|---|---|
ecommercen/eshop/models/Adv_skroutz_orders_model.php | 565 |
ecommercen/eshop/models/Adv_shopflix_orders_model.php | 374 |
ecommercen/eshop/models/Adv_public_orders_model.php | 253 |
ecommercen/eshop/models/Adv_order_model.php | 1521 |
This snapshot approach means that if a shelf code is renamed or deleted after import, the label captured on the order line is preserved for that historical order.
Bug at
Adv_orders_admin.php:1082-1085: the order admin stores'product_shelfcode' => $item->product_id-- writing the product ID under theproduct_shelfcodekey. This is a copy-paste error that corrupts the snapshot for any code path that flows through this branch. See Known Issues & Security Gaps.
Configuration
- Legacy routes:
application/config/routes.php:374-377(default + locale-prefixed). - REST routes:
application/config/rest_routes.php:352-357(read),1541-1546(write). - Admin menu:
application/config/admin_menu.php:570-576. - REST policies:
application/config/rest_policies.php:518. - DI:
src/Domains/Product/container.php:318-324,src/Rest/Product/container.php:242-247. - No environment variables. Shelf codes have no registry keys, no
.envdependencies, and no integration toggles.
Required roles
- Legacy admin CRUD + admin menu:
[AUTH_ROLE_ADVISABLE, AUTH_ROLE_ADMIN, AUTH_ROLE_PRODUCTS] - REST API:
[AUTH_ROLE_ADMIN, AUTH_ROLE_PRODUCTS]. ADVISABLE is intentionally not listed —application/config/rest_policies.php:33-34documents that ADVISABLE is a superuser role that auto-bypasses every roles array viaRequestContext::isSuperuser()(src/Rest/Middleware/AuthorizationMiddleware.php:70,src/Rest/Middleware/RequestContext.php:47-49). Effective access is identical to the legacy controller's allow-list. Scanaccess: any logged-in admin role (noallowRolecall)
Client Extension Points
- Override admin controller: Create a same-named class in
application/modules/eshop/controllers/Shelfcodes_admin.php(the empty wrapper atapplication/modules/eshop/controllers/Shelfcodes_admin.php:1-13is where the override lives). - Override model: Extend
Shelfcodes_modelatapplication/modules/eshop/models/Shelfcodes_model.php. - Empty hook stubs in
Adv_shelfcodes_admin.php:110-123(afterAdd,afterEdit,afterDelete) are available for client overrides to attach side effects. - Override modern domain / REST: In client repos, register
Custom\Domains\Product\Shelfcode\...classes and alias the upstream services via$services->alias(...)incustom/.../container.php.
Business Rules
- Optional assignment --
shop_product.shelfcode_iddefaults to NULL; products do not require a shelf code. - Legacy numeric-only enforcement -- the shelf codes admin form validates
shelfcode_valuewithtrim|numeric, forcing numeric codes in the UI even though the column isVARCHAR(255). - Default code "0" on insert -- the column default is the literal string
'0', so inserts that omit thecodefield materialize as shelf "0" rather than NULL. - Both delete paths soft-dereference first -- legacy
delete_record()(ecommercen/eshop/models/Adv_shelfcodes_model.php:113-118) NULLs every referencingshop_product.shelfcode_idthen drops the row; modernWriteService::delete()(src/Domains/Product/Shelfcode/WriteService.php:47-54) does the same viaWriteRepository::nullProductReferences()(src/Domains/Product/Shelfcode/Repository/WriteRepository.php:18-23). The modern path additionally wraps both operations in a transaction; the legacy path does not. Both always succeed with no confirmation, no blocking, and no count shown. - Marketplace order snapshots -- importers store the shelf code label as a string on the order line, divorced from
shelfcodes_id, so historical orders are stable against later shelf code renames. - Backend-only on Product REST --
shelfcodeIdonProductResourceis only exposed when the request context is backend (Resource.php:79-80), hiding warehouse location data from the storefront. - No MUI, no feeds, no Solr -- shelf codes are not translated, not indexed, and not published externally.
Known Issues & Security Gaps
Modern— RESOLVED.WriteService::delete()skips soft-dereferencesrc/Domains/Product/Shelfcode/WriteService.php:47-54now callsWriteRepository::nullProductReferences()(src/Domains/Product/Shelfcode/Repository/WriteRepository.php:18-23) inside atransactional()block before deleting the shelf row, matching legacy behavior and adding the bonus of atomic execution (the legacy path issues the two statements outside a transaction).- No DB-level FK constraint --
shop_product.shelfcode_idhas noFOREIGN KEYdeclaration, so orphans cannot be prevented at the database layer. - Legacy
trim|numericvalidation vsVARCHAR(255)column -- the admin form forces numeric-only codes (Adv_shelfcodes_admin.php:107), but nothing at the DB or modern REST layer enforces this. Direct inserts or RESTPOSTs can store arbitrary strings. - No uniqueness constraint -- neither the DB index nor the modern
Validator.phpenforces unique codes, so duplicates are allowed at every layer. Scanaccesshas noallowRolecheck -- it extendsAdmin_cwith no explicit role enforcement, meaning any logged-in admin role can adjust product shelf codes, base prices, and stock via the barcode scanner workflow.- Default
codevalue is literal'0'-- brand-new rows created without specifyingcodebecome shelf "0", which can collide with legitimate codes. RBAC divergence between layers— RESOLVED (closed as Advisable-com/ecommercen#54 — not-a-bug). Earlier read ofrest_policies.phpflagged the missingAUTH_ROLE_ADVISABLEin the shelfcode roles array as a divergence. It is not. ADVISABLE is a project-wide superuser role that auto-bypasses every roles array in REST middleware (src/Rest/Middleware/AuthorizationMiddleware.php:70,src/Rest/Middleware/RequestContext.php:47-49); the convention atrest_policies.php:33-34is that ADVISABLE is never listed in roles arrays. Effective access on the REST endpoint matches the legacy controller's allow-list.Adv_orders_admin.php:1082-1085copy-paste bug -- stores'product_shelfcode' => $item->product_id, writing the product ID under the shelf code key. Affects any order data path that flows through this branch.Scanaccess::setup()snake_case method call mismatch --application/controllers/Scanaccess.php:46calls$this->shelfcodes_model->get_combo()(snake_case), but the actual model method isgetCombo()(camelCase) atecommercen/eshop/models/Adv_shelfcodes_model.php:55-71. No method namedget_combois declared on the model. The scanner workflow's shelf code dropdown is silently broken.- OA schema name mismatch on Product relation --
src/Rest/Product/Resources/Product/Resource.php:46referencesShelfcodeResourcein its OA annotation, but the actual Resource class atsrc/Rest/Product/Resources/Shelfcode/Resource.phpdeclares its OA schema asProductShelfcodeResource. The OpenAPI spec will contain a dangling$reffor the embedded shelfcode relation.
Edge Cases & Non-Integration
- No marketplace feeds -- shelf codes are never published in marketplace XMLs. No references in
src/Feeds/orecommercen/feeds/. See IN-01 Feed Generation. - No Solr indexing -- no references in
ecommercen/search/; shelves cannot be used as a storefront facet. - No multi-language support -- no
*_muitable exists, andListRequest.phpexplicitly documents this ("Shelfcode does not have MUI translations"). - No audit trail -- the legacy model does not write to any history / journal table; edits and deletes leave no record.
- No batch import -- there is no CSV / Excel / ERP import path dedicated to shelf codes; they can only be created one at a time via the legacy admin form, the REST
storeendpoint, or via the product admin when a new code is typed intoshelfcode_value.
Changelog Highlights
docs/changelog/Changelog.4.99.md:769-774-- Shelfcode Domain + REST layer introduced in 4.99.0.docs/changelog/Changelog.4.99.md:95-96--shelfcodeIdon Product REST resource restricted to backend context.docs/changelog/Changelog.4.99.md:322-323-- removal of unused$tableShelfCodesproperty (legacy cleanup).
Related Flows
- AD-02 Product Management Admin -- products are assigned a shelf code via the create/edit/clone/ERP-import forms, the
shelveslist filter, and thealter_shelfcodebatch action. - AD-03 Order Management Admin -- shelf codes surface on admin order detail, build summary, and logistics packing slip views during picking.
- IN-01 Feed Generation -- shelf codes are intentionally not included in marketplace feeds.