refactor(fusion_tasks): strip all sync references from task model
Remove cross-instance sync fields (x_fc_sync_source, x_fc_sync_remote_id, x_fc_sync_uuid, x_fc_is_shadow, x_fc_sync_client_name, x_fc_sync_client_phone, x_fc_source_label), sync push calls in create/write/action methods, shadow task logic in constraints and email guards, and x_fc_tech_sync_id from res.users. Also remove task_sync import from models/__init__.py. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,5 @@ from . import res_company
|
||||
from . import res_users
|
||||
from . import res_config_settings
|
||||
from . import technician_task
|
||||
from . import task_sync
|
||||
from . import technician_location
|
||||
from . import push_subscription
|
||||
|
||||
@@ -18,9 +18,3 @@ class ResUsers(models.Model):
|
||||
readonly=False,
|
||||
string='Start Location',
|
||||
)
|
||||
x_fc_tech_sync_id = fields.Char(
|
||||
string='Tech Sync ID',
|
||||
help='Shared identifier for this technician across Odoo instances. '
|
||||
'Must be the same value on all instances for the same person.',
|
||||
copy=False,
|
||||
)
|
||||
|
||||
@@ -14,7 +14,6 @@ from odoo.osv import expression
|
||||
from markupsafe import Markup
|
||||
import logging
|
||||
import json
|
||||
import uuid
|
||||
import requests
|
||||
from datetime import datetime as dt_datetime, timedelta
|
||||
import urllib.parse
|
||||
@@ -33,7 +32,7 @@ class FusionTechnicianTask(models.Model):
|
||||
"""Richer display name: Client - Type | 9:00 AM - 10:00 AM [+2 techs]."""
|
||||
type_labels = dict(self._fields['task_type'].selection)
|
||||
for task in self:
|
||||
client = task.x_fc_sync_client_name if task.x_fc_sync_source else (task.partner_id.name or '')
|
||||
client = task.partner_id.name or ''
|
||||
ttype = type_labels.get(task.task_type, task.task_type or '')
|
||||
start = self._float_to_time_str(task.time_start)
|
||||
end = self._float_to_time_str(task.time_end)
|
||||
@@ -74,32 +73,6 @@ class FusionTechnicianTask(models.Model):
|
||||
)
|
||||
active = fields.Boolean(default=True)
|
||||
|
||||
# Cross-instance sync fields
|
||||
x_fc_sync_source = fields.Char(
|
||||
'Source Instance', readonly=True, index=True,
|
||||
help='Origin instance ID if this is a synced shadow task (e.g. westin, mobility)',
|
||||
)
|
||||
x_fc_sync_remote_id = fields.Integer(
|
||||
'Remote Task ID', readonly=True,
|
||||
help='ID of the task on the remote instance',
|
||||
)
|
||||
x_fc_sync_uuid = fields.Char(
|
||||
'Sync UUID', readonly=True, index=True, copy=False,
|
||||
help='Unique ID for cross-instance deduplication',
|
||||
)
|
||||
x_fc_is_shadow = fields.Boolean(
|
||||
'Shadow Task', compute='_compute_is_shadow', store=True,
|
||||
help='True if this task was synced from another instance',
|
||||
)
|
||||
x_fc_sync_client_name = fields.Char(
|
||||
'Synced Client Name', readonly=True,
|
||||
help='Client name from the remote instance (shadow tasks only)',
|
||||
)
|
||||
x_fc_sync_client_phone = fields.Char(
|
||||
'Synced Client Phone', readonly=True,
|
||||
help='Client phone from the remote instance (shadow tasks only)',
|
||||
)
|
||||
|
||||
client_display_name = fields.Char(
|
||||
compute='_compute_client_display', string='Client Name (Display)',
|
||||
)
|
||||
@@ -107,28 +80,11 @@ class FusionTechnicianTask(models.Model):
|
||||
compute='_compute_client_display', string='Client Phone (Display)',
|
||||
)
|
||||
|
||||
x_fc_source_label = fields.Char(
|
||||
'Source', compute='_compute_is_shadow', store=True,
|
||||
)
|
||||
|
||||
@api.depends('x_fc_sync_source')
|
||||
def _compute_is_shadow(self):
|
||||
local_id = self.env['ir.config_parameter'].sudo().get_param(
|
||||
'fusion_claims.sync_instance_id', '')
|
||||
for task in self:
|
||||
task.x_fc_is_shadow = bool(task.x_fc_sync_source)
|
||||
task.x_fc_source_label = task.x_fc_sync_source or local_id
|
||||
|
||||
@api.depends('x_fc_sync_source', 'x_fc_sync_client_name',
|
||||
'x_fc_sync_client_phone', 'partner_id')
|
||||
@api.depends('partner_id')
|
||||
def _compute_client_display(self):
|
||||
for task in self:
|
||||
if task.x_fc_sync_source:
|
||||
task.client_display_name = task.x_fc_sync_client_name or task.name or ''
|
||||
task.client_display_phone = task.x_fc_sync_client_phone or ''
|
||||
else:
|
||||
task.client_display_name = task.partner_id.name if task.partner_id else ''
|
||||
task.client_display_phone = task.partner_id.phone if task.partner_id else ''
|
||||
task.client_display_name = task.partner_id.name if task.partner_id else ''
|
||||
task.client_display_phone = task.partner_id.phone if task.partner_id else ''
|
||||
|
||||
technician_id = fields.Many2one(
|
||||
'res.users',
|
||||
@@ -1101,8 +1057,6 @@ class FusionTechnicianTask(models.Model):
|
||||
def _check_address_required(self):
|
||||
"""Non-in-store tasks must have a geocoded address."""
|
||||
for task in self:
|
||||
if task.x_fc_sync_source:
|
||||
continue
|
||||
if task.is_in_store:
|
||||
continue
|
||||
if not task.address_street:
|
||||
@@ -1121,8 +1075,6 @@ class FusionTechnicianTask(models.Model):
|
||||
for task in self:
|
||||
if task.status == 'cancelled':
|
||||
continue
|
||||
if task.x_fc_sync_source:
|
||||
continue
|
||||
# Validate time range
|
||||
if task.time_start >= task.time_end:
|
||||
raise ValidationError(_("Start time must be before end time."))
|
||||
@@ -1134,8 +1086,8 @@ class FusionTechnicianTask(models.Model):
|
||||
raise ValidationError(_(
|
||||
"Tasks must be scheduled within store hours (%s - %s)."
|
||||
) % (open_str, close_str))
|
||||
# Validate not in the past (only for new/scheduled local tasks)
|
||||
if task.status == 'scheduled' and task.scheduled_date and not task.x_fc_sync_source:
|
||||
# Validate not in the past (only for new/scheduled tasks)
|
||||
if task.status == 'scheduled' and task.scheduled_date:
|
||||
local_now = self._local_now()
|
||||
today = local_now.date()
|
||||
if task.scheduled_date < today:
|
||||
@@ -1408,8 +1360,6 @@ class FusionTechnicianTask(models.Model):
|
||||
for vals in vals_list:
|
||||
if vals.get('name', _('New')) == _('New'):
|
||||
vals['name'] = self.env['ir.sequence'].next_by_code('fusion.technician.task') or _('New')
|
||||
if not vals.get('x_fc_sync_uuid') and not vals.get('x_fc_sync_source'):
|
||||
vals['x_fc_sync_uuid'] = str(uuid.uuid4())
|
||||
# In-store tasks: auto-fill company address
|
||||
if vals.get('is_in_store') and not vals.get('address_street'):
|
||||
company_partner = self.env.company.partner_id
|
||||
@@ -1417,7 +1367,7 @@ class FusionTechnicianTask(models.Model):
|
||||
self._fill_address_vals(vals, company_partner)
|
||||
else:
|
||||
vals['address_street'] = self.env.company.name or 'In Store'
|
||||
# Hook: fill address from linked records (overridden by fusion_claims)
|
||||
# Hook: fill address from linked records
|
||||
self._create_vals_fill(vals)
|
||||
records = super().create(vals_list)
|
||||
# Hook: post-create actions for linked records
|
||||
@@ -1428,10 +1378,6 @@ class FusionTechnicianTask(models.Model):
|
||||
# Send "Appointment Scheduled" email
|
||||
for rec in records:
|
||||
rec._send_task_scheduled_email()
|
||||
# Push new local tasks to remote instances
|
||||
local_records = records.filtered(lambda r: not r.x_fc_sync_source)
|
||||
if local_records and not self.env.context.get('skip_task_sync'):
|
||||
self.env['fusion.task.sync.config']._push_tasks(local_records, 'create')
|
||||
# Sync to calendar for external calendar integrations
|
||||
records._sync_calendar_event()
|
||||
return records
|
||||
@@ -1457,16 +1403,7 @@ class FusionTechnicianTask(models.Model):
|
||||
|
||||
def write(self, vals):
|
||||
if self.env.context.get('skip_travel_recalc'):
|
||||
res = super().write(vals)
|
||||
if ('status' in vals and vals['status'] in ('completed', 'cancelled')
|
||||
and not self.env.context.get('skip_task_sync')):
|
||||
shadow_records = self.filtered(lambda r: r.x_fc_sync_source)
|
||||
if shadow_records:
|
||||
self.env['fusion.task.sync.config']._push_shadow_status(shadow_records)
|
||||
local_records = self.filtered(lambda r: not r.x_fc_sync_source)
|
||||
if local_records:
|
||||
self.env['fusion.task.sync.config']._push_tasks(local_records, 'write')
|
||||
return res
|
||||
return super().write(vals)
|
||||
|
||||
# Safety: ensure time_end is consistent when start/duration change
|
||||
# but time_end wasn't sent (readonly field in view may not save)
|
||||
@@ -1526,20 +1463,6 @@ class FusionTechnicianTask(models.Model):
|
||||
old_start=old['time_start'],
|
||||
old_end=old['time_end'],
|
||||
)
|
||||
# Push updates to remote instances for local tasks
|
||||
sync_fields = {'technician_id', 'additional_technician_ids',
|
||||
'scheduled_date', 'time_start', 'time_end',
|
||||
'duration_hours', 'status', 'task_type', 'address_street',
|
||||
'address_city', 'address_zip', 'address_lat', 'address_lng',
|
||||
'partner_id'}
|
||||
if sync_fields & set(vals.keys()) and not self.env.context.get('skip_task_sync'):
|
||||
local_records = self.filtered(lambda r: not r.x_fc_sync_source)
|
||||
if local_records:
|
||||
self.env['fusion.task.sync.config']._push_tasks(local_records, 'write')
|
||||
if 'status' in vals and vals['status'] in ('completed', 'cancelled'):
|
||||
shadow_records = self.filtered(lambda r: r.x_fc_sync_source)
|
||||
if shadow_records:
|
||||
self.env['fusion.task.sync.config']._push_shadow_status(shadow_records)
|
||||
# Re-sync calendar event when schedule fields change
|
||||
cal_fields = {'scheduled_date', 'time_start', 'time_end',
|
||||
'duration_hours', 'technician_id', 'task_type',
|
||||
@@ -1928,12 +1851,6 @@ class FusionTechnicianTask(models.Model):
|
||||
task._send_task_en_route_email()
|
||||
# Recalculate travel from tech's current location to THIS task
|
||||
task._recalculate_travel_from_current_location()
|
||||
if task.x_fc_sync_source:
|
||||
try:
|
||||
self.env['fusion.task.sync.config']._push_shadow_status(task)
|
||||
except Exception:
|
||||
_logger.exception(
|
||||
"Failed to push en_route for shadow %s", task.name)
|
||||
try:
|
||||
remaining = self.sudo().search_count([
|
||||
('technician_id', '=', task.technician_id.id),
|
||||
@@ -2012,14 +1929,7 @@ class FusionTechnicianTask(models.Model):
|
||||
task.status = 'cancelled'
|
||||
task._write_action_location()
|
||||
task._post_status_message('cancelled')
|
||||
if task.x_fc_sync_source:
|
||||
try:
|
||||
self.env['fusion.task.sync.config']._push_shadow_status(task)
|
||||
except Exception:
|
||||
_logger.exception(
|
||||
"Failed to push cancel for shadow %s", task.name)
|
||||
else:
|
||||
task._on_cancel_extra()
|
||||
task._on_cancel_extra()
|
||||
|
||||
def _on_cancel_extra(self):
|
||||
"""Hook: additional side-effects after task cancellation.
|
||||
@@ -2079,14 +1989,8 @@ class FusionTechnicianTask(models.Model):
|
||||
pass
|
||||
|
||||
def _notify_scheduler_on_completion(self):
|
||||
"""Send an Odoo notification to the person who scheduled the task.
|
||||
|
||||
Shadow tasks skip this -- the push-back to the source instance
|
||||
triggers the notification there where the real scheduler exists.
|
||||
"""
|
||||
"""Send an Odoo notification to the person who scheduled the task."""
|
||||
self.ensure_one()
|
||||
if self.x_fc_sync_source:
|
||||
return
|
||||
|
||||
recipient = None
|
||||
order = self._get_linked_order()
|
||||
@@ -2224,7 +2128,7 @@ class FusionTechnicianTask(models.Model):
|
||||
def _send_task_en_route_email(self):
|
||||
"""Email the client that the technician is on the way."""
|
||||
self.ensure_one()
|
||||
if self.x_fc_sync_source or not self.x_fc_send_client_updates:
|
||||
if not self.x_fc_send_client_updates:
|
||||
return False
|
||||
if not self.partner_id or not self.partner_id.email:
|
||||
return False
|
||||
@@ -2312,7 +2216,7 @@ class FusionTechnicianTask(models.Model):
|
||||
- Standard thank-you without review request
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.x_fc_sync_source or not self.x_fc_send_client_updates:
|
||||
if not self.x_fc_send_client_updates:
|
||||
return False
|
||||
if not self.partner_id or not self.partner_id.email:
|
||||
return False
|
||||
@@ -2457,7 +2361,7 @@ class FusionTechnicianTask(models.Model):
|
||||
'address_lat', 'address_lng', 'address_display',
|
||||
'time_start', 'time_end', 'time_start_display', 'time_end_display',
|
||||
'status', 'scheduled_date', 'travel_time_minutes',
|
||||
'x_fc_sync_client_name', 'x_fc_is_shadow', 'x_fc_sync_source'],
|
||||
],
|
||||
order='scheduled_date asc NULLS LAST, time_start asc',
|
||||
limit=500,
|
||||
)
|
||||
@@ -2802,7 +2706,6 @@ class FusionTechnicianTask(models.Model):
|
||||
('status', '=', 'scheduled'),
|
||||
('time_start', '<', current_hour),
|
||||
('x_fc_late_notified', '=', False),
|
||||
('x_fc_sync_source', '=', False),
|
||||
('technician_id', '!=', False),
|
||||
])
|
||||
|
||||
|
||||
Reference in New Issue
Block a user