Skip to content

MelisCommerce

Full e-commerce framework for MelisPlatform — catalog, customers, cart/checkout/orders, coupons, shipping and SEO — with a React back-office. Package melisplatform/melis-commerce.

Purpose

MelisCommerce adds a complete commerce layer to a Melis site: a catalog (products, variants, attributes, categories, prices, stock), a B2B customer model (accounts + contacts), the cart/checkout/order pipeline, coupons, currencies, shipping, returns, documents and SEO. It ships a back-office management suite and a front-office shop built from droppable templating plugins. The data layer uses 59 melis_ecom_* tables, 26 services, 10 rich entities and 33 listeners; data access uses Eloquent (bundled illuminate/database), wrapped by Melis-style event-driven services.

In Melis v6 the business logic is unchanged; the display layer is a React back-office (/melis-react). MelisCommerce ships one multi-brick bundle exposing 13 native React tools, each backed by JSON endpoints under /melis/react-api/…. Every tool keeps a per-tool New / Old toggle so you can fall back to the classic legacy screen in an iframe.

Enable it

Add to config/melis.module.load.php:

php
return [
    'MelisCommerce',
];

Requires melisplatform/melis-core. Database tables are provisioned automatically by MelisDbDeploy (deltas under install/dbdeploy/); the module is offered by the Melis installer as an optional component. The React tools appear in the back-office only when MelisCommerce is active (discovery via GET /melis/react-api/react-modules).

The React back-office — one bundle, thirteen tools

The bundle (public/ui-react/brick.js + brick.manifest.json) declares 13 tools that self-register in brick.tsx. All are native full-React; each renders a shared ViewModeToggle + LegacyFrame, so Old mounts the classic tool in an iframe (/melis/react-tool-page?key=<melisKey>). The seven "entity" tools set subTabs: true (opening a record adds a sub-tab); all 13 are persistent.

The MelisCommerce section in the React sidebar — the 13 commerce tools

Brick idRouteLabelmelisKey
commerce-accounts/melis-commerce/clients-listAccountsmeliscommerce_clients_list_page
commerce-contacts/melis-commerce/contact-listContactsmeliscommerce_contact_list_page
commerce-catalog/melis-commerce/categoriesCatalogsmeliscommerce_categories_page
commerce-products/melis-commerce/product-listProductsmeliscommerce_product_list_container
commerce-orders/melis-commerce/order-listOrdersmeliscommerce_order_list_page
commerce-coupons/melis-commerce/coupon-listCouponsmeliscommerce_coupon_list_page
commerce-attributes/melis-commerce/attribute-listAttributesmeliscommerce_attribute_list_page
commerce-countries/melis-commerce/country-listCountriesmeliscommerce_country_list_container
commerce-languages/melis-commerce/language-listCommerce languagesmeliscommerce_language_list_container
commerce-currencies/melis-commerce/currency-listsCurrenciesmeliscommerce_currency_conf
commerce-order-status/melis-commerce/order-status-listsOrder statusmeliscommerce_order_status_tool_page
commerce-clients-groups/melis-commerce/clients-group-listClient's groupsmeliscommerce_clients_group_tool_container
commerce-settings/melis-commerce/settingsCommerce settingsmeliscommerce_settings_page

Rule of thumb: build the catalog (attributes → catalogs → products/variants), manage customers (accounts + contacts), then run the order pipeline (orders + checkout wizard + coupons), all backed by the commerce reference lists.

Object model

ConceptWhat it is
ProductA container for variants — not itself a sellable unit.
VariantThe sellable unit: has its own SKU, stock and price. A product with no real options still has one main variant.
AttributeA filterable/variant-defining property (e.g. Color, Size) with typed, translatable values. Products declare which attributes they use; each variant picks one value per attribute.
PriceResolved for a (countryId, groupId) pair with VAT; falls back gracefully (see Pricing resolution).
AccountA B2B organisation (melis_ecom_client); has a company record, group and addresses.
Contact / PersonAn individual (melis_ecom_client_person) who logs in; can belong to multiple accounts.
BasketAnonymous (keyed by clientKey) or persistent (tied to an account); merges on login.
OrderCreated at status -1 (temporary) during checkout; moves to 1 (New order) after payment.

Accounts & contacts

Accounts (/melis-commerce/clients-list) manages B2B customers: the list has search, status/group filters, a column manager, export and CSV import. Opening an account is a sub-tab with Properties, Company, Contacts (link/unlink, set default), Addresses, Orders (history) and Files tabs.

The Accounts list — filters, column manager and CSV import

Account editor — Properties tab (status, name strategy, group, country, tags)

Account editor — Contacts tab (link / unlink / default)

