Technical architecture
Module map and dependencies, how the code is organised, the extension points the bridge modules use, the schedule's front end, and a data-model diagram.
This page is for developers and implementers. It explains how Planning is built so that you can extend it. It applies to SmartERP Planning 17.0.3 on Odoo 17 Community.
Module map#
[planning-module-map]
| Module | Depends on (besides the Planning core) | Installs by itself |
|---|---|---|
smarterp_planning | digest, hr, mail, portal, resource, web_timeline | No (an application) |
smarterp_planning_holidays | hr_holidays | Yes (auto_install) |
smarterp_planning_contract | hr_contract | Yes |
smarterp_planning_skills | hr_skills | Yes |
smarterp_planning_attendance | hr_attendance | Yes |
smarterp_planning_project | project | Yes |
smarterp_planning_sale | sale_management | Yes |
smarterp_planning_sms | sms | Yes |
smarterp_planning_timesheet | smarterp_planning_project, hr_timesheet | Yes |
web_timeline is the OCA timeline view (based on vis-timeline). Planning extends it instead of modifying it.
Design decisions#
- Shifts are planned against
resource.resource. Employees and materials (vehicles, rooms) share one schedule. The employee, user and department of a shift are stored computed fields from the resource. - The model names are
smarterp.planning.*. Theplanning.*namespace belongs to Odoo Enterprise; sharing it would collide on the same tables. - Allocated time comes from the calendar engine. Working hours are the intersection of the shift and the resource's valid work intervals (
_get_valid_work_intervals), so time off, public holidays and, withhr_contract, contract gaps are respected without extra code. - Utilisation is a stored table.
smarterp.planning.capacityis rebuilt nightly for a rolling window and refreshed at commit for the people and days a change touches. It is not a SQL view, because the calendar engine cannot be reproduced in SQL. - The schedule's data comes from the server. Workload per day, time off shading, the hover card, drop checks, undo snapshots, bulk actions and the To plan panel are model methods in
smarterp_planning_shift_schedule.py, so the schedule and the reports agree. - Bridges extend by hooks, not by patches. A bridge overrides a small
_planning_*method and adds fields and views. The core never imports a bridge. - Self-service goes through methods, not through writes. Employees may write only the
namefield of their own shift. Taking, releasing and switching are methods that check eligibility and then write withsudo. - Raw SQL is used in two searches (
_search_role_mismatch,_search_overlap_slot_count), both parameter-free, for speed.
How the core is organised#
| Path | Contents |
|---|---|
models/smarterp_planning_shift.py | The shift model: fields, constraints, publishing, change approval, self-service, Copy Previous Week, Auto Plan. |
models/smarterp_planning_shift_schedule.py | Server side of the schedule: workload, unavailability, card, drag checks, undo, bulk actions, header actions, To plan, spreading hours, split, duplicate. |
models/smarterp_planning_shift_notify.py | Reminders, open shift broadcast, and the smarterp.planning.broadcast log. |
models/smarterp_planning_capacity.py | The utilisation table, its refresh hooks, and the resource.calendar.leaves hook. |
models/smarterp_planning_dashboard.py, ..._insights.py | Dashboard cards, KPIs and charts; digest KPIs. |
models/smarterp_planning_recurrency.py | Repeat series and their generation. |
models/smarterp_planning_role.py, ..._template.py, ..._planning.py | Roles, templates, the sent-schedule record. |
models/res_company.py, res_config_settings.py, resource_resource.py, hr_employee.py | Settings and extensions of core models. |
wizard/ | Transient models for Publish and Send, period operations, Plan with Template, Plan (spread), Duplicate, Split, Print. |
controllers/portal.py | Public schedule and open shift pages. |
report/planning_rota_report.xml | The printed weekly rota (QWeb PDF). |
security/ | Groups, access rights and record rules. |
data/ | Scheduled actions and email templates (no update). |
static/src/views/planning_timeline/ | The schedule view (OWL). |
static/src/dashboard/ | The dashboard (OWL). |
static/src/tours/ | The first-use tour. |
migrations/ | Upgrade scripts for 17.0.2.0.0 and 17.0.3.0.0. |
Extension points#
These are the methods bridges override. Use the same pattern in your own module.
Method (on smarterp.planning.shift unless noted) | Purpose | Overridden by |
|---|---|---|
_can_plan(); _planning_user_can_plan_any() | Who may plan a shift; whether the user plans anything at all. | Project |
_check_self_service_write(vals) | Extra rules for non-planners' writes. | Project |
_planning_extra_issues(resource, start, end) | More eligibility issues (leave, skill, contract) for a resource. Called by drag checks, Auto Plan, broadcasts, Plan with Template and self-service. | Time Off, Contracts, Skills |
_planning_card_lines() | Extra lines on the hover card. | Attendance, Project, Sales, Skills, Timesheets |
_planning_card_warnings() | Extra warnings on the hover card (inserted first). | Contracts, Time Off, Skills, Sales |
_planning_unavailability_extra(resources, start, end) | Extra shaded blocks, such as pending time off. | Time Off |
get_planning_header_actions() | Buttons of the schedule header. | Sales |
get_planning_to_plan_items(...); _planning_spread_source_defaults(model, res_id) | Items in the To plan panel and what happens when dropped. | Project, Sales |
_planning_netting_key() | Forecast hours are reduced by shifts with the same key on the same day. | Project |
_prepare_template_values(); _apply_template(template) | What identifies a template; what a template sets. | Project, Skills |
_planning_dashboard_cards() | Cards of the dashboard (a list of tuples). | (available) |
_planning_send_reminder_sms() | SMS reminder. | SMS |
_compute_role_id; _compute_* | Role defaults. | Sales |
smarterp.planning.spread._shift_values() | Values given to every shift the Plan dialog creates. | Project, Sales |
smarterp.planning.period.wizard.action_confirm() | Operation selection (selection_add). | Sales (plan_orders) |
The schedule's front end#
The view type is timeline with js_class="smarterp_planning_timeline". planning_timeline_view.esm.js registers three classes that extend web_timeline:
| Class | Role |
|---|---|
PlanningTimelineModel | Loads only the visible window plus one window either side. Adds the Open Shifts and empty rows, workload, unavailability and thresholds from get_planning_schedule_data. Day cells in Week and Month. |
PlanningTimelineRenderer | Navigation, row labels with load, shading, the hover card (PlanningCard), row tints while dragging, quick add, multi-select, keyboard control, the To plan panel. |
PlanningTimelineController | Header actions from the server, drop checks, publish choice, Alt-drag copy, undo through planning_snapshot/planning_restore, bulk actions. |
Every change still goes through the server's rights and checks; the view only avoids offering what the server would refuse. Browser storage used: smarterp_planning.window (session storage, the visible window), smarterp_planning.empty_rows (local storage, the "Only people with shifts" switch) and smarterp_planning.dashboard.dark (local storage, dashboard theme).
The dashboard is a client action smarterp_planning.dashboard; it calls get_planning_dashboard once and draws the charts as SVG, with line icons from a small set of templates.
Data model#
[planning-data-model]
The full list of fields is in the Data model reference.
Performance notes#
- An index on
(resource_id, start_datetime, end_datetime)for active shifts serves conflict checks, busy checks and the schedule. - Dashboard and utilisation figures use
read_groupandsearch_count; there is no per-record loop over the ORM. - The utilisation refresh works in batches of 50 resources and merges the dirty spans of one transaction into one refresh at commit.
- The schedule limits one request to about 400 days, and the card and drop checks to the shifts and resources involved.