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

Explanation

Project board

How the projects board and calendar read model builds one payload from projects, their children, production schedules, and the group calendar.

The project board is a read-only read model in src/cluster_erp/modules/projects/board.py, served by src/cluster_erp/modules/projects/board_routes.py. One call returns every project placed along the lifecycle columns, with its schedule, meetings, actions, fixtures, items, and the production reality from the production module. The web app draws it as a kanban (project_board) and as a month calendar (project_calendar), and as two home widgets. Project fields are on projects, items, and fixtures.

In short

  • One endpoint, GET /api/v1/projects/project-board/, assembles the whole board; the board edits nothing (src/cluster_erp/modules/projects/board.py:1-11, src/cluster_erp/modules/projects/board_routes.py:17-47).
  • The columns are the project lifecycle PROJECT_STATUS_FLOW (src/cluster_erp/modules/projects/board.py:100).
  • Production orders and scheduled work orders come from GetProjectProductionSchedule; open days come from GetOwnerWorkingDays for the group calendar (src/cluster_erp/modules/projects/board.py:162-201).
  • scope=mine narrows the board to projects where the caller's party is manager or action owner (src/cluster_erp/modules/projects/board.py:57-82).

How it works

The diagram shows the data path of one board call.

flowchart TD
 R["GET /project-board/ with date_from, date_to, scope"] --> M{"scope is mine"}
 M -->|yes| E["GetPartyForEmployee"]
 M -->|no| B["ProjectBoardService.board"]
 E --> B
 B --> P["Project rows, newest first"]
 P --> F["_only_member_projects when mine"]
 F --> C["_load_children: schedules, kickoffs, meetings, actions, fixtures, items"]
 C --> PR["_load_production: GetProjectProductionSchedule"]
 PR --> OD["_load_open_days: GetOwnerWorkingDays group calendar"]
 OD --> K["_card per project"]
 K --> J["payload: range, columns, projects, calendar, scope"]

In prose:

  1. The route reads date_from, date_to, and scope. With scope=mine, it resolves the caller's employee to a party with GetPartyForEmployee (src/cluster_erp/modules/projects/board_routes.py:20-44).
  2. The service sets the date range; the default is 7 days back and 56 days ahead (src/cluster_erp/modules/projects/board.py:43-45, src/cluster_erp/modules/projects/board.py:70-72).
  3. It loads all live projects, newest first, and with mine keeps only the projects the party belongs to. A caller without a party gets an empty board (src/cluster_erp/modules/projects/board.py:73-82, src/cluster_erp/modules/projects/board.py:109-128).
  4. It loads the children of all projects in one pass per table: schedules by version descending, kick-offs, meetings by week, actions by due time, fixtures, and items by line (src/cluster_erp/modules/projects/board.py:129-161).
  5. It asks the production module for the orders and work orders of the projects, and the calendars module for the open days of the group calendar (src/cluster_erp/modules/projects/board.py:162-201).
  6. It builds one card per project with a summary: item count, open and overdue actions, open fixtures, meetings held, orders, work orders done, stale orders, and the schedule status. The active schedule is used, or the newest one when none is active (src/cluster_erp/modules/projects/board.py:202-325).
  7. It returns the range, the columns, the cards, the open days, and the scope (src/cluster_erp/modules/projects/board.py:88-107).

The web widgets read this one payload. The board bundle states that tapping a card opens the project's own detail card and that nothing on the board edits data (gated_apps/projects/app.js:7-17).

Why it is built this way

The module docstring gives two reasons: the client renders a single payload, and data of other modules is fetched over the EventBus because direct module imports are forbidden (src/cluster_erp/modules/projects/board.py:1-11). The membership rule for mine follows the intranet audience builder's rule, as the docstring states (src/cluster_erp/modules/projects/board.py:64-68). Other rationale is not stated in code or in the project's written engineering rules.

Rules and boundaries

  • The route needs project.read (src/cluster_erp/modules/projects/board_routes.py:27).
  • scope accepts only mine or all (src/cluster_erp/modules/projects/board_routes.py:26).
  • When the production query or the calendar query fails, the board renders without that part: the loaders return an empty result (src/cluster_erp/modules/projects/board.py:162-201).

Interaction with other parts

Contract Answered by Purpose Source
GetPartyForEmployee owned_organisations resolve the caller's party for mine src/cluster_erp/modules/projects/board_routes.py:36-40
GetProjectProductionSchedule production orders, operations, and scheduled work orders src/cluster_erp/modules/projects/board.py:168-170
GetOwnerWorkingDays calendars open days of the group calendar src/cluster_erp/modules/projects/board.py:184-186

The answering modules are listed on the contracts reference. The calendar rules are on working time.

Limits and open points

  • The board loads every live project before filtering, without paging (src/cluster_erp/modules/projects/board.py:73-82).
  • The board shows no money values; the summary counts records only (src/cluster_erp/modules/projects/board.py:250-265).

Terms introduced

Term Code identifier Definition Turkish term Source
project board ProjectBoardService The read-only read model that shows all projects along the lifecycle. Proje panosu src/cluster_erp/modules/projects/board.py:52
board card _card The per-project block of the board payload with its summary. Pano kartı src/cluster_erp/modules/projects/board.py:202

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

Verified against commit 92f8051 · 2026-11-13