Compare commits

..

11 Commits

Author SHA1 Message Date
gsinghpal
ba7c028c30 CHANGES 2026-06-04 09:49:51 -04:00
gsinghpal
41ce3784d7 fix(fusion_plating_reports): page-break-inside on CoC part rows
Final-review follow-up: per the entech wkhtmltopdf rule, page-break-inside
must sit on each <tr>, not just the <table>, so a large multi-part cert
can't split a part row mid-row under the company header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:59:29 -04:00
gsinghpal
98873c4e39 feat(fusion_plating): cert backfill migration + version bumps
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:54:54 -04:00
gsinghpal
28e5e7f9de feat(fusion_plating_jobs): group WOs by recipe step structure
Replace the old 5-tuple (recipe.id, part, spec, thickness, serial) grouping
key with a structural signature so multiple parts that share the same recipe
step tree (ENP clones) collapse onto one combined work order. Add three
helpers: _fp_recipe_signature, _fp_line_express_signature, _fp_line_group_key.
Add TransactionCase test covering merge, non-merge, and masking-split cases.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-03 22:49:51 -04:00
gsinghpal
c71c60350b feat(fusion_plating_jobs): traveller lists all parts in the batch
Additive 'Batch parts' roster in the Item Information cell, shown when a
WO covers 2+ distinct parts (the labeled block still details the primary
part). Keeps the existing table layout intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:41:16 -04:00
gsinghpal
ba1e15da07 feat(fusion_plating_reports): CoC parts table loops part_line_ids 2026-06-03 22:39:20 -04:00
gsinghpal
f1bf5b214c feat(fusion_plating_jobs): multi-part cert creation + requirement union
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:37:35 -04:00
gsinghpal
983e576fdc feat(fusion_plating_certificates): Parts page on certificate form 2026-06-03 22:28:27 -04:00
gsinghpal
7cbf4f25df feat(fusion_plating_certificates): add fp.certificate.part child model + ACL
Adds the fp.certificate.part model (one row per part on a combined CoC),
the part_line_ids O2M on fp.certificate, and ACL rows for all three
plating roles. No views yet — Task 2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:27:00 -04:00
gsinghpal
e35c120af8 docs(fusion_plating): implementation plan - WO grouping by recipe + combined CoC
8 bite-sized tasks (cert part-line model -> form -> creation -> CoC report
-> traveller -> grouping switch -> migration -> verify). Cert multi-part
support lands before the grouping flip so it is never a compliance
regression. Tests are committed TransactionCase artifacts run on an
Enterprise env (local Community cannot install fusion_plating); plus a
read-only entech signature smoke.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 22:01:12 -04:00
gsinghpal
e34892f5c0 docs(fusion_plating): spec - WO grouping by recipe structure + combined multi-part CoC
Group SO lines into one fp.job per distinct plating process (identical
recipe step structure) instead of one WO per line; make the Certificate
of Conformance multi-part via a new fp.certificate.part child model + CoC
parts-table loop + migration backfill.

Grounded in a read-only entech audit: 13 WOs -> 4 on real orders;
per-part recipe clones are structurally identical (same node_type +
kind_code + name sequence). cloned_from_id/process_type_id are empty on
existing data, so grouping keys off the step structure.

Phase 1 (this spec): grouping + combined cert + report + traveller +
migration. Phase 2 (deferred): per-part thickness + per-part stickers.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 21:52:50 -04:00
6715 changed files with 1273308 additions and 356 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@@ -1,12 +0,0 @@
{
"permissions": {
"allow": [
"WebFetch(domain:docs.clover.com)"
]
},
"hooks": {
"UserPromptSubmit": [],
"Stop": [],
"Notification": []
}
}

54
.gitignore vendored
View File

@@ -15,56 +15,4 @@ __pycache__/
# Local-only diagnostic logs from test runs # Local-only diagnostic logs from test runs
_test_*.log _test_*.log
.superpowers/
# --- Split-out module repos (now independent git repos; managed separately) ---
/disable_iap_calls/
/disable_odoo_online/
/disable_publisher_warranty/
/fusion_accounts/
/fusion_api/
/fusion_canada_post/
/fusion_centralize_billing/
/fusion_chatter_enhance/
/fusion_claims/
/fusion_clock/
/fusion_clock_ai/
/fusion_clover/
/fusion_digitize/
/fusion_faxes/
/fusion_helpdesk/
/fusion_helpdesk_central/
/fusion_inventory/
/fusion_loaners_management/
/fusion_login_audit/
/fusion_ltc_management/
/fusion_notes/
/fusion_odoo_fixes/
/fusion_payroll/
/fusion_pdf_preview/
/fusion_planning/
/fusion_portal/
/fusion_poynt/
/fusion_rental/
/fusion_repairs/
/fusion_reports_templates/
/fusion_ringcentral/
/fusion_schedule/
/fusion_service_charges/
/fusion_shipping/
/fusion_so_to_po/
/fusion_tasks/
/fusion_templates/
/fusion_theme_switcher/
/fusion_voip_ringcentral/
/fusion_whitelabels/
/network_logger/
/nexa_coa_setup/
/fusion_plating/
/fusion_accounting/
/fusion_iot/
/fusion_labels/
/fusion_projects/
/fusion-statements/
/fusion-woo-odoo/
/fusion-expenses/
/fusion_configurator/

58
SYNC.md
View File

@@ -1,58 +0,0 @@
# Syncing Odoo-Modules across machines (Mac + Windows)
Each module/suite folder here is its **own git repo** (private on GitHub at
`gsinghpal/<name>`, mirrored to gitea `admin/<name>`). This parent folder is a
separate repo that holds the shared files (CLAUDE.md, docs, scripts, these sync
helpers). The cloud (GitHub) is the hub: both machines push to it and pull from it.
Nothing here ever deletes your work. Pulls are fast-forward only, so local changes
are never overwritten; pushes only send commits.
## First-time setup on a new machine (e.g. the Windows PC)
1. Install **Git** (Git for Windows includes "Git Bash", which runs these scripts).
2. Sign in to GitHub once so git can push/pull:
- easiest: `gh auth login` (or let Git Credential Manager prompt on first pull)
3. Get everything:
```
git clone https://github.com/gsinghpal/Odoo-Modules.git
cd Odoo-Modules
bash sync-clone-all.sh
```
That clones the parent, then all 49 module repos into place.
(gitea is an optional second mirror. The first push to it will ask for your
`git.nexasystems.ca` login. If you only use GitHub, those gitea lines just fail
quietly and GitHub stays the source of truth.)
## Daily workflow (same on Mac and Windows)
- **Before you start:** `bash sync-pull-all.sh` - pulls the latest for the parent
and every module. Anything with local changes or a diverged history is skipped and
listed, so you can handle it yourself.
- **Do your work**, then **commit inside the module(s) you changed**:
```
cd fusion_clock
git add -A
git commit -m "..."
cd ..
```
- **When done:** `bash sync-push-all.sh` - pushes every committed change to GitHub
+ gitea, and flags any repo that still has uncommitted changes (so nothing is
silently left behind).
## Golden rule for two machines
Push from the machine you worked on **before** you switch to the other one, and run
`sync-pull-all.sh` on the other machine **before** you start. That keeps both in sync
and avoids diverged histories.
## Helper scripts
| Script | What it does |
|--------|--------------|
| `sync-clone-all.sh` | Clone any module repo listed in `repos.txt` that isn't here yet. |
| `sync-pull-all.sh` | Fast-forward pull the parent + all modules (safe, never clobbers). |
| `sync-push-all.sh` | Push committed work for the parent + all modules to GitHub + gitea. |
| `sync-refresh-list.sh` | Rebuild `repos.txt` from the repos present here (after adding/removing a module). |
| `repos.txt` | The list of module repo names the scripts act on. |

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
{
'name': 'Disable IAP Calls',
'version': '19.0.1.0.0',
'category': 'Tools',
'summary': 'Disables all IAP (In-App Purchase) external API calls',
'description': """
This module completely disables:
- IAP service calls to Odoo servers
- OCR/Extract API calls
- Lead enrichment API calls
- Any other external Odoo API communication
For local development use only.
""",
'author': 'Development',
'depends': ['iap'],
'data': [],
'installable': True,
'auto_install': True,
'license': 'LGPL-3',
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
{
'name': 'Disable IAP Calls',
'version': '19.0.1.0.0',
'category': 'Tools',
'summary': 'Disables all IAP (In-App Purchase) external API calls',
'description': """
This module completely disables:
- IAP service calls to Odoo servers
- OCR/Extract API calls
- Lead enrichment API calls
- Any other external Odoo API communication
For local development use only.
""",
'author': 'Development',
'depends': ['iap'],
'data': [],
'installable': True,
'auto_install': True,
'license': 'LGPL-3',
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import iap_account

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Disable all IAP external API calls for local development
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class IapAccountDisabled(models.Model):
_inherit = 'iap.account'
@api.model
def get_credits(self, service_name):
"""
DISABLED: Return fake unlimited credits
"""
_logger.info("IAP get_credits DISABLED - returning unlimited credits for %s", service_name)
return 999999

View File

@@ -0,0 +1,86 @@
# Graph Report - /Users/gurpreet/Github/Odoo-Modules/disable_iap_calls (2026-04-22)
## Corpus Check
- 8 files · ~284 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 11 nodes · 8 edges · 8 communities detected
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
- [[_COMMUNITY_Community 0|Community 0]]
- [[_COMMUNITY_Community 1|Community 1]]
- [[_COMMUNITY_Community 2|Community 2]]
- [[_COMMUNITY_Community 3|Community 3]]
- [[_COMMUNITY_Community 4|Community 4]]
- [[_COMMUNITY_Community 5|Community 5]]
- [[_COMMUNITY_Community 6|Community 6]]
- [[_COMMUNITY_Community 7|Community 7]]
## God Nodes (most connected - your core abstractions)
1. `IapAccountDisabled` - 2 edges
2. `get_credits()` - 2 edges
3. `DISABLED: Return fake unlimited credits` - 0 edges
## Surprising Connections (you probably didn't know these)
- None detected - all connections are within the same source files.
## Communities
### Community 0 - "Community 0"
Cohesion: 0.67
Nodes (2): get_credits(), IapAccountDisabled
### Community 1 - "Community 1"
Cohesion: 1.0
Nodes (0):
### Community 2 - "Community 2"
Cohesion: 1.0
Nodes (0):
### Community 3 - "Community 3"
Cohesion: 1.0
Nodes (0):
### Community 4 - "Community 4"
Cohesion: 1.0
Nodes (0):
### Community 5 - "Community 5"
Cohesion: 1.0
Nodes (0):
### Community 6 - "Community 6"
Cohesion: 1.0
Nodes (1): DISABLED: Return fake unlimited credits
### Community 7 - "Community 7"
Cohesion: 1.0
Nodes (0):
## Knowledge Gaps
- **1 isolated node(s):** `DISABLED: Return fake unlimited credits`
These have ≤1 connection - possible missing edges or undocumented components.
- **Thin community `Community 1`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 2`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 3`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 4`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 5`** (1 nodes): `__manifest__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 6`** (1 nodes): `DISABLED: Return fake unlimited credits`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 7`** (1 nodes): `__manifest__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **What connects `DISABLED: Return fake unlimited credits` to the rest of the system?**
_1 weakly-connected nodes found - possible documentation gaps or missing edges._

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py", "target": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py", "target": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py", "label": "iap_account.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L1"}, {"id": "iap_account_iapaccountdisabled", "label": "IapAccountDisabled", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L10"}, {"id": "iap_account_get_credits", "label": "get_credits()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L14"}, {"id": "iap_account_rationale_15", "label": "DISABLED: Return fake unlimited credits", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L15"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L4", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L5", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py", "target": "iap_account_iapaccountdisabled", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L10", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py", "target": "iap_account_get_credits", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L14", "weight": 1.0}, {"source": "iap_account_rationale_15", "target": "iap_account_iapaccountdisabled_get_credits", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L15", "weight": 1.0}], "raw_calls": [{"caller_nid": "iap_account_get_credits", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py", "source_location": "L18"}]}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py", "label": "iap_account.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L1"}, {"id": "iap_account_iapaccountdisabled", "label": "IapAccountDisabled", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L10"}, {"id": "iap_account_get_credits", "label": "get_credits()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L14"}, {"id": "iap_account_rationale_15", "label": "DISABLED: Return fake unlimited credits", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L15"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L4", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L5", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py", "target": "iap_account_iapaccountdisabled", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L10", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py", "target": "iap_account_get_credits", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L14", "weight": 1.0}, {"source": "iap_account_rationale_15", "target": "iap_account_iapaccountdisabled_get_credits", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L15", "weight": 1.0}], "raw_calls": [{"caller_nid": "iap_account_get_credits", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py", "source_location": "L18"}]}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,205 @@
{
"directed": false,
"multigraph": false,
"graph": {},
"nodes": [
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py",
"community": 1,
"norm_label": "__init__.py"
},
{
"label": "__manifest__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/__manifest__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_manifest_py",
"community": 5,
"norm_label": "__manifest__.py"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py",
"community": 2,
"norm_label": "__init__.py"
},
{
"label": "iap_account.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py",
"community": 0,
"norm_label": "iap_account.py"
},
{
"label": "IapAccountDisabled",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py",
"source_location": "L10",
"id": "iap_account_iapaccountdisabled",
"community": 0,
"norm_label": "iapaccountdisabled"
},
{
"label": "get_credits()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py",
"source_location": "L14",
"id": "iap_account_get_credits",
"community": 0,
"norm_label": "get_credits()"
},
{
"label": "DISABLED: Return fake unlimited credits",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py",
"source_location": "L15",
"id": "iap_account_rationale_15",
"community": 6,
"norm_label": "disabled: return fake unlimited credits"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py",
"community": 3,
"norm_label": "__init__.py"
},
{
"label": "__manifest__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/__manifest__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_manifest_py",
"community": 7,
"norm_label": "__manifest__.py"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py",
"community": 4,
"norm_label": "__init__.py"
},
{
"label": "iap_account.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py",
"community": 0,
"norm_label": "iap_account.py"
}
],
"links": [
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_iap_calls_init_py",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_init_py",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py",
"source_location": "L10",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py",
"_tgt": "iap_account_iapaccountdisabled",
"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py",
"target": "iap_account_iapaccountdisabled",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/models/iap_account.py",
"source_location": "L14",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py",
"_tgt": "iap_account_get_credits",
"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_models_iap_account_py",
"target": "iap_account_get_credits",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py",
"source_location": "L10",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py",
"_tgt": "iap_account_iapaccountdisabled",
"source": "iap_account_iapaccountdisabled",
"target": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/iap_account.py",
"source_location": "L14",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py",
"_tgt": "iap_account_get_credits",
"source": "iap_account_get_credits",
"target": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_iap_account_py",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_init_py",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_iap_calls/disable_iap_calls/models/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_iap_calls_disable_iap_calls_models_init_py",
"confidence_score": 1.0
}
],
"hyperedges": []
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import iap_account

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Disable all IAP external API calls for local development
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class IapAccountDisabled(models.Model):
_inherit = 'iap.account'
@api.model
def get_credits(self, service_name):
"""
DISABLED: Return fake unlimited credits
"""
_logger.info("IAP get_credits DISABLED - returning unlimited credits for %s", service_name)
return 999999