Contacts (/melis-commerce/contact-list) manages individual people. The editor has Information, Address and Association tabs (link/unlink a contact to accounts, set the default). Contacts are backed by MelisComContactService; accounts by MelisComClientService.

Contact editor — Association tab (link / unlink accounts)

Catalogs, products & variants

Catalogs (/melis-commerce/categories) is a drag-to-reorder tree of categories; a category has Properties, SEO and Products (reorderable) tabs.

The Catalog category tree (React)

Products (/melis-commerce/product-list) lists products with filters, duplicate and export. The product editor has Properties, Text (per-language), Variants, SEO and Prices tabs. The Variants tab is the richest: each variant has its own Properties, SEO, Prices, Stocks and Associations, plus media.

The Products list (React)

Product editor — Variants tab (per-variant prices, stocks, SEO, associations)

Product editor — Prices tab

Attributes (/melis-commerce/attribute-list) manages typed product characteristics: editor tabs Properties (reference, type, status, visible, searchable), Labels (per-language) and Values (values with typed translations).

Attribute editor — Values tab (values with typed translations)

Orders & the checkout wizard

Orders (/melis-commerce/order-list) has status filters and export. The per-order editor tabs are Properties, Basket (read-only), Addresses, Payment (read-only), Shipping, Messages and Returns — plus Invoices when MelisCommerceOrderInvoice is active.

The Orders list (React)

Order editor — Properties tab

