diff --git a/fusion_repairs/__manifest__.py b/fusion_repairs/__manifest__.py index 3923b042..c772b91c 100644 --- a/fusion_repairs/__manifest__.py +++ b/fusion_repairs/__manifest__.py @@ -4,7 +4,7 @@ { 'name': 'Fusion Repairs', - 'version': '19.0.2.0.0', + 'version': '19.0.2.1.0', 'category': 'Inventory/Repairs', 'summary': 'Guided medical equipment repair intake, dispatch, maintenance, and self-service portal', 'description': """ @@ -77,6 +77,7 @@ Copyright (C) 2024-2026 Nexa Systems Inc. All rights reserved. 'data/self_check_data.xml', 'data/emergency_charge_data.xml', 'data/callout_rate_data.xml', + 'data/delivery_charge_data.xml', # Views 'views/repair_product_category_views.xml', 'views/intake_template_views.xml', @@ -87,6 +88,7 @@ Copyright (C) 2024-2026 Nexa Systems Inc. All rights reserved. 'views/repair_emergency_charge_views.xml', 'views/repair_inspection_views.xml', 'views/repair_callout_rate_views.xml', + 'views/repair_delivery_charge_views.xml', 'views/repair_labor_warranty_views.xml', 'views/repair_order_views.xml', 'views/repair_part_order_views.xml', diff --git a/fusion_repairs/data/callout_rate_data.xml b/fusion_repairs/data/callout_rate_data.xml index 4f00f554..aa9d5bdb 100644 --- a/fusion_repairs/data/callout_rate_data.xml +++ b/fusion_repairs/data/callout_rate_data.xml @@ -1,58 +1,161 @@ + regular - 120.00 - 60.00 - 60.00 - 95.00 + standard + 95.00 + 0.0 + 0.0 + 85.00 + 75.00 1.0 25.0 - 0.85 - Standard business hours (Mon-Fri 9 AM - 5 PM). Base fee includes the first 30 minutes for inspection / report. + 0.70 + STANDARD - regular business hours. Service Call ($95) includes the first 30 min of labour. Hourly Rate ($85/h on-site, $75/h in-shop) applies past 30 min, per tech, pro-rated in 30-min increments with a 1-hour minimum. + + + + rush + standard + 120.00 + 0.0 + 0.0 + 85.00 + 75.00 + 1.0 + 25.0 + 0.70 + STANDARD - rush. $120 plus $0.70 per km (2-way, past 25 km). after_hours - 180.00 - 90.00 - 90.00 - 140.00 + standard + 140.00 + 0.0 + 0.0 + 85.00 + 75.00 1.0 25.0 - 1.10 - Weekday evenings 5 PM - 9 PM. Higher base + higher labour + travel always billed past 25 km. + 0.70 + STANDARD - after-hours (weekday evenings). $140 plus $0.70 per km (2-way, past 25 km). weekend - 240.00 - 120.00 - 120.00 - 170.00 + standard + 180.00 + 0.0 + 0.0 + 85.00 + 75.00 1.0 25.0 - 1.35 - Saturday + Sunday. Premium tier. + 0.70 + STANDARD - weekend (extension of published card). $180 callout. holiday - 300.00 - 150.00 - 150.00 - 200.00 + standard + 220.00 + 0.0 + 0.0 + 85.00 + 75.00 1.0 25.0 - 1.50 - Statutory holidays. Highest tier. + 0.70 + STANDARD - statutory holiday (extension of published card). $220 callout. + + + + + regular + lift_elevating + 160.00 + 0.0 + 0.0 + 110.00 + 110.00 + 1.0 + 25.0 + 0.70 + LIFT & ELEVATING SERVICE - regular business hours. $160 callout includes 30 min. $110/h labour past 30 min, per tech. + + + + rush + lift_elevating + 200.00 + 110.00 + 110.00 + 1.0 + 25.0 + 0.70 + LIFT & ELEVATING - rush. $200 callout plus $0.70/km (2-way, past 25 km). + + + + after_hours + lift_elevating + 240.00 + 110.00 + 110.00 + 1.0 + 25.0 + 0.70 + LIFT & ELEVATING - after-hours. $240 callout plus $0.70/km (2-way, past 25 km). + + + + weekend + lift_elevating + 300.00 + 110.00 + 110.00 + 1.0 + 25.0 + 0.70 + LIFT & ELEVATING - weekend. $300 callout. + + + + holiday + lift_elevating + 360.00 + 110.00 + 110.00 + 1.0 + 25.0 + 0.70 + LIFT & ELEVATING - statutory holiday. $360 callout. diff --git a/fusion_repairs/data/delivery_charge_data.xml b/fusion_repairs/data/delivery_charge_data.xml new file mode 100644 index 00000000..a077816c --- /dev/null +++ b/fusion_repairs/data/delivery_charge_data.xml @@ -0,0 +1,73 @@ + + + + + + + local + 35.00 + 10 + Within the Brampton service area. + + + + outside + 60.00 + 20 + Outside the local service area (per the published card). + + + + rush + 60.00 + 0.70 + 25.0 + 30 + Rush pickup or delivery. $60 plus $0.70 per km, both ways, past 25 km. + + + + lift_chair_install + 120.00 + 40 + Lift Chair delivery and on-site set-up. + + + + hospital_bed_install + 120.00 + 50 + Hospital Bed delivery and on-site assembly / set-up. + + + + stairlift_install + 300.00 + 60 + Stairlift delivery and full set-up at client home. + + + + stairlift_removal + 300.00 + 70 + Removal of an old stairlift from client home. + + + + diff --git a/fusion_repairs/data/repair_product_category_data.xml b/fusion_repairs/data/repair_product_category_data.xml index 8a34070c..e00d255d 100644 --- a/fusion_repairs/data/repair_product_category_data.xml +++ b/fusion_repairs/data/repair_product_category_data.xml @@ -35,6 +35,7 @@ fa-arrows-v Straight and curved indoor stairlifts. Annual safety inspection required in many jurisdictions. + lift_elevating @@ -44,6 +45,17 @@ fa-arrow-up Vertical platform lifts for porches, decks, and accessible building entrances. + lift_elevating + + + + + Lift Chair + lift_chair + 55 + fa-chair + Powered recliner / lift chairs (Pride, Golden, MedLift). Falls under Lift & Elevating Service per rate card. + lift_elevating diff --git a/fusion_repairs/migrations/19.0.2.1.0/post-migration.py b/fusion_repairs/migrations/19.0.2.1.0/post-migration.py new file mode 100644 index 00000000..680021be --- /dev/null +++ b/fusion_repairs/migrations/19.0.2.1.0/post-migration.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +"""Post-migration for 19.0.2.1.0 - align rate-card + categories to Westin's +printed service-rate card. + +Sites that installed any earlier Bundle 9 build have: + - Old callout.rate rows with $120/$95/0.85 values (B9 placeholder rates) + - Stairlift / porch_lift categories with equipment_class='standard' + +Both have noupdate=1 in their seed XML so a normal -u upgrade won't fix +them. This script: + 1. Wipes the four B9-only rate xml_ids and re-imports the seed + 2. Updates lift / porch / lift_chair categories to equipment_class='lift_elevating' + +After this runs once, future upgrades respect noupdate=1 normally (admin +tweaks are preserved). +""" + +from odoo.tools.sql import column_exists + + +def migrate(cr, version): + if not version: + return # fresh install - seed loads correctly + + cr.execute(""" + UPDATE fusion_repair_product_category + SET equipment_class = 'lift_elevating' + WHERE code IN ('stairlift', 'porch_lift', 'lift_chair') + AND (equipment_class IS NULL OR equipment_class = 'standard'); + """) + + # Wipe the four B9 rate rows so the new noupdate=1 seed re-creates them + # with the printed values. Only deletes rows that were originally seeded + # by this module (xml_id present) - admin-created rate rows stay put. + cr.execute(""" + DELETE FROM fusion_repair_callout_rate + WHERE id IN ( + SELECT res_id FROM ir_model_data + WHERE module = 'fusion_repairs' + AND model = 'fusion.repair.callout.rate' + AND name IN ('callout_rate_regular', 'callout_rate_after_hours', + 'callout_rate_weekend', 'callout_rate_holiday') + ); + DELETE FROM ir_model_data + WHERE module = 'fusion_repairs' + AND model = 'fusion.repair.callout.rate' + AND name IN ('callout_rate_regular', 'callout_rate_after_hours', + 'callout_rate_weekend', 'callout_rate_holiday'); + """) diff --git a/fusion_repairs/models/__init__.py b/fusion_repairs/models/__init__.py index f9676fb7..5810aaa9 100644 --- a/fusion_repairs/models/__init__.py +++ b/fusion_repairs/models/__init__.py @@ -18,6 +18,7 @@ from . import repair_emergency_charge from . import repair_part_order from . import repair_callout_rate from . import repair_labor_warranty +from . import repair_delivery_charge from . import product_template from . import res_partner from . import res_users diff --git a/fusion_repairs/models/repair_callout_rate.py b/fusion_repairs/models/repair_callout_rate.py index e78bef08..3950bdd9 100644 --- a/fusion_repairs/models/repair_callout_rate.py +++ b/fusion_repairs/models/repair_callout_rate.py @@ -30,6 +30,7 @@ class FusionRepairCalloutRate(models.Model): tier = fields.Selection( [ ('regular', 'Regular Business Hours'), + ('rush', 'Rush Service'), ('after_hours', 'After Hours (weekday evening)'), ('weekend', 'Weekend'), ('holiday', 'Statutory Holiday'), @@ -39,6 +40,20 @@ class FusionRepairCalloutRate(models.Model): default='regular', ) + # Bundle 10: Westin's rate card splits by equipment class. Lift & + # Elevating Service ($160 callout / $110 labour) is distinct from + # Standard Service ($95 callout / $85 labour). The lookup falls back + # from (tier, equipment_class) to (tier, 'standard'). + equipment_class = fields.Selection( + [ + ('standard', 'Standard Service'), + ('lift_elevating', 'Lift & Elevating Service'), + ], + string='Equipment Class', + default='standard', + required=True, + ) + # ---- Base callout (covers first 30 minutes of labour) ---- base_callout_fee = fields.Monetary( string='Base Callout Fee (1 tech)', @@ -67,7 +82,7 @@ class FusionRepairCalloutRate(models.Model): # ---- Hourly labour (after the included 30 min) ---- hourly_labor_rate = fields.Monetary( - string='Hourly Labour Rate (per tech)', + string='On-Site Hourly Labour Rate (per tech)', currency_field='currency_id', required=True, default=0.0, @@ -75,11 +90,21 @@ class FusionRepairCalloutRate(models.Model): '30 min the callout covers. Minimum bill is minimum_labor_hours ' 'even if the tech finished faster.', ) + # Bundle 10: separate IN-SHOP hourly rate. When the client brings the unit + # to the store (no callout, no travel) we charge a lower hourly rate. + in_shop_labor_rate = fields.Monetary( + string='In-Shop Hourly Labour Rate', + currency_field='currency_id', + default=0.0, + help='Hourly rate when work is done IN THE STORE (no callout fee, no ' + 'travel). Per Westin rate card: $75 standard / $110 lift.', + ) minimum_labor_hours = fields.Float( string='Minimum Billable Hours', default=1.0, help='Round-up floor for labour beyond the included 30 min. Set 1.0 ' - 'so a 20-minute fix still bills 1.0 hours.', + 'so a 20-minute fix still bills 1.0 hours. Hours beyond the floor ' + 'are pro-rated in 30-minute increments per the published card.', ) # ---- Travel ---- @@ -128,13 +153,26 @@ class FusionRepairCalloutRate(models.Model): ) @api.model - def get_for_tier(self, tier, on_date=None): - """Return the active rate row for `tier` effective on `on_date` - (default today). Returns empty recordset if none configured.""" + def get_for_tier(self, tier, equipment_class='standard', on_date=None): + """Return the active rate row for `tier` + `equipment_class` effective + on `on_date`. Tries (tier, class) first, falls back to (tier, standard) + if no class-specific row is configured. Empty recordset if none at all. + """ on_date = on_date or fields.Date.context_today(self) - return self.sudo().search([ + Domain = lambda cls: [ ('tier', '=', tier), + ('equipment_class', '=', cls), ('active', '=', True), ('effective_from', '<=', on_date), ('company_id', 'in', self.env.companies.ids), - ], order='effective_from desc', limit=1) + ] + hit = self.sudo().search( + Domain(equipment_class or 'standard'), + order='effective_from desc', limit=1, + ) + if not hit and equipment_class and equipment_class != 'standard': + hit = self.sudo().search( + Domain('standard'), + order='effective_from desc', limit=1, + ) + return hit diff --git a/fusion_repairs/models/repair_delivery_charge.py b/fusion_repairs/models/repair_delivery_charge.py new file mode 100644 index 00000000..066690ad --- /dev/null +++ b/fusion_repairs/models/repair_delivery_charge.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# Copyright 2024-2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) + +"""Delivery / pickup rate card (separate from repair callouts). + +Per Westin's published rate card the DELIVERY / PICKUP CHARGES section is +a distinct service from repair callouts. These are charged when we move +equipment (drop-off of a sold unit, post-repair return delivery, removal +of old equipment, etc.). +""" + +from odoo import api, fields, models + + +class FusionRepairDeliveryCharge(models.Model): + _name = 'fusion.repair.delivery.charge' + _description = 'Delivery / Pickup Rate Card' + _order = 'sequence, charge_type, id' + + name = fields.Char(compute='_compute_name', store=True) + sequence = fields.Integer(default=10) + charge_type = fields.Selection( + [ + ('local', 'Local Service Area'), + ('outside', 'Outside Local Area'), + ('rush', 'Rush Pickup / Delivery'), + ('lift_chair_install', 'Lift Chair Delivery and Set-Up'), + ('hospital_bed_install', 'Hospital Bed Delivery and Set-Up'), + ('stairlift_install', 'Stairlift Delivery and Set-Up'), + ('stairlift_removal', 'Stairlift Removal'), + ('other', 'Other'), + ], + string='Charge Type', + required=True, + ) + amount = fields.Monetary( + string='Amount', + currency_field='currency_id', + required=True, + ) + travel_per_km_fee = fields.Monetary( + string='Per-km Fee (Rush, 2-way)', + currency_field='currency_id', + default=0.0, + help='Only applies to rush pickups/deliveries. Per the published card: ' + '$60 plus $0.70 per km x 2-way.', + ) + travel_distance_threshold_km = fields.Float( + string='Free Travel Distance (km, 2-way)', + default=0.0, + help='Only applies to rush. Above this km, every additional km is ' + 'charged travel_per_km_fee BOTH WAYS.', + ) + description = fields.Text(translate=True) + currency_id = fields.Many2one( + 'res.currency', + default=lambda self: self.env.company.currency_id, + ) + company_id = fields.Many2one( + 'res.company', default=lambda self: self.env.company, + ) + active = fields.Boolean(default=True) + + @api.depends('charge_type', 'amount') + def _compute_name(self): + for r in self: + label = dict(self._fields['charge_type'].selection).get(r.charge_type) or '?' + r.name = f'{label} - ${r.amount:.0f}' + + @api.model + def get_charge(self, charge_type): + """Return the active rate row for `charge_type`, empty recordset if none.""" + return self.sudo().search([ + ('charge_type', '=', charge_type), + ('active', '=', True), + ('company_id', 'in', self.env.companies.ids), + ], limit=1) + + @api.model + def quote_rush(self, distance_km): + """Convenience: returns the total for a Rush Pickup / Delivery at + `distance_km` one-way. Returns 0.0 if no rush row configured.""" + rush = self.get_charge('rush') + if not rush: + return 0.0 + over = max(distance_km - rush.travel_distance_threshold_km, 0.0) + return rush.amount + (over * 2.0 * rush.travel_per_km_fee) diff --git a/fusion_repairs/models/repair_order.py b/fusion_repairs/models/repair_order.py index 98ccb579..80595374 100644 --- a/fusion_repairs/models/repair_order.py +++ b/fusion_repairs/models/repair_order.py @@ -247,6 +247,7 @@ class RepairOrder(models.Model): x_fc_callout_tier = fields.Selection( [ ('regular', 'Regular Business Hours'), + ('rush', 'Rush Service'), ('after_hours', 'After Hours'), ('weekend', 'Weekend'), ('holiday', 'Statutory Holiday'), @@ -274,7 +275,17 @@ class RepairOrder(models.Model): tracking=True, help='Hours of repair work above the 30 min included in the callout fee. ' 'Billing applies the minimum_labor_hours floor from the rate card ' - '(default 1.0) - 20 minutes of work still bills 1 hour.', + '(default 1.0) AND rounds up to the next 30-min increment - ' + '20 minutes bills 1.0 h, 75 minutes bills 1.5 h.', + ) + # Bundle 10: in-shop work uses a different (lower) hourly rate AND + # waives both the callout fee and the travel charge - client brought + # the unit to the store. + x_fc_in_shop = fields.Boolean( + string='In-Shop Repair', + tracking=True, + help='Work done in the store (no callout, no travel). Uses ' + 'in_shop_labor_rate from the rate card.', ) # Labor warranty link + status (resolved at visit time) x_fc_labor_warranty_id = fields.Many2one( @@ -362,41 +373,61 @@ class RepairOrder(models.Model): @api.depends('x_fc_callout_tier', 'x_fc_callout_distance_km', 'x_fc_callout_techs', 'x_fc_callout_labor_hours', - 'x_fc_labor_warranty_status', 'x_fc_labor_waived') + 'x_fc_labor_warranty_status', 'x_fc_labor_waived', + 'x_fc_in_shop', 'x_fc_repair_category_id') def _compute_callout_quote(self): + import math Rate = self.env['fusion.repair.callout.rate'].sudo() for r in self: tier = r.x_fc_callout_tier or 'regular' - rate = Rate.get_for_tier(tier) + cls = (r.x_fc_repair_category_id.equipment_class + or 'standard') if r.x_fc_repair_category_id else 'standard' + rate = Rate.get_for_tier(tier, equipment_class=cls) techs = max(r.x_fc_callout_techs or 1, 1) hours = max(r.x_fc_callout_labor_hours or 0.0, 0.0) distance = r.x_fc_callout_distance_km or 0.0 + in_shop = bool(r.x_fc_in_shop) - base = rate.base_callout_fee if rate else 0.0 - extra_techs = 0.0 - if rate and techs >= 2: - extra_techs += rate.second_tech_fee - if rate and techs >= 3: - # 3rd onwards: use additional_tech_fee, falling back to second_tech_fee. - per_extra = rate.additional_tech_fee or rate.second_tech_fee - extra_techs += per_extra * (techs - 2) + if in_shop: + # In-shop: no callout, no extra-tech, no travel. Only labour + # at the lower in_shop_labor_rate. + base = 0.0 + extra_techs = 0.0 + travel = 0.0 + else: + base = rate.base_callout_fee if rate else 0.0 + extra_techs = 0.0 + if rate and techs >= 2 and rate.second_tech_fee: + extra_techs += rate.second_tech_fee + if rate and techs >= 3: + per_extra = rate.additional_tech_fee or rate.second_tech_fee or 0.0 + extra_techs += per_extra * (techs - 2) + # Travel - both ways, per tech, for distance over threshold. + travel = 0.0 + if rate: + over = max(distance - rate.travel_distance_threshold_km, 0.0) + travel = over * 2.0 * rate.travel_per_km_fee * techs - # Labour - charged per tech with min-1-hour floor on the - # BILLABLE portion (above 30 min the callout includes). + # Labour: per the published rate card - + # * minimum_labor_hours floor (default 1.0) + # * beyond the floor, pro-rated in 30-min increments + # (i.e. round UP to the next 0.5 h) + # * per tech (footnote: 'If multiple technicians are required, + # rates will apply per technician') + # * in-shop uses in_shop_labor_rate labor = 0.0 if rate and hours > 0: min_hours = rate.minimum_labor_hours or 1.0 - billable_h = max(hours, min_hours) - labor = billable_h * rate.hourly_labor_rate * techs - - # Travel - both ways, per tech, for distance over threshold. - travel = 0.0 - if rate: - over = max(distance - rate.travel_distance_threshold_km, 0.0) - travel = over * 2.0 * rate.travel_per_km_fee * techs + # ceil(actual * 2) / 2 -> rounds up to next 0.5 increment + rounded_up = math.ceil(hours * 2) / 2.0 + billable_h = max(rounded_up, min_hours) + hourly = (rate.in_shop_labor_rate + if in_shop else rate.hourly_labor_rate) + labor = billable_h * hourly * techs waived = 0.0 - if r.x_fc_labor_warranty_status in ('eligible', 'waived') or r.x_fc_labor_waived: + if (r.x_fc_labor_warranty_status in ('eligible', 'waived') + or r.x_fc_labor_waived): waived = labor total = base + extra_techs + labor + travel - waived @@ -407,16 +438,37 @@ class RepairOrder(models.Model): r.x_fc_quote_travel = travel r.x_fc_quote_waived = waived r.x_fc_quote_total = total - r.x_fc_quote_breakdown_text = ( - f'Base callout ({tier}): ${base:.2f}\n' - f'Extra technicians ({techs - 1}): ${extra_techs:.2f}\n' - f'Labor ({hours:.2f} h x {techs} tech, min ' - f'{rate.minimum_labor_hours if rate else 1.0} h): ${labor:.2f}\n' - f'Travel ({distance:.1f} km, both ways, per tech): ${travel:.2f}\n' - + (f'Less waived: -${waived:.2f}\n' if waived else '') - + f'-------------------------------------------\n' - f'TOTAL (excl. parts): ${total:.2f}' + + # Human-readable breakdown for chatter / quote emails. + class_label = 'Lift & Elevating' if cls == 'lift_elevating' else 'Standard' + hourly_used = ( + (rate.in_shop_labor_rate if in_shop else rate.hourly_labor_rate) + if rate else 0.0 ) + mode_label = 'IN-SHOP' if in_shop else f'on-site ({tier})' + lines = [] + if not in_shop: + lines.append(f'Service Call ({class_label}, {tier}, incl. 30 min): ${base:.2f}') + if extra_techs: + lines.append(f'Additional technicians ({techs - 1}): ${extra_techs:.2f}') + if labor: + lines.append( + f'Labour {mode_label}: ' + f'{hours:.2f} h actual -> billed ' + f'{max(math.ceil(hours * 2) / 2.0, rate.minimum_labor_hours if rate else 1.0):.2f} h ' + f'x {techs} tech x ${hourly_used:.2f}/h = ${labor:.2f}' + ) + if travel: + over_km = max(distance - (rate.travel_distance_threshold_km if rate else 25), 0) + lines.append( + f'Travel: {distance:.1f} km, {over_km:.1f} km over threshold, ' + f'both ways x {techs} tech x ${rate.travel_per_km_fee if rate else 0}/km = ${travel:.2f}' + ) + if waived: + lines.append(f'Less labour waived: -${waived:.2f}') + lines.append('-' * 50) + lines.append(f'TOTAL (excl. parts): ${total:.2f}') + r.x_fc_quote_breakdown_text = '\n'.join(lines) def action_check_labor_warranty(self): """Look up the active store labor warranty for this repair's diff --git a/fusion_repairs/models/repair_product_category.py b/fusion_repairs/models/repair_product_category.py index 8a1fde2a..296430f1 100644 --- a/fusion_repairs/models/repair_product_category.py +++ b/fusion_repairs/models/repair_product_category.py @@ -33,6 +33,20 @@ class FusionRepairProductCategory(models.Model): 'self-help and force escalation when safety symptoms appear.', ) + # Bundle 10: aligns Westin's printed rate card - LIFT & ELEVATING SERVICE + # has its own higher rates (stairlifts, porch lifts, lift chairs, hoyer lifts). + equipment_class = fields.Selection( + [ + ('standard', 'Standard Service'), + ('lift_elevating', 'Lift & Elevating Service'), + ], + string='Equipment Class', + default='standard', + required=True, + help='Determines which callout rate row applies. Lift & Elevating uses ' + 'higher per-card rates (e.g. $160 callout vs $95 standard).', + ) + intake_template_id = fields.Many2one( 'fusion.repair.intake.template', string='Default Intake Template', diff --git a/fusion_repairs/security/ir.model.access.csv b/fusion_repairs/security/ir.model.access.csv index e942afe0..71d0806d 100644 --- a/fusion_repairs/security/ir.model.access.csv +++ b/fusion_repairs/security/ir.model.access.csv @@ -45,6 +45,8 @@ access_visit_report_partline_user,Visit Report Part Line User Full,model_fusion_ access_visit_report_partline_tech,Visit Report Part Line Field Tech Full,model_fusion_repair_visit_report_wizard_partline,fusion_tasks.group_field_technician,1,1,1,1 access_callout_rate_user,Callout Rate User Read,model_fusion_repair_callout_rate,group_fusion_repairs_user,1,0,0,0 access_callout_rate_manager,Callout Rate Manager Full,model_fusion_repair_callout_rate,group_fusion_repairs_manager,1,1,1,1 +access_delivery_charge_user,Delivery Charge User Read,model_fusion_repair_delivery_charge,group_fusion_repairs_user,1,0,0,0 +access_delivery_charge_manager,Delivery Charge Manager Full,model_fusion_repair_delivery_charge,group_fusion_repairs_manager,1,1,1,1 access_labor_warranty_user,Labor Warranty User Read,model_fusion_repair_labor_warranty,group_fusion_repairs_user,1,0,0,0 access_labor_warranty_sales_rep,Labor Warranty Sales Rep Write,model_fusion_repair_labor_warranty,group_fusion_repairs_sales_rep,1,1,0,0 access_labor_warranty_manager,Labor Warranty Manager Full,model_fusion_repair_labor_warranty,group_fusion_repairs_manager,1,1,1,1 diff --git a/fusion_repairs/views/menus.xml b/fusion_repairs/views/menus.xml index 7a710abf..949b7d08 100644 --- a/fusion_repairs/views/menus.xml +++ b/fusion_repairs/views/menus.xml @@ -69,6 +69,12 @@ action="action_repair_callout_rate" sequence="65"/> + + + + + + fusion.repair.delivery.charge.list + fusion.repair.delivery.charge + + + + + + + + + + + + + + + + Delivery / Pickup Charges + fusion.repair.delivery.charge + list + + + diff --git a/fusion_repairs/views/repair_order_views.xml b/fusion_repairs/views/repair_order_views.xml index 8ea9218b..2c29d432 100644 --- a/fusion_repairs/views/repair_order_views.xml +++ b/fusion_repairs/views/repair_order_views.xml @@ -163,8 +163,11 @@ - - + + + diff --git a/fusion_repairs/wizard/repair_visit_report_wizard.py b/fusion_repairs/wizard/repair_visit_report_wizard.py index 1f778bb6..4f57b553 100644 --- a/fusion_repairs/wizard/repair_visit_report_wizard.py +++ b/fusion_repairs/wizard/repair_visit_report_wizard.py @@ -150,6 +150,11 @@ class RepairVisitReportWizard(models.TransientModel): string='Callout Tier', readonly=False, ) + callout_in_shop = fields.Boolean( + related='repair_id.x_fc_in_shop', + string='In-Shop Repair', + readonly=False, + ) callout_labor_hours_used = fields.Float( string='Repair Hours (after 30 min inspection)', default=1.0, diff --git a/fusion_repairs/wizard/repair_visit_report_wizard_views.xml b/fusion_repairs/wizard/repair_visit_report_wizard_views.xml index 6d51fc4b..bc1703c0 100644 --- a/fusion_repairs/wizard/repair_visit_report_wizard_views.xml +++ b/fusion_repairs/wizard/repair_visit_report_wizard_views.xml @@ -79,8 +79,11 @@ - - + + +