Production Reference Public

Labor & Downtime

Advanced Manufacturing Labor & Downtime Record who worked on which work order and for how long, and log the periods when a machine or station was not producing — two shop-floor…

Guide version: r1 Module version: 1.0.0 Updated: 2026-07-22 Estimated time: 19 min 2 views
Advanced Manufacturing

Labor & Downtime

Record who worked on which work order and for how long, and log the periods when a machine or station was not producing — two shop-floor logs that feed accurate actual production cost.

ℹ️
What this feature is. Labor & Downtime is a pair of straightforward CRUD logs. There is no multi-step approval workflow — you simply add, edit, and delete entries. Labor Tracking captures time booked against a work order/operation by an employee, at an hourly rate, so ERPat computes hours and labor cost. Downtime Tracking captures when a work center was down, why, for how long, and at what maintenance cost. The recorded labor cost is picked up by Production Costing for actual-cost analysis.
Labor Tracking

One time entry per employee per work order/operation. Clock In → Clock Out gives hours; hours × hourly rate gives labor cost. Open entries show an Active badge until you close them. Route: production/LaborTracking.

Downtime Tracking

One event per stoppage against a work center. Categorised by type, described by a reason, timed Start → End for a duration in minutes, with an optional maintenance cost. Open events show an Ongoing badge. Route: production/LaborTracking/downtime.

Production Supervisor Operator Maintenance / Line Lead Production Manager
⚠️
Access. Both logs are gated by a single permission, manufacturing_labor, and require the manufacturing module to be enabled. Without either, you are redirected away. See Actions & permissions below and the full Permissions & Roles matrix.

Labor time entries

A labor time entry answers four questions: which work order (and optional operation), who worked, how long (Clock In to Clock Out), and at what rate. From those, ERPat computes the hours worked and the labor cost — you never type those two directly. The Labor Tracking list page (production/LaborTracking) shows every entry across all work orders and is the entry point to log, edit, delete, and jump to Downtime.

The Labor Tracking list

The page header carries a Log Labor button (opens the Log Labor Entry modal) and a Downtime button (links to the Downtime Tracking page). A Labor Type filter dropdown narrows the table. The DataTable #labor-table shows these columns:

ColumnMeaningNotes
Work OrderThe order the labor was booked against.Shown as order_number - product_name.
OperationThe routing step, if one was chosen.May be blank when no operation was selected.
EmployeeThe staff member who performed the labor.First + last name.
TypeLabor Type classification.Direct / Indirect / Setup / Rework (see the legend).
Clock InWhen work started.Date-and-time.
Clock OutWhen work finished.Shows an Active badge when no clock-out has been recorded.
HoursComputed elapsed hours.Displayed as X.XX hrs; 0 while Active.
RateThe hourly rate used for costing.Per-hour amount.
CostComputed labor cost.Hours × Rate; falls back to that product if the stored cost is empty.
Actions (bars)Row menu.Pencil to edit, X to delete.
ℹ️
Filter scope gotcha. The list page's own work-order filter pre-loads only work orders with status in progress. The Log Labor modal, however, lets you pick any work order (including completed or draft ones). Work-order filtering is also available through the list_data endpoint's order_id option.

Log Labor Entry — every field

The Log Labor Entry modal (production/LaborTracking/modal_form) creates or edits one entry. Required fields carry a red asterisk and client-side data-rule-required.

FieldMeaningRequiredNotes
order_id — Work Order The manufacturing work order the labor was performed against. Required Select2 dropdown. In the modal it lists all work orders as order_number - product_name. Joins to show order number and product name in the list.
operation_id — Operation The specific order operation / routing step worked on. Optional Select2 dropdown sourced from manufacturing_order_operations (operation name); a blank / - choice is allowed. Stored as null when left blank.
employee_id — Employee The staff member who performed the labor. Required Select2 dropdown of active staff (first + last name). Sourced from users where status=active and user_type=staff.
labor_type — Labor Type Classifies the nature of the labor time. Optional Dropdown: Direct / Indirect / Setup / Rework. Defaults to direct if not chosen. DB enum('direct','indirect','setup','rework').
clock_in — Clock In Date-and-time the worker started. Required HTML datetime-local input. Used to derive work_date (the Y-m-d of clock-in) and to compute hours worked.
clock_out — Clock Out Date-and-time the worker finished. Optional datetime-local. If blank, the entry is Active and hours stay 0. Stored null when empty.
hourly_rate — Hourly Rate Pay/cost rate per hour used to compute labor cost. Optional Number input, step 0.01, default 0.00. DB decimal(10,2).
notes — Notes Free-text remarks about the labor entry. Optional Textarea, 3 rows.

