Initial commit
This commit is contained in:
596
commands/fix-sync.md
Normal file
596
commands/fix-sync.md
Normal file
@@ -0,0 +1,596 @@
|
||||
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
|
||||
Reference in New Issue
Block a user