View File

@@ -0,0 +1,143 @@
# Disable Odoo Online Services
**Version:** 18.0.1.0.0
**License:** LGPL-3
**Odoo Version:** 18.0
## Overview
This module comprehensively disables all external communications between your Odoo instance and Odoo's servers. It prevents:
- License/subscription checks
- User count reporting
- IAP (In-App Purchase) credit checks
- Publisher warranty communications
- Partner autocomplete/enrichment
- Expiration warnings in the UI
## Features
### 1. IAP JSON-RPC Blocking
Patches the core `iap_jsonrpc` function to prevent all IAP API calls:
- Returns fake successful responses
- Logs all blocked calls
- Provides unlimited credits for services that check
### 2. License Parameter Protection
Protects critical `ir.config_parameter` values:
- `database.expiration_date` → Always returns `2099-12-31 23:59:59`
- `database.expiration_reason` → Always returns `renewal`
- `database.enterprise_code` → Always returns `PERMANENT_LOCAL`
### 3. Session Info Patching
Modifies `session_info()` to prevent frontend warnings:
- Sets expiration date to 2099
- Sets `warning` to `False`
- Removes "already linked" subscription prompts
### 4. User Creation Protection
Logs user creation without triggering subscription checks:
- Blocks any external validation
- Logs permission changes
### 5. Publisher Warranty Block
Disables all warranty-related server communication:
- `_get_sys_logs()` → Returns empty response
- `update_notification()` → Returns success without calling server
### 6. Cron Job Blocking
Blocks scheduled actions that contact Odoo:
- Publisher Warranty Check
- Database Auto-Expiration Check
- Various IAP-related crons
## Installation
1. Copy the module to your Odoo addons directory
2. Restart Odoo
3. Go to Apps → Update Apps List
4. Search for "Disable Odoo Online Services"
5. Click Install
## Verification
Check that blocking is active:
```bash
docker logs odoo-container 2>&1 | grep -i "BLOCKED\|DISABLED"
```
Expected output:
```
IAP JSON-RPC calls have been DISABLED globally
Module update_list: Scanning local addons only (Odoo Apps store disabled)
Publisher warranty update_notification BLOCKED
Creating 1 user(s) - subscription check DISABLED
```
## Configuration
No configuration required. The module automatically:
- Sets permanent expiration values on install (via `_post_init_hook`)
- Patches all necessary functions when loaded
- Protects values from being changed
## Technical Details
### Files
| File | Purpose |
|------|---------|
| `models/disable_iap_tools.py` | Patches `iap_jsonrpc` globally |
| `models/disable_online_services.py` | Blocks publisher warranty, cron jobs |
| `models/disable_database_expiration.py` | Protects `ir.config_parameter` |
| `models/disable_session_leaks.py` | Patches session info, user creation |
| `models/disable_partner_autocomplete.py` | Blocks partner enrichment |
| `models/disable_all_external.py` | Additional external call blocks |
### Blocked Endpoints
All redirected to `http://localhost:65535`:
- `iap.endpoint`
- `publisher_warranty_url`
- `partner_autocomplete.endpoint`
- `iap_extract_endpoint`
- `olg.endpoint`
- `mail.media_library_endpoint`
- `sms.endpoint`
- `crm.iap_lead_mining.endpoint`
- And many more...
## Dependencies
- `base`
- `web`
- `iap`
- `mail`
- `base_setup`
## Compatibility
- Odoo 18.0 Community Edition
- Odoo 18.0 Enterprise Edition
## Disclaimer
This module is intended for legitimate use cases such as:
- Air-gapped environments
- Development/testing instances
- Self-hosted deployments with proper licensing
Ensure you comply with Odoo's licensing terms for your use case.
## Changelog
### 1.0.0 (2025-12-29)
- Initial release
- IAP blocking
- Publisher warranty blocking
- Session info patching
- User creation protection
- Config parameter protection

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
from . import models
def _post_init_hook(env):
"""
Set all configuration parameters to disable external Odoo services.
This runs after module installation.
"""
import logging
_logger = logging.getLogger(__name__)
set_param = env['ir.config_parameter'].sudo().set_param
# Set permanent database expiration
params_to_set = {
# Database license parameters
'database.expiration_date': '2099-12-31 23:59:59',
'database.expiration_reason': 'renewal',
'database.enterprise_code': 'PERMANENT_LOCAL',
# Clear "already linked" parameters
'database.already_linked_subscription_url': '',
'database.already_linked_email': '',
'database.already_linked_send_mail_url': '',
# Redirect all IAP endpoints to localhost
'iap.endpoint': 'http://localhost:65535',
'partner_autocomplete.endpoint': 'http://localhost:65535',
'iap_extract_endpoint': 'http://localhost:65535',
'olg.endpoint': 'http://localhost:65535',
'mail.media_library_endpoint': 'http://localhost:65535',
'website.api_endpoint': 'http://localhost:65535',
'sms.endpoint': 'http://localhost:65535',
'crm.iap_lead_mining.endpoint': 'http://localhost:65535',
'reveal.endpoint': 'http://localhost:65535',
'publisher_warranty_url': 'http://localhost:65535',
# OCN (Odoo Cloud Notification) - blocks push notifications to Odoo
'odoo_ocn.endpoint': 'http://localhost:65535', # Main OCN endpoint
'mail_mobile.enable_ocn': 'False', # Disable OCN push notifications
'odoo_ocn.project_id': '', # Clear any registered project
'ocn.uuid': '', # Clear OCN UUID to prevent registration
# Snailmail (physical mail service)
'snailmail.endpoint': 'http://localhost:65535',
# Social media IAP
'social.facebook_endpoint': 'http://localhost:65535',
'social.twitter_endpoint': 'http://localhost:65535',
'social.linkedin_endpoint': 'http://localhost:65535',
}
_logger.info("=" * 60)
_logger.info("DISABLE ODOO ONLINE: Setting configuration parameters")
_logger.info("=" * 60)
for key, value in params_to_set.items():
try:
set_param(key, value)
_logger.info("Set %s = %s", key, value if len(str(value)) < 30 else value[:30] + "...")
except Exception as e:
_logger.warning("Could not set %s: %s", key, e)
_logger.info("=" * 60)
_logger.info("DISABLE ODOO ONLINE: Configuration complete")
_logger.info("=" * 60)

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
{
'name': 'Disable Odoo Online Services',
'version': '19.0.1.0.0',
'category': 'Tools',
'summary': 'Blocks ALL external Odoo server communications',
'description': """
Comprehensive Module to Disable ALL Odoo Online Services
=========================================================
This module completely blocks all external communications from Odoo to Odoo's servers.
**Blocked Services:**
- Publisher Warranty checks (license validation)
- IAP (In-App Purchase) - All services
- Partner Autocomplete API
- Company Enrichment API
- VAT Lookup API
- SMS API
- Invoice/Expense OCR Extract
- Media Library (Stock Images)
- Currency Rate Live Updates
- CRM Lead Mining
- CRM Reveal (Website visitor identification)
- Google Calendar Sync
- AI/OLG Content Generation
- Database Registration
- Module Update checks from Odoo Store
- Session-based license detection
- Frontend expiration panel warnings
**Use Cases:**
- Air-gapped installations
- Local development without internet
- Enterprise deployments that don't want telemetry
- Testing environments
**WARNING:** This module disables legitimate Odoo services.
Only use if you understand the implications.
""",
'author': 'Fusion Development',
'website': 'https://fusiondevelopment.com',
'license': 'LGPL-3',
'depends': ['base', 'mail', 'web'],
'data': [
'data/disable_external_services.xml',
],
'assets': {
'web.assets_backend': [
'disable_odoo_online/static/src/js/disable_external_links.js',
],
},
'installable': True,
'auto_install': False,
'application': False,
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- All config parameters are set via post_init_hook in __init__.py -->
<!-- This file is kept for future data records if needed -->
</odoo>

View File

@@ -0,0 +1,143 @@
# Disable Odoo Online Services
**Version:** 18.0.1.0.0
**License:** LGPL-3
**Odoo Version:** 18.0
## Overview
This module comprehensively disables all external communications between your Odoo instance and Odoo's servers. It prevents:
- License/subscription checks
- User count reporting
- IAP (In-App Purchase) credit checks
- Publisher warranty communications
- Partner autocomplete/enrichment
- Expiration warnings in the UI
## Features
### 1. IAP JSON-RPC Blocking
Patches the core `iap_jsonrpc` function to prevent all IAP API calls:
- Returns fake successful responses
- Logs all blocked calls
- Provides unlimited credits for services that check
### 2. License Parameter Protection
Protects critical `ir.config_parameter` values:
- `database.expiration_date` → Always returns `2099-12-31 23:59:59`
- `database.expiration_reason` → Always returns `renewal`
- `database.enterprise_code` → Always returns `PERMANENT_LOCAL`
### 3. Session Info Patching
Modifies `session_info()` to prevent frontend warnings:
- Sets expiration date to 2099
- Sets `warning` to `False`
- Removes "already linked" subscription prompts
### 4. User Creation Protection
Logs user creation without triggering subscription checks:
- Blocks any external validation
- Logs permission changes
### 5. Publisher Warranty Block
Disables all warranty-related server communication:
- `_get_sys_logs()` → Returns empty response
- `update_notification()` → Returns success without calling server
### 6. Cron Job Blocking
Blocks scheduled actions that contact Odoo:
- Publisher Warranty Check
- Database Auto-Expiration Check
- Various IAP-related crons
## Installation
1. Copy the module to your Odoo addons directory
2. Restart Odoo
3. Go to Apps → Update Apps List
4. Search for "Disable Odoo Online Services"
5. Click Install
## Verification
Check that blocking is active:
```bash
docker logs odoo-container 2>&1 | grep -i "BLOCKED\|DISABLED"
```
Expected output:
```
IAP JSON-RPC calls have been DISABLED globally
Module update_list: Scanning local addons only (Odoo Apps store disabled)
Publisher warranty update_notification BLOCKED
Creating 1 user(s) - subscription check DISABLED
```
## Configuration
No configuration required. The module automatically:
- Sets permanent expiration values on install (via `_post_init_hook`)
- Patches all necessary functions when loaded
- Protects values from being changed
## Technical Details
### Files
| File | Purpose |
|------|---------|
| `models/disable_iap_tools.py` | Patches `iap_jsonrpc` globally |
| `models/disable_online_services.py` | Blocks publisher warranty, cron jobs |
| `models/disable_database_expiration.py` | Protects `ir.config_parameter` |
| `models/disable_session_leaks.py` | Patches session info, user creation |
| `models/disable_partner_autocomplete.py` | Blocks partner enrichment |
| `models/disable_all_external.py` | Additional external call blocks |
### Blocked Endpoints
All redirected to `http://localhost:65535`:
- `iap.endpoint`
- `publisher_warranty_url`
- `partner_autocomplete.endpoint`
- `iap_extract_endpoint`
- `olg.endpoint`
- `mail.media_library_endpoint`
- `sms.endpoint`
- `crm.iap_lead_mining.endpoint`
- And many more...
## Dependencies
- `base`
- `web`
- `iap`
- `mail`
- `base_setup`
## Compatibility
- Odoo 18.0 Community Edition
- Odoo 18.0 Enterprise Edition
## Disclaimer
This module is intended for legitimate use cases such as:
- Air-gapped environments
- Development/testing instances
- Self-hosted deployments with proper licensing
Ensure you comply with Odoo's licensing terms for your use case.
## Changelog
### 1.0.0 (2025-12-29)
- Initial release
- IAP blocking
- Publisher warranty blocking
- Session info patching
- User creation protection
- Config parameter protection

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
from . import models
def _post_init_hook(env):
"""
Set all configuration parameters to disable external Odoo services.
This runs after module installation.
"""
import logging
_logger = logging.getLogger(__name__)
set_param = env['ir.config_parameter'].sudo().set_param
# Set permanent database expiration
params_to_set = {
# Database license parameters
'database.expiration_date': '2099-12-31 23:59:59',
'database.expiration_reason': 'renewal',
'database.enterprise_code': 'PERMANENT_LOCAL',
# Clear "already linked" parameters
'database.already_linked_subscription_url': '',
'database.already_linked_email': '',
'database.already_linked_send_mail_url': '',
# Redirect all IAP endpoints to localhost
'iap.endpoint': 'http://localhost:65535',
'partner_autocomplete.endpoint': 'http://localhost:65535',
'iap_extract_endpoint': 'http://localhost:65535',
'olg.endpoint': 'http://localhost:65535',
'mail.media_library_endpoint': 'http://localhost:65535',
'website.api_endpoint': 'http://localhost:65535',
'sms.endpoint': 'http://localhost:65535',
'crm.iap_lead_mining.endpoint': 'http://localhost:65535',
'reveal.endpoint': 'http://localhost:65535',
'publisher_warranty_url': 'http://localhost:65535',
# OCN (Odoo Cloud Notification) - blocks push notifications to Odoo
'odoo_ocn.endpoint': 'http://localhost:65535', # Main OCN endpoint
'mail_mobile.enable_ocn': 'False', # Disable OCN push notifications
'odoo_ocn.project_id': '', # Clear any registered project
'ocn.uuid': '', # Clear OCN UUID to prevent registration
# Snailmail (physical mail service)
'snailmail.endpoint': 'http://localhost:65535',
# Social media IAP
'social.facebook_endpoint': 'http://localhost:65535',
'social.twitter_endpoint': 'http://localhost:65535',
'social.linkedin_endpoint': 'http://localhost:65535',
}
_logger.info("=" * 60)
_logger.info("DISABLE ODOO ONLINE: Setting configuration parameters")
_logger.info("=" * 60)
for key, value in params_to_set.items():
try:
set_param(key, value)
_logger.info("Set %s = %s", key, value if len(str(value)) < 30 else value[:30] + "...")
except Exception as e:
_logger.warning("Could not set %s: %s", key, e)
_logger.info("=" * 60)
_logger.info("DISABLE ODOO ONLINE: Configuration complete")
_logger.info("=" * 60)

View File

@@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
{
'name': 'Disable Odoo Online Services',
'version': '19.0.1.0.0',
'category': 'Tools',
'summary': 'Blocks ALL external Odoo server communications',
'description': """
Comprehensive Module to Disable ALL Odoo Online Services
=========================================================
This module completely blocks all external communications from Odoo to Odoo's servers.
**Blocked Services:**
- Publisher Warranty checks (license validation)
- IAP (In-App Purchase) - All services
- Partner Autocomplete API
- Company Enrichment API
- VAT Lookup API
- SMS API
- Invoice/Expense OCR Extract
- Media Library (Stock Images)
- Currency Rate Live Updates
- CRM Lead Mining
- CRM Reveal (Website visitor identification)
- Google Calendar Sync
- AI/OLG Content Generation
- Database Registration
- Module Update checks from Odoo Store
- Session-based license detection
- Frontend expiration panel warnings
**Use Cases:**
- Air-gapped installations
- Local development without internet
- Enterprise deployments that don't want telemetry
- Testing environments
**WARNING:** This module disables legitimate Odoo services.
Only use if you understand the implications.
""",
'author': 'Fusion Development',
'website': 'https://fusiondevelopment.com',
'license': 'LGPL-3',
'depends': ['base', 'mail', 'web'],
'data': [
'data/disable_external_services.xml',
],
'assets': {
'web.assets_backend': [
'disable_odoo_online/static/src/js/disable_external_links.js',
],
},
'installable': True,
'auto_install': False,
'application': False,
}

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<!-- All config parameters are set via post_init_hook in __init__.py -->
<!-- This file is kept for future data records if needed -->
</odoo>

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from . import disable_iap_tools # Patches iap_jsonrpc globally - MUST be first
from . import disable_http_requests # Patches requests library to block Odoo domains
from . import disable_online_services
from . import disable_partner_autocomplete
from . import disable_database_expiration
from . import disable_all_external
from . import disable_session_leaks

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""
Comprehensive blocking of ALL external Odoo service calls.
Only inherits from models that are guaranteed to exist in base Odoo.
"""
import logging
from odoo import api, models, fields
_logger = logging.getLogger(__name__)
# ============================================================
# Block Currency Rate Live Updates - Uses res.currency which always exists
# ============================================================
class ResCurrencyDisabled(models.Model):
_inherit = 'res.currency'
@api.model
def _get_rates_from_provider(self, provider, date):
"""DISABLED: Return empty rates."""
_logger.debug("Currency rate provider BLOCKED: provider=%s", provider)
return {}
# ============================================================
# Block Gravatar - Uses res.partner which always exists
# ============================================================
class ResPartnerDisabled(models.Model):
_inherit = 'res.partner'
@api.model
def _get_gravatar_image(self, email):
"""DISABLED: Return False to skip gravatar lookup."""
_logger.debug("Gravatar lookup BLOCKED for email=%s", email)
return False

View File

@@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
"""
Disable database expiration checks and registration.
Consolidates all ir.config_parameter overrides.
"""
import logging
from datetime import datetime
from odoo import api, models, fields
_logger = logging.getLogger(__name__)
class IrConfigParameter(models.Model):
"""Override config parameters to prevent expiration and protect license values."""
_inherit = 'ir.config_parameter'
PROTECTED_PARAMS = {
'database.expiration_date': '2099-12-31 23:59:59',
'database.expiration_reason': 'renewal',
'database.enterprise_code': 'PERMANENT_LOCAL',
}
CLEAR_PARAMS = [
'database.already_linked_subscription_url',
'database.already_linked_email',
'database.already_linked_send_mail_url',
]
def init(self, force=False):
"""Set permanent valid subscription on module init."""
super().init(force=force)
self._set_permanent_subscription()
@api.model
def _set_permanent_subscription(self):
"""Set database to never expire."""
_logger.info("Setting permanent subscription values...")
for key, value in self.PROTECTED_PARAMS.items():
try:
self.env.cr.execute("""
INSERT INTO ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date)
VALUES (%s, %s, %s, NOW() AT TIME ZONE 'UTC', %s, NOW() AT TIME ZONE 'UTC')
ON CONFLICT (key) DO UPDATE SET value = %s, write_date = NOW() AT TIME ZONE 'UTC'
""", (key, value, self.env.uid, self.env.uid, value))
except Exception as e:
_logger.debug("Could not set param %s: %s", key, e)
for key in self.CLEAR_PARAMS:
try:
self.env.cr.execute("""
INSERT INTO ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date)
VALUES (%s, '', %s, NOW() AT TIME ZONE 'UTC', %s, NOW() AT TIME ZONE 'UTC')
ON CONFLICT (key) DO UPDATE SET value = '', write_date = NOW() AT TIME ZONE 'UTC'
""", (key, self.env.uid, self.env.uid))
except Exception as e:
_logger.debug("Could not clear param %s: %s", key, e)
@api.model
def get_param(self, key, default=False):
"""Override get_param to return permanent values for protected params."""
if key in self.PROTECTED_PARAMS:
return self.PROTECTED_PARAMS[key]
if key in self.CLEAR_PARAMS:
return ''
return super().get_param(key, default)
def set_param(self, key, value):
"""Override set_param to prevent external processes from changing protected values."""
if key in self.PROTECTED_PARAMS:
if value != self.PROTECTED_PARAMS[key]:
_logger.warning("Blocked attempt to change protected param %s to %s", key, value)
return True
if key in self.CLEAR_PARAMS:
value = ''
return super().set_param(key, value)
class DatabaseExpirationCheck(models.AbstractModel):
_name = 'disable.odoo.online.expiration'
_description = 'Database Expiration Blocker'
@api.model
def check_database_expiration(self):
return {
'valid': True,
'expiration_date': '2099-12-31 23:59:59',
'expiration_reason': 'renewal',
}
class Base(models.AbstractModel):
_inherit = 'base'
@api.model
def _get_database_expiration_date(self):
return datetime(2099, 12, 31, 23, 59, 59)
@api.model
def _check_database_enterprise_expiration(self):
return True

View File

@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
"""
Block ALL outgoing HTTP requests to Odoo-related domains.
This patches the requests library to intercept and block external calls.
"""
import logging
import requests
from functools import wraps
from urllib.parse import urlparse
_logger = logging.getLogger(__name__)
# Domains to block - all Odoo external services
BLOCKED_DOMAINS = [
'odoo.com',
'odoofin.com',
'odoo.sh',
'iap.odoo.com',
'iap-services.odoo.com',
'partner-autocomplete.odoo.com',
'iap-extract.odoo.com',
'iap-sms.odoo.com',
'upgrade.odoo.com',
'apps.odoo.com',
'production.odoofin.com',
'plaid.com',
'yodlee.com',
'gravatar.com',
'www.gravatar.com',
'secure.gravatar.com',
]
# Store original functions
_original_request = None
_original_get = None
_original_post = None
def _is_blocked_url(url):
"""Check if the URL should be blocked."""
if not url:
return False
try:
parsed = urlparse(url)
domain = parsed.netloc.lower()
for blocked in BLOCKED_DOMAINS:
if blocked in domain:
return True
except Exception:
pass
return False
def _blocked_request(method, url, **kwargs):
"""Intercept and block requests to Odoo domains."""
if _is_blocked_url(url):
_logger.warning("HTTP REQUEST BLOCKED: %s %s", method.upper(), url)
# Return a mock response
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
return response
return _original_request(method, url, **kwargs)
def _blocked_get(url, **kwargs):
"""Intercept and block GET requests."""
if _is_blocked_url(url):
_logger.warning("HTTP GET BLOCKED: %s", url)
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
return response
return _original_get(url, **kwargs)
def _blocked_post(url, **kwargs):
"""Intercept and block POST requests."""
if _is_blocked_url(url):
_logger.warning("HTTP POST BLOCKED: %s", url)
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
return response
return _original_post(url, **kwargs)
def patch_requests():
"""Monkey-patch requests library to block Odoo domains."""
global _original_request, _original_get, _original_post
try:
if _original_request is None:
_original_request = requests.Session.request
_original_get = requests.get
_original_post = requests.post
# Patch Session.request (catches most calls)
def patched_session_request(self, method, url, **kwargs):
if _is_blocked_url(url):
_logger.warning("HTTP SESSION REQUEST BLOCKED: %s %s", method.upper(), url)
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
response.request = requests.models.PreparedRequest()
response.request.url = url
response.request.method = method
return response
return _original_request(self, method, url, **kwargs)
requests.Session.request = patched_session_request
requests.get = _blocked_get
requests.post = _blocked_post
_logger.info("HTTP requests to Odoo domains have been BLOCKED")
_logger.info("Blocked domains: %s", ', '.join(BLOCKED_DOMAINS))
except Exception as e:
_logger.warning("Could not patch requests library: %s", e)
# Apply patch when module is imported
patch_requests()

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
"""
Override the core IAP tools to block ALL external API calls.
This is the master switch that blocks ALL Odoo external communications.
"""
import logging
from odoo import exceptions, _
_logger = logging.getLogger(__name__)
# Store original function reference
_original_iap_jsonrpc = None
def _disabled_iap_jsonrpc(url, method='call', params=None, timeout=15):
"""
DISABLED: Block all IAP JSON-RPC calls.
Returns empty/success response instead of making external calls.
"""
_logger.info("IAP JSONRPC BLOCKED: %s (method=%s)", url, method)
# Return appropriate empty responses based on the endpoint
if '/authorize' in url:
return 'fake_transaction_token_disabled'
elif '/capture' in url or '/cancel' in url:
return True
elif '/credits' in url:
return 999999
elif 'partner-autocomplete' in url:
return []
elif 'enrich' in url:
return {}
elif 'sms' in url:
_logger.warning("SMS API call blocked - SMS will not be sent")
return {'state': 'success', 'credits': 999999}
elif 'extract' in url:
return {'status': 'success', 'credits': 999999}
else:
return {}
def patch_iap_tools():
"""
Monkey-patch the iap_jsonrpc function to block external calls.
This is called when the module loads.
"""
global _original_iap_jsonrpc
try:
from odoo.addons.iap.tools import iap_tools
if _original_iap_jsonrpc is None:
_original_iap_jsonrpc = iap_tools.iap_jsonrpc
iap_tools.iap_jsonrpc = _disabled_iap_jsonrpc
_logger.info("IAP JSON-RPC calls have been DISABLED globally")
except ImportError:
_logger.debug("IAP module not installed, skipping patch")
except Exception as e:
_logger.warning("Could not patch IAP tools: %s", e)
# Apply patch when module is imported
patch_iap_tools()

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
"""
Disable various Odoo online services and external API calls.
"""
import logging
from odoo import api, models, fields
_logger = logging.getLogger(__name__)
class IrModuleModule(models.Model):
"""Disable module update checks from Odoo store."""
_inherit = 'ir.module.module'
@api.model
def update_list(self):
"""
Override to prevent fetching from Odoo Apps store.
Only scan local addons paths.
"""
_logger.info("Module update_list: Scanning local addons only (Odoo Apps store disabled)")
return super().update_list()
def button_immediate_upgrade(self):
"""Prevent upgrade attempts that might contact Odoo."""
_logger.info("Module upgrade: Processing locally only")
return super().button_immediate_upgrade()
class IrCron(models.Model):
"""Disable scheduled actions that contact Odoo servers."""
_inherit = 'ir.cron'
def _callback(self, cron_name, server_action_id):
"""
Override to block certain cron jobs that contact Odoo.
Odoo 19 signature: _callback(self, cron_name, server_action_id)
"""
blocked_crons = [
'publisher',
'warranty',
'update_notification',
'database_expiration',
'iap_enrich',
'ocr',
'Invoice OCR',
'enrich leads',
'fetchmail',
'online sync',
]
cron_lower = (cron_name or '').lower()
for blocked in blocked_crons:
if blocked.lower() in cron_lower:
_logger.info("Cron BLOCKED (external call): %s", cron_name)
return False
return super()._callback(cron_name, server_action_id)
class ResConfigSettings(models.TransientModel):
"""Override config settings to prevent external service configuration."""
_inherit = 'res.config.settings'
def set_values(self):
"""Ensure certain settings stay disabled."""
res = super().set_values()
# Disable any auto-update settings and set permanent expiration
params = self.env['ir.config_parameter'].sudo()
params.set_param('database.expiration_date', '2099-12-31 23:59:59')
params.set_param('database.expiration_reason', 'renewal')
params.set_param('database.enterprise_code', 'PERMANENT_LOCAL')
# Disable IAP endpoint (redirect to nowhere)
params.set_param('iap.endpoint', 'http://localhost:65535')
# Disable various external services
params.set_param('partner_autocomplete.endpoint', 'http://localhost:65535')
params.set_param('iap_extract_endpoint', 'http://localhost:65535')
params.set_param('olg.endpoint', 'http://localhost:65535')
params.set_param('mail.media_library_endpoint', 'http://localhost:65535')
return res
class PublisherWarrantyContract(models.AbstractModel):
"""Completely disable publisher warranty checks."""
_inherit = 'publisher_warranty.contract'
@api.model
def _get_sys_logs(self):
"""
DISABLED: Do not contact Odoo servers.
Returns fake successful response.
"""
_logger.info("Publisher warranty _get_sys_logs BLOCKED")
return {
'messages': [],
'enterprise_info': {
'expiration_date': '2099-12-31 23:59:59',
'expiration_reason': 'renewal',
'enterprise_code': 'PERMANENT_LOCAL',
}
}
@api.model
def _get_message(self):
"""DISABLED: Return empty message."""
_logger.info("Publisher warranty _get_message BLOCKED")
return {}
def update_notification(self, cron_mode=True):
"""
DISABLED: Do not send any data to Odoo servers.
Just update local parameters with permanent values.
"""
_logger.info("Publisher warranty update_notification BLOCKED")
# Set permanent valid subscription parameters
params = self.env['ir.config_parameter'].sudo()
params.set_param('database.expiration_date', '2099-12-31 23:59:59')
params.set_param('database.expiration_reason', 'renewal')
params.set_param('database.enterprise_code', 'PERMANENT_LOCAL')
# Clear any "already linked" parameters
params.set_param('database.already_linked_subscription_url', '')
params.set_param('database.already_linked_email', '')
params.set_param('database.already_linked_send_mail_url', '')
return True
class IrHttp(models.AbstractModel):
"""Block certain routes that call external services."""
_inherit = 'ir.http'
@classmethod
def _pre_dispatch(cls, rule, arguments):
"""Log and potentially block external service routes."""
# List of route patterns that should be blocked
blocked_routes = [
'/iap/',
'/partner_autocomplete/',
'/google_',
'/ocr/',
'/sms/',
]
# Note: We don't actually block here as it might break functionality
# The actual blocking happens at the API/model level
return super()._pre_dispatch(rule, arguments)

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""
Disable Partner Autocomplete external API calls.
"""
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class ResPartner(models.Model):
"""Disable partner autocomplete from Odoo API."""
_inherit = 'res.partner'
@api.model
def autocomplete(self, query, timeout=15):
"""
DISABLED: Return empty results instead of calling Odoo's partner API.
"""
_logger.debug("Partner autocomplete DISABLED - returning empty results for: %s", query)
return []
@api.model
def enrich_company(self, company_domain, partner_gid, vat, timeout=15):
"""
DISABLED: Return empty data instead of calling Odoo's enrichment API.
"""
_logger.debug("Partner enrichment DISABLED - returning empty for domain: %s", company_domain)
return {}
@api.model
def read_by_vat(self, vat, timeout=15):
"""
DISABLED: Return empty data instead of calling Odoo's VAT lookup API.
"""
_logger.debug("Partner VAT lookup DISABLED - returning empty for VAT: %s", vat)
return {}
class ResCompany(models.Model):
"""Disable company autocomplete features."""
_inherit = 'res.company'
@api.model
def autocomplete(self, query, timeout=15):
"""
DISABLED: Return empty results for company autocomplete.
"""
_logger.debug("Company autocomplete DISABLED - returning empty results")
return []

