Split 49 modules/suites into independent git repos; untrack from monorepo
Each top-level module/suite folder is now its own private repo on GitHub (gsinghpal/<name>) and gitea (admin/<name>), with a fresh single initial commit. The monorepo no longer tracks them (added to .gitignore + git rm --cached); working-tree files are retained on disk and managed in their own repos. The monorepo keeps shared root files (CLAUDE.md, docs/, scripts/, tools/, AGENTS.md, WIP/obsolete dirs) and full history. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
51
.gitignore
vendored
51
.gitignore
vendored
@@ -16,3 +16,54 @@ __pycache__/
|
||||
# Local-only diagnostic logs from test runs
|
||||
_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/
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# -*- 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',
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
# -*- 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',
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import iap_account
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# -*- 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
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
# 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._
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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"}]}
|
||||
@@ -1 +0,0 @@
|
||||
{"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"}]}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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
@@ -1,205 +0,0 @@
|
||||
{
|
||||
"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": []
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import iap_account
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
# -*- 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
|
||||
|
||||
@@ -1,143 +0,0 @@
|
||||
# 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
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# -*- 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)
|
||||
@@ -1,56 +0,0 @@
|
||||
# -*- 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,
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,143 +0,0 @@
|
||||
# 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
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# -*- 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)
|
||||
@@ -1,56 +0,0 @@
|
||||
# -*- 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,
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,8 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,38 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,106 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,129 +0,0 @@
|
||||
# -*- 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()
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# -*- 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()
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
# -*- 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)
|
||||
@@ -1,52 +0,0 @@
|
||||
# -*- 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 []
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,38 +0,0 @@
|
||||
/** @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');
|
||||
|
||||
@@ -1,220 +0,0 @@
|
||||
# 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
@@ -1 +0,0 @@
|
||||
{"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"}]}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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"}]}
|
||||
@@ -1 +0,0 @@
|
||||
{"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
@@ -1 +0,0 @@
|
||||
{"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
@@ -1 +0,0 @@
|
||||
{"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
@@ -1 +0,0 @@
|
||||
{"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
@@ -1,8 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,38 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,106 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,129 +0,0 @@
|
||||
# -*- 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()
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
# -*- 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()
|
||||
|
||||
@@ -1,153 +0,0 @@
|
||||
# -*- 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)
|
||||
@@ -1,52 +0,0 @@
|
||||
# -*- 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 []
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
# -*- 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
|
||||
@@ -1,38 +0,0 @@
|
||||
/** @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');
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# -*- 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',
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import models
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
# -*- 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',
|
||||
}
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import publisher_warranty
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# -*- 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
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
# 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
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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": []}
|
||||
@@ -1 +0,0 @@
|
||||
{"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
@@ -1 +0,0 @@
|
||||
{"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
@@ -1,247 +0,0 @@
|
||||
{
|
||||
"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": []
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from . import publisher_warranty
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
# -*- 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
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
from . import models
|
||||
from . import wizard
|
||||
@@ -1,22 +0,0 @@
|
||||
{
|
||||
'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,
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
from . import import_log
|
||||
from . import account_journal
|
||||
@@ -1,16 +0,0 @@
|
||||
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},
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
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.'),
|
||||
]
|
||||
@@ -1,5 +0,0 @@
|
||||
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,22 +0,0 @@
|
||||
<?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>
|
||||
@@ -1 +0,0 @@
|
||||
from . import import_statement
|
||||
@@ -1,243 +0,0 @@
|
||||
import base64
|
||||
import io
|
||||
import logging
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
from ofxparse import OfxParser
|
||||
except ImportError:
|
||||
OfxParser = None
|
||||
_logger.warning("ofxparse library not installed — OFX import disabled.")
|
||||
|
||||
|
||||
class FusionStatementImportLine(models.TransientModel):
|
||||
_name = 'fusion.statement.import.line'
|
||||
_description = 'Statement Import Preview Line'
|
||||
_order = 'date desc, id desc'
|
||||
|
||||
wizard_id = fields.Many2one('fusion.statement.import', ondelete='cascade')
|
||||
selected = fields.Boolean(default=True)
|
||||
is_duplicate = fields.Boolean(readonly=True)
|
||||
fitid = fields.Char(string='Transaction ID', readonly=True)
|
||||
date = fields.Date(readonly=True)
|
||||
payment_ref = fields.Char(string='Description', readonly=True)
|
||||
amount = fields.Float(digits=(16, 2), readonly=True)
|
||||
|
||||
|
||||
class FusionStatementImport(models.TransientModel):
|
||||
_name = 'fusion.statement.import'
|
||||
_description = 'Import Bank Statement'
|
||||
|
||||
step = fields.Selection([
|
||||
('upload', 'Upload'),
|
||||
('review', 'Review'),
|
||||
], default='upload', readonly=True)
|
||||
|
||||
journal_id = fields.Many2one(
|
||||
'account.journal', string='Bank Journal', required=True,
|
||||
domain="[('type', '=', 'bank')]",
|
||||
)
|
||||
data_file = fields.Binary(string='Statement File', attachment=False)
|
||||
filename = fields.Char()
|
||||
|
||||
line_ids = fields.One2many('fusion.statement.import.line', 'wizard_id')
|
||||
|
||||
total_new = fields.Integer(compute='_compute_counts')
|
||||
total_duplicate = fields.Integer(compute='_compute_counts')
|
||||
total_selected = fields.Integer(compute='_compute_counts')
|
||||
|
||||
balance_start = fields.Float(digits=(16, 2), readonly=True)
|
||||
balance_end = fields.Float(digits=(16, 2), readonly=True)
|
||||
currency_code = fields.Char(readonly=True)
|
||||
account_number = fields.Char(readonly=True)
|
||||
|
||||
@api.depends('line_ids.selected', 'line_ids.is_duplicate')
|
||||
def _compute_counts(self):
|
||||
for rec in self:
|
||||
lines = rec.line_ids
|
||||
rec.total_new = len(lines.filtered(lambda l: not l.is_duplicate))
|
||||
rec.total_duplicate = len(lines.filtered(lambda l: l.is_duplicate))
|
||||
rec.total_selected = len(lines.filtered(lambda l: l.selected))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step 1 → Step 2: Parse file
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def action_parse(self):
|
||||
self.ensure_one()
|
||||
if not self.data_file:
|
||||
raise UserError(_("Please upload a statement file."))
|
||||
if not OfxParser:
|
||||
raise UserError(_(
|
||||
"The 'ofxparse' Python library is not installed. "
|
||||
"Ask your administrator to run: pip install ofxparse"
|
||||
))
|
||||
|
||||
raw = base64.b64decode(self.data_file)
|
||||
try:
|
||||
ofx = OfxParser.parse(io.BytesIO(raw))
|
||||
except Exception as e:
|
||||
raise UserError(_(
|
||||
"Could not parse the file. Make sure it is a valid "
|
||||
"OFX/QFX/QBO file.\n\nError: %s"
|
||||
) % str(e)) from e
|
||||
|
||||
if not ofx.accounts:
|
||||
raise UserError(_("No accounts found in the file."))
|
||||
|
||||
account = ofx.accounts[0]
|
||||
transactions = account.statement.transactions
|
||||
if not transactions:
|
||||
raise UserError(_("No transactions found in the file."))
|
||||
|
||||
ImportLog = self.env['fusion.statement.import.log']
|
||||
existing_fitids = set(
|
||||
ImportLog.search([
|
||||
('journal_id', '=', self.journal_id.id),
|
||||
]).mapped('fitid')
|
||||
)
|
||||
|
||||
lines = []
|
||||
for tx in transactions:
|
||||
fitid = str(tx.id).strip()
|
||||
payee = tx.payee or ''
|
||||
if tx.checknum:
|
||||
payee += ' ' + tx.checknum
|
||||
if tx.memo:
|
||||
payee += ' : ' + tx.memo
|
||||
|
||||
is_dup = fitid in existing_fitids
|
||||
lines.append((0, 0, {
|
||||
'fitid': fitid,
|
||||
'date': tx.date.date() if hasattr(tx.date, 'date') else tx.date,
|
||||
'payment_ref': payee.strip(),
|
||||
'amount': float(tx.amount),
|
||||
'is_duplicate': is_dup,
|
||||
'selected': not is_dup,
|
||||
}))
|
||||
|
||||
balance = float(account.statement.balance)
|
||||
total_amt = sum(float(tx.amount) for tx in transactions)
|
||||
|
||||
self.write({
|
||||
'step': 'review',
|
||||
'line_ids': [(5, 0, 0)] + lines,
|
||||
'balance_end': balance,
|
||||
'balance_start': balance - total_amt,
|
||||
'currency_code': account.statement.currency or '',
|
||||
'account_number': account.number or '',
|
||||
})
|
||||
|
||||
return self._reopen()
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Step 2: Import selected lines
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def action_import(self):
|
||||
self.ensure_one()
|
||||
selected = self.line_ids.filtered(lambda l: l.selected)
|
||||
if not selected:
|
||||
raise UserError(_("No transactions selected for import."))
|
||||
|
||||
journal = self.journal_id
|
||||
|
||||
statement = self.env['account.bank.statement'].create({
|
||||
'name': self.filename or 'OFX Import',
|
||||
'reference': self.filename or '',
|
||||
'journal_id': journal.id,
|
||||
'balance_start': self.balance_start,
|
||||
'balance_end_real': self.balance_end,
|
||||
})
|
||||
|
||||
ImportLog = self.env['fusion.statement.import.log']
|
||||
created_lines = self.env['account.bank.statement.line']
|
||||
|
||||
for line in selected.sorted('date'):
|
||||
st_line = self.env['account.bank.statement.line'].create({
|
||||
'journal_id': journal.id,
|
||||
'date': line.date,
|
||||
'payment_ref': line.payment_ref,
|
||||
'amount': line.amount,
|
||||
'statement_id': statement.id,
|
||||
})
|
||||
created_lines |= st_line
|
||||
|
||||
ImportLog.create({
|
||||
'journal_id': journal.id,
|
||||
'fitid': line.fitid,
|
||||
'date': line.date,
|
||||
'amount': line.amount,
|
||||
'payment_ref': line.payment_ref,
|
||||
'statement_line_id': st_line.id,
|
||||
'company_id': journal.company_id.id,
|
||||
})
|
||||
|
||||
all_lines = self.line_ids
|
||||
dup_count = len(all_lines.filtered(lambda l: l.is_duplicate))
|
||||
manual_skip = len(all_lines.filtered(lambda l: not l.selected and not l.is_duplicate))
|
||||
date_min = min(selected.mapped('date'))
|
||||
date_max = max(selected.mapped('date'))
|
||||
|
||||
parts = ['%d transactions imported.' % len(selected)]
|
||||
if dup_count:
|
||||
parts.append('%d duplicates detected.' % dup_count)
|
||||
if manual_skip:
|
||||
parts.append('%d manually excluded.' % manual_skip)
|
||||
parts.append('Date range: %s to %s' % (date_min, date_max))
|
||||
|
||||
return {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'display_notification',
|
||||
'params': {
|
||||
'title': _('Bank Statement Imported'),
|
||||
'message': ' '.join(parts),
|
||||
'type': 'success',
|
||||
'sticky': False,
|
||||
'next': {
|
||||
'type': 'ir.actions.act_window',
|
||||
'name': _('Imported Statement'),
|
||||
'res_model': 'account.bank.statement',
|
||||
'res_id': statement.id,
|
||||
'views': [(False, 'form')],
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Navigation helpers
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
def action_back(self):
|
||||
self.ensure_one()
|
||||
self.write({'step': 'upload', 'line_ids': [(5, 0, 0)]})
|
||||
return self._reopen()
|
||||
|
||||
def action_select_all_new(self):
|
||||
self.ensure_one()
|
||||
for line in self.line_ids:
|
||||
line.selected = not line.is_duplicate
|
||||
return self._reopen()
|
||||
|
||||
def action_select_none(self):
|
||||
self.ensure_one()
|
||||
self.line_ids.write({'selected': False})
|
||||
return self._reopen()
|
||||
|
||||
def action_select_all(self):
|
||||
self.ensure_one()
|
||||
self.line_ids.write({'selected': True})
|
||||
return self._reopen()
|
||||
|
||||
def _reopen(self):
|
||||
return {
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': self._name,
|
||||
'res_id': self.id,
|
||||
'views': [(False, 'form')],
|
||||
'target': 'new',
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="fusion_statement_import_form" model="ir.ui.view">
|
||||
<field name="name">fusion.statement.import.form</field>
|
||||
<field name="model">fusion.statement.import</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Import Bank Statement">
|
||||
|
||||
<!-- Step 1: Upload -->
|
||||
<group invisible="step != 'upload'">
|
||||
<group>
|
||||
<field name="journal_id"/>
|
||||
<field name="data_file" filename="filename"/>
|
||||
<field name="filename" invisible="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<p class="text-muted">
|
||||
Upload an OFX, QFX, or QBO file exported from your bank portal.
|
||||
Duplicate transactions will be detected automatically.
|
||||
</p>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Step 2: Review -->
|
||||
<group invisible="step != 'review'" string="File Summary">
|
||||
<group>
|
||||
<field name="account_number" readonly="1"/>
|
||||
<field name="currency_code" readonly="1"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="balance_start" readonly="1"/>
|
||||
<field name="balance_end" readonly="1"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<div invisible="step != 'review'" class="mb-2">
|
||||
<div class="d-flex gap-2 align-items-center mb-3">
|
||||
<span class="badge text-bg-success fs-6">
|
||||
New: <field name="total_new" class="d-inline" readonly="1"/>
|
||||
</span>
|
||||
<span class="badge text-bg-warning fs-6">
|
||||
Duplicates: <field name="total_duplicate" class="d-inline" readonly="1"/>
|
||||
</span>
|
||||
<span class="badge text-bg-primary fs-6">
|
||||
Selected: <field name="total_selected" class="d-inline" readonly="1"/>
|
||||
</span>
|
||||
<span class="flex-grow-1"/>
|
||||
<button name="action_select_all_new" type="object"
|
||||
class="btn btn-secondary btn-sm">
|
||||
Select New Only
|
||||
</button>
|
||||
<button name="action_select_all" type="object"
|
||||
class="btn btn-secondary btn-sm">
|
||||
Select All
|
||||
</button>
|
||||
<button name="action_select_none" type="object"
|
||||
class="btn btn-secondary btn-sm">
|
||||
Deselect All
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<field name="line_ids" nolabel="1">
|
||||
<list editable="bottom"
|
||||
decoration-danger="is_duplicate and selected"
|
||||
decoration-muted="is_duplicate and not selected"
|
||||
decoration-success="not is_duplicate and selected">
|
||||
<field name="selected"/>
|
||||
<field name="is_duplicate" string="Dup?" widget="boolean"/>
|
||||
<field name="date"/>
|
||||
<field name="payment_ref"/>
|
||||
<field name="amount"/>
|
||||
<field name="fitid"/>
|
||||
</list>
|
||||
</field>
|
||||
</div>
|
||||
|
||||
<field name="step" invisible="1"/>
|
||||
|
||||
<footer>
|
||||
<button name="action_parse" type="object"
|
||||
string="Parse File" class="btn-primary"
|
||||
invisible="step != 'upload'"/>
|
||||
<button name="action_import" type="object"
|
||||
string="Import Selected" class="btn-primary"
|
||||
invisible="step != 'review'"/>
|
||||
<button name="action_back" type="object"
|
||||
string="Back" class="btn-secondary"
|
||||
invisible="step != 'review'"/>
|
||||
<button string="Cancel" class="btn-secondary" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fusion_statement_import" model="ir.actions.act_window">
|
||||
<field name="name">Import Bank Statement</field>
|
||||
<field name="res_model">fusion.statement.import</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="fusion_statement_import_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fusion_statement_import"
|
||||
name="Import Bank Statement (OFX)"
|
||||
parent="account.account_transactions_menu"
|
||||
action="action_fusion_statement_import"
|
||||
sequence="90"/>
|
||||
|
||||
</odoo>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user