Diagnose and fix synchronization issues between your PWA and Odoo.
Diagnoses and fixes synchronization issues between PWA and Odoo systems.
/plugin marketplace add jamshu/jamshi-marketplace/plugin install odoo-pwa-generator@jamshi-marketplaceDiagnose and fix synchronization issues between your PWA and Odoo.
Run through these quick checks first:
ā” Check browser console for errors
ā” Look at "Last synced" timestamp
ā” Try manual refresh
ā” Check network tab for failed requests
// In browser console:
// 1. Check if cache exists
localStorage.getItem('expenseCache');
// 2. Test API endpoint
fetch('/api/odoo', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'search',
model: 'res.partner',
domain: [],
fields: ['name'],
limit: 1
})
}).then(r => r.json()).then(console.log);
// 3. Force refresh
expenseCache.refresh();
/test-connection
Symptoms:
Diagnosis:
// Check if sync timer is running
// In cache store, look for:
setInterval(() => syncInBackground(), 180000);
// Check if stale detection works
const cacheData = JSON.parse(localStorage.getItem('expenseCache'));
console.log('Last sync:', new Date(cacheData?.lastSyncTime));
console.log('Is stale?', Date.now() - cacheData?.lastSyncTime > 5 * 60 * 1000);
Solutions:
// In browser console
expenseCache.stopAutoSync();
expenseCache.startAutoSync();
// In cache store file (e.g., expenseCache.js)
const CACHE_VALIDITY = 5 * 60 * 1000; // 5 minutes
function isCacheStale() {
const cacheData = JSON.parse(localStorage.getItem('expenseCache'));
if (!cacheData) return true;
const now = Date.now();
const age = now - cacheData.lastSyncTime;
console.log(`Cache age: ${Math.floor(age / 1000)}s`);
return age > CACHE_VALIDITY;
}
// Add a refresh button to your UI
<button onclick={() => expenseCache.refresh()}>
Sync Now
</button>
Symptoms:
lastRecordId not updatingDiagnosis:
// Check lastRecordId
const cacheData = JSON.parse(localStorage.getItem('expenseCache'));
console.log('Last record ID:', cacheData?.lastRecordId);
// Check if it's being updated after sync
Solutions:
// In syncInBackground() function
async function syncInBackground() {
const { lastRecordId } = getCacheMetadata();
console.log('Fetching records with id >', lastRecordId);
const domain = lastRecordId > 0
? [['id', '>', lastRecordId]]
: [];
const newRecords = await odoo.searchRecords(
MODEL_NAME,
domain,
fields
);
console.log('Fetched:', newRecords.length, 'new records');
if (newRecords.length > 0) {
// Update lastRecordId
const maxId = Math.max(...newRecords.map(r => r.id));
updateCacheMetadata({ lastRecordId: maxId });
}
}
// If stuck, reset to fetch all
const cacheData = JSON.parse(localStorage.getItem('expenseCache'));
cacheData.lastRecordId = 0;
localStorage.setItem('expenseCache', JSON.stringify(cacheData));
// Then refresh
expenseCache.refresh();
Symptoms:
Diagnosis:
// Check for temp IDs
console.log($expenseCache.filter(e => e.id.toString().startsWith('temp-')));
// Check browser console for API errors
Solutions:
// Test create
fetch('/api/odoo', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
action: 'create',
model: 'x_expense',
fields: {
x_studio_description: 'Test',
x_studio_amount: 10.00
}
})
})
.then(r => r.json())
.then(console.log)
.catch(console.error);
// In cache store create() function
export async function create(data) {
const tempId = `temp-${Date.now()}`;
const tempRecord = { id: tempId, ...data };
// Add to cache
records.update(r => [...r, tempRecord]);
try {
// Create in Odoo
const realId = await odoo.createRecord(MODEL_NAME, data);
// Replace temp ID
records.update(r =>
r.map(rec => rec.id === tempId ? { ...rec, id: realId } : rec)
);
// Update cache metadata
await refresh(); // Sync to get the complete record
return realId;
} catch (error) {
console.error('Failed to create record:', error);
// Rollback
records.update(r => r.filter(rec => rec.id !== tempId));
// Re-throw
throw error;
}
}
// Remove stuck temp records
expenseCache.records.update(records =>
records.filter(r => !r.id.toString().startsWith('temp-'))
);
// Then refresh
expenseCache.refresh();
Symptoms:
Diagnosis:
// Check partner field format
const record = $expenseCache[0];
console.log('Employee field:', record.x_studio_employee);
// Should be: [12, "John Doe"] or 12
Solutions:
// In syncInBackground()
async function syncInBackground() {
// ... fetch records ...
// Resolve partner names
const partnerIds = new Set();
records.forEach(record => {
if (record.x_studio_employee) {
const id = Array.isArray(record.x_studio_employee)
? record.x_studio_employee[0]
: record.x_studio_employee;
partnerIds.add(id);
}
});
if (partnerIds.size > 0) {
const partners = await odoo.fetchPartners(Array.from(partnerIds));
const partnerMap = new Map(partners.map(p => [p.id, p.name]));
// Cache partners
localStorage.setItem('partnerCache', JSON.stringify(
Array.from(partnerMap.entries())
));
// Update records with names
records = records.map(record => {
if (record.x_studio_employee) {
const id = Array.isArray(record.x_studio_employee)
? record.x_studio_employee[0]
: record.x_studio_employee;
return {
...record,
x_studio_employee: [id, partnerMap.get(id) || 'Unknown']
};
}
return record;
});
}
}
// Helper to display partner name
function getPartnerName(field) {
if (!field) return 'None';
if (Array.isArray(field)) return field[1] || `ID: ${field[0]}`;
return `ID: ${field}`;
}
// In component
{getPartnerName(expense.x_studio_employee)}
Symptoms:
Diagnosis:
// Check for duplicates
const ids = $expenseCache.map(r => r.id);
const duplicates = ids.filter((id, index) => ids.indexOf(id) !== index);
console.log('Duplicate IDs:', duplicates);
Solutions:
function deduplicateCache() {
records.update(currentRecords => {
const seen = new Set();
return currentRecords.filter(record => {
if (seen.has(record.id)) {
return false;
}
seen.add(record.id);
return true;
});
});
}
// Run deduplication
deduplicateCache();
// When appending new records, check for duplicates
async function appendToCache(newRecords) {
records.update(currentRecords => {
const existingIds = new Set(currentRecords.map(r => r.id));
// Only add records that don't exist
const toAdd = newRecords.filter(r => !existingIds.has(r.id));
return [...currentRecords, ...toAdd];
});
}
Symptoms:
Solutions:
// Store failed operations
const offlineQueue = writable([]);
async function queueOperation(operation) {
offlineQueue.update(q => [...q, operation]);
saveOfflineQueue();
}
async function processOfflineQueue() {
const queue = get(offlineQueue);
for (const operation of queue) {
try {
if (operation.type === 'create') {
await odoo.createRecord(operation.model, operation.data);
} else if (operation.type === 'update') {
await odoo.updateRecord(operation.model, operation.id, operation.data);
} else if (operation.type === 'delete') {
await odoo.deleteRecord(operation.model, operation.id);
}
// Remove from queue
offlineQueue.update(q => q.filter(op => op !== operation));
} catch (error) {
console.error('Failed to process queued operation:', error);
// Keep in queue, will retry
}
}
saveOfflineQueue();
}
// Listen for online event
window.addEventListener('online', () => {
console.log('Back online, processing queue...');
processOfflineQueue();
});
// Clear all caches
localStorage.clear();
// Clear IndexedDB
// DevTools ā Application ā IndexedDB ā Delete database
/test-connection
// Refresh page
location.reload();
// Should fetch all data fresh
// Watch console for sync messages
// Should see:
// "Syncing x_expense..."
// "Fetched X records"
// "Cache updated"
// Test create
await expenseCache.create({ /* data */ });
// Test update
await expenseCache.update(id, { /* data */ });
// Test delete
await expenseCache.remove(id);
// Verify in Odoo
ā” Browser console shows no errors
ā” /api/odoo endpoint responds
ā” ODOO_API_KEY is valid
ā” lastSyncTime is updating
ā” lastRecordId is updating
ā” Background sync interval is running
ā” Stale detection works correctly
ā” Incremental fetch has correct domain
ā” Optimistic updates resolve temp IDs
ā” Partner names resolve correctly
ā” No duplicate records
ā” Offline queue processes when online
ā” IndexedDB is storing data
ā” localStorage has metadata
// Add to cache store
const DEBUG = true;
function log(...args) {
if (DEBUG) console.log('[ExpenseCache]', ...args);
}
async function syncInBackground() {
log('Starting background sync...');
log('lastRecordId:', getLastRecordId());
const records = await fetch();
log('Fetched records:', records.length);
await saveToCache(records);
log('Cache updated');
}
// Log all API calls
const originalFetch = window.fetch;
window.fetch = async (...args) => {
console.log('Fetch:', args[0]);
const response = await originalFetch(...args);
console.log('Response:', response.status);
return response;
};
// Measure sync time
console.time('sync');
await expenseCache.refresh();
console.timeEnd('sync');
/fix-sync - Diagnose sync issues/test-connection - Test Odoo connectivity/clear-cache - Clear all cached data/troubleshoot - General troubleshooting/help - Full documentationMonitor sync health
Handle errors gracefully
Test offline scenarios
Keep sync simple
Regular maintenance