View File

@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
"""
Block session-based information leaks and frontend detection mechanisms.
Specifically targets the web_enterprise module's subscription checks.
"""
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class IrHttp(models.AbstractModel):
"""
Override session info to prevent frontend from detecting license status.
This specifically blocks web_enterprise's ExpirationPanel from showing.
"""
_inherit = 'ir.http'
def session_info(self):
"""
Override session info to set permanent valid subscription data.
This prevents the frontend ExpirationPanel from showing warnings.
Key overrides:
- expiration_date: Set to far future (2099)
- expiration_reason: Set to 'renewal' (valid subscription)
- warning: Set to False to hide all warning banners
"""
result = super().session_info()
# Override expiration-related session data
# These are read by enterprise_subscription_service.js
result['expiration_date'] = '2099-12-31 23:59:59'
result['expiration_reason'] = 'renewal'
result['warning'] = False # Critical: prevents warning banners
# Remove any "already linked" subscription info
# These could trigger redirect prompts
result.pop('already_linked_subscription_url', None)
result.pop('already_linked_email', None)
result.pop('already_linked_send_mail_url', None)
_logger.debug("Session info patched - expiration set to 2099, warnings disabled")
return result
class ResUsers(models.Model):
"""
Override user creation/modification to prevent subscription checks.
When users are created, Odoo Enterprise normally contacts Odoo servers
to verify the subscription allows that many users.
"""
_inherit = 'res.users'
@api.model_create_multi
def create(self, vals_list):
"""
Override create to ensure no external subscription check is triggered.
The actual check happens in publisher_warranty.contract which we've
already blocked, but this is an extra safety measure.
"""
_logger.info("Creating %d user(s) - subscription check DISABLED", len(vals_list))
# Create users normally - no external checks will happen
# because publisher_warranty.contract.update_notification is blocked
users = super().create(vals_list)
# Don't trigger any warranty checks
return users
def write(self, vals):
"""
Override write to log user modifications.
"""
result = super().write(vals)
# If internal user status changed, log it
if 'share' in vals or 'groups_id' in vals:
_logger.info("User permissions updated - subscription check DISABLED")
return result

