597 lines
13 KiB
Markdown
597 lines
13 KiB
Markdown
Diagnose and fix synchronization issues between your PWA and Odoo.
|
|
|
|
## What this command does:
|
|
- Identifies sync problems
|
|
- Tests each component of the sync system
|
|
- Provides step-by-step fixes
|
|
- Clears problematic cached data
|
|
- Verifies sync works correctly
|
|
|
|
---
|
|
|
|
## Quick Diagnosis 🔍
|
|
|
|
Run through these quick checks first:
|
|
|
|
### 1. Visual Inspection
|
|
```
|
|
□ Check browser console for errors
|
|
□ Look at "Last synced" timestamp
|
|
□ Try manual refresh
|
|
□ Check network tab for failed requests
|
|
```
|
|
|
|
### 2. Quick Tests
|
|
```javascript
|
|
// 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();
|
|
```
|
|
|
|
### 3. Automatic Diagnosis
|
|
```bash
|
|
/test-connection
|
|
```
|
|
|
|
---
|
|
|
|
## Common Sync Issues
|
|
|
|
### Issue 1: Initial Load Works, But No Updates 🔄
|
|
|
|
**Symptoms:**
|
|
- Data loads on first visit
|
|
- But never updates with new Odoo data
|
|
- "Last synced" timestamp is old
|
|
- Background sync not happening
|
|
|
|
**Diagnosis:**
|
|
```javascript
|
|
// 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:**
|
|
|
|
#### Solution A: Restart Background Sync
|
|
```javascript
|
|
// In browser console
|
|
expenseCache.stopAutoSync();
|
|
expenseCache.startAutoSync();
|
|
```
|
|
|
|
#### Solution B: Check Cache Validity
|
|
```javascript
|
|
// 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;
|
|
}
|
|
```
|
|
|
|
#### Solution C: Force Manual Sync
|
|
```javascript
|
|
// Add a refresh button to your UI
|
|
<button onclick={() => expenseCache.refresh()}>
|
|
Sync Now
|
|
</button>
|
|
```
|
|
|
|
---
|
|
|
|
### Issue 2: Incremental Sync Not Working 📈
|
|
|
|
**Symptoms:**
|
|
- Always fetches all records
|
|
- Slow sync times
|
|
- High bandwidth usage
|
|
- `lastRecordId` not updating
|
|
|
|
**Diagnosis:**
|
|
```javascript
|
|
// 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:**
|
|
|
|
#### Solution A: Verify Domain Filter
|
|
```javascript
|
|
// 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 });
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Solution B: Reset lastRecordId
|
|
```javascript
|
|
// 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();
|
|
```
|
|
|
|
---
|
|
|
|
### Issue 3: Optimistic Updates Not Syncing ⚡
|
|
|
|
**Symptoms:**
|
|
- Create/update works in UI
|
|
- But changes don't save to Odoo
|
|
- Records disappear on refresh
|
|
- Temp IDs persist
|
|
|
|
**Diagnosis:**
|
|
```javascript
|
|
// Check for temp IDs
|
|
console.log($expenseCache.filter(e => e.id.toString().startsWith('temp-')));
|
|
|
|
// Check browser console for API errors
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
#### Solution A: Check API Route
|
|
```javascript
|
|
// 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);
|
|
```
|
|
|
|
#### Solution B: Fix Error Handling
|
|
```javascript
|
|
// 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;
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Solution C: Clean Up Temp Records
|
|
```javascript
|
|
// Remove stuck temp records
|
|
expenseCache.records.update(records =>
|
|
records.filter(r => !r.id.toString().startsWith('temp-'))
|
|
);
|
|
|
|
// Then refresh
|
|
expenseCache.refresh();
|
|
```
|
|
|
|
---
|
|
|
|
### Issue 4: Partner Names Not Resolving 👥
|
|
|
|
**Symptoms:**
|
|
- Partner fields show IDs instead of names
|
|
- Many2one fields display as arrays
|
|
- "undefined" or "[object Object]" displayed
|
|
|
|
**Diagnosis:**
|
|
```javascript
|
|
// Check partner field format
|
|
const record = $expenseCache[0];
|
|
console.log('Employee field:', record.x_studio_employee);
|
|
// Should be: [12, "John Doe"] or 12
|
|
```
|
|
|
|
**Solutions:**
|
|
|
|
#### Solution A: Add Partner Resolution
|
|
```javascript
|
|
// 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;
|
|
});
|
|
}
|
|
}
|
|
```
|
|
|
|
#### Solution B: Display Helper Function
|
|
```javascript
|
|
// 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)}
|
|
```
|
|
|
|
---
|
|
|
|
### Issue 5: Duplicate Records 📋📋
|
|
|
|
**Symptoms:**
|
|
- Same record appears multiple times
|
|
- ID conflicts
|
|
- Sync creates duplicates
|
|
|
|
**Diagnosis:**
|
|
```javascript
|
|
// 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:**
|
|
|
|
#### Solution A: Deduplicate Cache
|
|
```javascript
|
|
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();
|
|
```
|
|
|
|
#### Solution B: Fix Sync Logic
|
|
```javascript
|
|
// 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];
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### Issue 6: Offline Queue Not Syncing 📴
|
|
|
|
**Symptoms:**
|
|
- Changes made offline
|
|
- Online now, but changes not syncing
|
|
- Stuck in "pending" state
|
|
|
|
**Solutions:**
|
|
|
|
#### Solution A: Implement Offline Queue
|
|
```javascript
|
|
// 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();
|
|
});
|
|
```
|
|
|
|
---
|
|
|
|
## Step-by-Step Fix Procedure 🔧
|
|
|
|
### Step 1: Clear Everything
|
|
```javascript
|
|
// Clear all caches
|
|
localStorage.clear();
|
|
|
|
// Clear IndexedDB
|
|
// DevTools → Application → IndexedDB → Delete database
|
|
```
|
|
|
|
### Step 2: Test API
|
|
```bash
|
|
/test-connection
|
|
```
|
|
|
|
### Step 3: Fresh Sync
|
|
```javascript
|
|
// Refresh page
|
|
location.reload();
|
|
|
|
// Should fetch all data fresh
|
|
```
|
|
|
|
### Step 4: Monitor Sync
|
|
```javascript
|
|
// Watch console for sync messages
|
|
// Should see:
|
|
// "Syncing x_expense..."
|
|
// "Fetched X records"
|
|
// "Cache updated"
|
|
```
|
|
|
|
### Step 5: Test CRUD
|
|
```javascript
|
|
// Test create
|
|
await expenseCache.create({ /* data */ });
|
|
|
|
// Test update
|
|
await expenseCache.update(id, { /* data */ });
|
|
|
|
// Test delete
|
|
await expenseCache.remove(id);
|
|
|
|
// Verify in Odoo
|
|
```
|
|
|
|
---
|
|
|
|
## Sync Debugging Checklist ✅
|
|
|
|
```
|
|
□ 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
|
|
```
|
|
|
|
---
|
|
|
|
## Advanced Debugging 🐛
|
|
|
|
### Enable Verbose Logging
|
|
```javascript
|
|
// 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');
|
|
}
|
|
```
|
|
|
|
### Monitor Network
|
|
```javascript
|
|
// 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;
|
|
};
|
|
```
|
|
|
|
### Profile Performance
|
|
```javascript
|
|
// Measure sync time
|
|
console.time('sync');
|
|
await expenseCache.refresh();
|
|
console.timeEnd('sync');
|
|
```
|
|
|
|
---
|
|
|
|
## Example prompts to use this command:
|
|
- `/fix-sync` - Diagnose sync issues
|
|
- User: "Data is not syncing"
|
|
- User: "My changes aren't saving"
|
|
- User: "Sync is broken"
|
|
|
|
## Related Commands:
|
|
- `/test-connection` - Test Odoo connectivity
|
|
- `/clear-cache` - Clear all cached data
|
|
- `/troubleshoot` - General troubleshooting
|
|
- `/help` - Full documentation
|
|
|
|
---
|
|
|
|
## Prevention Tips 🛡️
|
|
|
|
1. **Monitor sync health**
|
|
- Display "Last synced" timestamp in UI
|
|
- Show sync status indicator
|
|
- Alert on sync failures
|
|
|
|
2. **Handle errors gracefully**
|
|
- Catch and log all errors
|
|
- Show user-friendly messages
|
|
- Provide retry mechanisms
|
|
|
|
3. **Test offline scenarios**
|
|
- Test creating records offline
|
|
- Test going offline mid-sync
|
|
- Test coming back online
|
|
|
|
4. **Keep sync simple**
|
|
- Stick to generated patterns
|
|
- Don't overcomplicate logic
|
|
- Follow proven examples
|
|
|
|
5. **Regular maintenance**
|
|
- Clear old data periodically
|
|
- Update dependencies
|
|
- Monitor performance
|