# Technical documentation — odoo_modules

Evidence date: 2026-10-08. Source: `main` at `df1bfde` (`df1bfdeb7dce783aff88ae7045dc7a95ff780742`).

Remote: `git@bitbucket.org:overdrive/odoo_modules.git` (HTTPS clone used for this review: `https://bitbucket.org/overdrive/odoo_modules.git`).

## What this repository is

A custom-addon repository for Odoo 17. The application runtime is the official `odoo:17.0` image. This repo does not vendor Odoo core. Extra addons are mounted at `/mnt/extra-addons`.

History on `main` is 23 commits, 2026-07-01 to 2026-07-08. About 4808 paths are tracked. A large share is module static assets, seed images, and the production sync bundle. This review did not open the database dump.

## Repository layout

| Path | Role |
|---|---|
| `addons/so_helpdesk` | Helpdesk application |
| `addons/my_company` | Social hub application |
| `addons/jb_ai_assistant` | OpenAI chat application |
| `addons/jb_timesheet` | Timesheet extension (not flagged as an Odoo application) |
| `addons/jb_timesheet_v2` | Weekly planning application |
| `addons/jb_mail_inbox` | IMAP inbox application |
| `config/odoo.conf` | Local Odoo config, mounted in dev compose |
| `config/odoo.prod.conf` | Production Odoo config (no password in the file; startup appends it) |
| `docker-compose.yml` | Local Postgres + Odoo |
| `docker-compose.dokploy.yml` | Production compose for Dokploy |
| `scripts/odoo-start.sh` | Production entrypoint |
| `work_scripts/` | Install, upgrade, seed, and test helper scripts; production dump |
| `docs/` | Markdown for three modules, an HTML guide, CI PDF, ER screenshots |
| `erd.html` | Entity diagram page at the repo root |
| `CHANGELOG.md` | Only the AI assistant 17.0.1.0.0 entry |
| `.env` | Tracked. Listed in `.gitignore`. Contents were not read for this document |
| `data/postgres`, `data/odoo` | Gitignored local runtime data (`/data/`) |

Not in `main`, though an earlier workspace scan named them: `nerd.html`, `sonar-project.properties`.

## Languages and frameworks

- Python 3, Odoo ORM (`odoo.models`, `odoo.http`, `odoo.api`).
- XML data files for views, security, crons, and seed records.
- OWL client actions and SCSS for My Company and AI Assistant (`web.assets_backend`).
- Helpdesk adds SCSS only; its UI is standard Odoo views plus portal QWeb.
- Shell for the production entrypoint.
- JavaScript is Odoo module assets, not a separate Node application. No `package.json` was found at the root.

HTTP clients: Python `requests` in the timesheet integrations and the AI assistant. IMAP via the Python standard library in both mail modules.

## Databases

- Engine: PostgreSQL 15 (`postgres:15`).
- Dev data directory: `./data/postgres` mounted at `/var/lib/postgresql/data/pgdata`.
- Database name used by bootstrap and by startup scripts: `odoo`.
- `db_name = False` in both Odoo config files, so Odoo does not pin a single database in the config. The startup script still passes `-d odoo` for the URL fix and module checks.
- Custom tables are created by the Odoo ORM from the models in `addons/`. There are no SQL migration files.

Principal custom models:

| Model | Module |
|---|---|
| `so.helpdesk.ticket`, `.team`, `.stage`, `.sla`, `.sla.status`, `.tag`, `.ticket.type`, `.mail.server`, `.canned.response` | `so_helpdesk` |
| `so.helpdesk.dashboard` | transient |
| `mycompany.post` | `my_company` |
| `jb.ai.conversation`, `jb.ai.message`, `jb.ai.model`, `jb.ai.card.rail` | `jb_ai_assistant` |
| `timesheet.v2.week.plan`, `.line` | `jb_timesheet_v2` |
| `timesheet.v2.dashboard` | transient |
| `mail.sync.server`, `mail.sync.message` | `jb_mail_inbox` |
| `jb.eit.copy.wizard`, `jb.mark.invoiced.wizard`, `jb.create.ticket.wizard` | wizards |

Extensions of standard models: `res.partner`, `res.users`, `hr.employee`, `hr.department` (via many2many), `project.project`, `project.task`, `account.analytic.line`, `crm.lead`, `res.config.settings`.

`erd.html` is a static diagram in the repo. It was not reconciled field-by-field with the models for this document.

## APIs and routes

Odoo JSON-RPC and the standard web client are the main API. Custom HTTP routes found:

| Route | Auth | Module |
|---|---|---|
| `/my/helpdesk`, `/my/helpdesk/page/<page>` | user, website | portal ticket list |
| `/my/helpdesk/ticket/<ticket_id>` | user, website | portal detail |
| `/helpdesk/submit` | public, CSRF, GET and POST | website form |
| `/helpdesk/rate/<ticket_id>/<token>` | see `controllers/rating.py` | rating form |

OWL hubs call model methods (`get_hub_feed`, `create_hub_post`, AI conversation RPC). Those are Odoo `call_kw` methods, not separate REST resources.