Computed & derived values (not on the form)

These are calculated on save — you never type them, and they are what flows into costing.

FieldMeaningRequiredNotes
hours_worked — Hours Computed elapsed hours between clock in and clock out. Computed = round((clock_out − clock_in) / 3600, 2). Shown in the list as X.XX hrs. DB decimal(5,2).
labor_cost — Cost Computed cost of the labor entry. Computed = round(hours_worked × hourly_rate, 2). The list falls back to hours × rate if the stored cost is empty. DB decimal(15,2).
work_date — (not on form) The calendar date of the labor, used for filtering/sorting. Derived Auto-set to the date portion of clock_in (or today if clock-in is missing). Not a visible form field.

How labor rolls into actual cost

When you save a labor entry, ERPat recomputes Hours from Clock In / Clock Out and Cost as Hours × Hourly Rate, then stores both on the entry. Production Costing's actual-cost model reads directly from the labor-tracking table, so the labor cost you record here is summed into the actual production cost of the associated work order. In short:

Clock In / Out
you enter times
Hours worked
(out − in) / 3600
Labor cost
hours × rate
Actual cost
rolled into Costing
????
Because cost is always computed, an entry with a blank Clock Out contributes 0 to actual cost until you close it. To make sure a work order's actual labor cost is complete, close every Active entry before you read the numbers in Production Costing.

Labor entry lifecycle

A labor entry is Active until it has a Clock Out; once closed, hours and cost are computed. An entry can be re-edited (recomputing hours/cost) or soft-deleted.

Active
Clock In only · 0 hrs / 0 cost
Closed
Clock Out set · hours & cost computed
Deleted
soft delete (deleted = 1)
ActiveHas a Clock In but no Clock Out yet. Hours and cost stay 0 until closed; shown as an orange badge in the Clock Out column.
ClosedHas both Clock In and Clock Out; hours worked and labor cost are computed and displayed. Can be re-edited to change times/rate (recomputing) or deleted.
DeletedSoft-deleted (deleted = 1); the row disappears from the list but is not physically removed.

Labor Type legend

The Type column and filter classify each entry. These are the exact enum values.

DirectLabor that directly produces the product (labor_type = direct). The default when none is chosen.
IndirectSupporting labor not directly making the product (labor_type = indirect).
SetupTime spent setting up / changing over before production (labor_type = setup).
ReworkTime spent redoing / fixing defective output (labor_type = rework).

SOP — Record labor time against a work order

????
Precondition. The manufacturing module is enabled and you hold the manufacturing_labor permission; the Work Order and (optionally) its Operation already exist.
  1. Open Labor Tracking

    Navigate to Production → Labor Tracking (production/LaborTracking).

  2. Start a new entry

    Click Log Labor to open the entry modal with empty fields.

  3. Pick the work order and operation

    Choose the Work Order (required) and, if relevant, the Operation. The modal lists all work orders, not just in-progress ones.

  4. Pick the employee

    Choose the Employee (required) — only active staff appear in the dropdown.

  5. Set the labor type

    Choose Labor Type (Direct / Indirect / Setup / Rework). Direct is the default.

  6. Enter the times

    Enter Clock In (required). Enter Clock Out if the work is finished; otherwise leave it blank to record an Active entry.

  7. Enter the rate

    Enter the Hourly Rate so the labor cost can be computed.

  8. Add notes and save

    Add Notes if useful, then click Save.

  9. Verify the totals

    Confirm the computed Hours and Cost in the table. If you left Clock Out blank, re-open the entry later to close it.

Recording an Active entry and closing it later

  1. Open a new entry

    In Labor Tracking, click Log Labor.

  2. Save without a Clock Out

    Fill Work Order, Employee, Labor Type, and Clock In, leaving Clock Out blank, then Save.

  3. Confirm it is Active

    The entry shows an Active badge with 0 hours and 0 cost.

  4. Edit when the shift ends

    When the worker finishes, click the pencil to edit the entry.

  5. Enter Clock Out and save

    Enter the Clock Out time and Save; Hours and Cost are computed automatically.

????
Self-service clock in/out. Beyond the modal, the controller also exposes clock_in and clock_out actions for the current logged-in user. Clock in creates an entry against a work order (optionally an operation) stamping clock_in = now, work_date = today, labor type direct, and an hourly rate from input or 0 — leaving it Active. Clock out updates that entry with clock_out = now and computes hours and cost. Both use the same manufacturing_labor permission.