View File

@@ -0,0 +1,38 @@
/** @odoo-module **/
/**
* This module intercepts clicks on external Odoo links to prevent
* referrer leakage when users click help/documentation/upgrade links.
*/
import { browser } from "@web/core/browser/browser";
// Store original window.open
const originalOpen = browser.open;
// Override browser.open to add referrer protection
browser.open = function(url, target, features) {
if (url && typeof url === 'string') {
const urlLower = url.toLowerCase();
// Check if it's an Odoo external link
const odooPatterns = [
'odoo.com',
'odoo.sh',
'accounts.odoo',
];
const isOdooLink = odooPatterns.some(pattern => urlLower.includes(pattern));
if (isOdooLink) {
// For Odoo links, open with noreferrer to prevent leaking your domain
const newWindow = originalOpen.call(this, url, target || '_blank', 'noopener,noreferrer');
return newWindow;
}
}
return originalOpen.call(this, url, target, features);
};
console.log('[disable_odoo_online] External link protection loaded');

View File

@@ -0,0 +1,220 @@
# Graph Report - /Users/gurpreet/Github/Odoo-Modules/disable_odoo_online (2026-04-22)
## Corpus Check
- 22 files · ~5,870 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 106 nodes · 119 edges · 27 communities detected
- Extraction: 97% EXTRACTED · 3% INFERRED · 0% AMBIGUOUS · INFERRED: 3 edges (avg confidence: 0.8)
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
- [[_COMMUNITY_Community 0|Community 0]]
- [[_COMMUNITY_Community 1|Community 1]]
- [[_COMMUNITY_Community 2|Community 2]]
- [[_COMMUNITY_Community 3|Community 3]]
- [[_COMMUNITY_Community 4|Community 4]]
- [[_COMMUNITY_Community 5|Community 5]]
- [[_COMMUNITY_Community 6|Community 6]]
- [[_COMMUNITY_Community 7|Community 7]]
- [[_COMMUNITY_Community 8|Community 8]]
- [[_COMMUNITY_Community 9|Community 9]]
- [[_COMMUNITY_Community 10|Community 10]]
- [[_COMMUNITY_Community 11|Community 11]]
- [[_COMMUNITY_Community 12|Community 12]]
- [[_COMMUNITY_Community 13|Community 13]]
- [[_COMMUNITY_Community 14|Community 14]]
- [[_COMMUNITY_Community 15|Community 15]]
- [[_COMMUNITY_Community 16|Community 16]]
- [[_COMMUNITY_Community 17|Community 17]]
- [[_COMMUNITY_Community 18|Community 18]]
- [[_COMMUNITY_Community 19|Community 19]]
- [[_COMMUNITY_Community 20|Community 20]]
- [[_COMMUNITY_Community 21|Community 21]]
- [[_COMMUNITY_Community 22|Community 22]]
- [[_COMMUNITY_Community 23|Community 23]]
- [[_COMMUNITY_Community 24|Community 24]]
- [[_COMMUNITY_Community 25|Community 25]]
- [[_COMMUNITY_Community 26|Community 26]]
## God Nodes (most connected - your core abstractions)
1. `_is_blocked_url()` - 6 edges
2. `IrConfigParameter` - 5 edges
3. `_post_init_hook()` - 4 edges
4. `IrModuleModule` - 4 edges
5. `IrCron` - 4 edges
6. `ResConfigSettings` - 4 edges
7. `PublisherWarrantyContract` - 4 edges
8. `_blocked_request()` - 4 edges
9. `_blocked_get()` - 4 edges
10. `_blocked_post()` - 4 edges
## Surprising Connections (you probably didn't know these)
- None detected - all connections are within the same source files.
## Communities
### Community 0 - "Community 0"
Cohesion: 0.14
Nodes (16): _get_message(), _get_sys_logs(), IrCron, IrHttp, IrModuleModule, _pre_dispatch(), PublisherWarrantyContract, Disable module update checks from Odoo store. (+8 more)
### Community 1 - "Community 1"
Cohesion: 0.26
Nodes (10): Base, _check_database_enterprise_expiration(), check_database_expiration(), DatabaseExpirationCheck, _get_database_expiration_date(), get_param(), IrConfigParameter, Override config parameters to prevent expiration and protect license values. (+2 more)
### Community 2 - "Community 2"
Cohesion: 0.27
Nodes (10): _blocked_get(), _blocked_post(), _blocked_request(), _is_blocked_url(), patch_requests(), Check if the URL should be blocked., Intercept and block requests to Odoo domains., Intercept and block GET requests. (+2 more)
### Community 3 - "Community 3"
Cohesion: 0.22
Nodes (7): create(), IrHttp, Override session info to prevent frontend from detecting license status. Thi, Override session info to set permanent valid subscription data. This pre, Override user creation/modification to prevent subscription checks. When use, Override write to log user modifications., ResUsers
### Community 4 - "Community 4"
Cohesion: 0.24
Nodes (5): Override set_param to prevent external processes from changing protected values., DISABLED: Do not send any data to Odoo servers. Just update local parame, Ensure certain settings stay disabled., _post_init_hook(), Set all configuration parameters to disable external Odoo services. This run
### Community 5 - "Community 5"
Cohesion: 0.33
Nodes (7): autocomplete(), enrich_company(), Disable partner autocomplete from Odoo API., Disable company autocomplete features., read_by_vat(), ResCompany, ResPartner
### Community 6 - "Community 6"
Cohesion: 0.4
Nodes (4): _disabled_iap_jsonrpc(), patch_iap_tools(), DISABLED: Block all IAP JSON-RPC calls. Returns empty/success response inste, Monkey-patch the iap_jsonrpc function to block external calls. This is calle
### Community 7 - "Community 7"
Cohesion: 0.53
Nodes (4): _get_gravatar_image(), _get_rates_from_provider(), ResCurrencyDisabled, ResPartnerDisabled
### Community 8 - "Community 8"
Cohesion: 1.0
Nodes (0):
### Community 9 - "Community 9"
Cohesion: 1.0
Nodes (0):
### Community 10 - "Community 10"
Cohesion: 1.0
Nodes (0):
### Community 11 - "Community 11"
Cohesion: 1.0
Nodes (0):
### Community 12 - "Community 12"
Cohesion: 1.0
Nodes (1): Set database to never expire.
### Community 13 - "Community 13"
Cohesion: 1.0
Nodes (1): Override get_param to return permanent values for protected params.
### Community 14 - "Community 14"
Cohesion: 1.0
Nodes (1): Override to prevent fetching from Odoo Apps store. Only scan local addon
### Community 15 - "Community 15"
Cohesion: 1.0
Nodes (1): DISABLED: Do not contact Odoo servers. Returns fake successful response.
### Community 16 - "Community 16"
Cohesion: 1.0
Nodes (1): DISABLED: Return empty message.
### Community 17 - "Community 17"
Cohesion: 1.0
Nodes (1): Log and potentially block external service routes.
### Community 18 - "Community 18"
Cohesion: 1.0
Nodes (1): DISABLED: Return empty rates.
### Community 19 - "Community 19"
Cohesion: 1.0
Nodes (1): DISABLED: Return False to skip gravatar lookup.
### Community 20 - "Community 20"
Cohesion: 1.0
Nodes (1): DISABLED: Return empty results instead of calling Odoo's partner API.
### Community 21 - "Community 21"
Cohesion: 1.0
Nodes (1): DISABLED: Return empty data instead of calling Odoo's enrichment API.
### Community 22 - "Community 22"
Cohesion: 1.0
Nodes (1): DISABLED: Return empty data instead of calling Odoo's VAT lookup API.
### Community 23 - "Community 23"
Cohesion: 1.0
Nodes (1): DISABLED: Return empty results for company autocomplete.
### Community 24 - "Community 24"
Cohesion: 1.0
Nodes (1): Override create to ensure no external subscription check is triggered. T
### Community 25 - "Community 25"
Cohesion: 1.0
Nodes (0):
### Community 26 - "Community 26"
Cohesion: 1.0
Nodes (0):
## Knowledge Gaps
- **39 isolated node(s):** `Set all configuration parameters to disable external Odoo services. This run`, `Override config parameters to prevent expiration and protect license values.`, `Set permanent valid subscription on module init.`, `Set database to never expire.`, `Override get_param to return permanent values for protected params.` (+34 more)
These have ≤1 connection - possible missing edges or undocumented components.
- **Thin community `Community 8`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 9`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 10`** (1 nodes): `__manifest__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 11`** (1 nodes): `__manifest__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 12`** (1 nodes): `Set database to never expire.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 13`** (1 nodes): `Override get_param to return permanent values for protected params.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 14`** (1 nodes): `Override to prevent fetching from Odoo Apps store. Only scan local addon`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 15`** (1 nodes): `DISABLED: Do not contact Odoo servers. Returns fake successful response.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 16`** (1 nodes): `DISABLED: Return empty message.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 17`** (1 nodes): `Log and potentially block external service routes.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 18`** (1 nodes): `DISABLED: Return empty rates.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 19`** (1 nodes): `DISABLED: Return False to skip gravatar lookup.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 20`** (1 nodes): `DISABLED: Return empty results instead of calling Odoo's partner API.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 21`** (1 nodes): `DISABLED: Return empty data instead of calling Odoo's enrichment API.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 22`** (1 nodes): `DISABLED: Return empty data instead of calling Odoo's VAT lookup API.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 23`** (1 nodes): `DISABLED: Return empty results for company autocomplete.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 24`** (1 nodes): `Override create to ensure no external subscription check is triggered. T`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 25`** (1 nodes): `disable_external_links.js`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 26`** (1 nodes): `disable_external_links.js`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `IrConfigParameter` connect `Community 1` to `Community 4`?**
_High betweenness centrality (0.069) - this node is a cross-community bridge._
- **Why does `ResConfigSettings` connect `Community 0` to `Community 4`?**
_High betweenness centrality (0.042) - this node is a cross-community bridge._
- **Why does `PublisherWarrantyContract` connect `Community 0` to `Community 4`?**
_High betweenness centrality (0.042) - this node is a cross-community bridge._
- **What connects `Set all configuration parameters to disable external Odoo services. This run`, `Override config parameters to prevent expiration and protect license values.`, `Set permanent valid subscription on module init.` to the rest of the system?**
_39 weakly-connected nodes found - possible documentation gaps or missing edges._
- **Should `Community 0` be split into smaller, more focused modules?**
_Cohesion score 0.14 - nodes in this community are weakly interconnected._

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_all_external_py", "label": "disable_all_external.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L1"}, {"id": "disable_all_external_rescurrencydisabled", "label": "ResCurrencyDisabled", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L17"}, {"id": "disable_all_external_get_rates_from_provider", "label": "_get_rates_from_provider()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L21"}, {"id": "disable_all_external_respartnerdisabled", "label": "ResPartnerDisabled", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L31"}, {"id": "disable_all_external_get_gravatar_image", "label": "_get_gravatar_image()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L35"}, {"id": "disable_all_external_rationale_22", "label": "DISABLED: Return empty rates.", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L22"}, {"id": "disable_all_external_rationale_36", "label": "DISABLED: Return False to skip gravatar lookup.", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L36"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_all_external_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L7", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_all_external_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L8", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_all_external_py", "target": "disable_all_external_rescurrencydisabled", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L17", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_all_external_py", "target": "disable_all_external_get_rates_from_provider", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L21", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_all_external_py", "target": "disable_all_external_respartnerdisabled", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L31", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_all_external_py", "target": "disable_all_external_get_gravatar_image", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L35", "weight": 1.0}, {"source": "disable_all_external_rationale_22", "target": "disable_all_external_rescurrencydisabled_get_rates_from_provider", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L22", "weight": 1.0}, {"source": "disable_all_external_rationale_36", "target": "disable_all_external_respartnerdisabled_get_gravatar_image", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L36", "weight": 1.0}], "raw_calls": [{"caller_nid": "disable_all_external_get_rates_from_provider", "callee": "debug", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L23"}, {"caller_nid": "disable_all_external_get_gravatar_image", "callee": "debug", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_all_external.py", "source_location": "L37"}]}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_static_src_js_disable_external_links_js", "label": "disable_external_links.js", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/static/src/js/disable_external_links.js", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_static_src_js_disable_external_links_js", "target": "browser", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/static/src/js/disable_external_links.js", "source_location": "L8", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L3", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L6", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L7", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/__init__.py", "source_location": "L8", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_static_src_js_disable_external_links_js", "label": "disable_external_links.js", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/static/src/js/disable_external_links.js", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_static_src_js_disable_external_links_js", "target": "browser", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/static/src/js/disable_external_links.js", "source_location": "L8", "weight": 1.0}], "raw_calls": []}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L3", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L4", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L6", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L7", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/__init__.py", "source_location": "L8", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_disable_iap_tools_py", "label": "disable_iap_tools.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L1"}, {"id": "disable_iap_tools_disabled_iap_jsonrpc", "label": "_disabled_iap_jsonrpc()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L16"}, {"id": "disable_iap_tools_patch_iap_tools", "label": "patch_iap_tools()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L43"}, {"id": "disable_iap_tools_rationale_17", "label": "DISABLED: Block all IAP JSON-RPC calls. Returns empty/success response inste", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L17"}, {"id": "disable_iap_tools_rationale_44", "label": "Monkey-patch the iap_jsonrpc function to block external calls. This is calle", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L44"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_disable_iap_tools_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L7", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_disable_iap_tools_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L8", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_disable_iap_tools_py", "target": "disable_iap_tools_disabled_iap_jsonrpc", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L16", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_models_disable_iap_tools_py", "target": "disable_iap_tools_patch_iap_tools", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L43", "weight": 1.0}, {"source": "disable_iap_tools_rationale_17", "target": "disable_iap_tools_disabled_iap_jsonrpc", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L17", "weight": 1.0}, {"source": "disable_iap_tools_rationale_44", "target": "disable_iap_tools_patch_iap_tools", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L44", "weight": 1.0}], "raw_calls": [{"caller_nid": "disable_iap_tools_disabled_iap_jsonrpc", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L21"}, {"caller_nid": "disable_iap_tools_disabled_iap_jsonrpc", "callee": "warning", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L35"}, {"caller_nid": "disable_iap_tools_patch_iap_tools", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L57"}, {"caller_nid": "disable_iap_tools_patch_iap_tools", "callee": "debug", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L60"}, {"caller_nid": "disable_iap_tools_patch_iap_tools", "callee": "warning", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L62"}]}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L1"}, {"id": "init_post_init_hook", "label": "_post_init_hook()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L5"}, {"id": "init_rationale_6", "label": "Set all configuration parameters to disable external Odoo services. This run", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L6"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_init_py", "target": "init_post_init_hook", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "init_rationale_6", "target": "init_post_init_hook", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "init_post_init_hook", "callee": "getLogger", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L11"}, {"caller_nid": "init_post_init_hook", "callee": "sudo", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L13"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L54"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L55"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L56"}, {"caller_nid": "init_post_init_hook", "callee": "items", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L58"}, {"caller_nid": "init_post_init_hook", "callee": "set_param", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L60"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L61"}, {"caller_nid": "init_post_init_hook", "callee": "len", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L61"}, {"caller_nid": "init_post_init_hook", "callee": "str", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L61"}, {"caller_nid": "init_post_init_hook", "callee": "warning", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L63"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L65"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L66"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__init__.py", "source_location": "L67"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L1"}, {"id": "init_post_init_hook", "label": "_post_init_hook()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L5"}, {"id": "init_rationale_6", "label": "Set all configuration parameters to disable external Odoo services. This run", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L6"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_init_py", "target": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L2", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_disable_odoo_online_init_py", "target": "init_post_init_hook", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L5", "weight": 1.0}, {"source": "init_rationale_6", "target": "init_post_init_hook", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L6", "weight": 1.0}], "raw_calls": [{"caller_nid": "init_post_init_hook", "callee": "getLogger", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L11"}, {"caller_nid": "init_post_init_hook", "callee": "sudo", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L13"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L54"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L55"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L56"}, {"caller_nid": "init_post_init_hook", "callee": "items", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L58"}, {"caller_nid": "init_post_init_hook", "callee": "set_param", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L60"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L61"}, {"caller_nid": "init_post_init_hook", "callee": "len", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L61"}, {"caller_nid": "init_post_init_hook", "callee": "str", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L61"}, {"caller_nid": "init_post_init_hook", "callee": "warning", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L63"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L65"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L66"}, {"caller_nid": "init_post_init_hook", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/disable_odoo_online/__init__.py", "source_location": "L67"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_iap_tools_py", "label": "disable_iap_tools.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L1"}, {"id": "disable_iap_tools_disabled_iap_jsonrpc", "label": "_disabled_iap_jsonrpc()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L16"}, {"id": "disable_iap_tools_patch_iap_tools", "label": "patch_iap_tools()", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L43"}, {"id": "disable_iap_tools_rationale_17", "label": "DISABLED: Block all IAP JSON-RPC calls. Returns empty/success response inste", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L17"}, {"id": "disable_iap_tools_rationale_44", "label": "Monkey-patch the iap_jsonrpc function to block external calls. This is calle", "file_type": "rationale", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L44"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_iap_tools_py", "target": "logging", "relation": "imports", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L7", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_iap_tools_py", "target": "odoo", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L8", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_iap_tools_py", "target": "disable_iap_tools_disabled_iap_jsonrpc", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L16", "weight": 1.0}, {"source": "users_gurpreet_github_odoo_modules_disable_odoo_online_models_disable_iap_tools_py", "target": "disable_iap_tools_patch_iap_tools", "relation": "contains", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L43", "weight": 1.0}, {"source": "disable_iap_tools_rationale_17", "target": "disable_iap_tools_disabled_iap_jsonrpc", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L17", "weight": 1.0}, {"source": "disable_iap_tools_rationale_44", "target": "disable_iap_tools_patch_iap_tools", "relation": "rationale_for", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L44", "weight": 1.0}], "raw_calls": [{"caller_nid": "disable_iap_tools_disabled_iap_jsonrpc", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L21"}, {"caller_nid": "disable_iap_tools_disabled_iap_jsonrpc", "callee": "warning", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L35"}, {"caller_nid": "disable_iap_tools_patch_iap_tools", "callee": "info", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L57"}, {"caller_nid": "disable_iap_tools_patch_iap_tools", "callee": "debug", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L60"}, {"caller_nid": "disable_iap_tools_patch_iap_tools", "callee": "warning", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_odoo_online/models/disable_iap_tools.py", "source_location": "L62"}]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,8 @@
# -*- coding: utf-8 -*-
from . import disable_iap_tools # Patches iap_jsonrpc globally - MUST be first
from . import disable_http_requests # Patches requests library to block Odoo domains
from . import disable_online_services
from . import disable_partner_autocomplete
from . import disable_database_expiration
from . import disable_all_external
from . import disable_session_leaks

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
"""
Comprehensive blocking of ALL external Odoo service calls.
Only inherits from models that are guaranteed to exist in base Odoo.
"""
import logging
from odoo import api, models, fields
_logger = logging.getLogger(__name__)
# ============================================================
# Block Currency Rate Live Updates - Uses res.currency which always exists
# ============================================================
class ResCurrencyDisabled(models.Model):
_inherit = 'res.currency'
@api.model
def _get_rates_from_provider(self, provider, date):
"""DISABLED: Return empty rates."""
_logger.debug("Currency rate provider BLOCKED: provider=%s", provider)
return {}
# ============================================================
# Block Gravatar - Uses res.partner which always exists
# ============================================================
class ResPartnerDisabled(models.Model):
_inherit = 'res.partner'
@api.model
def _get_gravatar_image(self, email):
"""DISABLED: Return False to skip gravatar lookup."""
_logger.debug("Gravatar lookup BLOCKED for email=%s", email)
return False

