Files
GSB-Construction/labor-module.md
Christopher Boyles 64d4b331a8
Some checks failed
Tests / PHP 8.3 (push) Has been cancelled
Tests / PHP 8.4 (push) Has been cancelled
Tests / PHP 8.5 (push) Has been cancelled
Additional Changes
2026-06-02 22:04:03 +08:00

6.9 KiB

Plan: Labors Module with Skillset Grouping

This plan implements a standalone Labors module in the GSB Construction ERP application. It replaces/supplements the basic labor_rates lookup catalog with a robust labors table supporting Skilled vs Unskilled categories and many-to-many relationships with a predefined dictionary of skills. These labor records are then integrated into the Project Wizard's Manpower Rate Allocation phase (Step 4).

Success Criteria

  • Creation of the Labors standalone module.
  • Database migrations for skills, labors, and labor_skills (grouped skillsets per labor record).
  • Category attribute on Labor records with values "skilled" and "unskilled".
  • Backend Models Labor and Skill with correct relations.
  • Backend CRUD routes & controllers (LaborController, SkillController) for managing labor profiles and skillsets.
  • Frontend UI (Modules/Labors/resources/js/Pages/Labors/Index.tsx and Skills/Index.tsx or similar) to manage Labors (name, category, skills list, hourly rate, status) and Skills list.
  • Integration into Project Wizard Step 4 (Manpower allocation). Instead of just choosing from general labor_rates, users select from the new labors module records, which display the Laborer category (Skilled / Unskilled) and their grouped skillsets as tooltips/badges in the dropdown/table.
  • Verification Plan with specific tests and manual checks.

Open Questions

  1. Coexistence of general Trade rates: Do you want to keep labor_rates for generic bidding/budgeting estimates and use labors for specific crew assignments? Or should labors fully replace labor_rates? (Our plan assumes fully replacing/extending it by pointing task_labors to labors to ensure consistency).
  2. Predefined Skill Dictionary: Should skills be managed as a master list (e.g. masonry, plumbing) that administrators can edit, or simple free-text tags? (We propose a master skills table with a many-to-many relationship).

Proposed Changes

Component 1: Labors Laravel Module Creation & Database Schema

We will create a new Laravel module called Labors using Laravel Modules structure:

c:\laragon\bin\php\php-8.2.30-Win32-vs16-x64\php.exe artisan module:make Labors

[NEW] create_skills_table

Contains the predefined dictionary of skills.

  • id (BIGINT, PK)
  • ulid (CHAR(26), Unique)
  • name (VARCHAR, Unique) — e.g., "Bricklaying", "Wiring", "Concrete Finishing"
  • description (TEXT, Nullable)
  • timestamps

[NEW] create_labors_table

Contains the Labor Records.

  • id (BIGINT, PK)
  • ulid (CHAR(26), Unique)
  • name (VARCHAR) — e.g., "Mason - Class A", "General Laborer"
  • category (ENUM: 'skilled', 'unskilled')
  • hourly_rate (DECIMAL(10, 2))
  • status (VARCHAR, Default: 'active')
  • timestamps

[NEW] create_labor_skills_table

Junction table grouping skillsets per Labor Record.

  • labor_id (FK -> labors.id ON DELETE CASCADE)
  • skill_id (FK -> skills.id ON DELETE CASCADE)
  • PK is (labor_id, skill_id)

Component 2: Models and Backend Services

[NEW] Skill.php

  • Uses App\Traits\HasPublicIdentifier.
  • Relationship: labors() -> belongsToMany(Labor::class)

[NEW] Labor.php

  • Uses App\Traits\HasPublicIdentifier.
  • Casts hourly_rate to decimal:2.
  • Relationship: skills() -> belongsToMany(Skill::class, 'labor_skills')
  • Scope: scopeActive()

[NEW] LaborController.php

  • Handles CRUD endpoints for managing labor records.
  • Handles syncing many-to-many skills during store and update.

[NEW] SkillController.php

  • Handles simple CRUD for the skill dictionary.

Component 3: Navigation and Frontend Views

[NEW] Index.tsx

A premium management page featuring:

  • Tabs for "Labor Records" and "Skill Dictionary".
  • Forms/modals to create and edit labor records with a skillset selector (multi-select badges).
  • Filtering by category (Skilled / Unskilled) or status (Active / Inactive).

[MODIFY] nav-config.ts

  • Add a new "Labors" link under the "Management" or "Materials & Logistics" section pointing to the new labors.index route.

Component 4: Project Wizard Step 4 Integration

[MODIFY] 2026_05_28_100004_create_task_resources_tables.php

  • Modify task_labors to reference labor_id (FK to new labors table) instead of labor_rate_id.
  • Note: We will run a clean migrate/reset since this is local dev database, or write a modifying migration.

[MODIFY] ProjectController.php

  • In wizard(): Load $labors = \Modules\Labors\Models\Labor::with('skills')->active()->get() instead of laborRates.
  • Update saveWizardLabor() validation and database transaction to reference the new labor_id.

[MODIFY] Wizard.tsx

  • Update Step 4 table to show Labor Category (Skilled/Unskilled) badge.
  • Update dropdown select to list labor profiles with their category and grouped skillsets (e.g., in a tooltip or a badge list).
  • Change variable names from laborRates to labors and labor_rate_ulid to labor_ulid.

Verification Plan

Automated Tests

  • Build verification: npm run build to verify TypeScript compile.
  • Run tests: c:\laragon\bin\php\php-8.2.30-Win32-vs16-x64\php.exe artisan test Modules/ProjectManagement/tests/Feature/ProjectWizardTest.php to verify no regressions in the wizard.

Manual Verification

  1. Access the new "Labors" menu in the sidebar and create multiple skillsets (e.g. Masonry, Plumbing, Carpentry).
  2. Create labor records matching "Skilled Mason" (category: Skilled) with Masonry skill, and "General Helper" (category: Unskilled) with no skills.
  3. Access the Project Wizard Step 4 (Manpower allocation). Verify that the new labors are listed in the dropdown with their category badges and their grouped skillsets visible.
  4. Allocate hours to tasks, save the step, and verify that the database table task_labors correctly associates the values.