Downtime logging

A downtime event records a period when a work center (machine or station) was not producing: which center, why (a type + a free-text reason), how long (Start → End), and any maintenance cost. The Downtime Tracking list (production/LaborTracking/downtime) shows every event and is the entry point to log, edit, delete, and jump back to Labor.

The Downtime Tracking list

The header carries a Log Downtime button (opens the Log Downtime modal) and a Labor button (links back to Labor Tracking). Two filter dropdowns are available — Type and Status. The DataTable #downtime-table shows these columns:

ColumnMeaningNotes
Work CenterThe machine/station that was down.Shown by name/code.
TypeDowntime category.Breakdown / Maintenance / Setup / Material Shortage / Other (see the legend).
ReasonFree-text description of why the center was down.Its own column.
Start TimeWhen the downtime began.Date-and-time.
End TimeWhen it ended / the machine came back up.Shows an Ongoing badge when no end time has been recorded.
Duration (min)Computed length in minutes.0 while ongoing.
CostMaintenance cost incurred to resolve.Per-event amount.
Reported ByThe user who logged the event.Set on create; not editable.
Actions (bars)Row menu.Pencil to edit, X to delete.
ℹ️
Filters. The Type filter offers Breakdown / Maintenance / Setup / Material Shortage / Other, and the Status filter offers Open / Resolved — handy for showing only unresolved stoppages by category.

Log Downtime — every field

The Log Downtime modal (production/LaborTracking/downtime_modal_form) creates or edits one event. Required fields carry a red asterisk and data-rule-required.

FieldMeaningRequiredNotes
work_center_id — Work Center The machine / work station that was down. Required Select2 dropdown of active work centers (code - name). Sourced from manufacturing_work_centers where is_active = 1.
downtime_type — Type Category / root-cause bucket of the downtime. Required Dropdown: Breakdown / Maintenance / Setup / Material Shortage / Other. The modal defaults the dropdown to breakdown; if somehow blank, save defaults to other. DB enum('breakdown','maintenance','setup','material_shortage','other').
reason — Reason Description of why the work center was down. Required Textarea, 2 rows. Shown as its own column in the list.
start_time — Start Time When the downtime began. Required datetime-local. Derives downtime_date and the duration.
end_time — End Time When the downtime ended / the machine came back up. Optional datetime-local. If blank, the event is Ongoing and status stays open; if filled, status becomes resolved and the current user is recorded as resolver. Stored null when empty.
maintenance_cost — Maintenance Cost Cost incurred to fix / resolve the downtime. Optional Number input, step 0.01, default 0.00. DB decimal(15,2).
notes — Notes Free-text remarks about the downtime. Optional Textarea, 2 rows.

Computed, derived & auto fields (not directly entered)

FieldMeaningRequiredNotes
duration_minutes — Duration (min) Computed length of the downtime in minutes. Computed = round((end_time − start_time) / 60, 0). 0 while ongoing. DB int.
downtime_date — (not on form) Calendar date of the downtime, used for filter/sort. Derived Auto-set to the date portion of start_time (or today). Not a visible field.
reported_by — Reported By The user who logged the downtime. Auto Set to the logged-in user on create only; shown in the list. Not editable.
resolved_by — (not shown in list) The user who marked the downtime resolved. Auto Set to the logged-in user whenever an End Time is provided on save.
production_loss_quantity — (DB only) Quantity of production lost due to downtime. Not used Column exists in the DB (decimal(15,4)) but is NOT exposed on the downtime form or list — it always defaults to 0.

Downtime lifecycle

A downtime event's status is driven entirely by the End Time. With no End Time it is Open (Ongoing); provide an End Time on save and it flips to Resolved. There is no explicit "resolve" button.

Open
Start only · Ongoing · 0 min
Resolved
End set · duration computed · resolver recorded
Deleted
soft delete (deleted = 1)
Open / OngoingHas a Start Time but no End Time; status = open. Duration is 0; shown with an orange Ongoing badge in the End Time column.
ResolvedHas an End Time; status = resolved, duration computed, resolver recorded. Can be re-edited; clearing the End Time on save reverts it to Open.
DeletedSoft-deleted (deleted = 1); the row disappears but is not physically removed.

Downtime Type legend (exact categories)

The Type column and filter classify each event into one of these buckets. These are the exact enum values — enum('breakdown','maintenance','setup','material_shortage','other').