View File

@@ -0,0 +1,106 @@
# -*- coding: utf-8 -*-
"""
Disable database expiration checks and registration.
Consolidates all ir.config_parameter overrides.
"""
import logging
from datetime import datetime
from odoo import api, models, fields
_logger = logging.getLogger(__name__)
class IrConfigParameter(models.Model):
"""Override config parameters to prevent expiration and protect license values."""
_inherit = 'ir.config_parameter'
PROTECTED_PARAMS = {
'database.expiration_date': '2099-12-31 23:59:59',
'database.expiration_reason': 'renewal',
'database.enterprise_code': 'PERMANENT_LOCAL',
}
CLEAR_PARAMS = [
'database.already_linked_subscription_url',
'database.already_linked_email',
'database.already_linked_send_mail_url',
]
def init(self, force=False):
"""Set permanent valid subscription on module init."""
super().init(force=force)
self._set_permanent_subscription()
@api.model
def _set_permanent_subscription(self):
"""Set database to never expire."""
_logger.info("Setting permanent subscription values...")
for key, value in self.PROTECTED_PARAMS.items():
try:
self.env.cr.execute("""
INSERT INTO ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date)
VALUES (%s, %s, %s, NOW() AT TIME ZONE 'UTC', %s, NOW() AT TIME ZONE 'UTC')
ON CONFLICT (key) DO UPDATE SET value = %s, write_date = NOW() AT TIME ZONE 'UTC'
""", (key, value, self.env.uid, self.env.uid, value))
except Exception as e:
_logger.debug("Could not set param %s: %s", key, e)
for key in self.CLEAR_PARAMS:
try:
self.env.cr.execute("""
INSERT INTO ir_config_parameter (key, value, create_uid, create_date, write_uid, write_date)
VALUES (%s, '', %s, NOW() AT TIME ZONE 'UTC', %s, NOW() AT TIME ZONE 'UTC')
ON CONFLICT (key) DO UPDATE SET value = '', write_date = NOW() AT TIME ZONE 'UTC'
""", (key, self.env.uid, self.env.uid))
except Exception as e:
_logger.debug("Could not clear param %s: %s", key, e)
@api.model
def get_param(self, key, default=False):
"""Override get_param to return permanent values for protected params."""
if key in self.PROTECTED_PARAMS:
return self.PROTECTED_PARAMS[key]
if key in self.CLEAR_PARAMS:
return ''
return super().get_param(key, default)
def set_param(self, key, value):
"""Override set_param to prevent external processes from changing protected values."""
if key in self.PROTECTED_PARAMS:
if value != self.PROTECTED_PARAMS[key]:
_logger.warning("Blocked attempt to change protected param %s to %s", key, value)
return True
if key in self.CLEAR_PARAMS:
value = ''
return super().set_param(key, value)
class DatabaseExpirationCheck(models.AbstractModel):
_name = 'disable.odoo.online.expiration'
_description = 'Database Expiration Blocker'
@api.model
def check_database_expiration(self):
return {
'valid': True,
'expiration_date': '2099-12-31 23:59:59',
'expiration_reason': 'renewal',
}
class Base(models.AbstractModel):
_inherit = 'base'
@api.model
def _get_database_expiration_date(self):
return datetime(2099, 12, 31, 23, 59, 59)
@api.model
def _check_database_enterprise_expiration(self):
return True

