Factorizer/Cluster ERP WikiCluster ERP Türkçe
Home / Business modules

Explanation

Production scheduling

How the production scheduling board reads work orders, resolves each work center's working calendar, detects conflicts and capacity, and moves or auto-schedules work orders.

The scheduling board lives in src/cluster_erp/modules/production/scheduling/. It shows work orders as bars on work-center rows over time, computed against each work center's effective working calendar, and it moves, resizes, or auto-schedules them. The web app (scheduling_board) and the desktop (scheduling_board.py) draw the same data. Work order fields are on work orders; calendar math is on working time.

In short

  • GET /api/v1/production/scheduling/board returns one read model: work centers with shifts and holidays, scheduled bars, the unscheduled pool, conflicts, capacity cells, and dependencies (src/cluster_erp/modules/production/scheduling/routes.py:41-86, src/cluster_erp/modules/production/scheduling/services.py:170-313).
  • Each work center's calendar is resolved through the owner chain work center, then the party of its organisation or of the headquarters, then the company group calendar (src/cluster_erp/modules/production/scheduling/services.py:72-94).
  • A work order's end is always derived from its duration laid over working hours; a caller cannot set it (src/cluster_erp/modules/production/scheduling/services.py:314-388).
  • The time math comes from the shared engine core.working_time, re-exported by scheduling/calendar.py (src/cluster_erp/modules/production/scheduling/calendar.py:1-30).

How it works

The diagram shows the data path of a board read.

flowchart TD
 R["GET /scheduling/board with date range and filters"] --> WC["_load_work_centers"]
 WC --> SH["_load_shifts: calendar_candidates per work center"]
 SH --> EC["GetEffectiveWorkingCalendars in one query"]
 EC --> HO["_load_holidays: GetCalendarWindows per calendar"]
 HO --> CA["_load_capabilities"]
 CA --> WO["_load_work_orders: bars and unscheduled pool"]
 WO --> OV["_flag_overlaps with capacity_units"]
 OV --> CP["_compute_capacity: cells and utilization"]
 CP --> DP["_compute_dependencies: finish-to-start"]
 DP --> RS["BoardResponse"]

In prose:

  1. The route takes date_from, date_to, and optional work center, production order, project, and scope filters; it needs work_order.read (src/cluster_erp/modules/production/scheduling/routes.py:41-86).
  2. The service loads the work centers and resolves each one's effective calendar with one GetEffectiveWorkingCalendars query over the owner chains (src/cluster_erp/modules/production/scheduling/services.py:891-946).
  3. Holidays and exceptions per date come from GetCalendarWindows; the effective time zone comes from the calendar, the organisation, or GetCompanyDefaultTimezone (src/cluster_erp/modules/production/scheduling/services.py:780-874, src/cluster_erp/modules/production/scheduling/services.py:982-1016).
  4. Work orders in the window become bars; pending ones without a schedule form the unscheduled pool (src/cluster_erp/modules/production/scheduling/services.py:1038-1204).
  5. Overlaps are flagged per work center, allowing up to capacity_units parallel bars; capacity cells and over-capacity days are computed; finish-to-start dependencies and predecessor violations are added (src/cluster_erp/modules/production/scheduling/services.py:1205-1384).

Writes:

  • PUT /scheduling/work-orders/{work_order_id}/schedule moves, resizes, reassigns, or unschedules one pending work order. It checks the record version, snaps an explicit start forward to the next working instant, and derives the end (src/cluster_erp/modules/production/scheduling/routes.py:87-109, src/cluster_erp/modules/production/scheduling/services.py:314-388).
  • POST /scheduling/work-orders/{work_order_id}/auto-schedule snaps a work order to the nearest valid free slot after its predecessors; no slot gives a 422 (src/cluster_erp/modules/production/scheduling/routes.py:110-135, src/cluster_erp/modules/production/scheduling/services.py:389-426).
  • POST /scheduling/production-orders/{order_id}/auto-schedule schedules all pending, unlocked work orders of an order forward in operation order (src/cluster_erp/modules/production/scheduling/routes.py:136-174, src/cluster_erp/modules/production/scheduling/services.py:427-496).
  • POST /scheduling/recompute re-derives the ends of stale scheduled work orders over the current calendar, keeps the starts, and clears the stale marks (src/cluster_erp/modules/production/scheduling/routes.py:175-190, src/cluster_erp/modules/production/scheduling/services.py:605-657).

Why it is built this way

Code comments state three reasons. Only pending work orders are schedulable because started or completed ones are pinned by their actuals (src/cluster_erp/modules/production/scheduling/services.py:321-323). The end is derived because a job's length is a function of quantity and routing (src/cluster_erp/modules/production/scheduling/services.py:363-368). The organisation's calendar is owned by its party, so the chain asks for the party (src/cluster_erp/modules/production/scheduling/services.py:77-84). Other rationale is not stated in code or in the project's written engineering rules.

Rules and boundaries

  • Writes need work_order.update (src/cluster_erp/modules/production/scheduling/routes.py:91-178).
  • scope=mine without an employee link gives an empty board (src/cluster_erp/modules/production/scheduling/services.py:182-187).
  • A working calendar change marks the affected scheduled work orders stale through WorkingCalendarChanged (src/cluster_erp/modules/production/module.py:1119-1138).

Interaction with other parts

Contract Answered by Purpose Source
GetEffectiveWorkingCalendars, GetEffectiveWorkingCalendar calendars the effective calendar per owner chain src/cluster_erp/modules/production/scheduling/services.py:927, src/cluster_erp/modules/production/scheduling/services.py:836
GetCalendarWindows calendars holidays and exceptions per date src/cluster_erp/modules/production/scheduling/services.py:792, src/cluster_erp/modules/production/scheduling/services.py:1000
GetCompanyDefaultTimezone owned_organisations default time zone src/cluster_erp/modules/production/scheduling/services.py:865, src/cluster_erp/modules/production/scheduling/services.py:976
GetOrganisationById owned_organisations the organisation's party src/cluster_erp/modules/production/scheduling/services.py:960
WorkingCalendarChanged emitted by calendars stale marks src/cluster_erp/modules/production/module.py:183

The contracts reference lists the answering modules. The projects board reads scheduled work orders through GetProjectProductionSchedule; see project board.

Limits and open points

Not determined from code: a limit on the number of work orders the board returns per call; the code reads all work orders that overlap the window (src/cluster_erp/modules/production/scheduling/services.py:1038-1204).

Terms introduced

Term Code identifier Definition Turkish term Source
scheduling board SchedulingBoardService The read model and write service that places work orders on work-center rows over time. Çizelgeleme panosu src/cluster_erp/modules/production/scheduling/services.py:163
calendar owner chain calendar_candidates The ordered owners whose calendar applies to a work center. Takvim sahip zinciri src/cluster_erp/modules/production/scheduling/services.py:72
unscheduled pool BoardUnscheduled Pending work orders that have no scheduled time yet. Çizelgelenmemiş havuz src/cluster_erp/modules/production/scheduling/schemas.py:90

Verified against commit 92f8051 on 2026-11-13.

Verified against commit 92f8051 · 2026-11-13