BreakdownUnplanned equipment failure (breakdown). The modal's default type.
MaintenancePlanned / servicing downtime (maintenance).
SetupSetup / changeover stoppage (setup).
Material ShortageDowntime caused by lack of input materials (material_shortage).
OtherOther / uncategorised (other). Also the fallback if the type saves blank.
⚠️
Default mismatch. The Log Downtime modal visually defaults the Type dropdown to Breakdown, but the server's fallback when the value saves empty is Other. Similarly, labor type defaults to Direct if blank. Pick the type deliberately rather than relying on the default.

SOP — Log and resolve a work-center downtime

????
Precondition. You hold the manufacturing_labor permission; the affected Work Center exists and is active (is_active = 1) — inactive centers won't appear in the dropdown.
  1. Open Downtime Tracking

    Go to Production → Downtime Tracking (or click Downtime from Labor Tracking).

  2. Start a new event

    Click Log Downtime.

  3. Pick the work center

    Select the Work Center (required) that went down.

  4. Choose the type

    Select the Type (required; Breakdown by default).

  5. Describe the reason

    Enter the Reason (required) describing the stoppage.

  6. Record the start

    Enter the Start Time (required). Leave End Time blank while the stoppage is ongoing, then Save — the record is Open / Ongoing and stamped with you as reporter.

  7. Resume production

    Once the machine is back up, edit the record, enter the End Time and Maintenance Cost, and Save.

  8. Confirm it is Resolved

    The record flips to Resolved; Duration (min) is computed and you are recorded as the resolver.

Reviewing open downtimes

  1. Open Downtime Tracking

    Navigate to the Downtime list.

  2. Filter to what matters

    Use the Status filter to show only Open events, and the Type filter to focus on e.g. Breakdown / Maintenance.

  3. Prioritise by impact

    Check the Duration (min) and Cost columns to prioritise which stoppages to resolve first.


SOP — Log labor and downtime for a shift

At the end of a shift, a supervisor typically records the operators' labor and any machine stoppages together. This end-to-end procedure combines both logs.

????
Precondition. The manufacturing module is enabled and you hold manufacturing_labor. The work orders and their operations exist; the affected work centers are active.
  1. Open Labor Tracking

    Go to Production → Labor Tracking. This is your starting point for the shift's time entries.

  2. Log each operator's labor

    For every operator, click Log Labor, choose the Work Order (and Operation where relevant), the Employee, and the Labor Type. Enter Clock In and Clock Out and the Hourly Rate, then Save.

  3. Leave in-progress work Active

    If someone is still working at handover, save their entry with a blank Clock Out — it stays Active and the next shift closes it.

  4. Verify the labor totals

    Scan the Hours / Rate / Cost columns; every closed entry should show computed hours and cost.

  5. Switch to Downtime

    Click the Downtime button to open the Downtime Tracking page.

  6. Log each stoppage

    For every machine stoppage during the shift, click Log Downtime, choose the Work Center and Type, write the Reason, and enter the Start Time. If the stoppage is resolved, add the End Time and Maintenance Cost; if still down, leave End Time blank.

  7. Confirm status

    Resolved events show a computed Duration; ongoing ones show the Ongoing badge and status Open, reported by you.

  8. Hand off cost review

    The labor costs and stoppages you recorded now feed Production Costing; a manager can compare standard vs. actual cost per work order.


Actions

Every action below is gated by the single manufacturing_labor permission (see Permissions). Saves run inline form validation only — there is no multi-step confirmation except on delete.

ActionEffectConfirmation
Log Labor (open create modal)Opens the Log Labor Entry modal with empty fields to create a new labor time record.None
Edit labor (pencil)Opens the Log Labor Entry modal pre-filled with the selected record for update.None
Save laborCreates or updates the entry. Recomputes hours from clock in/out and cost = hours × hourly rate; sets work_date from clock in; defaults labor type to direct. On create sets uuid and created_at; always sets updated_at.None (inline validation only)
Delete labor (X)Soft-deletes the entry (deleted = 1); the row disappears from the list.Standard delete-confirmation dialog
Clock in (self)Creates a new entry for the current user against a work order (and optional operation), stamping clock_in = now, work_date = today, labor type direct, rate from input or 0. No clock out — the entry shows Active.None
Clock out (self)Updates an existing entry: sets clock_out = now, computes hours from the stored clock in, and cost = hours × stored rate; stamps updated_at.None
Downtime (nav button)Navigates from Labor Tracking to the Downtime Tracking page.None
Labor (nav button)Navigates from Downtime Tracking back to Labor Tracking.None
Log Downtime (open create modal)Opens the Log Downtime modal with empty fields to create a new event.None
Edit downtime (pencil)Opens the Log Downtime modal pre-filled with the selected record for update.None
Save downtimeCreates or updates the event. Computes duration from start/end; sets downtime_date from start; defaults type to other if blank. If End Time is present: status resolved and resolver = current user; else status open. On create sets uuid, reporter = current user, created_at; always sets updated_at.None (inline validation only)
Delete downtime (X)Soft-deletes the event (deleted = 1); the row disappears from the list.Standard delete-confirmation dialog