View File

@@ -0,0 +1,129 @@
# -*- coding: utf-8 -*-
"""
Block ALL outgoing HTTP requests to Odoo-related domains.
This patches the requests library to intercept and block external calls.
"""
import logging
import requests
from functools import wraps
from urllib.parse import urlparse
_logger = logging.getLogger(__name__)
# Domains to block - all Odoo external services
BLOCKED_DOMAINS = [
'odoo.com',
'odoofin.com',
'odoo.sh',
'iap.odoo.com',
'iap-services.odoo.com',
'partner-autocomplete.odoo.com',
'iap-extract.odoo.com',
'iap-sms.odoo.com',
'upgrade.odoo.com',
'apps.odoo.com',
'production.odoofin.com',
'plaid.com',
'yodlee.com',
'gravatar.com',
'www.gravatar.com',
'secure.gravatar.com',
]
# Store original functions
_original_request = None
_original_get = None
_original_post = None
def _is_blocked_url(url):
"""Check if the URL should be blocked."""
if not url:
return False
try:
parsed = urlparse(url)
domain = parsed.netloc.lower()
for blocked in BLOCKED_DOMAINS:
if blocked in domain:
return True
except Exception:
pass
return False
def _blocked_request(method, url, **kwargs):
"""Intercept and block requests to Odoo domains."""
if _is_blocked_url(url):
_logger.warning("HTTP REQUEST BLOCKED: %s %s", method.upper(), url)
# Return a mock response
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
return response
return _original_request(method, url, **kwargs)
def _blocked_get(url, **kwargs):
"""Intercept and block GET requests."""
if _is_blocked_url(url):
_logger.warning("HTTP GET BLOCKED: %s", url)
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
return response
return _original_get(url, **kwargs)
def _blocked_post(url, **kwargs):
"""Intercept and block POST requests."""
if _is_blocked_url(url):
_logger.warning("HTTP POST BLOCKED: %s", url)
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
return response
return _original_post(url, **kwargs)
def patch_requests():
"""Monkey-patch requests library to block Odoo domains."""
global _original_request, _original_get, _original_post
try:
if _original_request is None:
_original_request = requests.Session.request
_original_get = requests.get
_original_post = requests.post
# Patch Session.request (catches most calls)
def patched_session_request(self, method, url, **kwargs):
if _is_blocked_url(url):
_logger.warning("HTTP SESSION REQUEST BLOCKED: %s %s", method.upper(), url)
response = requests.models.Response()
response.status_code = 200
response._content = b'{}'
response.headers['Content-Type'] = 'application/json'
response.request = requests.models.PreparedRequest()
response.request.url = url
response.request.method = method
return response
return _original_request(self, method, url, **kwargs)
requests.Session.request = patched_session_request
requests.get = _blocked_get
requests.post = _blocked_post
_logger.info("HTTP requests to Odoo domains have been BLOCKED")
_logger.info("Blocked domains: %s", ', '.join(BLOCKED_DOMAINS))
except Exception as e:
_logger.warning("Could not patch requests library: %s", e)
# Apply patch when module is imported
patch_requests()

View File

@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-
"""
Override the core IAP tools to block ALL external API calls.
This is the master switch that blocks ALL Odoo external communications.
"""
import logging
from odoo import exceptions, _
_logger = logging.getLogger(__name__)
# Store original function reference
_original_iap_jsonrpc = None
def _disabled_iap_jsonrpc(url, method='call', params=None, timeout=15):
"""
DISABLED: Block all IAP JSON-RPC calls.
Returns empty/success response instead of making external calls.
"""
_logger.info("IAP JSONRPC BLOCKED: %s (method=%s)", url, method)
# Return appropriate empty responses based on the endpoint
if '/authorize' in url:
return 'fake_transaction_token_disabled'
elif '/capture' in url or '/cancel' in url:
return True
elif '/credits' in url:
return 999999
elif 'partner-autocomplete' in url:
return []
elif 'enrich' in url:
return {}
elif 'sms' in url:
_logger.warning("SMS API call blocked - SMS will not be sent")
return {'state': 'success', 'credits': 999999}
elif 'extract' in url:
return {'status': 'success', 'credits': 999999}
else:
return {}
def patch_iap_tools():
"""
Monkey-patch the iap_jsonrpc function to block external calls.
This is called when the module loads.
"""
global _original_iap_jsonrpc
try:
from odoo.addons.iap.tools import iap_tools
if _original_iap_jsonrpc is None:
_original_iap_jsonrpc = iap_tools.iap_jsonrpc
iap_tools.iap_jsonrpc = _disabled_iap_jsonrpc
_logger.info("IAP JSON-RPC calls have been DISABLED globally")
except ImportError:
_logger.debug("IAP module not installed, skipping patch")
except Exception as e:
_logger.warning("Could not patch IAP tools: %s", e)
# Apply patch when module is imported
patch_iap_tools()