Outbound:

| Call | Timeout | Module |
|---|---|---|
| `POST {jb_timesheet.convergence_api_url}` | 15s | timesheet create/write |
| `POST {jb_timesheet.eit_api_url}` | 15s | EIT wizard |
| `POST {base_url}/chat/completions` | 60s | AI assistant |

Default OpenAI base URL: `https://api.openai.com/v1`.

## External integrations

See the PRD integrations table. Configuration keys:

| Key | Purpose | Seed value |
|---|---|---|
| `jb_ai_assistant.openai_api_key` | Bearer token | empty |
| `jb_ai_assistant.openai_base_url` | API origin | `https://api.openai.com/v1` |
| `jb_ai_assistant.temperature` | Sampling | `0.7` |
| `jb_ai_assistant.max_tokens` | Response cap | `2048` |
| `jb_ai_assistant.system_prompt` | Extra system text | empty |
| `jb_timesheet.eit_api_url` | EIT endpoint | empty |
| `jb_timesheet.eit_api_token` | Optional bearer | empty |
| `jb_timesheet.convergence_api_url` | Convergence endpoint | empty |
| `jb_timesheet.convergence_api_token` | Optional bearer | empty |
| `my_company.engagement_reminders` | Weekly nudge | settings boolean, cron treats missing as on |
| `my_company.default_startup` | Home action | settings |
| `my_company.hub_name` | Menu label | default My Company |
| `web.base.url` | Links in mail | set on production start from `SODOO_WEB_BASE_URL` |

IMAP hosts are rows in `so.helpdesk.mail.server` and `mail.sync.server`, not system parameters.

## Infrastructure and deployment

### Local

`docker-compose.yml`:

- `db`: `postgres:15`, container `odoo_db`, port `5432`, healthcheck `pg_isready -U odoo`.
- `odoo`: `odoo:17.0`, container `odoo_app`, depends on healthy db.
- Ports `8069` (HTTP), `8071`, `8072` (longpolling).
- Volumes: `./addons` → `/mnt/extra-addons`, `./config/odoo.conf` → `/etc/odoo/odoo.conf`, `./data/odoo` → `/var/lib/odoo`.
- `proxy_mode = False`, `workers = 0`, `dev_mode = reload,qweb,werkzeug,xml` in `config/odoo.conf`.

The local admin password in `odoo.conf` is a pbkdf2 hash. It is not repeated here.

### Production (Dokploy)

`docker-compose.dokploy.yml`:

- Same images, no published host ports in the file (Dokploy / reverse proxy is assumed).
- Postgres password `${SODOO_DB_PASSWORD}`.
- Odoo command: `/bin/sh /odoo-start.sh`.
- Volumes: addons, work_scripts, read-only `odoo.prod.conf`, read-only start script, named volume `odoo_data`.
- Environment: `SODOO_DB_PASSWORD`, `SODOO_ADMIN_PASSWORD`, `SODOO_WEB_BASE_URL=https://sodoo.appnotify.co.za`.
- `config/odoo.prod.conf`: `proxy_mode = True`, `workers = 2`, `max_cron_threads = 1`. No `dev_mode`. No `db_password` in the file.

`scripts/odoo-start.sh`:

1. Copy the Odoo config to `/tmp/odoo.runtime.conf`.
2. Append `admin_passwd` and `db_password` when the environment variables are set.
3. If `/mnt/repo/addons` exists, override `addons_path` to that directory. The compose file at `df1bfde` does not mount `/mnt/repo`. The tip commit message says compose mounts were reverted to restore uptime.
4. Bootstrap from `odoo_full.zip` only when the flag file is missing and the database `odoo` does not exist.
5. Set `web.base.url`, upgrade My Company, install or grant Helpdesk, install AI Assistant.
6. `exec odoo -c` the runtime config.

Named production host in compose and in `work_scripts/production_sync/README.md`: `sodoo.appnotify.co.za`.

## Auth and security groups

Odoo users, portal, and public sessions. No custom authentication provider was found (no OAuth module, no SSO config).

Group chains are in `business-rules.md`. Record rules are XML `ir.rule` records in each module's `security/` directory.

Passwords of IMAP mailboxes and the OpenAI key are stored as Odoo fields or `ir.config_parameter` values. The AI key parameter is seeded empty. Field groups on the helpdesk mailbox password include `so_helpdesk.group_helpdesk_manager` and `base.group_system` (see `helpdesk_mail_server.py`). Confirm the inbox password field's groups in the form view before treating it as hidden from inbox users.

`.env` is tracked even though `.gitignore` contains `.env`. `work_scripts/production_sync/odoo_full.zip` and `odoo_backup.dump` are tracked. This document does not quote their contents. Treat both as sensitive until a human says otherwise.

## Background jobs