Validation & system messages

Required fields are enforced both client-side (red asterisk, data-rule-required) and on the server. The exact server rules and messages:

ActionServer rules
Save laborid numeric; order_id required & numeric; employee_id required & numeric; clock_in required.
Delete laborid required & numeric.
Clock in (self)order_id required & numeric.
Clock out (self)id required & numeric.
Save downtimeid numeric; work_center_id required & numeric; reason required; start_time required.
Delete downtimeid required & numeric.
Success messages. Saving a labor or downtime record shows “Record saved successfully.” (record_saved); deleting shows “Record deleted successfully.” (record_deleted). The self-service clock actions show the hard-coded “Clocked in successfully” and “Clocked out. Hours: <computed hours>”.
Failure messages. A failed save shows “Some error occurred. Please try again.” (error_occurred); a record that can't be removed shows “This record cannot be deleted.” (record_cannot_be_deleted). Deletes always go through the standard delete-confirmation dialog first.

Permissions

Access to the whole feature — viewing and every create/edit/delete/clock action for both labor and downtime, plus the sidebar menu item — is controlled by one permission key.

Permission / gateMeaningNotes
manufacturing_labor The single permission enforced by the LaborTracking controller. Gates viewing and all create/edit/delete/clock actions for both labor and downtime, plus the sidebar menu item. Category “Manufacturing: Labor Tracking”, default level module. Constructor: with_permission('manufacturing_labor', 'redirect').
manufacturing (module gate) The manufacturing module must be enabled. Constructor: with_module('manufacturing', 'redirect') — otherwise the user is redirected.
manufacturing_labor_create Registered permission (“Create Labor Entry”). Not separately enforced in this controller — all actions use manufacturing_labor.
manufacturing_labor_update Registered permission (“Update Labor Entry”). Not separately enforced in this controller.
manufacturing_labor_delete Registered permission (“Delete Labor Entry”). Not separately enforced in this controller.
⚠️
One key controls everything. The granular _create / _update / _delete permissions exist in config but are not enforced here — so anyone with manufacturing_labor can add, edit, and delete both labor and downtime records. Grant it deliberately. Full matrix: Permissions & Roles.

Things to watch out for

Editing Clock In/Out or the Hourly Rate and saving recalculates them. If Clock Out is blank, hours and cost are 0 — the entry contributes nothing to actual cost until closed.
The Labor Tracking index pre-loads only in-progress work orders into its filter dropdown, but the Log Labor modal lets you pick any work order — including completed or draft ones.
Times use the browser's datetime-local input, and elapsed time is computed by simple string-to-time subtraction. Be consistent with the values you enter — no time-zone conversion happens to the clock times.
Providing an End Time sets status Resolved and stamps you as resolver; clearing it on a later edit reverts the event to Open. There is no explicit “resolve” button. reported_by is set only on creation and never changed on edits; resolved_by is re-set to the current user every time you save with an End Time present.
Delete sets deleted = 1 after the standard delete-confirmation dialog; records are hidden, not physically removed.
Labor type defaults to Direct and downtime type defaults to Other on save if the value is empty — even though the downtime modal visually defaults the dropdown to Breakdown.
The downtime table has a production_loss_quantity column, but it is not shown or editable in this feature and always defaults to 0.
Work centers must be active (is_active = 1) to appear in the downtime dropdown and filter; inactive centers won't be selectable. Manage them under Work Centers.

Where this connects

Labor & Downtime is a time-and-cost log — it does not move stock. It reads from and feeds these areas:

????
Employees & APIs. The Employee dropdown lists active staff (status=active, user_type=staff); reporters and resolvers also reference user records. The manufacturing REST/EU APIs expose downtime read endpoints (GET manufacturing/downtime and manufacturing/downtime/{id}) plus the self-service clock in/out actions, all gated by the same manufacturing_labor permission. There is no direct Inventory integration — this feature deals with time and cost, not stock movements.
Was this guide helpful?

Report a content problem