Compare commits
90 Commits
f3766c2898
...
2a363c6b40
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a363c6b40 | ||
|
|
5b76037988 | ||
|
|
39e007b42f | ||
|
|
8c01deb2e3 | ||
|
|
1a679a45c3 | ||
|
|
88305a4ce0 | ||
|
|
e3d784f566 | ||
|
|
27955c8c41 | ||
|
|
21c1e37211 | ||
|
|
ed426912ce | ||
|
|
768731da0f | ||
|
|
75ceee1e69 | ||
|
|
71dea1f91b | ||
|
|
842832cc41 | ||
|
|
08d7ee17ff | ||
|
|
39d4fe5020 | ||
|
|
8983c8bd50 | ||
|
|
1bf4092b39 | ||
|
|
2afe54d15e | ||
|
|
a0ad52fe46 | ||
|
|
a3fa1ced16 | ||
|
|
9d483fb474 | ||
|
|
05c84d077d | ||
|
|
bc2bba14aa | ||
|
|
b83c9fd318 | ||
|
|
5e806745da | ||
|
|
52be90c10d | ||
|
|
9f0badfb7e | ||
|
|
c46e4c0b28 | ||
|
|
c169704687 | ||
|
|
4efd5066d0 | ||
|
|
f759bf558f | ||
|
|
3493c43916 | ||
|
|
3179cc1f7b | ||
|
|
1a1a37b3e3 | ||
|
|
3de513fb7b | ||
|
|
4941df8131 | ||
|
|
b3fb2ef40c | ||
|
|
c5b519f8f4 | ||
|
|
8354e82dc4 | ||
|
|
8b723086b9 | ||
|
|
80f9ddd0d6 | ||
|
|
b1e4ed5ec8 | ||
|
|
0e84b97c89 | ||
|
|
1cc3ea7a18 | ||
|
|
f9fcb6612b | ||
|
|
80b7d3d620 | ||
|
|
cec499e9e9 | ||
|
|
3ea283247a | ||
|
|
919dbcb4cf | ||
|
|
ab16040eb4 | ||
|
|
396f895ae2 | ||
|
|
c5d2ac1d6b | ||
|
|
645119c9b7 | ||
|
|
84f01ebd7b | ||
|
|
5d361d8c8a | ||
|
|
ad50c579c3 | ||
|
|
cc35c28760 | ||
|
|
7e302b0a27 | ||
|
|
9c2c043de9 | ||
|
|
2dd1a2be6c | ||
|
|
de14a28112 | ||
|
|
1f86a7c497 | ||
|
|
102fbd65f2 | ||
|
|
116c0b03bf | ||
|
|
0ce599c4ac | ||
|
|
50dbd63dd2 | ||
|
|
7b085a87a1 | ||
|
|
fdd67c9e51 | ||
|
|
cd710065c6 | ||
|
|
2cc146b253 | ||
|
|
45775fa295 | ||
|
|
126264217e | ||
|
|
149a45a752 | ||
|
|
a1c479f916 | ||
|
|
2563208f53 | ||
|
|
bd7275881f | ||
|
|
92369be6e0 | ||
|
|
595dccc17d | ||
|
|
e56974d46f | ||
|
|
fdca9518ab | ||
|
|
a839285bd4 | ||
|
|
0e04f4ecc6 | ||
|
|
e9cf75ee48 | ||
|
|
fc3c966484 | ||
|
|
db4b9aa278 | ||
|
|
f81e0cd918 | ||
|
|
acd3fc455e | ||
|
|
a3e85a23ef | ||
|
|
b925766966 |
135
.cursor/rules/fusion-api-integration.mdc
Normal file
@@ -0,0 +1,135 @@
|
||||
---
|
||||
description: Use the Fusion API module for all API calls (OpenAI, Anthropic, Google Maps, Twilio, OAuth) in any Fusion module
|
||||
globs: fusion_*/models/**/*.py, fusion_*/services/**/*.py
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
# Fusion API Integration Guide
|
||||
|
||||
When any Fusion module needs to call an external API (OpenAI, Anthropic, Google Maps, Twilio, Google/Microsoft OAuth), it MUST use the centralized `fusion.api.service` instead of managing its own keys.
|
||||
|
||||
The service lives at `fusion_api/models/api_service.py` and is an AbstractModel accessible via `self.env['fusion.api.service']`.
|
||||
|
||||
## Available Methods
|
||||
|
||||
### 1. `call_openai()` -- AI text generation via OpenAI
|
||||
|
||||
```python
|
||||
result = self.env['fusion.api.service'].call_openai(
|
||||
consumer='fusion_clock_ai', # your module's technical name
|
||||
feature='timesheet_summary', # descriptive feature name for tracking
|
||||
messages=[
|
||||
{'role': 'system', 'content': 'You are a helpful assistant.'},
|
||||
{'role': 'user', 'content': 'Summarize this timesheet...'},
|
||||
],
|
||||
model='gpt-4o-mini', # optional, defaults to gpt-4o-mini
|
||||
max_tokens=1024, # optional, defaults to 1024
|
||||
user=self.env.user, # optional, defaults to current user
|
||||
)
|
||||
# Returns: str (the text response)
|
||||
```
|
||||
|
||||
### 2. `call_anthropic()` -- AI text generation via Claude
|
||||
|
||||
```python
|
||||
result = self.env['fusion.api.service'].call_anthropic(
|
||||
consumer='fusion_notes',
|
||||
feature='voice_transcription',
|
||||
messages=[
|
||||
{'role': 'user', 'content': 'Transcribe this text...'},
|
||||
],
|
||||
system='You are a medical transcription assistant.', # optional system prompt
|
||||
model='claude-sonnet-4-20250514', # optional, defaults to claude-sonnet-4-20250514
|
||||
max_tokens=1024, # optional
|
||||
)
|
||||
# Returns: str (the text response)
|
||||
```
|
||||
|
||||
### 3. `get_api_key()` -- raw API key for non-AI services
|
||||
|
||||
```python
|
||||
api_key = self.env['fusion.api.service'].get_api_key(
|
||||
provider_type='google_maps', # one of: google_maps, twilio, custom
|
||||
consumer='fusion_shipping', # your module's technical name
|
||||
feature='geocoding', # optional feature name
|
||||
)
|
||||
# Returns: str (the raw API key to use in your own HTTP calls)
|
||||
```
|
||||
|
||||
### 4. `get_oauth_credentials()` -- OAuth tokens for Google/Microsoft
|
||||
|
||||
```python
|
||||
creds = self.env['fusion.api.service'].get_oauth_credentials(
|
||||
provider_type='google_oauth', # google_oauth or microsoft_oauth
|
||||
consumer='fusion_schedule',
|
||||
feature='calendar_sync',
|
||||
)
|
||||
# Returns: dict with keys:
|
||||
# client_id, client_secret, access_token, refresh_token, token_expiry, redirect_uri
|
||||
```
|
||||
|
||||
## Provider Types
|
||||
|
||||
| Type | Use For |
|
||||
|------|---------|
|
||||
| `openai` | GPT-4o, GPT-4o-mini, o1, etc. |
|
||||
| `anthropic` | Claude Sonnet, Haiku, Opus |
|
||||
| `google_maps` | Geocoding, Places, Distance Matrix |
|
||||
| `google_oauth` | Google Calendar, Drive OAuth |
|
||||
| `microsoft_oauth` | Microsoft Calendar, Graph OAuth |
|
||||
| `twilio` | SMS, Voice |
|
||||
| `custom` | Any other API provider |
|
||||
|
||||
## What Happens Automatically
|
||||
|
||||
- The calling module is auto-registered as a consumer in Fusion API
|
||||
- Every call is logged with: tokens, cost, response time, user, feature, model
|
||||
- Budget caps (monthly/daily) are enforced before each call
|
||||
- Rate limits (RPM/RPD) are enforced before each call
|
||||
- Per-user limits are checked if configured
|
||||
- On limit exceeded, a `UserError` is raised with a clear message
|
||||
|
||||
## Phased Migration Pattern
|
||||
|
||||
Do NOT add `fusion_api` as a hard dependency. Use a try/fallback pattern so the module works with or without Fusion API installed:
|
||||
|
||||
```python
|
||||
def _call_ai(self, messages, feature='general'):
|
||||
"""Call OpenAI through Fusion API, falling back to own key."""
|
||||
try:
|
||||
return self.env['fusion.api.service'].call_openai(
|
||||
consumer='fusion_clock_ai',
|
||||
feature=feature,
|
||||
messages=messages,
|
||||
)
|
||||
except Exception:
|
||||
api_key = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'fusion_clock_ai.openai_api_key'
|
||||
)
|
||||
if not api_key:
|
||||
raise
|
||||
from openai import OpenAI
|
||||
client = OpenAI(api_key=api_key)
|
||||
response = client.chat.completions.create(
|
||||
model='gpt-4o-mini', messages=messages,
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
```
|
||||
|
||||
## Rules
|
||||
|
||||
- NEVER hardcode API keys in Python code
|
||||
- NEVER store API keys in `ir.config_parameter` for new modules -- use Fusion API
|
||||
- ALWAYS pass the module's technical name (e.g. `fusion_clock_ai`) as the `consumer` parameter
|
||||
- ALWAYS pass a descriptive `feature` string for usage tracking granularity
|
||||
- NEVER catch and silently swallow errors from `fusion.api.service` -- let UserError propagate
|
||||
- DO NOT add `fusion_api` to `depends` in `__manifest__.py` -- use the fallback pattern above
|
||||
- The `consumer` string should match the module's technical name exactly (folder name)
|
||||
|
||||
## Odoo 19 Conventions
|
||||
|
||||
- This project targets Odoo 19 (Community/Enterprise)
|
||||
- No hardcoded colors in views or SCSS -- let Odoo handle dark/light theming
|
||||
- Use `res.groups.privilege` for security groups (not `category_id`)
|
||||
- Do not use deprecated `ir.cron` fields (`numbercall`, `doall`)
|
||||
- Settings views: use `name=` attribute on `<app>` tags, not `data-key=`
|
||||
40
CLAUDE.md
Normal file
@@ -0,0 +1,40 @@
|
||||
# Odoo Modules — Claude Code Instructions
|
||||
|
||||
## Project
|
||||
27 custom Odoo 19 modules for Fusion Central (Westin Healthcare + NEXA Systems).
|
||||
|
||||
## Critical Rules — Odoo 19
|
||||
1. **NEVER code from memory** — Always read a reference file from Docker first:
|
||||
```bash
|
||||
docker exec odoo-dev-app cat /usr/lib/python3/dist-packages/odoo/addons/<module>/static/src/<path>
|
||||
```
|
||||
2. **Frontend JS**: Use `Interaction` class from `@web/public/interaction`, registered via `registry.category("public.interactions")`. NOT IIFE/DOMContentLoaded.
|
||||
3. **Backend OWL**: Use standalone `rpc()` from `@web/core/network/rpc`. NOT `useService("rpc")`. `static props = []` not `{}`.
|
||||
4. **HTTP routes**: `type="jsonrpc"` — NOT `type="json"` (deprecated).
|
||||
5. **res.config.settings**: Only boolean/integer/float/char/selection/many2one/datetime. NO Date fields.
|
||||
6. **res.groups**: NO `users` field, NO `category_id` field.
|
||||
7. **Search views**: NO `group expand="0"` syntax.
|
||||
|
||||
## Naming
|
||||
- New fields: `x_fc_*` prefix
|
||||
- Legacy fields: `x_studio_*`
|
||||
- Canadian English for all user-facing text
|
||||
- Currency: `$` sign with Monetary fields + currency_id
|
||||
|
||||
## Cursor-Managed Modules
|
||||
- **fusion_clock** is currently being modified in Cursor — always read files fresh before editing, don't assume you know the current state
|
||||
|
||||
## Workflow
|
||||
- Local dev: `docker exec odoo-dev-app odoo -d fusion-dev -u <module> --stop-after-init`
|
||||
- Local URL: http://localhost:8069
|
||||
- Test before deploying. Edit existing files — don't create unnecessary new ones.
|
||||
|
||||
## Supabase Knowledge Base
|
||||
Before starting unfamiliar work, check Supabase for context:
|
||||
```bash
|
||||
PGPASSWORD='a09e12e0995dc29446631fa458f3d4b3' psql -h 100.74.28.73 -p 5433 -U postgres -d postgres
|
||||
```
|
||||
- `fusionapps.decisions` — past architecture decisions
|
||||
- `fusionapps.issues` — known issues and fixes
|
||||
- `fusionapps.code_snippets` — reference code
|
||||
- `fusionapps.quick_commands` — deployment and admin commands
|
||||
BIN
Obsolete Files/.DS_Store
vendored
Normal file
BIN
Obsolete Files/garazd_product_label/.DS_Store
vendored
Normal file
BIN
Obsolete Files/garazd_product_label/garazd_product_label/.DS_Store
vendored
Normal file
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 74 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 75 KiB After Width: | Height: | Size: 75 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 120 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 55 KiB |
|
Before Width: | Height: | Size: 223 KiB After Width: | Height: | Size: 223 KiB |
|
Before Width: | Height: | Size: 161 KiB After Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 191 KiB After Width: | Height: | Size: 191 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |