Complete API reference for the Odoo client and cache stores in generated PWAs.
Generates comprehensive API documentation for Odoo client methods and cache stores in PWAs.
/plugin marketplace add jamshu/jamshi-marketplace/plugin install odoo-pwa-generator@jamshi-marketplaceComplete API reference for the Odoo client and cache stores in generated PWAs.
The Odoo API client (src/lib/odoo.js or equivalent) provides methods for interacting with Odoo Studio models.
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
Create a new record in Odoo.
Signature:
async function createRecord(model, fields)
Parameters:
model (string) - Odoo model name (e.g., 'x_expense')fields (object) - Field values to setReturns: Promise<number> - ID of created record
Example:
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:
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.)
}
Search and read records from Odoo.
Signature:
async function searchRecords(model, domain, fields, limit = null, offset = null)
Parameters:
model (string) - Odoo model namedomain (array) - Odoo domain filter (e.g., [['id', '>', 100]])fields (array) - List of field names to fetchlimit (number, optional) - Maximum number of recordsoffset (number, optional) - Number of records to skipReturns: Promise<Array<Object>> - Array of record objects
Example:
// 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:
// 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']
]
Update an existing record.
Signature:
async function updateRecord(model, id, values)
Parameters:
model (string) - Odoo model nameid (number) - Record ID to updatevalues (object) - Fields to updateReturns: Promise<boolean> - true if successful
Example:
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'
});
Delete a record from Odoo.
Signature:
async function deleteRecord(model, id)
Parameters:
model (string) - Odoo model nameid (number) - Record ID to deleteReturns: Promise<boolean> - true if successful
Example:
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:
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);
}
Fetch partner (res.partner) records.
Signature:
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:
// 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 }))
Format a Many2one field value for Odoo.
Signature:
function formatMany2one(id)
Parameters:
id (number | null) - Partner/record IDReturns: Array<number, boolean> | false - Odoo-formatted value
Example:
// 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
};
Format a Many2many field value for Odoo.
Signature:
function formatMany2many(ids)
Parameters:
ids (array) - Array of record IDsReturns: Array - Odoo command format
Example:
// 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:
// (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
The cache store provides reactive state management with offline-first capabilities.
Type: Reactive Array<Object>
Current cached records.
Example:
// 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)
);
Type: Reactive Boolean
Loading state indicator.
Example:
{#if $expenseCache.isLoading}
<LoadingSpinner />
{:else}
<ExpenseList />
{/if}
Type: Reactive String | null
Current error message, if any.
Example:
{#if $expenseCache.error}
<ErrorAlert message={$expenseCache.error} />
{/if}
Type: Reactive Number (timestamp)
Timestamp of last successful sync.
Example:
const timeSinceSync = Date.now() - $expenseCache.lastSync;
const minutes = Math.floor(timeSinceSync / 60000);
// Display: "Last synced ${minutes} minutes ago"
Load records from cache and trigger background sync.
Signature:
async function load()
Returns: Promise<void>
Behavior:
Example:
// SvelteKit
$effect(() => {
expenseCache.load();
});
// React
useEffect(() => {
expenseCache.load();
}, []);
// Vue
onMounted(() => {
expenseStore.load();
});
Create a new record with optimistic update.
Signature:
async function create(data)
Parameters:
data (object) - Field values for new recordReturns: Promise<number> - ID of created record
Behavior:
Example:
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 an existing record.
Signature:
async function update(id, data)
Parameters:
id (number) - Record IDdata (object) - Fields to updateReturns: Promise<boolean>
Behavior:
Example:
await expenseCache.update(123, {
x_studio_amount: 50.00,
x_studio_status: 'submitted'
});
Delete a record.
Signature:
async function remove(id)
Parameters:
id (number) - Record ID to deleteReturns: Promise<boolean>
Behavior:
Example:
if (confirm('Delete this expense?')) {
try {
await expenseCache.remove(123);
navigate('/expenses');
} catch (error) {
alert('Failed to delete: ' + error.message);
}
}
Force refresh from Odoo.
Signature:
async function refresh()
Returns: Promise<void>
Behavior:
Example:
// Manual refresh button
<button onclick={() => expenseCache.refresh()}>
Refresh
</button>
Clear all cached data.
Signature:
function clearCache()
Returns: void
Behavior:
Example:
// Logout function
async function logout() {
expenseCache.clearCache();
// Clear other caches
navigate('/login');
}
// 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')
);
export const sortedExpenses = derived(
expenseCache,
$cache => [...$cache].sort((a, b) =>
b.x_studio_date.localeCompare(a.x_studio_date)
)
);
function searchExpenses(query) {
return records.filter(e =>
e.x_studio_description.toLowerCase().includes(query.toLowerCase())
);
}
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;
}, {});
}
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;
}, {});
}
The server route (src/routes/api/odoo/+server.js) handles Odoo communication.
URL: /api/odoo
Method: POST
Content-Type: application/json
{
"action": "create|search|update|delete",
"model": "x_expense",
...parameters
}
{
"action": "create",
"model": "x_expense",
"fields": {
"x_studio_description": "Lunch",
"x_studio_amount": 45.50
}
}
Response: { "id": 123 }
{
"action": "search",
"model": "x_expense",
"domain": [["id", ">", 100]],
"fields": ["x_studio_description", "x_studio_amount"]
}
Response: { "records": [...] }
{
"action": "update",
"model": "x_expense",
"id": 123,
"values": {
"x_studio_amount": 50.00
}
}
Response: { "success": true }
{
"action": "delete",
"model": "x_expense",
"id": 123
}
Response: { "success": true }
/api-reference - Show complete API documentation/examples for practical use cases/architecture for design patterns/help for more information