Skip to content

feat: add production targets and first-pass yield - #380

Merged
jakub-przepiora merged 3 commits into
developfrom
cez/f6a10c67
Oct 25, 2026
Merged

jakub-przepiora merged 3 commits into
developfrom
cez/f6a10c67

Conversation

@jakub-przepiora

Copy link
Copy Markdown
Contributor

What

The dashboard rebuild needs targets for OEE, scrap and first-pass yield (FPY), but none existed: the 85% OEE target was a hard-coded constant and FPY was not calculated anywhere.

  • Admin → Settings → System → Production has a new "Production targets" card with "Max scrap rate (%)" (default 2.0) and "Min first-pass yield (%)" (default 95.0). Values are 0–100 with at most one decimal. A wrong value (150, -1, text) shows a readable error in English and Polish. Only Admins can save.
  • Next to the FPY field the card shows "Current shift: XX.X%", or "No quality checks this shift" when there is nothing to measure.
  • A note explains that the OEE target is not a separate setting: it comes from each line's "Planning efficiency (%)". It links to the line settings, and the link is shown only to users who can open the Production tab.
  • Backend only, for the upcoming dashboard sections:
    • ProductionTargets service: the OEE target per line, and a plant-wide target weighted by planned working time.
    • FirstPassYieldService: FPY for a time window, optionally for one line.

Why

The scrap and FPY targets have to be configurable per plant. The OEE target should follow how the planner actually schedules, so it reuses the line's planning efficiency instead of adding a second number to maintain.

OEE target.

  • A line's target is its planning_efficiency_pct when that is below 100. The default of 100 means "not set" and falls back to OeeBand::GREEN_AT_LEAST (85).
  • The plant target is the line targets averaged and weighted by each line's planned working minutes in the shift.
  • Overlapping operations are merged and clipped to the shift. What-if drafts, infeasible operations and inactive lines are ignored.
  • Lines with no plan are skipped. If no line has a plan, the target is 85.
  • The OeeBand thresholds and OeeGauge are unchanged.

FPY definition. I checked the data model first (QualityCheck.all_passed, several periodic checks per batch, re-checks after rework). I chose:

  • Unit: the batch.
  • Denominator: batches whose first-ever quality check falls inside the window.
  • Numerator: those whose first check had every parameter in tolerance.
  • Why the first check:
    • It matches "passed on the first attempt", so a batch that failed and then passed on a re-check does not count as first-pass.
    • A batch is counted once, in the shift it was first inspected, so consecutive shifts never double-count it.
    • Routine periodic checks of a batch that already passed do not inflate the denominator.
  • A later periodic failure of an already-passed batch is a scrap/quality signal and does not lower FPY.
  • Inbound (supplier) inspections and serial-unit test attempts are separate flows and are excluded.
  • The result is rounded to one decimal.
  • No checks in the window returns null, not 0% or 100%. The quality module being disabled also returns null.
  • The window is half-open, [from, to), so shifts that cross midnight work.

How it was verified

Laravel tests added:

  • Feature/Dashboard/ProductionTargetsTest:
    • a line at 80% gives 80, and a line at the default 100 gives 85;
    • the plant target is weighted by planned minutes;
    • no plan gives 85;
    • overlapping operations are merged;
    • what-if, infeasible and inactive-line exclusions.
  • Feature/Quality/FirstPassYieldServiceTest:
    • all first attempts pass gives 100%;
    • one batch fixed on the second try lowers the result accordingly;
    • no checks gives null;
    • quality module off gives null;
    • line filter;
    • a night-shift window across midnight;
    • a batch first checked in an earlier shift is not recounted.
  • Feature/Web/ProductionTargetsSettingsTest:
    • saving valid values (including a decimal comma) persists them;
    • 422 for 150, -1 and text, with the EN and PL messages;
    • operator and supervisor get 403, and a guest is redirected to login;
    • saving the targets leaves other system settings untouched;
    • the page shows the defaults and the current-shift FPY.

Click-through demo recorded by an independent QA agent on a fresh instance. The demo video will be added in a follow-up comment.

Review notes

  • No migrations and no new dependencies. Targets are stored in the existing system_settings table, and missing or out-of-range stored values fall back to the defaults.
  • The targets card is a separate form from the main system settings form and posts to its own route, POST /settings/production-targets (Admin only).
  • Added 15 i18n keys to both en.json and pl.json, and a CHANGELOG entry.
  • Not touched, as required: OeeBand, OeeGauge, ShiftMonitorService, OeeCalculationService, the mobile admin dashboard API and the shared synced collections.
  • The Playwright demo spec is tests/e2e/demo/cez-f6a10c67.spec.ts, next to the existing demo specs.

Open items from code review (none are blockers; none fixed):

  • ProductionTargets.php:86: scheduled operations of finished or cancelled orders still count toward a line's planned minutes, because whereHas('workOrder') only excludes deleted orders. Restrict it to WorkOrder::ACTIVE_STATUSES, as the order-level query below it does.
  • FirstPassYieldService.php:71-78: finding each batch's first check loads all checks of the batches seen in the window. This is fine at current volumes. A SQL MIN(checked_at) per batch would keep the work proportional to the number of batches.
  • SettingsController.php:219: productionTargetsPayload() runs the FPY and planned-minutes queries on every Settings → System load, whichever tab is open. Load it lazily (Inertia::optional) for the Production tab only.

🤖 Generated with Claude Code

jakub-przepiora and others added 2 commits October 24, 2026 15:53
…scrap/FPY settings, first-pass yield

- ProductionTargets service: per-line OEE target = planning_efficiency_pct
  (100 = unset -> OeeBand::GREEN_AT_LEAST), plant target weighted by each
  line's planned minutes in the shift (committed scheduled operations, or the
  order's planned window when it has none; intervals clipped and merged).
- FirstPassYieldService: batches whose first-ever quality check passed /
  batches first checked in the window; null without checks or with the
  quality module off.
- Settings -> System -> Production: "Production targets" card (admin only,
  own route + Form Request) for max scrap rate and min first-pass yield,
  showing the current shift's FPY and plant OEE target.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are limited based on label configuration.

🏷️ Required labels (at least one): 1
  • cla-signed

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

Run configuration

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Resolve conflicts with develop's plant overview header (#379) and the
translation update (#378): keep both CHANGELOG entries; lang files merged
per key with the i18n-lang-files resolver (en/pl parity 7136/7136).
@jakub-przepiora
jakub-przepiora marked this pull request as ready for review October 25, 2026 12:37
@jakub-przepiora
jakub-przepiora merged commit 64c5af7 into develop Oct 25, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant