Metadata-Version: 2.4
Name: odoo-inspect
Version: 0.1.0
Summary: Static analyzer that reads Odoo community/enterprise source code and produces a merged, versioned knowledge base of models, fields, and methods.
Author: Dakshal Jethava
License: MIT License
        
        Copyright (c) 2026 Dakshal Jethava
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in
        all copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
        THE SOFTWARE.
        
Project-URL: Homepage, https://github.com/jethavadakshal/odoo-inspect
Project-URL: Documentation, https://github.com/jethavadakshal/odoo-inspect#readme
Project-URL: Issues, https://github.com/jethavadakshal/odoo-inspect/issues
Project-URL: Changelog, https://github.com/jethavadakshal/odoo-inspect/blob/main/CHANGELOG.md
Keywords: odoo,static-analysis,ast,schema,upgrade,migration,diff
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Code Generators
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# odoo-inspect

Static analyzer for Odoo source code. It reads Odoo community and enterprise
addons directly from disk (no running Odoo instance needed), parses every
`.py` / `.xml` file with the standard library, and produces a merged,
versioned knowledge base of models, fields, and methods — tagged with the
module that contributed each piece.

`odoo-inspect` is built for Odoo developers who have to reason about
schema changes between versions, plan module upgrades, or answer
questions like *"when did `account.move.payment_state` appear?"* without
spinning up four Odoo instances in parallel.

## Why

Odoo models are extended across many modules. `sale.order` is defined in
`sale/`, but `account/`, `sale_stock/`, `sale_timesheet/` and dozens of
others add fields and methods on top. A plain grep can tell you *where* a
field is declared, but not what the complete shape of the model looks
like once every addon you enable has been merged together.

`odoo-inspect` walks every module on disk, runs each `.py` file through
`ast`, and merges every extension into a single JSON file per model — so
you can open `knowledge/v17/models/sale.order.json` and see the full
contributed surface in one place.

## Install

```bash
pip install odoo-inspect
```

Requires Python **3.12** or newer.

## Quickstart — CLI

Scan a single Odoo version:

```bash
odoo-inspect scan \
  --community /path/to/odoo17/odoo/addons /path/to/odoo17/addons \
  --enterprise /path/to/enterprise17 \
  --version 17.0 \
  --output knowledge/v17
```

`--community` accepts multiple paths (space-separated) so you can include
both `odoo/addons/` and the top-level `addons/` where `base` lives.
`--enterprise` is optional. The output directory is **overwritten** on
every scan — each run is a fresh snapshot.

Compare two previously-scanned versions:

```bash
odoo-inspect diff \
  --old knowledge/v16 \
  --new knowledge/v17 \
  --output knowledge/diffs/v16_to_v17.json
```

Build a keyword → model router from a knowledge directory:

```bash
odoo-inspect router \
  --knowledge knowledge/v17 \
  --output router/v17
```

### Render an upgrade report

Once you have a diff JSON, `report` turns it into something a human can
actually read. Two formats:

**HTML** — a single self-contained file you can open in any browser.
No server, no external assets, no build step. Inline CSS + JS. Key
features:

- Overview cards at the top (models added / removed / changed counts).
- One collapsible `<details>` card per model with field changes.
  Click to expand; badges on each card show `+N` added, `−N` removed,
  `~N` type/selection changes at a glance.
- A sticky search box that filters the model cards client-side as you
  type — works fine with thousands of models.
- Separate "Models added" / "Models removed" / "Financial changes"
  sections.

```bash
odoo-inspect report \
  --diff knowledge/diffs/v16_to_v17.json \
  --format html \
  --output upgrade_v16_to_v17.html

# then just open it
xdg-open upgrade_v16_to_v17.html   # Linux
open upgrade_v16_to_v17.html       # macOS
```

**Markdown** — for pasting into GitHub issues, PR descriptions, Slack,
or committing into a repo alongside a release. Produces a single
`.md` file with tables per model and a summary section at the bottom.

```bash
odoo-inspect report \
  --diff knowledge/diffs/v16_to_v17.json \
  --format markdown \
  --output upgrade_v16_to_v17.md
```

If you omit `--output`, the report is printed to stdout — handy for
piping into a pager or another tool.

### Full end-to-end example

```bash
# 1. Scan both versions
odoo-inspect scan --community /path/to/odoo16/odoo/addons \
                  --version 16.0 --output knowledge/v16

odoo-inspect scan --community /path/to/odoo17/odoo/addons \
                  --version 17.0 --output knowledge/v17

# 2. Compute the diff
odoo-inspect diff --old knowledge/v16 --new knowledge/v17 \
                  --output knowledge/diffs/v16_to_v17.json

# 3. Render a human-readable report
odoo-inspect report --diff knowledge/diffs/v16_to_v17.json \
                    --format html --output upgrade_v16_to_v17.html
```

## Quickstart — Python API

```python
from pathlib import Path
from odoo_inspect import Scanner, Differ, Router, KnowledgeLoader

# Scan from source
scanner = Scanner(
    community_paths=[Path("/path/to/odoo17/odoo/addons")],
    enterprise_paths=[Path("/path/to/enterprise17")],
    version="17.0",
)
result = scanner.scan()
result.write(Path("knowledge/v17"))

# Load and diff
old = KnowledgeLoader(Path("knowledge/v16")).load()
new = KnowledgeLoader(Path("knowledge/v17")).load()
diff = Differ(old, new).diff()

print(f"Added models:   {len(diff.models_added)}")
print(f"Removed models: {len(diff.models_removed)}")
print(f"Models with field changes: {len(diff.field_changes)}")

# Build a router
router = Router(new).build()
router.write(Path("router/v17"))

# Render the diff as a single-file HTML report
from odoo_inspect.writers import write_html_diff, write_markdown_diff
write_html_diff(diff, Path("upgrade_v16_to_v17.html"))
write_markdown_diff(diff, Path("upgrade_v16_to_v17.md"))
```

## Output format

```
knowledge/v17/
  models/<model.name>.json   one file per merged model
  _meta.json                 version info, module index, model index, stats
  _financial.json            account-type classification for this version
```

Every field entry records the module that contributed it in a `source`
field. When the same field is declared in a base module and then replaced
by an extension, the base module's declaration is kept under
`original_source`.

See [`docs/output-format.md`](docs/output-format.md) for the full schema.

## How merging works

For each `models.Model` / `TransientModel` / `AbstractModel` class:

| `_name` | `_inherit` | Treated as |
|---|---|---|
| set, not in `_inherit` | — | **definition** (locks base module) |
| set, equals `_inherit` (`_name = _inherit = 'x'`) | — | **in-place extension** |
| set, `_inherit` = different model | — | **new model from parent** (parent tracked in `inherits`) |
| not set, `_inherit` = existing model | — | **extension** (fields/methods merged in, source-tagged) |
| set, `_inherit` = list of mixins | — | **definition with mixins** |

The scanner makes two passes:

1. Collect every class across every scanned module.
2. Apply **definitions first**, so every model's `base_module` is locked
   by its real owner before any extension can influence it. Only after
   all definitions are in does the merger walk the extensions.

This is what keeps `res.partner.base_module` equal to `base` instead of
getting overwritten by whichever `_inherit = 'res.partner'` extension
happened to be scanned first.

## Module filtering

`test_*`, `hw_*`, `theme_*`, most `l10n_*` (except `l10n_in` and
`l10n_generic_coa`), `bus`, `web`, `base_setup`, `iap`, `gamification`,
`auth_*`, `google_*`, and `microsoft_*` are skipped by default. Priority
modules (`base`, `sale`, `account`, `stock`, `crm`, `hr`, `mail`, …) are
tagged `priority` in `_meta.json`; everything else is still scanned but
tagged `extra`.

Override these lists by editing the constants in
`src/odoo_inspect/constants.py`, or by passing a custom `ScanConfig` to
the `Scanner` class.

## License

MIT. See [LICENSE](LICENSE).
