feat(fusion_claims): service pricing resolver + draft-SO builder from rate table

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-06-04 00:54:39 -04:00
parent a022eaaabe
commit 245e551c68
2 changed files with 80 additions and 0 deletions

View File

@@ -72,6 +72,15 @@ class FusionTechnicianTaskClaims(models.Model):
default=False,
)
# ------------------------------------------------------------------
# SERVICE BOOKING FIELDS
# ------------------------------------------------------------------
x_fc_service_call_type = fields.Char(
string='Service Call Type',
help='Rate code resolved by the booking wizard (e.g. callout_standard_rush).',
)
# ------------------------------------------------------------------
# ONCHANGES
# ------------------------------------------------------------------
@@ -389,6 +398,55 @@ class FusionTechnicianTaskClaims(models.Model):
order.name, e,
)
# ------------------------------------------------------------------
# SERVICE BOOKING HELPERS
# ------------------------------------------------------------------
@api.model
def _resolve_service_lines(self, category, timing, in_shop, distance_km):
"""Return a list of sale.order.line vals dicts for a service booking,
priced from fusion.service.rate. Empty when in-shop (labour-only, added later)."""
Rate = self.env['fusion.service.rate']
lines = []
callout = Rate.get_callout(category, timing, in_shop=in_shop)
if not callout:
return lines
lines.append({
'product_id': callout.product_id.id,
'name': callout.name,
'product_uom_qty': 1.0,
'price_unit': callout.price,
'name_is_km': False,
})
if callout.adds_per_km and distance_km:
per_km = Rate.get_rate('per_km')
if per_km:
lines.append({
'product_id': per_km.product_id.id,
'name': '%s%.1f km \xd7 2-way' % (per_km.name, distance_km),
'product_uom_qty': round(distance_km * 2.0, 1),
'price_unit': per_km.price,
'name_is_km': True,
})
return lines
@api.model
def _build_service_so(self, partner, category, timing, in_shop, distance_km):
"""Create a draft repair sale.order with the resolved call-out (+per-km) lines.
Repair-SO identity is the x_fc_is_service_repair boolean (no crm.tag: fusion_claims
has no crm dependency). x_fc_sale_type is intentionally left blank — a service repair
is not one of the ADP/ODSP funder workflows, and the draft is editable afterwards.
"""
line_vals = self._resolve_service_lines(category, timing, in_shop, distance_km)
order_lines = [(0, 0, {k: v for k, v in l.items() if k != 'name_is_km'}) for l in line_vals]
so_vals = {
'partner_id': partner.id,
'x_fc_is_service_repair': True,
'order_line': order_lines,
}
return self.env['sale.order'].create(so_vals)
# ------------------------------------------------------------------
# VIEW ACTIONS
# ------------------------------------------------------------------

View File

@@ -29,3 +29,25 @@ class TestServiceBooking(TransactionCase):
def test_sale_order_has_service_repair_flag(self):
so = self.env['sale.order'].new({})
self.assertIn('x_fc_is_service_repair', so._fields)
def test_resolve_service_lines_standard_rush(self):
Task = self.Task
lines = Task._resolve_service_lines('standard', 'rush', in_shop=False, distance_km=10.0)
# call-out $120 + per-km line qty 20 @ $0.70
callout = [l for l in lines if l['price_unit'] == 120.0]
per_km = [l for l in lines if l['name_is_km']]
self.assertTrue(callout)
self.assertEqual(per_km[0]['product_uom_qty'], 20.0)
self.assertEqual(per_km[0]['price_unit'], 0.70)
def test_resolve_service_lines_in_shop_empty_callout(self):
lines = self.Task._resolve_service_lines('standard', 'normal', in_shop=True, distance_km=5.0)
self.assertEqual(lines, [])
def test_build_service_so(self):
partner = self.env['res.partner'].create({'name': 'Walk-in Wanda'})
so = self.Task._build_service_so(partner, 'standard', 'normal', False, 0.0)
self.assertEqual(so.state, 'draft')
self.assertTrue(so.x_fc_is_service_repair)
self.assertEqual(so.partner_id, partner)
self.assertEqual(so.order_line[0].price_unit, 95.0)