Initial commit
This commit is contained in:
734
commands/api-reference.md
Normal file
734
commands/api-reference.md
Normal file
@@ -0,0 +1,734 @@
|
||||
Complete API reference for the Odoo client and cache stores in generated PWAs.
|
||||
|
||||
## What this command does:
|
||||
- Provides comprehensive API documentation
|
||||
- Lists all available methods and functions
|
||||
- Shows parameter types and return values
|
||||
- Includes code examples for each method
|
||||
- Helps developers use the generated code effectively
|
||||
|
||||
---
|
||||
|
||||
## Odoo API Client Reference 🔌
|
||||
|
||||
The Odoo API client (`src/lib/odoo.js` or equivalent) provides methods for interacting with Odoo Studio models.
|
||||
|
||||
### Configuration
|
||||
|
||||
#### Environment Variables
|
||||
```bash
|
||||
VITE_ODOO_URL=https://yourcompany.odoo.com
|
||||
VITE_ODOO_DB=yourcompany-main
|
||||
ODOO_API_KEY=your_api_key
|
||||
ODOO_USERNAME=your.email@company.com
|
||||
VITE_MODEL_NAME=x_expense
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CRUD Operations
|
||||
|
||||
### createRecord()
|
||||
Create a new record in Odoo.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function createRecord(model, fields)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `model` (string) - Odoo model name (e.g., 'x_expense')
|
||||
- `fields` (object) - Field values to set
|
||||
|
||||
**Returns:** Promise<number> - ID of created record
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
const newId = await odoo.createRecord('x_expense', {
|
||||
x_studio_description: 'Team lunch',
|
||||
x_studio_amount: 45.50,
|
||||
x_studio_date: '2025-01-15',
|
||||
x_studio_category: 'meal',
|
||||
x_studio_employee: odoo.formatMany2one(12)
|
||||
});
|
||||
|
||||
console.log(`Created expense with ID: ${newId}`);
|
||||
```
|
||||
|
||||
**Error Handling:**
|
||||
```javascript
|
||||
try {
|
||||
const id = await odoo.createRecord('x_expense', fields);
|
||||
} catch (error) {
|
||||
console.error('Failed to create:', error.message);
|
||||
// Handle error (show message to user, retry, etc.)
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### searchRecords()
|
||||
Search and read records from Odoo.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function searchRecords(model, domain, fields, limit = null, offset = null)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `model` (string) - Odoo model name
|
||||
- `domain` (array) - Odoo domain filter (e.g., [['id', '>', 100]])
|
||||
- `fields` (array) - List of field names to fetch
|
||||
- `limit` (number, optional) - Maximum number of records
|
||||
- `offset` (number, optional) - Number of records to skip
|
||||
|
||||
**Returns:** Promise<Array<Object>> - Array of record objects
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Get all expenses
|
||||
const allExpenses = await odoo.searchRecords(
|
||||
'x_expense',
|
||||
[],
|
||||
['x_studio_description', 'x_studio_amount', 'x_studio_date']
|
||||
);
|
||||
|
||||
// Get expenses > $100
|
||||
const largeExpenses = await odoo.searchRecords(
|
||||
'x_expense',
|
||||
[['x_studio_amount', '>', 100]],
|
||||
['x_studio_description', 'x_studio_amount']
|
||||
);
|
||||
|
||||
// Get recent 10 expenses
|
||||
const recentExpenses = await odoo.searchRecords(
|
||||
'x_expense',
|
||||
[],
|
||||
['x_studio_description', 'x_studio_date'],
|
||||
10 // limit
|
||||
);
|
||||
|
||||
// Get expenses with pagination
|
||||
const page2 = await odoo.searchRecords(
|
||||
'x_expense',
|
||||
[],
|
||||
fields,
|
||||
20, // limit: 20 per page
|
||||
20 // offset: skip first 20 (page 2)
|
||||
);
|
||||
```
|
||||
|
||||
**Domain Syntax:**
|
||||
```javascript
|
||||
// Equals
|
||||
[['x_studio_status', '=', 'draft']]
|
||||
|
||||
// Greater than
|
||||
[['x_studio_amount', '>', 100]]
|
||||
|
||||
// In list
|
||||
[['x_studio_category', 'in', ['meal', 'travel']]]
|
||||
|
||||
// Multiple conditions (AND)
|
||||
[
|
||||
['x_studio_amount', '>', 50],
|
||||
['x_studio_status', '=', 'draft']
|
||||
]
|
||||
|
||||
// OR conditions
|
||||
['|',
|
||||
['x_studio_amount', '>', 100],
|
||||
['x_studio_category', '=', 'travel']
|
||||
]
|
||||
|
||||
// Complex: (amount > 100 OR category = travel) AND status = draft
|
||||
['&',
|
||||
'|',
|
||||
['x_studio_amount', '>', 100],
|
||||
['x_studio_category', '=', 'travel'],
|
||||
['x_studio_status', '=', 'draft']
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### updateRecord()
|
||||
Update an existing record.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function updateRecord(model, id, values)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `model` (string) - Odoo model name
|
||||
- `id` (number) - Record ID to update
|
||||
- `values` (object) - Fields to update
|
||||
|
||||
**Returns:** Promise<boolean> - true if successful
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
await odoo.updateRecord('x_expense', 123, {
|
||||
x_studio_status: 'approved',
|
||||
x_studio_amount: 55.00
|
||||
});
|
||||
|
||||
// Update multiple fields
|
||||
await odoo.updateRecord('x_expense', 123, {
|
||||
x_studio_description: 'Updated description',
|
||||
x_studio_date: '2025-01-20',
|
||||
x_studio_notes: 'Added receipt'
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### deleteRecord()
|
||||
Delete a record from Odoo.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function deleteRecord(model, id)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `model` (string) - Odoo model name
|
||||
- `id` (number) - Record ID to delete
|
||||
|
||||
**Returns:** Promise<boolean> - true if successful
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
await odoo.deleteRecord('x_expense', 123);
|
||||
|
||||
// With confirmation
|
||||
if (confirm('Are you sure you want to delete this expense?')) {
|
||||
await odoo.deleteRecord('x_expense', expenseId);
|
||||
}
|
||||
```
|
||||
|
||||
**Error Handling:**
|
||||
```javascript
|
||||
try {
|
||||
await odoo.deleteRecord('x_expense', id);
|
||||
console.log('Deleted successfully');
|
||||
} catch (error) {
|
||||
console.error('Failed to delete:', error.message);
|
||||
alert('Could not delete: ' + error.message);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Helper Methods
|
||||
|
||||
### fetchPartners()
|
||||
Fetch partner (res.partner) records.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function fetchPartners(ids = null)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ids` (array, optional) - Specific partner IDs to fetch. If null, fetches all.
|
||||
|
||||
**Returns:** Promise<Array<Object>> - Array of partner objects
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Fetch all partners
|
||||
const allPartners = await odoo.fetchPartners();
|
||||
|
||||
// Fetch specific partners
|
||||
const somePartners = await odoo.fetchPartners([1, 2, 3]);
|
||||
|
||||
// Use in dropdown
|
||||
const partners = await odoo.fetchPartners();
|
||||
// Display: partners.map(p => ({ value: p.id, label: p.name }))
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### formatMany2one()
|
||||
Format a Many2one field value for Odoo.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
function formatMany2one(id)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `id` (number | null) - Partner/record ID
|
||||
|
||||
**Returns:** Array<number, boolean> | false - Odoo-formatted value
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Set employee field
|
||||
const fields = {
|
||||
x_studio_employee: odoo.formatMany2one(12)
|
||||
// Result: [12, false]
|
||||
};
|
||||
|
||||
// Clear employee field
|
||||
const fields = {
|
||||
x_studio_employee: odoo.formatMany2one(null)
|
||||
// Result: false
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### formatMany2many()
|
||||
Format a Many2many field value for Odoo.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
function formatMany2many(ids)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ids` (array) - Array of record IDs
|
||||
|
||||
**Returns:** Array - Odoo command format
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Set tags (replace all)
|
||||
const fields = {
|
||||
x_studio_tags: odoo.formatMany2many([1, 2, 3])
|
||||
// Result: [[6, 0, [1, 2, 3]]]
|
||||
};
|
||||
|
||||
// Clear tags
|
||||
const fields = {
|
||||
x_studio_tags: odoo.formatMany2many([])
|
||||
// Result: [[6, 0, []]]
|
||||
};
|
||||
```
|
||||
|
||||
**Odoo Many2many Commands:**
|
||||
```javascript
|
||||
// (6, 0, [ids]) - Replace all (what formatMany2many uses)
|
||||
// (4, id) - Add link to id
|
||||
// (3, id) - Remove link to id
|
||||
// (5, 0) - Remove all links
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Cache Store API Reference 💾
|
||||
|
||||
The cache store provides reactive state management with offline-first capabilities.
|
||||
|
||||
### Properties
|
||||
|
||||
#### records
|
||||
**Type:** Reactive Array<Object>
|
||||
|
||||
Current cached records.
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// SvelteKit
|
||||
$: totalAmount = $expenseCache.reduce((sum, e) => sum + e.x_studio_amount, 0);
|
||||
|
||||
// React
|
||||
const totalAmount = useMemo(() =>
|
||||
records.reduce((sum, e) => sum + e.x_studio_amount, 0),
|
||||
[records]
|
||||
);
|
||||
|
||||
// Vue
|
||||
const totalAmount = computed(() =>
|
||||
expenseStore.records.reduce((sum, e) => sum + e.x_studio_amount, 0)
|
||||
);
|
||||
```
|
||||
|
||||
#### isLoading
|
||||
**Type:** Reactive Boolean
|
||||
|
||||
Loading state indicator.
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
{#if $expenseCache.isLoading}
|
||||
<LoadingSpinner />
|
||||
{:else}
|
||||
<ExpenseList />
|
||||
{/if}
|
||||
```
|
||||
|
||||
#### error
|
||||
**Type:** Reactive String | null
|
||||
|
||||
Current error message, if any.
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
{#if $expenseCache.error}
|
||||
<ErrorAlert message={$expenseCache.error} />
|
||||
{/if}
|
||||
```
|
||||
|
||||
#### lastSync
|
||||
**Type:** Reactive Number (timestamp)
|
||||
|
||||
Timestamp of last successful sync.
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
const timeSinceSync = Date.now() - $expenseCache.lastSync;
|
||||
const minutes = Math.floor(timeSinceSync / 60000);
|
||||
// Display: "Last synced ${minutes} minutes ago"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Methods
|
||||
|
||||
### load()
|
||||
Load records from cache and trigger background sync.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function load()
|
||||
```
|
||||
|
||||
**Returns:** Promise<void>
|
||||
|
||||
**Behavior:**
|
||||
1. Loads from cache immediately (instant UI update)
|
||||
2. Checks if cache is stale (> 5 minutes)
|
||||
3. If stale, syncs in background
|
||||
4. Updates UI when new data arrives
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// SvelteKit
|
||||
$effect(() => {
|
||||
expenseCache.load();
|
||||
});
|
||||
|
||||
// React
|
||||
useEffect(() => {
|
||||
expenseCache.load();
|
||||
}, []);
|
||||
|
||||
// Vue
|
||||
onMounted(() => {
|
||||
expenseStore.load();
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### create()
|
||||
Create a new record with optimistic update.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function create(data)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `data` (object) - Field values for new record
|
||||
|
||||
**Returns:** Promise<number> - ID of created record
|
||||
|
||||
**Behavior:**
|
||||
1. Generates temporary ID
|
||||
2. Adds to cache immediately (optimistic)
|
||||
3. Creates in Odoo (background)
|
||||
4. Replaces temp ID with real ID
|
||||
5. Rolls back on error
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
try {
|
||||
const newId = await expenseCache.create({
|
||||
x_studio_description: 'Lunch meeting',
|
||||
x_studio_amount: 45.50,
|
||||
x_studio_date: '2025-01-15',
|
||||
x_studio_category: 'meal'
|
||||
});
|
||||
|
||||
console.log('Created:', newId);
|
||||
navigate(`/expenses/${newId}`);
|
||||
} catch (error) {
|
||||
alert('Failed to create: ' + error.message);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### update()
|
||||
Update an existing record.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function update(id, data)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `id` (number) - Record ID
|
||||
- `data` (object) - Fields to update
|
||||
|
||||
**Returns:** Promise<boolean>
|
||||
|
||||
**Behavior:**
|
||||
1. Updates cache immediately (optimistic)
|
||||
2. Updates in Odoo (background)
|
||||
3. Rolls back on error
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
await expenseCache.update(123, {
|
||||
x_studio_amount: 50.00,
|
||||
x_studio_status: 'submitted'
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### delete()
|
||||
Delete a record.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function remove(id)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `id` (number) - Record ID to delete
|
||||
|
||||
**Returns:** Promise<boolean>
|
||||
|
||||
**Behavior:**
|
||||
1. Removes from cache immediately
|
||||
2. Deletes from Odoo (background)
|
||||
3. Restores on error
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
if (confirm('Delete this expense?')) {
|
||||
try {
|
||||
await expenseCache.remove(123);
|
||||
navigate('/expenses');
|
||||
} catch (error) {
|
||||
alert('Failed to delete: ' + error.message);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### refresh()
|
||||
Force refresh from Odoo.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
async function refresh()
|
||||
```
|
||||
|
||||
**Returns:** Promise<void>
|
||||
|
||||
**Behavior:**
|
||||
1. Fetches all records from Odoo
|
||||
2. Replaces cache
|
||||
3. Updates UI
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Manual refresh button
|
||||
<button onclick={() => expenseCache.refresh()}>
|
||||
Refresh
|
||||
</button>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### clearCache()
|
||||
Clear all cached data.
|
||||
|
||||
**Signature:**
|
||||
```javascript
|
||||
function clearCache()
|
||||
```
|
||||
|
||||
**Returns:** void
|
||||
|
||||
**Behavior:**
|
||||
1. Clears localStorage
|
||||
2. Clears IndexedDB
|
||||
3. Resets records to empty array
|
||||
|
||||
**Example:**
|
||||
```javascript
|
||||
// Logout function
|
||||
async function logout() {
|
||||
expenseCache.clearCache();
|
||||
// Clear other caches
|
||||
navigate('/login');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Custom Filters
|
||||
```javascript
|
||||
// Derived store (SvelteKit)
|
||||
import { derived } from 'svelte/store';
|
||||
|
||||
export const draftExpenses = derived(
|
||||
expenseCache,
|
||||
$cache => $cache.filter(e => e.x_studio_status === 'draft')
|
||||
);
|
||||
|
||||
// Hook (React)
|
||||
function useDraftExpenses() {
|
||||
const { records } = useExpense();
|
||||
return useMemo(
|
||||
() => records.filter(e => e.x_studio_status === 'draft'),
|
||||
[records]
|
||||
);
|
||||
}
|
||||
|
||||
// Computed (Vue)
|
||||
const draftExpenses = computed(() =>
|
||||
expenseStore.records.filter(e => e.x_studio_status === 'draft')
|
||||
);
|
||||
```
|
||||
|
||||
### Sorting
|
||||
```javascript
|
||||
export const sortedExpenses = derived(
|
||||
expenseCache,
|
||||
$cache => [...$cache].sort((a, b) =>
|
||||
b.x_studio_date.localeCompare(a.x_studio_date)
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### Search
|
||||
```javascript
|
||||
function searchExpenses(query) {
|
||||
return records.filter(e =>
|
||||
e.x_studio_description.toLowerCase().includes(query.toLowerCase())
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Grouping
|
||||
```javascript
|
||||
function groupByCategory(records) {
|
||||
return records.reduce((groups, record) => {
|
||||
const category = record.x_studio_category;
|
||||
if (!groups[category]) groups[category] = [];
|
||||
groups[category].push(record);
|
||||
return groups;
|
||||
}, {});
|
||||
}
|
||||
```
|
||||
|
||||
### Aggregation
|
||||
```javascript
|
||||
function getTotalByCategory(records) {
|
||||
return records.reduce((totals, record) => {
|
||||
const cat = record.x_studio_category;
|
||||
totals[cat] = (totals[cat] || 0) + record.x_studio_amount;
|
||||
return totals;
|
||||
}, {});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Server Route API Reference 🔐
|
||||
|
||||
The server route (`src/routes/api/odoo/+server.js`) handles Odoo communication.
|
||||
|
||||
### Endpoint
|
||||
**URL:** `/api/odoo`
|
||||
**Method:** POST
|
||||
**Content-Type:** application/json
|
||||
|
||||
### Request Format
|
||||
```json
|
||||
{
|
||||
"action": "create|search|update|delete",
|
||||
"model": "x_expense",
|
||||
...parameters
|
||||
}
|
||||
```
|
||||
|
||||
### Actions
|
||||
|
||||
#### create
|
||||
```json
|
||||
{
|
||||
"action": "create",
|
||||
"model": "x_expense",
|
||||
"fields": {
|
||||
"x_studio_description": "Lunch",
|
||||
"x_studio_amount": 45.50
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** `{ "id": 123 }`
|
||||
|
||||
#### search
|
||||
```json
|
||||
{
|
||||
"action": "search",
|
||||
"model": "x_expense",
|
||||
"domain": [["id", ">", 100]],
|
||||
"fields": ["x_studio_description", "x_studio_amount"]
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** `{ "records": [...] }`
|
||||
|
||||
#### update
|
||||
```json
|
||||
{
|
||||
"action": "update",
|
||||
"model": "x_expense",
|
||||
"id": 123,
|
||||
"values": {
|
||||
"x_studio_amount": 50.00
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** `{ "success": true }`
|
||||
|
||||
#### delete
|
||||
```json
|
||||
{
|
||||
"action": "delete",
|
||||
"model": "x_expense",
|
||||
"id": 123
|
||||
}
|
||||
```
|
||||
|
||||
**Response:** `{ "success": true }`
|
||||
|
||||
---
|
||||
|
||||
## Example prompts to use this command:
|
||||
- `/api-reference` - Show complete API documentation
|
||||
- User: "What methods are available in the Odoo client?"
|
||||
- User: "How do I use the cache store?"
|
||||
- User: "Show me API examples"
|
||||
|
||||
## Next Steps:
|
||||
- Try the methods in your project
|
||||
- Review `/examples` for practical use cases
|
||||
- See `/architecture` for design patterns
|
||||
- Check `/help` for more information
|
||||
Reference in New Issue
Block a user