View File

@@ -0,0 +1,153 @@
# -*- coding: utf-8 -*-
"""
Disable various Odoo online services and external API calls.
"""
import logging
from odoo import api, models, fields
_logger = logging.getLogger(__name__)
class IrModuleModule(models.Model):
"""Disable module update checks from Odoo store."""
_inherit = 'ir.module.module'
@api.model
def update_list(self):
"""
Override to prevent fetching from Odoo Apps store.
Only scan local addons paths.
"""
_logger.info("Module update_list: Scanning local addons only (Odoo Apps store disabled)")
return super().update_list()
def button_immediate_upgrade(self):
"""Prevent upgrade attempts that might contact Odoo."""
_logger.info("Module upgrade: Processing locally only")
return super().button_immediate_upgrade()
class IrCron(models.Model):
"""Disable scheduled actions that contact Odoo servers."""
_inherit = 'ir.cron'
def _callback(self, cron_name, server_action_id):
"""
Override to block certain cron jobs that contact Odoo.
Odoo 19 signature: _callback(self, cron_name, server_action_id)
"""
blocked_crons = [
'publisher',
'warranty',
'update_notification',
'database_expiration',
'iap_enrich',
'ocr',
'Invoice OCR',
'enrich leads',
'fetchmail',
'online sync',
]
cron_lower = (cron_name or '').lower()
for blocked in blocked_crons:
if blocked.lower() in cron_lower:
_logger.info("Cron BLOCKED (external call): %s", cron_name)
return False
return super()._callback(cron_name, server_action_id)
class ResConfigSettings(models.TransientModel):
"""Override config settings to prevent external service configuration."""
_inherit = 'res.config.settings'
def set_values(self):
"""Ensure certain settings stay disabled."""
res = super().set_values()
# Disable any auto-update settings and set permanent expiration
params = self.env['ir.config_parameter'].sudo()
params.set_param('database.expiration_date', '2099-12-31 23:59:59')
params.set_param('database.expiration_reason', 'renewal')
params.set_param('database.enterprise_code', 'PERMANENT_LOCAL')
# Disable IAP endpoint (redirect to nowhere)
params.set_param('iap.endpoint', 'http://localhost:65535')
# Disable various external services
params.set_param('partner_autocomplete.endpoint', 'http://localhost:65535')
params.set_param('iap_extract_endpoint', 'http://localhost:65535')
params.set_param('olg.endpoint', 'http://localhost:65535')
params.set_param('mail.media_library_endpoint', 'http://localhost:65535')
return res
class PublisherWarrantyContract(models.AbstractModel):
"""Completely disable publisher warranty checks."""
_inherit = 'publisher_warranty.contract'
@api.model
def _get_sys_logs(self):
"""
DISABLED: Do not contact Odoo servers.
Returns fake successful response.
"""
_logger.info("Publisher warranty _get_sys_logs BLOCKED")
return {
'messages': [],
'enterprise_info': {
'expiration_date': '2099-12-31 23:59:59',
'expiration_reason': 'renewal',
'enterprise_code': 'PERMANENT_LOCAL',
}
}
@api.model
def _get_message(self):
"""DISABLED: Return empty message."""
_logger.info("Publisher warranty _get_message BLOCKED")
return {}
def update_notification(self, cron_mode=True):
"""
DISABLED: Do not send any data to Odoo servers.
Just update local parameters with permanent values.
"""
_logger.info("Publisher warranty update_notification BLOCKED")
# Set permanent valid subscription parameters
params = self.env['ir.config_parameter'].sudo()
params.set_param('database.expiration_date', '2099-12-31 23:59:59')
params.set_param('database.expiration_reason', 'renewal')
params.set_param('database.enterprise_code', 'PERMANENT_LOCAL')
# Clear any "already linked" parameters
params.set_param('database.already_linked_subscription_url', '')
params.set_param('database.already_linked_email', '')
params.set_param('database.already_linked_send_mail_url', '')
return True
class IrHttp(models.AbstractModel):
"""Block certain routes that call external services."""
_inherit = 'ir.http'
@classmethod
def _pre_dispatch(cls, rule, arguments):
"""Log and potentially block external service routes."""
# List of route patterns that should be blocked
blocked_routes = [
'/iap/',
'/partner_autocomplete/',
'/google_',
'/ocr/',
'/sms/',
]
# Note: We don't actually block here as it might break functionality
# The actual blocking happens at the API/model level
return super()._pre_dispatch(rule, arguments)

View File