| Cron | Interval | Model | Code |
|---|---|---|---|
| Helpdesk: Fetch Support Mailboxes | 5 minutes | `so.helpdesk.mail.server` | `_cron_fetch_all_mailboxes()` |
| SLA failure check | 1 hour | SLA status model | `_cron_check_sla_failures()` |
| My Company engagement | 1 week | `mycompany.post` | `_cron_engagement_reminder()` |
| Task hour overruns | 1 day | `project.task` | `_check_overrun_and_notify()` |
| Weekly timesheet reminder | 1 week, initial nextcall Monday 08:00 UTC at data load | `hr.employee` | `_send_timesheet_reminders()` |
| Remind count reset | 1 day (acts only on the last day of the month) | `hr.employee` | `_reset_monthly_remind_count_if_last_day()` |
| JB Mail Inbox fetch | 5 minutes | `mail.sync.server` | `_cron_fetch_all_servers()` |

Crons are `noupdate="1"` except where the XML file does not set noupdate. Helpdesk mail cron and SLA cron are noupdate. My Company cron file was not marked noupdate in the lines that were read (`data/ir_cron.xml` has no `noupdate` on the root). Re-install behaviour for that cron is **Requires Confirmation**.

`max_cron_threads = 1` in both configs. There is no separate queue worker (no Celery, no Redis).

## Storage

- Filestore: Odoo data dir `/var/lib/odoo` (named volume in production, `./data/odoo` in dev).
- Post images and mail attachments use `ir.attachment`.
- Weekly plan layout for the hub is documented as browser-local, not server storage.
- Production seed files live in Git under `work_scripts/production_sync/`.

## Monitoring and logging

- Odoo `log_level = info`, `logfile = False` (container stdout).
- Integration failures: Convergence and overrun mail and reminders use the Python logger. EIT and OpenAI raise `UserError` to the UI.
- No application metrics, health endpoint beyond the database image healthcheck, or Sonar project file is in `main`.
- `odoo.prod.conf` does not set a log file.

## Development and testing

Local run is `docker compose` with the dev file (the docs for the AI assistant say `docker compose up -d`). Install examples in `docs/so_helpdesk.md` use `docker exec odoo_app odoo -d odoo -i so_helpdesk --stop-after-init`.

Helper scripts (not a CI suite):

| Script | Role |
|---|---|
| `work_scripts/so_helpdesk/install_module.sh` | Install helpdesk; called by startup |
| `work_scripts/so_helpdesk/grant_access.sh` | Grant agent group; called by startup |
| `work_scripts/my_company/upgrade_module.sh` | Upgrade hub; called by startup |
| `work_scripts/jb_ai_assistant/install_module.sh` | Install assistant; called by startup |
| `work_scripts/jb_ai_assistant/test_*.py` | Manual chat / settings scripts |
| `work_scripts/jb_mail_inbox/backfill_crm_routing.py` | Backfill |
| `work_scripts/test_mail_inbox_routing/run_tests.py` | Routing script |
| `work_scripts/seed_timesheet_v2/` | Seed week plans |
| `work_scripts/my_company_seed_images/seed_feed_images.py` | Seed images |

No pytest configuration, no GitHub/Bitbucket pipeline file, and no `sonar-project.properties` were found. `jb_timesheet_v2` `post_init_hook` is defined as `post_init_hook(cr, registry)`. Odoo 17 calls `post_init_hook(env)`. `so_helpdesk` uses the `env` signature. Whether V2's hook runs is **Requires Confirmation**.

`addons/jb_timesheet_v2/hooks.py` recomputes `_compute_amount` and `_compute_is_internal` on analytic lines that have a project.

## Module versions

| Module | Manifest version |
|---|---|
| `so_helpdesk` | 17.0.2.1.0 |
| `my_company` | 17.0.1.10.0 |
| `jb_ai_assistant` | 17.0.1.1.0 |
| `jb_timesheet` | 17.0.1.0.0 |
| `jb_timesheet_v2` | 17.0.1.2.0 |
| `jb_mail_inbox` | 17.0.3.0.0 |

`CHANGELOG.md` records only AI assistant `17.0.1.0.0`.

## Security notes for reviewers

These are observations, not a penetration test.

- Public ticket create uses `sudo()`.
- Local compose and `odoo.conf` contain a fixed database password.
- `.env` and a full Odoo dump are in Git.
- AI administrators can read every user's prompts and replies.
- Helpdesk install grants Agent to all internal users, which with the agent record rule allows write on their teams. Combined with the user read-all rule, internal users can read every ticket after install.
- Convergence and EIT tokens are global system parameters, not per company, in the seed XML (no `company` field on those parameters).

## Docs inside the source repository

| File | Covers |
|---|---|
| `docs/so_helpdesk.md` | Helpdesk teams, mail, ratings, groups |
| `docs/my_company.md` | Hub layout and roadmap |
| `docs/jb_ai_assistant.md` | Assistant, privacy, roadmap |
| `docs/odoo_modules_guide.html` | HTML guide; not fully transcribed here |
| `docs/scope_jb_ai_assistant.html` | Scope page for the assistant |
| `docs/Silicon Overdrive - Corporate Identity (CI) Guidelines V1.3.pdf` | Brand PDF |
| `erd.html` | Diagram |
| `CHANGELOG.md` | Partial |

Timesheet and mail inbox behaviour is in code and in this Silicon Ark set. They do not have a matching `docs/*.md`.
