Files
gh-jamshu-jamshi-marketplac…/commands/fix-sync.md
2025-11-29 18:50:06 +08:00

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