Skip to content

feat(demo): pack the production planner with sample data#206

Merged
jakub-przepiora merged 1 commit into
mainfrom
feat/demo-planner-density
Jul 24, 2026
Merged

feat(demo): pack the production planner with sample data#206
jakub-przepiora merged 1 commit into
mainfrom
feat/demo-planner-density

Conversation

@jakub-przepiora

@jakub-przepiora jakub-przepiora commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

What

The "Load sample data" demo previously left the production planner nearly empty on any given week: the seeder scattered ~46 work orders one-every-two-days across a 3-month horizon, so a single week had ~9 orders spread over 5 lines × 7 days × 3 shifts.

Change

PrintShopDemoSeeder now densely fills the current week (an order in most line × day × shift slots, anchored to now()->startOfWeek() so the visible week is always the busy one), with a lighter taper over neighbouring weeks and last week marked DONE. The current week stays active end-to-end (in-progress / accepted / pending) so every day renders blocks — including days already behind "today" (DONE orders aren't drawn as planner blocks).

Result (verified on a fresh local install)

  • Current week: ~74 active work orders, ~70/78 grid cells occupied (~90% full).
  • Planner opens as a realistic, almost-full weekly board across all 5 lines and 3 shifts (line loads ~33-52%).

Seeder-only; no schema or app-logic change. Full suite green (1922).

🤖 Generated with claude-flow

Summary by CodeRabbit

  • Changed
    • Updated sample data generation to create a denser, more realistic weekly planner.
    • Sample work orders now concentrate in the current week, taper into nearby weeks, and show the previous week as completed.
    • Added varied day and shift scheduling, including applicable night shifts, for a fuller planning view.

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The demo seeder now densely populates the current planner week with shift-based work orders, tapers generation in neighboring weeks, restricts night shifts by line and weekday, and derives timestamps and completion data from each order’s scheduled week.

Changes

Planner seeding

Layer / File(s) Summary
Current-week work-order generation
backend/database/seeders/PrintShopDemoSeeder.php, CHANGELOG.md
Additional work orders are generated by week, production line, day, and shift using tapered fill probabilities. Night shifts are limited to DTG/SITO on Monday–Thursday, while status, quantities, completion timestamps, and planned times are derived from the selected week and shift.
Estimated code review effort: 2 (Simple) ~10 minutes
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main demo seeding change: densely packing the production planner with sample data.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/demo-planner-density

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@backend/database/seeders/PrintShopDemoSeeder.php`:
- Around line 485-541: Make the generated work-order key in the seeding loop
deterministic from each slot’s coordinates (weekOffset, line, day, and shift)
instead of incrementing $n only after the random fill gate. Update the
updateOrCreate order_no construction accordingly, preserving the existing fill
randomness and status/data generation so unchanged slots refresh the same
records and skipped slots do not leave counter-based stale orders.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: e07b5edd-70e4-4c07-bae1-cd336d851f89

📥 Commits

Reviewing files that changed from the base of the PR and between 97a9474 and 9c1bfb7.

📒 Files selected for processing (2)
  • CHANGELOG.md
  • backend/database/seeders/PrintShopDemoSeeder.php

Comment on lines +485 to +541
$weekStart = now()->startOfWeek();
$n = 100; // WO-2026-0100+, clear of the hand-authored WO-2026-001..010

foreach ($weekFill as $weekOffset => $fill) {
foreach ($allLines as $line) {
for ($d = 0; $d < 7; $d++) {
foreach ($shifts as $shift) {
// Night is DTG/SITO, Mon–Thu only — keep the board honest.
if ($shift['night'] && (! in_array($line->code, $nightLines, true) || $d > 3)) {
continue;
}
if (mt_rand(1, 100) > (int) round($fill * 100)) {
continue;
}

$n++;
$start = $weekStart->copy()->addWeeks($weekOffset)->addDays($d)->setTime($shift['h'], 0);
$end = $start->copy()->addHours(mt_rand(2, 7));
$qty = mt_rand(10, 200);

// Last week's work is DONE (drops off the active board);
// the current week stays ACTIVE end-to-end so every day
// renders blocks (a packed board), even the days already
// behind "today" (long / carried-over jobs); future weeks
// are still-to-start. DONE orders aren't drawn as blocks,
// so keeping the visible week active is what fills the grid.
$status = match (true) {
$weekOffset < 0 => WorkOrder::STATUS_DONE,
$weekOffset > 0 => mt_rand(0, 1) ? WorkOrder::STATUS_PENDING : WorkOrder::STATUS_ACCEPTED,
default => [
WorkOrder::STATUS_IN_PROGRESS,
WorkOrder::STATUS_IN_PROGRESS,
WorkOrder::STATUS_ACCEPTED,
WorkOrder::STATUS_PENDING,
][mt_rand(0, 3)],
};

WorkOrder::updateOrCreate(
['order_no' => sprintf('WO-2026-%04d', $n)],
[
'line_id' => $line->id,
'product_type_id' => $allPt[array_rand($allPt)]->id,
'planned_qty' => $qty,
'produced_qty' => match ($status) {
WorkOrder::STATUS_DONE => $qty,
WorkOrder::STATUS_IN_PROGRESS => intdiv($qty, 2),
default => 0,
},
'status' => $status,
'priority' => mt_rand(1, 5),
'due_date' => $end->copy()->addDays(mt_rand(0, 2)),
'planned_start_at' => $start,
'planned_end_at' => $end,
'description' => $descriptions[array_rand($descriptions)],
'completed_at' => $status === WorkOrder::STATUS_DONE ? $end : null,
]
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Order numbering isn't idempotent across re-seeds — random skip decisions shift which slot each $n maps to.

$n only increments when the mt_rand(1,100) <= fill*100 gate passes (Line 496-498), and that gate is unseeded, so on a second run of this seeder the same $n/order_no can end up bound to a completely different (weekOffset, line, day, shift) slot than it was on the previous run. updateOrCreate will then overwrite that pre-existing order's line/product/quantities/status with data for an unrelated slot, and any slot count difference between runs leaves stale orders (higher $n) untouched from the prior run. Every other seeder method in this file keys updateOrCreate by a stable natural key (code, name, etc.) specifically to make re-seeding safe — this block breaks that invariant.

Derive the order_no key deterministically from the slot coordinates instead of a counter gated by randomness, so re-running the seeder is a true no-op/refresh for unchanged slots:

🛠️ Suggested deterministic keying
-        $weekStart = now()->startOfWeek();
-        $n = 100; // WO-2026-0100+, clear of the hand-authored WO-2026-001..010
+        $weekStart = now()->startOfWeek();
+        $weekOffsets = array_keys($weekFill);
+        $shiftCount = count($shifts);
+        $lineCount = count($allLines);
 
-        foreach ($weekFill as $weekOffset => $fill) {
-            foreach ($allLines as $line) {
-                for ($d = 0; $d < 7; $d++) {
-                    foreach ($shifts as $shift) {
+        foreach ($weekFill as $weekOffset => $fill) {
+            $weekIdx = array_search($weekOffset, $weekOffsets, true);
+            foreach ($allLines as $lineIdx => $line) {
+                for ($d = 0; $d < 7; $d++) {
+                    foreach ($shifts as $shiftIdx => $shift) {
                         if ($shift['night'] && (! in_array($line->code, $nightLines, true) || $d > 3)) {
                             continue;
                         }
                         if (mt_rand(1, 100) > (int) round($fill * 100)) {
                             continue;
                         }
 
-                        $n++;
+                        // Deterministic slot index — same (week, line, day, shift) always
+                        // maps to the same order_no, so re-seeding updates in place instead
+                        // of colliding with an unrelated slot's counter value.
+                        $n = 100 + ((($weekIdx * $lineCount + $lineIdx) * 7 + $d) * $shiftCount + $shiftIdx);
                         $start = $weekStart->copy()->addWeeks($weekOffset)->addDays($d)->setTime($shift['h'], 0);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
$weekStart = now()->startOfWeek();
$n = 100; // WO-2026-0100+, clear of the hand-authored WO-2026-001..010
foreach ($weekFill as $weekOffset => $fill) {
foreach ($allLines as $line) {
for ($d = 0; $d < 7; $d++) {
foreach ($shifts as $shift) {
// Night is DTG/SITO, Mon–Thu only — keep the board honest.
if ($shift['night'] && (! in_array($line->code, $nightLines, true) || $d > 3)) {
continue;
}
if (mt_rand(1, 100) > (int) round($fill * 100)) {
continue;
}
$n++;
$start = $weekStart->copy()->addWeeks($weekOffset)->addDays($d)->setTime($shift['h'], 0);
$end = $start->copy()->addHours(mt_rand(2, 7));
$qty = mt_rand(10, 200);
// Last week's work is DONE (drops off the active board);
// the current week stays ACTIVE end-to-end so every day
// renders blocks (a packed board), even the days already
// behind "today" (long / carried-over jobs); future weeks
// are still-to-start. DONE orders aren't drawn as blocks,
// so keeping the visible week active is what fills the grid.
$status = match (true) {
$weekOffset < 0 => WorkOrder::STATUS_DONE,
$weekOffset > 0 => mt_rand(0, 1) ? WorkOrder::STATUS_PENDING : WorkOrder::STATUS_ACCEPTED,
default => [
WorkOrder::STATUS_IN_PROGRESS,
WorkOrder::STATUS_IN_PROGRESS,
WorkOrder::STATUS_ACCEPTED,
WorkOrder::STATUS_PENDING,
][mt_rand(0, 3)],
};
WorkOrder::updateOrCreate(
['order_no' => sprintf('WO-2026-%04d', $n)],
[
'line_id' => $line->id,
'product_type_id' => $allPt[array_rand($allPt)]->id,
'planned_qty' => $qty,
'produced_qty' => match ($status) {
WorkOrder::STATUS_DONE => $qty,
WorkOrder::STATUS_IN_PROGRESS => intdiv($qty, 2),
default => 0,
},
'status' => $status,
'priority' => mt_rand(1, 5),
'due_date' => $end->copy()->addDays(mt_rand(0, 2)),
'planned_start_at' => $start,
'planned_end_at' => $end,
'description' => $descriptions[array_rand($descriptions)],
'completed_at' => $status === WorkOrder::STATUS_DONE ? $end : null,
]
);
$weekStart = now()->startOfWeek();
$weekOffsets = array_keys($weekFill);
$shiftCount = count($shifts);
$lineCount = count($allLines);
foreach ($weekFill as $weekOffset => $fill) {
$weekIdx = array_search($weekOffset, $weekOffsets, true);
foreach ($allLines as $lineIdx => $line) {
for ($d = 0; $d < 7; $d++) {
foreach ($shifts as $shiftIdx => $shift) {
// Night is DTG/SITO, Mon–Thu only — keep the board honest.
if ($shift['night'] && (! in_array($line->code, $nightLines, true) || $d > 3)) {
continue;
}
if (mt_rand(1, 100) > (int) round($fill * 100)) {
continue;
}
// Deterministic slot index — same (week, line, day, shift) always
// maps to the same order_no, so re-seeding updates in place instead
// of colliding with an unrelated slot's counter value.
$n = 100 + ((($weekIdx * $lineCount + $lineIdx) * 7 + $d) * $shiftCount + $shiftIdx);
$start = $weekStart->copy()->addWeeks($weekOffset)->addDays($d)->setTime($shift['h'], 0);
$end = $start->copy()->addHours(mt_rand(2, 7));
$qty = mt_rand(10, 200);
// Last week's work is DONE (drops off the active board);
// the current week stays ACTIVE end-to-end so every day
// renders blocks (a packed board), even the days already
// behind "today" (long / carried-over jobs); future weeks
// are still-to-start. DONE orders aren't drawn as blocks,
// so keeping the visible week active is what fills the grid.
$status = match (true) {
$weekOffset < 0 => WorkOrder::STATUS_DONE,
$weekOffset > 0 => mt_rand(0, 1) ? WorkOrder::STATUS_PENDING : WorkOrder::STATUS_ACCEPTED,
default => [
WorkOrder::STATUS_IN_PROGRESS,
WorkOrder::STATUS_IN_PROGRESS,
WorkOrder::STATUS_ACCEPTED,
WorkOrder::STATUS_PENDING,
][mt_rand(0, 3)],
};
WorkOrder::updateOrCreate(
['order_no' => sprintf('WO-2026-%04d', $n)],
[
'line_id' => $line->id,
'product_type_id' => $allPt[array_rand($allPt)]->id,
'planned_qty' => $qty,
'produced_qty' => match ($status) {
WorkOrder::STATUS_DONE => $qty,
WorkOrder::STATUS_IN_PROGRESS => intdiv($qty, 2),
default => 0,
},
'status' => $status,
'priority' => mt_rand(1, 5),
'due_date' => $end->copy()->addDays(mt_rand(0, 2)),
'planned_start_at' => $start,
'planned_end_at' => $end,
'description' => $descriptions[array_rand($descriptions)],
'completed_at' => $status === WorkOrder::STATUS_DONE ? $end : null,
]
);
🧰 Tools
🪛 ast-grep (0.44.1)

[error] 495-495: Avoid pseudo-random numbers
Context: mt_rand(1, 100)
Note: [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

(no-pseudo-random-php)


[error] 501-501: Avoid pseudo-random numbers
Context: mt_rand(2, 7)
Note: [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

(no-pseudo-random-php)


[error] 502-502: Avoid pseudo-random numbers
Context: mt_rand(10, 200)
Note: [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

(no-pseudo-random-php)


[error] 512-512: Avoid pseudo-random numbers
Context: mt_rand(0, 1)
Note: [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

(no-pseudo-random-php)


[error] 518-518: Avoid pseudo-random numbers
Context: mt_rand(0, 3)
Note: [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

(no-pseudo-random-php)


[error] 533-533: Avoid pseudo-random numbers
Context: mt_rand(1, 5)
Note: [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

(no-pseudo-random-php)


[error] 534-534: Avoid pseudo-random numbers
Context: mt_rand(0, 2)
Note: [CWE-338] Use of Cryptographically Weak Pseudo-Random Number Generator (PRNG).

(no-pseudo-random-php)

🪛 PHPStan (2.2.5)

[error] 522-522: Call to an undefined static method App\Models\WorkOrder::updateOrCreate().

(staticMethod.notFound)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@backend/database/seeders/PrintShopDemoSeeder.php` around lines 485 - 541,
Make the generated work-order key in the seeding loop deterministic from each
slot’s coordinates (weekOffset, line, day, and shift) instead of incrementing $n
only after the random fill gate. Update the updateOrCreate order_no construction
accordingly, preserving the existing fill randomness and status/data generation
so unchanged slots refresh the same records and skipped slots do not leave
counter-based stale orders.

@jakub-przepiora
jakub-przepiora merged commit b3bdd5d into main Jul 24, 2026
2 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