New Order opens a guided 7-step checkout wizard (contact → account → products → addresses → summary → payment → confirmation), backed by a server-side checkout session (endpoints under /orders/checkout/*) that resumes where you left off.

The New Order checkout wizard — contact / account / products steps

The checkout wizard — addresses / summary step

Coupons

Coupons (/melis-commerce/coupon-list) manages discount codes (% or amount). Editor tabs: Properties, Assign account (clients), Assign product, and Orders (usage history). Backed by MelisComCouponService; the built-in discount is itself a listener on meliscommerce_service_get_item_price_end.

Coupon editor — Assign account tab

Commerce reference tools

Small "settings-style" tools — single-page lists with add/edit modals or a single form:

ToolRouteManages
Countries/melis-commerce/country-listCommerce country list (add/edit)
Commerce languages/melis-commerce/language-listCommerce languages (list + modal)
Currencies/melis-commerce/currency-listsCurrencies (list + modal, set default)
Order status/melis-commerce/order-status-listsStatuses; editor has Properties (colour) + Labels
Client's groups/melis-commerce/clients-group-listCustomer groups (list + modal)
Commerce settings/melis-commerce/settingsSingle config page — Properties + Accounts tabs

The Order status list (React)

Commerce settings — Properties tab (stock-alert threshold, account-name strategy)

Commerce settings hold the global stock-alert threshold and the account-name strategy (sa_type).

Key services

All services extend MelisComGeneralService and are registered in config/module.config.php. Every public method is wrapped in meliscommerce_service_*_start / *_end events (see Events & listeners). React controllers only validate input and shape JSON — the real work stays in these services.

Service aliasRole
MelisComProductServicegetProductById, getProductListMelisProduct
MelisComVariantServicegetVariantById, getVariantListByProductId, getVariantBySKU, getMainVariantByProductIdMelisVariant
MelisComCategoryServicegetCategoryById, getCategoryListById(Recursive)MelisCategory
MelisComAttributeServicegetAttributeById, getAttributesMelisAttribute
MelisComPriceServicegetItemPrice($itemId, $countryId, $groupId, $type) — price with fallback hierarchy
MelisComProductSearchServiceFront-office product search
MelisComSeoServiceCommerce SEO (URLs / meta for products and categories)
MelisComClientServicegetClientById, getClientList, getClientByIdAndClientPersonMelisClient
MelisComContactServiceContact (person) management
MelisComClientGroupsServiceClient groups (used for group-specific pricing)
MelisComAuthenticationServiceFront-office login: login, getClientId, getPersonId, getClientGroup, setClientId, logout, hasIdentity
MelisComBasketServicegetBasket, getPersistentBasket, getAnonymousBasket, addVariantToBasket, transferAnonymousBasketToPersistentBasketMelisBasket
MelisComOrderServicegetOrderById, getOrderListMelisOrder
MelisComOrderCheckoutServiceTwo-phase checkout: checkoutStep1_prePayment, checkoutStep2_postPayment
MelisComPostPaymentServicePost-payment transaction recording
MelisComOrderProductReturnServiceProduct returns / RMA
MelisComCouponServicegetCouponById, getCouponListMelisCoupon
MelisComCurrencyServiceCurrencies
MelisComShipmentCostServiceShipping cost computation
MelisComStockEmailAlertServiceLow-stock email alerts (VARIANTSLOWSTOCK)
MelisComDocumentServicegetDocumentById, getDocumentsByRelationMelisDocument
MelisComDuplicationServiceDuplicate products / variants
MelisComLinksServiceFront-office commerce link builder
MelisComCacheServiceCommerce cache (commerce_big_services)
MelisComHeadSEO head helper (updateTitleAndDescription)
MelisComGeneralServiceBase class; helpers: getTableColumns, getEcomLang, getFrontPluginLangId

Pricing & stock resolution

MelisComPriceService::getItemPrice($itemId, $countryId, $groupId, $type) walks a fallback chain:

  1. specific country + specific group
  2. specific country + general group
  3. general country (price_country_id = 0) + specific group
  4. general country + general group
  5. (for a variant) fall back to the product price

Stock is per variant per country (melis_ecom_variant_stock). When an order drops stock below the threshold, the VARIANTSLOWSTOCK email is sent to the configured recipients.

Checkout pipeline

Two phases in MelisComOrderCheckoutService (the React wizard drives the same server logic):

Phase 1 — checkoutStep1_prePayment($clientId) validates basket and addresses, computes all costs and shipping, generates the order reference, then calls MelisComOrderService::saveOrder(). The order is saved with ord_status = -1 (temporary). Fires meliscommerce_service_checkout_step1_prepayment_start/_end and …_save_success (carries the new orderId).

Phase 2 — checkoutStep2_postPayment() is called after the gateway returns. MelisComPostPaymentService::processPostPayment() records the transaction in melis_ecom_order_payment and moves the order off status -1 to a real status. Fires meliscommerce_service_checkout_step2_postpayment_start/_end.

Order statuses: -1 temporary · 1 New order · 2 On hold · 3 Shipped · 4 Delivered · 5 Cancelled · 6 Payment error.

Events & listeners

Every service method emits meliscommerce_service_*_start and *_end events. The named arguments (built by makeArrayFromParameters via reflection) and the results key travel with the event.

  • _start listener: mutate inputs before the work runs.
  • _end listener: mutate $params['results'] before the caller sees it.

This is the primary extension mechanism — no subclassing needed. The 33 bundled listeners fall into four families:

FamilyExamples
Save / validate…SaveProductListener, …SaveOrderListener, …SaveClientListener, …ValidateVariantListener
Cascade cleanup (country/language removed)…ProductPriceCountryDeletedListener, …CategoryCountryLink…, …SEOLanguageDeletedListener
Checkout / pricing / stock…CheckoutCouponListener, …CouponProductPriceListener, …ShipmentCostListener, …PostPaymentListener, …VariantCheckLowStockListener
Front-office SEO routing…SEOReformatToRoutePageUrlListener, …SEODispatchRouterCommerceUrlListener, …SEOMetaPageListener

React API & capabilities

All routes are child routes of melis-react-api (merged from config/react-api.php): 13 invokable controllers in src/Controller/ReactApi/, 184 routes under /melis/react-api/…. The response contract everywhere is { success, data, error? }; every action calls denyUnlessAccess() first (auth + MelisCoreRights::canAccess(<melisKey>), 401/403). Each entity tool exposes roughly GET /<tool> (keyset list), /<tool>/stats, /<tool>/options, POST /<tool>/save, DELETE /<tool>/delete/:id, GET /<tool>/:id, plus tool-specific sub-resources (e.g. Orders adds the full /orders/checkout/* wizard).

⚠ Commerce languages are namespaced /commerce-languages (not /languages) because the core Languages tool already owns /languages under the shared melis-react-api parent.

config/react.capabilities.php declares one melisReactToolCapabilities entry per tool, keyed under its melisKey. These are declarative default-allow UI hints — they drive the Users → Rights checkbox tree and let React mask tabs/buttons via useCaps(...) — but there is no server-side denyUnlessCan() yet; only denyUnlessAccess() on the tool's melisKey gates the API. Treat capabilities as UI hints, not security.

Front office

Templating plugins are registered under config/plugins/{products,categories,clients,orders}/ and as controller_plugins in module.config.php. Drop them into a CMS page drag-drop zone.

AreaPlugins
CatalogProductShowPlugin, ProductListPlugin, ProductSearchPlugin, CategoryTreePlugin, CategoryProductListPlugin, RelatedProductsPlugin, AttributesShowPlugin, ProductAttributePlugin, ProductPriceRangePlugin
Cart & checkoutAddToCartPlugin, CartPlugin, CheckoutPlugin, CheckoutCartPlugin, CheckoutAddressesPlugin, CheckoutCouponPlugin, CheckoutSummaryPlugin, CheckoutConfirmSummaryPlugin, CheckoutConfirmPlugin
AccountLoginPlugin, RegisterPlugin, AccountPlugin, ProfilePlugin, BillingAddressPlugin, DeliveryAddressPlugin, LostPasswordGetEmailPlugin, LostPasswordResetPlugin
Orders (customer)OrderPlugin, OrderHistoryPlugin, OrderMessagesPlugin, OrderShippingDetailsPlugin, OrderReturnProductPlugin, OrderAddressPlugin

Database tables

59 tables with the melis_ecom_* prefix, grouped by subsystem:

GroupKey tables
Products & variantsmelis_ecom_product, melis_ecom_product_text + _text_type, melis_ecom_product_attribute, melis_ecom_product_category, melis_ecom_variant, melis_ecom_variant_attribute_value, melis_ecom_variant_stock, melis_ecom_assoc_variant + _type
Attributesmelis_ecom_attribute + _trans, melis_ecom_attribute_type, melis_ecom_attribute_value + _value_trans
Categories & geomelis_ecom_category + _trans, melis_ecom_country_category, melis_ecom_country, melis_ecom_lang
Pricing & currencymelis_ecom_price, melis_ecom_currency
Clients (B2B)melis_ecom_client, melis_ecom_client_person + _person_emails, melis_ecom_client_company, melis_ecom_client_account_rel, melis_ecom_client_person_rel, melis_ecom_client_address + _address_type, melis_ecom_client_groups, melis_ecom_civility, melis_ecom_settings_account
Baskets & ordersmelis_ecom_basket_anonymous, melis_ecom_basket_persistent, melis_ecom_order, melis_ecom_order_basket, melis_ecom_order_address, melis_ecom_order_payment + _type, melis_ecom_order_shipping, melis_ecom_order_message, melis_ecom_order_status + _trans, melis_ecom_order_product_return + _details
Couponsmelis_ecom_coupon + _coupon_client / _coupon_order / _coupon_product
Documents & SEOmelis_ecom_document + _doc_type + _doc_relations, melis_ecom_seo, melis_ecom_stock_email_alert

Examples

Read a product, its main variant and a resolved price

php
$prdSrv   = $sm->get('MelisComProductService');
$varSrv   = $sm->get('MelisComVariantService');
$priceSrv = $sm->get('MelisComPriceService');

$product = $prdSrv->getProductById($prdId, $langId, $countryId);  // → MelisProduct
$variant = $varSrv->getMainVariantByProductId($prdId, $langId, $countryId);  // → MelisVariant
$price   = $priceSrv->getItemPrice($variant->getId(), $countryId, $groupId, 'variant');
// $price['price'] (net), $price['price_currency'], $price['price_details']

Add to basket and run the two-phase checkout

php
$basketSrv   = $sm->get('MelisComBasketService');
$checkoutSrv = $sm->get('MelisComOrderCheckoutService');

$basketSrv->addVariantToBasket($variantId, 1, $clientId);
$basketSrv->transferAnonymousBasketToPersistentBasket($clientKey, $clientId);

$step1 = $checkoutSrv->checkoutStep1_prePayment($clientId);  // order at status -1
// $step1['orderId'] — hand to payment gateway
$step2 = $checkoutSrv->checkoutStep2_postPayment();          // records payment, moves off -1

Hook the shop without subclassing

php
// _start → mutate inputs; _end → mutate $params['results'].
$this->attachEventListener(
    $events, '*', 'meliscommerce_service_get_item_price_end',
    function ($e) {
        $params = $e->getParams();
        $price  = $params['results'];
        // modify $price, then:
        $params['results'] = $price;
        return $params;
    }
);

Key files

ConcernPath
Module config (services, controllers, plugins)vendor/melisplatform/melis-commerce/config/module.config.php
React API routes / capabilitiesvendor/melisplatform/melis-commerce/config/react-api.php, config/react.capabilities.php
React brick (source / build)vendor/melisplatform/melis-commerce/ui-react/src/, public/ui-react/{brick.js, brick.manifest.json}
React API controllers (13)vendor/melisplatform/melis-commerce/src/Controller/ReactApi/
Front-office plugin configvendor/melisplatform/melis-commerce/config/plugins/
Services / Entities (10) / Table gateways (59)vendor/melisplatform/melis-commerce/src/Service/, src/Entity/, src/Model/Tables/
Listeners (33)vendor/melisplatform/melis-commerce/src/Listener/
DB deltasvendor/melisplatform/melis-commerce/install/dbdeploy/

See also: Module reference · melis-react-api · melis-commerce-order-invoice · melis-core · melis-cms.