@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""
Disable Partner Autocomplete external API calls.
"""
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class ResPartner(models.Model):
"""Disable partner autocomplete from Odoo API."""
_inherit = 'res.partner'
@api.model
def autocomplete(self, query, timeout=15):
"""
DISABLED: Return empty results instead of calling Odoo's partner API.
"""
_logger.debug("Partner autocomplete DISABLED - returning empty results for: %s", query)
return []
@api.model
def enrich_company(self, company_domain, partner_gid, vat, timeout=15):
"""
DISABLED: Return empty data instead of calling Odoo's enrichment API.
"""
_logger.debug("Partner enrichment DISABLED - returning empty for domain: %s", company_domain)
return {}
@api.model
def read_by_vat(self, vat, timeout=15):
"""
DISABLED: Return empty data instead of calling Odoo's VAT lookup API.
"""
_logger.debug("Partner VAT lookup DISABLED - returning empty for VAT: %s", vat)
return {}
class ResCompany(models.Model):
"""Disable company autocomplete features."""
_inherit = 'res.company'
@api.model
def autocomplete(self, query, timeout=15):
"""
DISABLED: Return empty results for company autocomplete.
"""
_logger.debug("Company autocomplete DISABLED - returning empty results")
return []

View File

@@ -0,0 +1,82 @@
# -*- coding: utf-8 -*-
"""
Block session-based information leaks and frontend detection mechanisms.
Specifically targets the web_enterprise module's subscription checks.
"""
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class IrHttp(models.AbstractModel):
"""
Override session info to prevent frontend from detecting license status.
This specifically blocks web_enterprise's ExpirationPanel from showing.
"""
_inherit = 'ir.http'
def session_info(self):
"""
Override session info to set permanent valid subscription data.
This prevents the frontend ExpirationPanel from showing warnings.
Key overrides:
- expiration_date: Set to far future (2099)
- expiration_reason: Set to 'renewal' (valid subscription)
- warning: Set to False to hide all warning banners
"""
result = super().session_info()
# Override expiration-related session data
# These are read by enterprise_subscription_service.js
result['expiration_date'] = '2099-12-31 23:59:59'
result['expiration_reason'] = 'renewal'
result['warning'] = False # Critical: prevents warning banners
# Remove any "already linked" subscription info
# These could trigger redirect prompts
result.pop('already_linked_subscription_url', None)
result.pop('already_linked_email', None)
result.pop('already_linked_send_mail_url', None)
_logger.debug("Session info patched - expiration set to 2099, warnings disabled")
return result
class ResUsers(models.Model):
"""
Override user creation/modification to prevent subscription checks.
When users are created, Odoo Enterprise normally contacts Odoo servers
to verify the subscription allows that many users.
"""
_inherit = 'res.users'
@api.model_create_multi
def create(self, vals_list):
"""
Override create to ensure no external subscription check is triggered.
The actual check happens in publisher_warranty.contract which we've
already blocked, but this is an extra safety measure.
"""
_logger.info("Creating %d user(s) - subscription check DISABLED", len(vals_list))
# Create users normally - no external checks will happen
# because publisher_warranty.contract.update_notification is blocked
users = super().create(vals_list)
# Don't trigger any warranty checks
return users
def write(self, vals):
"""
Override write to log user modifications.
"""
result = super().write(vals)
# If internal user status changed, log it
if 'share' in vals or 'groups_id' in vals:
_logger.info("User permissions updated - subscription check DISABLED")
return result

View File

@@ -0,0 +1,38 @@
/** @odoo-module **/
/**
* This module intercepts clicks on external Odoo links to prevent
* referrer leakage when users click help/documentation/upgrade links.
*/
import { browser } from "@web/core/browser/browser";
// Store original window.open
const originalOpen = browser.open;
// Override browser.open to add referrer protection
browser.open = function(url, target, features) {
if (url && typeof url === 'string') {
const urlLower = url.toLowerCase();
// Check if it's an Odoo external link
const odooPatterns = [
'odoo.com',
'odoo.sh',
'accounts.odoo',
];
const isOdooLink = odooPatterns.some(pattern => urlLower.includes(pattern));
if (isOdooLink) {
// For Odoo links, open with noreferrer to prevent leaking your domain
const newWindow = originalOpen.call(this, url, target || '_blank', 'noopener,noreferrer');
return newWindow;
}
}
return originalOpen.call(this, url, target, features);
};
console.log('[disable_odoo_online] External link protection loaded');

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
{
'name': 'Disable Publisher Warranty',
'version': '19.0.1.0.0',
'category': 'Tools',
'summary': 'Disables all communication with Odoo publisher warranty servers',
'description': """
This module completely disables:
- Publisher warranty server communication
- Subscription expiration checks
- Automatic license updates
For local development use only.
""",
'author': 'Development',
'depends': ['mail'],
'data': [],
'installable': True,
'auto_install': True,
'license': 'LGPL-3',
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
{
'name': 'Disable Publisher Warranty',
'version': '19.0.1.0.0',
'category': 'Tools',
'summary': 'Disables all communication with Odoo publisher warranty servers',
'description': """
This module completely disables:
- Publisher warranty server communication
- Subscription expiration checks
- Automatic license updates
For local development use only.
""",
'author': 'Development',
'depends': ['mail'],
'data': [],
'installable': True,
'auto_install': True,
'license': 'LGPL-3',
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import publisher_warranty

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Disable all publisher warranty / subscription checks for local development
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class PublisherWarrantyContractDisabled(models.AbstractModel):
_inherit = "publisher_warranty.contract"
@api.model
def _get_sys_logs(self):
"""
DISABLED: Do not contact Odoo servers.
Returns fake successful response.
"""
_logger.info("Publisher warranty check DISABLED - not contacting Odoo servers")
return {
"messages": [],
"enterprise_info": {
"expiration_date": "2099-12-31 23:59:59",
"expiration_reason": "renewal",
"enterprise_code": self.env['ir.config_parameter'].sudo().get_param('database.enterprise_code', ''),
}
}
def update_notification(self, cron_mode=True):
"""
DISABLED: Do not send any data to Odoo servers.
Just update local parameters with permanent values.
"""
_logger.info("Publisher warranty update_notification DISABLED - no server contact")
# Set permanent valid subscription parameters
set_param = self.env['ir.config_parameter'].sudo().set_param
set_param('database.expiration_date', '2099-12-31 23:59:59')
set_param('database.expiration_reason', 'renewal')
# Clear any "already linked" parameters
set_param('database.already_linked_subscription_url', False)
set_param('database.already_linked_email', False)
set_param('database.already_linked_send_mail_url', False)
return True

View File

@@ -0,0 +1,96 @@
# Graph Report - /Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty (2026-04-22)
## Corpus Check
- 8 files · ~414 words
- Verdict: corpus is large enough that graph structure adds value.
## Summary
- 13 nodes · 10 edges · 9 communities detected
- Extraction: 100% EXTRACTED · 0% INFERRED · 0% AMBIGUOUS
- Token cost: 0 input · 0 output
## Community Hubs (Navigation)
- [[_COMMUNITY_Community 0|Community 0]]
- [[_COMMUNITY_Community 1|Community 1]]
- [[_COMMUNITY_Community 2|Community 2]]
- [[_COMMUNITY_Community 3|Community 3]]
- [[_COMMUNITY_Community 4|Community 4]]
- [[_COMMUNITY_Community 5|Community 5]]
- [[_COMMUNITY_Community 6|Community 6]]
- [[_COMMUNITY_Community 7|Community 7]]
- [[_COMMUNITY_Community 8|Community 8]]
## God Nodes (most connected - your core abstractions)
1. `PublisherWarrantyContractDisabled` - 3 edges
2. `_get_sys_logs()` - 2 edges
3. `DISABLED: Do not send any data to Odoo servers. Just update local parame` - 1 edges
4. `DISABLED: Do not contact Odoo servers. Returns fake successful response.` - 0 edges
## Surprising Connections (you probably didn't know these)
- None detected - all connections are within the same source files.
## Communities
### Community 0 - "Community 0"
Cohesion: 0.67
Nodes (2): _get_sys_logs(), PublisherWarrantyContractDisabled
### Community 1 - "Community 1"
Cohesion: 1.0
Nodes (1): DISABLED: Do not send any data to Odoo servers. Just update local parame
### Community 2 - "Community 2"
Cohesion: 1.0
Nodes (0):
### Community 3 - "Community 3"
Cohesion: 1.0
Nodes (0):
### Community 4 - "Community 4"
Cohesion: 1.0
Nodes (0):
### Community 5 - "Community 5"
Cohesion: 1.0
Nodes (0):
### Community 6 - "Community 6"
Cohesion: 1.0
Nodes (0):
### Community 7 - "Community 7"
Cohesion: 1.0
Nodes (0):
### Community 8 - "Community 8"
Cohesion: 1.0
Nodes (1): DISABLED: Do not contact Odoo servers. Returns fake successful response.
## Knowledge Gaps
- **2 isolated node(s):** `DISABLED: Do not contact Odoo servers. Returns fake successful response.`, `DISABLED: Do not send any data to Odoo servers. Just update local parame`
These have ≤1 connection - possible missing edges or undocumented components.
- **Thin community `Community 1`** (2 nodes): `.update_notification()`, `DISABLED: Do not send any data to Odoo servers. Just update local parame`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 2`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 3`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 4`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 5`** (1 nodes): `__init__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 6`** (1 nodes): `__manifest__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 7`** (1 nodes): `__manifest__.py`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
- **Thin community `Community 8`** (1 nodes): `DISABLED: Do not contact Odoo servers. Returns fake successful response.`
Too small to be a meaningful cluster - may be noise or needs more connections extracted.
## Suggested Questions
_Questions this graph is uniquely positioned to answer:_
- **Why does `PublisherWarrantyContractDisabled` connect `Community 0` to `Community 1`?**
_High betweenness centrality (0.098) - this node is a cross-community bridge._
- **What connects `DISABLED: Do not contact Odoo servers. Returns fake successful response.`, `DISABLED: Do not send any data to Odoo servers. Just update local parame` to the rest of the system?**
_2 weakly-connected nodes found - possible documentation gaps or missing edges._

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py", "target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py", "target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/models/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py", "label": "__init__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/__init__.py", "source_location": "L1"}], "edges": [{"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py", "target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py", "relation": "imports_from", "confidence": "EXTRACTED", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/__init__.py", "source_location": "L2", "weight": 1.0}], "raw_calls": []}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
{"nodes": [{"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_manifest_py", "label": "__manifest__.py", "file_type": "code", "source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/__manifest__.py", "source_location": "L1"}], "edges": [], "raw_calls": []}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,247 @@
{
"directed": false,
"multigraph": false,
"graph": {},
"nodes": [
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py",
"community": 2,
"norm_label": "__init__.py"
},
{
"label": "__manifest__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/__manifest__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_manifest_py",
"community": 6,
"norm_label": "__manifest__.py"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py",
"community": 3,
"norm_label": "__init__.py"
},
{
"label": "__manifest__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/__manifest__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_manifest_py",
"community": 7,
"norm_label": "__manifest__.py"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/models/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py",
"community": 4,
"norm_label": "__init__.py"
},
{
"label": "publisher_warranty.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_publisher_warranty_py",
"community": 0,
"norm_label": "publisher_warranty.py"
},
{
"label": "PublisherWarrantyContractDisabled",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L10",
"id": "publisher_warranty_publisherwarrantycontractdisabled",
"community": 0,
"norm_label": "publisherwarrantycontractdisabled"
},
{
"label": "_get_sys_logs()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L14",
"id": "publisher_warranty_get_sys_logs",
"community": 0,
"norm_label": "_get_sys_logs()"
},
{
"label": ".update_notification()",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L29",
"id": "publisher_warranty_publisherwarrantycontractdisabled_update_notification",
"community": 1,
"norm_label": ".update_notification()"
},
{
"label": "DISABLED: Do not contact Odoo servers. Returns fake successful response.",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L15",
"id": "publisher_warranty_rationale_15",
"community": 8,
"norm_label": "disabled: do not contact odoo servers. returns fake successful response."
},
{
"label": "DISABLED: Do not send any data to Odoo servers. Just update local parame",
"file_type": "rationale",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L30",
"id": "publisher_warranty_rationale_30",
"community": 1,
"norm_label": "disabled: do not send any data to odoo servers. just update local parame"
},
{
"label": "__init__.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/__init__.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py",
"community": 5,
"norm_label": "__init__.py"
},
{
"label": "publisher_warranty.py",
"file_type": "code",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L1",
"id": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_publisher_warranty_py",
"community": 0,
"norm_label": "publisher_warranty.py"
}
],
"links": [
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_init_py",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_init_py",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/models/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_init_py",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L10",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_publisher_warranty_py",
"_tgt": "publisher_warranty_publisherwarrantycontractdisabled",
"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_publisher_warranty_py",
"target": "publisher_warranty_publisherwarrantycontractdisabled",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L14",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_publisher_warranty_py",
"_tgt": "publisher_warranty_get_sys_logs",
"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_disable_publisher_warranty_models_publisher_warranty_py",
"target": "publisher_warranty_get_sys_logs",
"confidence_score": 1.0
},
{
"relation": "method",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L29",
"weight": 1.0,
"_src": "publisher_warranty_publisherwarrantycontractdisabled",
"_tgt": "publisher_warranty_publisherwarrantycontractdisabled_update_notification",
"source": "publisher_warranty_publisherwarrantycontractdisabled",
"target": "publisher_warranty_publisherwarrantycontractdisabled_update_notification",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L10",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_publisher_warranty_py",
"_tgt": "publisher_warranty_publisherwarrantycontractdisabled",
"source": "publisher_warranty_publisherwarrantycontractdisabled",
"target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_publisher_warranty_py",
"confidence_score": 1.0
},
{
"relation": "contains",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L14",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_publisher_warranty_py",
"_tgt": "publisher_warranty_get_sys_logs",
"source": "publisher_warranty_get_sys_logs",
"target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_publisher_warranty_py",
"confidence_score": 1.0
},
{
"relation": "rationale_for",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/publisher_warranty.py",
"source_location": "L30",
"weight": 1.0,
"_src": "publisher_warranty_rationale_30",
"_tgt": "publisher_warranty_publisherwarrantycontractdisabled_update_notification",
"source": "publisher_warranty_publisherwarrantycontractdisabled_update_notification",
"target": "publisher_warranty_rationale_30",
"confidence_score": 1.0
},
{
"relation": "imports_from",
"confidence": "EXTRACTED",
"source_file": "/Users/gurpreet/Github/Odoo-Modules/disable_publisher_warranty/models/__init__.py",
"source_location": "L2",
"weight": 1.0,
"_src": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py",
"_tgt": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py",
"source": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py",
"target": "users_gurpreet_github_odoo_modules_disable_publisher_warranty_models_init_py",
"confidence_score": 1.0
}
],
"hyperedges": []
}

View File

@@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import publisher_warranty

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
# Disable all publisher warranty / subscription checks for local development
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
class PublisherWarrantyContractDisabled(models.AbstractModel):
_inherit = "publisher_warranty.contract"
@api.model
def _get_sys_logs(self):
"""
DISABLED: Do not contact Odoo servers.
Returns fake successful response.
"""
_logger.info("Publisher warranty check DISABLED - not contacting Odoo servers")
return {
"messages": [],
"enterprise_info": {
"expiration_date": "2099-12-31 23:59:59",
"expiration_reason": "renewal",
"enterprise_code": self.env['ir.config_parameter'].sudo().get_param('database.enterprise_code', ''),
}
}
def update_notification(self, cron_mode=True):
"""
DISABLED: Do not send any data to Odoo servers.
Just update local parameters with permanent values.
"""
_logger.info("Publisher warranty update_notification DISABLED - no server contact")
# Set permanent valid subscription parameters
set_param = self.env['ir.config_parameter'].sudo().set_param
set_param('database.expiration_date', '2099-12-31 23:59:59')
set_param('database.expiration_reason', 'renewal')
# Clear any "already linked" parameters
set_param('database.already_linked_subscription_url', False)
set_param('database.already_linked_email', False)
set_param('database.already_linked_send_mail_url', False)
return True

View File

@@ -0,0 +1,2 @@
from . import models
from . import wizard

View File

@@ -0,0 +1,22 @@
{
'name': 'Fusion Bank Statements',
'version': '19.0.1.0.0',
'category': 'Accounting',
'summary': 'Import OFX/QFX bank statements with automatic duplicate detection',
'description': 'Upload OFX, QFX, or QBO files exported from your bank '
'(ScotiaConnect, TD, RBC, etc.) and import them as bank '
'statement lines. Smart duplicate detection using the bank\'s '
'transaction ID (fitid). No external server communication.',
'author': 'Fusion Central',
'website': 'https://fusionsoft.ca',
'license': 'LGPL-3',
'depends': ['account'],
'data': [
'security/ir.model.access.csv',
'wizard/import_statement_views.xml',
'views/account_journal_views.xml',
],
'external_dependencies': {'python': ['ofxparse']},
'installable': True,
'auto_install': False,
}

View File

@@ -0,0 +1,2 @@
from . import import_log
from . import account_journal

View File

@@ -0,0 +1,16 @@
from odoo import models
class AccountJournal(models.Model):
_inherit = 'account.journal'
def action_open_statement_import(self):
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': 'Import Bank Statement',
'res_model': 'fusion.statement.import',
'view_mode': 'form',
'target': 'new',
'context': {'default_journal_id': self.id},
}

View File

@@ -0,0 +1,26 @@
from odoo import fields, models
class FusionStatementImportLog(models.Model):
_name = 'fusion.statement.import.log'
_description = 'Imported Bank Transaction Log'
_order = 'date desc, id desc'
_rec_name = 'fitid'
journal_id = fields.Many2one(
'account.journal', required=True, ondelete='cascade', index=True,
)
fitid = fields.Char(string='Bank Transaction ID', required=True, index=True)
date = fields.Date()
amount = fields.Float(digits=(16, 2))
payment_ref = fields.Char(string='Description')
import_date = fields.Datetime(default=fields.Datetime.now, readonly=True)
statement_line_id = fields.Many2one('account.bank.statement.line', ondelete='set null')
company_id = fields.Many2one(
'res.company', required=True, default=lambda self: self.env.company,
)
_sql_constraints = [
('journal_fitid_unique', 'UNIQUE(journal_id, fitid)',
'This transaction has already been imported for this journal.'),
]

View File

@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_fusion_import_log_accountant,fusion.statement.import.log accountant,model_fusion_statement_import_log,account.group_account_invoice,1,1,1,0
access_fusion_import_log_manager,fusion.statement.import.log manager,model_fusion_statement_import_log,account.group_account_manager,1,1,1,1
access_fusion_import_wizard,fusion.statement.import wizard,model_fusion_statement_import,account.group_account_invoice,1,1,1,1
access_fusion_import_line,fusion.statement.import.line wizard,model_fusion_statement_import_line,account.group_account_invoice,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_fusion_import_log_accountant fusion.statement.import.log accountant model_fusion_statement_import_log account.group_account_invoice 1 1 1 0
3 access_fusion_import_log_manager fusion.statement.import.log manager model_fusion_statement_import_log account.group_account_manager 1 1 1 1
4 access_fusion_import_wizard fusion.statement.import wizard model_fusion_statement_import account.group_account_invoice 1 1 1 1
5 access_fusion_import_line fusion.statement.import.line wizard model_fusion_statement_import_line account.group_account_invoice 1 1 1 1

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Add "Import Statement" button to bank journal form view -->
<record id="view_account_journal_form_inherit_fusion" model="ir.ui.view">
<field name="name">account.journal.form.fusion.statements</field>
<field name="model">account.journal</field>
<field name="inherit_id" ref="account.view_account_journal_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_open_statement_import"
type="object"
class="oe_stat_button"
icon="fa-upload"
invisible="type != 'bank'">
<span class="o_stat_text">Import Statement</span>
</button>
</xpath>
</field>
</record>
</odoo>

Some files were not shown because too many files have changed in this diff Show More