fix(task_sync): defend against silent sync_id integrity violations

The cross-instance sync silently drops tasks when x_fc_tech_sync_id is
missing on the technician, and silently collapses duplicates via dict
comprehension. Both make sync break in ways that are invisible until
someone notices a missing task on the other instance.

- _get_remote_tech_map / _get_local_syncid_to_uid: warn on duplicates
- _push_tasks_to_remote: info-log when a task is skipped because the
  tech has no sync_id or no remote counterpart
- res.users onchange: warn in the form when entering a sync_id that
  is already used by another active field staff

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-20 22:29:48 -04:00
parent d4fb1eebbf
commit 8ef57a4bb1
3 changed files with 53 additions and 8 deletions

View File

@@ -135,11 +135,18 @@ class FusionTaskSyncConfig(models.Model):
], {'fields': ['id', 'x_fc_tech_sync_id']})
if not remote_users:
return {}
return {
ru['x_fc_tech_sync_id']: ru['id']
for ru in remote_users
if ru.get('x_fc_tech_sync_id')
}
by_sync_id = {}
for ru in remote_users:
sid = ru.get('x_fc_tech_sync_id')
if not sid:
continue
if sid in by_sync_id:
_logger.warning(
"Task sync: duplicate x_fc_tech_sync_id %r on remote %s "
"(uids %d and %d) — only the last seen will be reachable",
sid, self.name, by_sync_id[sid], ru['id'])
by_sync_id[sid] = ru['id']
return by_sync_id
def _get_local_syncid_to_uid(self):
"""Build {x_fc_tech_sync_id: local_user_id} for local field staff."""
@@ -148,7 +155,16 @@ class FusionTaskSyncConfig(models.Model):
('x_fc_tech_sync_id', '!=', False),
('active', '=', True),
])
return {u.x_fc_tech_sync_id: u.id for u in techs}
by_sync_id = {}
for u in techs:
sid = u.x_fc_tech_sync_id
if sid in by_sync_id:
_logger.warning(
"Task sync: duplicate x_fc_tech_sync_id %r locally "
"(uids %d and %d) — only the last seen will be reachable",
sid, by_sync_id[sid], u.id)
by_sync_id[sid] = u.id
return by_sync_id
# ------------------------------------------------------------------
# Connection test
@@ -219,9 +235,15 @@ class FusionTaskSyncConfig(models.Model):
for task in tasks:
sync_id = local_map.get(task.technician_id.id)
if not sync_id:
_logger.info(
"Task sync: skipping task %s — technician %s has no x_fc_tech_sync_id",
task.name, task.technician_id.login or task.technician_id.id)
continue
remote_tech_uid = remote_map.get(sync_id)
if not remote_tech_uid:
_logger.info(
"Task sync: skipping task %s — sync_id %r has no matching tech on %s",
task.name, sync_id, self.name)
continue
# Map additional technicians to remote user IDs