Table of Contents
Description
The MES Module provides the Manufacturing Execution System foundation for Laraplate. It is designed to host production workflows, work orders, shop-floor events, traceability, and manufacturing KPIs.
It depends on the ERP module (items, warehouses, stock, companies, sales orders); the dependency runs one way, ERP knows nothing of MES.
Installation
If you want to add this module to your project, you can use the joshbrw/laravel-module-installer package.
Add repository to your composer.json file:
"repositories": [
{
"type": "composer",
"url": "https://github.com/getlaraplate/laracore.git"
},
{
"type": "composer",
"url": "https://github.com/getlaraplate/larames.git"
}
]
composer require joshbrw/laravel-module-installer getlaraplate/laracore getlaraplate/larames
Then, you can install the module by running the following command:
php artisan module:install Core
php artisan module:install MES
Configuration
The module configuration is automatically mapped as mes.* when the module is active.
Configuration file: Modules/MES/config/config.php.
# MES activation toggle (example)
MES_ENABLED=true
# Queue used by MES jobs and listeners (backflush, production-order auto-creation)
MES_QUEUE_CONNECTION=database
MES_QUEUE_NAME=mes
# Machine connectivity (see docs/MACHINE_CONNECTIVITY.md)
MES_MACHINE_QUEUE=mes-machine
MES_MACHINE_MAX_SAMPLES=5000
MES_MACHINE_MAX_BODY_KB=1024
MES_MACHINE_CLOCK_SKEW_SECONDS=30
MES_MACHINE_INBOX_RETENTION_DAYS=7
MES_MACHINE_RATE_LIMIT_PER_MINUTE=600
MES_MACHINE_CONFIG_RATE_LIMIT_PER_MINUTE=60
MES_MACHINE_RAW_RETENTION_DAYS=30
MES_MACHINE_MINUTE_AGGREGATE_RETENTION_DAYS=90
MES_MACHINE_PROCESS_STORE=database
# MQTT broker of the bridge (`php artisan mes:machine-bridge`), one per installation
MES_MACHINE_MQTT_HOST=127.0.0.1
MES_MACHINE_MQTT_PORT=1883
MES_MACHINE_MQTT_USERNAME=
MES_MACHINE_MQTT_PASSWORD=
MES_MACHINE_MQTT_TLS=false
MES_MACHINE_MQTT_CLIENT_ID=laraplate-mes-bridge
MES_MACHINE_MQTT_TOPIC_PREFIX=laraplate
The machine variables set the config keys mes.machine.queue, mes.machine.max_samples,
mes.machine.max_body_kb, mes.machine.clock_skew_seconds, mes.machine.inbox_retention_days,
mes.machine.rate_limit_per_minute, mes.machine.config_rate_limit_per_minute (configuration polls and apply reports of the edge agent, per source and minute), mes.machine.raw_retention_days, mes.machine.minute_aggregate_retention_days and mes.machine.process_store; the MQTT ones set mes.machine.mqtt.host, mes.machine.mqtt.port, mes.machine.mqtt.username, mes.machine.mqtt.password, mes.machine.mqtt.tls, mes.machine.mqtt.client_id and mes.machine.mqtt.topic_prefix. The machine queue (mes-machine) needs its own Horizon supervisor;
incident notifications go to the roles under mes.notifications.machine_incident.
The receiving warehouse for auto-created production orders is resolved per company.
Set it explicitly through the mes.production.default_warehouse config map
([company_id => warehouse_id]) when a company owns more than one warehouse; with a
single warehouse the module picks it automatically.
Production planning tunables are Settings (module MES, group mes), edited like any other Setting
and applied without a restart. The seeder (MESDatabaseSeeder) creates them with the defaults below
and a re-seed keeps any value an operator changed. Both are floored where they are read, so a stored
0 behaves as 1.
| Setting | Default | Purpose |
|---|---|---|
production.daily_minutes |
480 |
Working minutes per day used to turn the routing's standard minutes into a planned lead time. |
production.default_lead_time_days |
5 |
Planned lead time in working days when the item has no routing to estimate from. |
The effective set of environment variables will be expanded as domain features are introduced.
Current Status
The manufacturing domain is implemented and covered by the module test suite:
-
Work centers with a weekly calendar, BOMs and routings with validity windows and DIFF versioning (lines and operations included)
-
Production orders numbered by ERP
DocumentNumberAllocator, with immutable BOM/routing snapshots, release/complete/cancel. Starting the first operation moves a released order toin_progress; cancelling an order that has not completed ends its running and pending operations as skipped; an order cannot complete while an operation is in progress. Every transition emits a typed event (ProductionOrderReleased/Started/Completed/Cancelled,OperationStarted/Completed/Skipped,DowntimeOpened/Closed) -
Automatic draft orders from confirmed ERP sales orders (
SalesOrderConfirmedlistener) -
Operation execution (start/complete/skip) with efficiency, operator logs and non-blocking shift warnings
-
Backflush and manual material consumption through the
StockMovementRecordercontract, with partial consumption and a shortage notification when stock is short -
Completion receives the finished goods into stock, valued at the material cost consumed per unit produced (labour and machine time carry no cost rate yet)
-
Lot and serial generation with forward/backward lot genealogy
-
Quality plans, automatic quality checks, non-conformances with dispositions (rework spawns a linked order)
-
Downtime, OEE (A x P x Q, clamped to [0, 1]) and work-center capacity load, schedule and overload check (available minutes from the work center calendar, net of every downtime, planned maintenance included; operations get planned dates on release, forward and infinite-capacity, and
estimateCompletionDate()plans what is left from now). A downtime is opened through theopen_downtimeaction on its work center (one open downtime per work center). OEE and capacity are materialised per work center and day bymes:kpis:materialize(hourly, queued) into the cache and shown as an OEE column in the work-center list; starting an operation on a work center materialised as overloaded emitsCapacityOverloadDetected, notified throughmes.notifications.capacity_overload -
No custom routes: entities go through Core's generic CRUD, domain verbs through the domain-action registry (
MesDomainActionRegistrar,MesModelPolicy, permissions seeded byMESDatabaseSeeder) -
Filament backoffice (
Modules\MES\Filament\MESPlugin): resources for work centers (with calendar), BOMs (with lines), routings, production orders (read-only operations, consumptions, quality checks and lots, and Release/Complete/Cancel header actions), quality plans, quality checks, non-conformances, downtimes and shifts, plus a production dashboard widget with four cached counts -
Machine connectivity foundation: machines and probes push
laraplate-machine/1messages over HTTP (POST api/v1/insert/mes/machine-messages; each source authenticates as its own service account, holding theapirolemes-machine-agent) into a durable inbox processed by an idempotent, per-source serialised job (normalise, resolve, attribute, typed events); unmapped signals, incidents, a watchdog for silent devices, reprocessing, importable machine profiles, and a "Machine connectivity" backoffice; a long-running MQTT bridge (php artisan mes:machine-bridge, QoS 1, persistent session, topics from the sources,bridge_downincident) and asparkplug_bnormaliser with a persisted alias map; a state history per device with downtimes derived from it (micro-stop threshold, alarm codes,mes:machine-open-stops, syntheticOffline), manual downtimes refused on connected work centers, and ISO 22400 OEE availability with an incomplete-data flag; stored piece counts (rollover and reset aware) feeding operation machine quantities, a once-onlyOperationTargetReachednotification, declared quantities with an audit trail, assignment of unattributed counts by time range, and ISO 22400 performance and quality; probe measurements that fill the quality check of their operation (required samples, immediateOutOfToleranceMeasured, waiting measurements attached when the check is created or assigned by hand); process values stored behind aProcessValueStorecontract (relational driver, minute and hour aggregates rebuilt bymes:machine-rollup, retention pruning) with permanent per-operation summaries and lot traceability. Guide:docs/MACHINE_CONNECTIVITY.md. The machine routes are/apiroutes: they answer403until Core'sexpose_apisetting is on, they authenticate the bearer token of the source's service account through Core (the token is issued on the user page, optionally narrowed byallowed_cidrs), and they are limited per service account (mes.machine.rate_limit_per_minutefor ingest,mes.machine.config_rate_limit_per_minutefor the two configuration routes) on top of Core's per-tokenapilimit. Edge agent backend: an optionallosses[]in the envelope recordsdata_droppedincidents; each source receives a versionedlaraplate-edge-config/1document (GET api/v1/detail/mes/machine-agent-configs,304on an unchanged version; versions only grow and activating an older one publishes it again as a new version; structural validation only) and reports the outcome of applying it (POST api/v1/insert/mes/machine-agent-config-reports; arejectedorrolled_backreport opens aconfig_rejectedincident); both are managed in two sections of the source page in the backoffice
Developer reference: docs/rag/MODULE.md. Operator guide (Italian): docs/MES_GUIDA_SEMPLICE.md.
Roadmap
Machine data acquisition (design: docs/superpowers/specs/2026-10-05-mes-machine-data-acquisition-design.md at the stack root). All six steps are built, each with its own plan: the foundation, the MQTT bridge, the machine states, the piece counts, the probe measurements and the process values. The backend side of the laraplate-edge agent (service accounts, configuration delivery, loss reports) is built too: docs/superpowers/plans/2026-10-15-mes-edge-agent-backend.md.
Scripts
The module has no Composer scripts of its own. Run tests and checks from the laraplate root:
# MES test suite
php artisan test --compact Modules/MES/tests
# Static analysis of the module with the root configuration
vendor/bin/phpstan analyse Modules/MES/app
# Formatting: always pass an explicit file list
vendor/bin/pint --format agent Modules/MES/app/Services/CapacityService.php
Contributing
If you want to contribute to this project, follow these steps:
- Fork the repository.
- Create a new branch for your feature or correction.
- Send a pull request.
License
MES Module is open-sourced software licensed under the GNU AGPL v3.
Copyright (c) 2025 Marco P. The full licence text is in LICENSE.