Add polygon FSM zones and auto zone detection by coordinates

This commit is contained in:
Ruslan Bakiev
2026-02-13 17:00:37 +07:00
parent d9dd9eeb77
commit 3ad65b0d89
8 changed files with 158 additions and 2 deletions

View File

@@ -10,6 +10,9 @@ class RepairWorkOrder(models.Model):
name = fields.Char(default="New", copy=False, readonly=True, tracking=True)
contact_id = fields.Many2one("dsrpt.contact", required=True, tracking=True)
service_address = fields.Char(tracking=True)
service_latitude = fields.Float(digits=(10, 6), tracking=True)
service_longitude = fields.Float(digits=(10, 6), tracking=True)
zone_id = fields.Many2one("repair.fsm.zone", string="FSM Zone", tracking=True)
description = fields.Text(tracking=True)
requested_datetime = fields.Datetime(default=fields.Datetime.now, tracking=True)
@@ -39,7 +42,15 @@ class RepairWorkOrder(models.Model):
for vals in vals_list:
if vals.get("name", "New") == "New":
vals["name"] = self.env["ir.sequence"].next_by_code("repair.work.order") or "New"
return super().create(vals_list)
records = super().create(vals_list)
for record, vals in zip(records, vals_list):
if vals.get("zone_id"):
continue
if record._has_service_point():
zone = record._find_zone_for_point(record.service_latitude, record.service_longitude)
if zone and record.zone_id != zone:
record.zone_id = zone.id
return records
def _group_expand_states(self, states, domain, order):
return [key for key, _label in self._fields["state"].selection]
@@ -50,6 +61,45 @@ class RepairWorkOrder(models.Model):
rec.total_time_hours = sum(rec.time_line_ids.mapped("hours"))
rec.total_material_cost = sum(rec.material_line_ids.mapped("subtotal"))
def _has_service_point(self):
self.ensure_one()
return self.service_latitude is not False and self.service_longitude is not False
def _find_zone_for_point(self, latitude, longitude):
zones = self.env["repair.fsm.zone"].search([("active", "=", True), ("state", "=", "active")])
for zone in zones:
if zone.contains_point(latitude, longitude):
return zone
return self.env["repair.fsm.zone"]
@api.onchange("service_latitude", "service_longitude")
def _onchange_service_coordinates(self):
for rec in self:
if rec._has_service_point():
zone = rec._find_zone_for_point(rec.service_latitude, rec.service_longitude)
if zone:
rec.zone_id = zone
def action_detect_zone(self):
for rec in self:
if not rec._has_service_point():
raise ValidationError("Service coordinates are required to detect FSM Zone.")
zone = rec._find_zone_for_point(rec.service_latitude, rec.service_longitude)
if not zone:
raise ValidationError("No active FSM Zone contains this point.")
rec.zone_id = zone.id
return True
def write(self, vals):
result = super().write(vals)
if ("service_latitude" in vals or "service_longitude" in vals) and "zone_id" not in vals:
for rec in self:
if rec._has_service_point():
zone = rec._find_zone_for_point(rec.service_latitude, rec.service_longitude)
if zone and rec.zone_id != zone:
super(RepairWorkOrder, rec).write({"zone_id": zone.id})
return result
def action_confirm(self):
self.write({"state": "confirmed"})

View File

@@ -8,6 +8,7 @@
<header>
<button name="action_confirm" type="object" string="Confirm" class="btn-primary" invisible="state != 'draft'"/>
<button name="action_assign_to_me" type="object" string="Assign to me" class="btn-primary" invisible="state not in ('confirmed','assigned')"/>
<button name="action_detect_zone" type="object" string="Detect Zone" invisible="not service_latitude or not service_longitude"/>
<button name="action_start" type="object" string="Start" invisible="state != 'assigned'"/>
<button name="action_done" type="object" string="Done" class="btn-primary" invisible="state not in ('in_progress','assigned')"/>
<button name="action_cancel" type="object" string="Cancel" invisible="state in ('done','cancelled')"/>
@@ -19,6 +20,9 @@
<group>
<field name="name" readonly="1"/>
<field name="contact_id"/>
<field name="service_address"/>
<field name="service_latitude"/>
<field name="service_longitude"/>
<field name="zone_id"/>
</group>
<group>

View File

@@ -7,6 +7,7 @@
<kanban default_group_by="state">
<field name="name"/>
<field name="contact_id"/>
<field name="service_address"/>
<field name="scheduled_datetime"/>
<field name="technician_id"/>
<field name="state"/>
@@ -19,6 +20,9 @@
<div>
<field name="contact_id"/>
</div>
<div>
<field name="service_address"/>
</div>
<div>
<field name="scheduled_datetime"/>
</div>

View File

@@ -7,12 +7,15 @@
<list>
<field name="name"/>
<field name="contact_id" optional="show"/>
<field name="service_address" optional="show"/>
<field name="zone_id" optional="show"/>
<field name="scheduled_datetime" optional="show"/>
<field name="technician_id" optional="show"/>
<field name="state" widget="badge" optional="show"/>
<field name="total_time_hours" optional="show"/>
<field name="total_material_cost" optional="show"/>
<field name="service_latitude" optional="hide"/>
<field name="service_longitude" optional="hide"/>
<field name="assigned_user_id" optional="hide"/>
<field name="requested_datetime" optional="hide"/>
</list>