# -*- coding: utf-8 -*- # Copyright 2024-2025 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) # Part of the Fusion Claim Assistant product family. from odoo import api, fields, models, _ from odoo.exceptions import ValidationError class ProductTemplate(models.Model): _inherit = 'product.template' # ========================================================================== # ADP PRODUCT FIELDS # ========================================================================== x_fc_is_adp_product = fields.Boolean( string='ADP Product', default=False, tracking=True, help='Toggle to mark this as an ADP product. Shows ADP fields when enabled.', ) x_fc_adp_device_code_id = fields.Many2one( 'fusion.adp.device.code', string='ADP Device Code', ondelete='set null', tracking=True, help='Link to the ADP Mobility Manual device code record', ) x_fc_adp_device_code = fields.Char( string='Device Code', help='Device code string used for ADP claims export', copy=True, tracking=True, ) x_fc_adp_price = fields.Float( string='ADP Price', digits='Product Price', help='ADP retail price from the device codes database.', copy=True, tracking=True, ) x_fc_adp_device_type = fields.Char( related='x_fc_adp_device_code_id.device_type', string='Device Type', store=True, readonly=True, ) x_fc_adp_build_type = fields.Selection( related='x_fc_adp_device_code_id.build_type', string='Build Type', store=True, readonly=True, ) x_fc_adp_max_quantity = fields.Integer( related='x_fc_adp_device_code_id.max_quantity', string='Max Quantity', store=True, readonly=True, ) # ========================================================================== # LOANER PRODUCT FIELDS # ========================================================================== x_fc_can_be_loaned = fields.Boolean( string='Can be Loaned', default=False, help='If checked, this product can be loaned out to clients', ) x_fc_loaner_period_days = fields.Integer( string='Loaner Period (Days)', default=7, help='Default number of free loaner days before rental conversion', ) x_fc_rental_price_weekly = fields.Float( string='Weekly Rental Price', digits='Product Price', help='Rental price per week if loaner converts to rental', ) x_fc_rental_price_monthly = fields.Float( string='Monthly Rental Price', digits='Product Price', help='Rental price per month if loaner converts to rental', ) # ========================================================================== # LOANER EQUIPMENT FIELDS # ========================================================================== x_fc_equipment_type = fields.Selection([ ('type_1_walker', 'Type 1 Walker'), ('type_2_mw', 'Type 2 MW'), ('type_2_pw', 'Type 2 PW'), ('type_2_walker', 'Type 2 Walker'), ('type_3_mw', 'Type 3 MW'), ('type_3_pw', 'Type 3 PW'), ('type_3_walker', 'Type 3 Walker'), ('type_4_mw', 'Type 4 MW'), ('type_5_mw', 'Type 5 MW'), ('ceiling_lift', 'Ceiling Lift'), ('mobility_scooter', 'Mobility Scooter'), ('patient_lift', 'Patient Lift'), ('transport_wheelchair', 'Transport Wheelchair'), ('standard_wheelchair', 'Standard Wheelchair'), ('power_wheelchair', 'Power Wheelchair'), ('cushion', 'Cushion'), ('backrest', 'Backrest'), ('stairlift', 'Stairlift'), ('others', 'Others'), ], string='Equipment Type') x_fc_wheelchair_category = fields.Selection([ ('type_1', 'Type 1'), ('type_2', 'Type 2'), ('type_3', 'Type 3'), ('type_4', 'Type 4'), ('type_5', 'Type 5'), ], string='Wheelchair Category') x_fc_seat_width = fields.Char(string='Seat Width') x_fc_seat_depth = fields.Char(string='Seat Depth') x_fc_seat_height = fields.Char(string='Seat Height') x_fc_storage_location = fields.Selection([ ('warehouse', 'Warehouse'), ('westin_brampton', 'Westin Brampton'), ('mobility_etobicoke', 'Mobility Etobicoke'), ('scarborough_storage', 'Scarborough Storage'), ('client_loaned', 'Client/Loaned'), ('rented_out', 'Rented Out'), ], string='Storage Location') x_fc_listing_type = fields.Selection([ ('owned', 'Owned'), ('borrowed', 'Borrowed'), ], string='Listing Type') x_fc_asset_number = fields.Char(string='Asset Number') x_fc_package_info = fields.Text(string='Package Information') # ========================================================================== # ONCHANGE / CONSTRAINTS # ========================================================================== @api.onchange('x_fc_adp_device_code_id') def _onchange_adp_device_code_id(self): """Populate device code string and price from the selected device record.""" if self.x_fc_adp_device_code_id: self.x_fc_adp_device_code = self.x_fc_adp_device_code_id.device_code self.x_fc_adp_price = self.x_fc_adp_device_code_id.adp_price else: self.x_fc_adp_device_code = False self.x_fc_adp_price = 0.0 @api.constrains('x_fc_is_adp_product', 'x_fc_adp_device_code_id') def _check_adp_product_device_code(self): for product in self: if product.x_fc_is_adp_product and not product.x_fc_adp_device_code_id: raise ValidationError( _("'%s' is marked as an ADP Product but has no ADP Device Code selected.") % product.name ) # ========================================================================== # HELPER METHODS # ========================================================================== def get_adp_price(self): """Get ADP price, preferring the linked device code record. Checks in order: 1. Linked device code record price 2. x_fc_adp_price (stored field) 3. list_price (default product price) """ self.ensure_one() if self.x_fc_adp_device_code_id and self.x_fc_adp_device_code_id.adp_price: return self.x_fc_adp_device_code_id.adp_price if self.x_fc_adp_price: return self.x_fc_adp_price return self.list_price or 0.0 def get_adp_device_code(self): """Get ADP device code, preferring the linked device code record. Checks in order: 1. Linked device code record 2. x_fc_adp_device_code (stored char) 3. default_code (internal reference) """ self.ensure_one() if self.x_fc_adp_device_code_id: return self.x_fc_adp_device_code_id.device_code or '' if self.x_fc_adp_device_code: return self.x_fc_adp_device_code return self.default_code or '' # ========================================================================== # SECURITY DEPOSIT (added by fusion_rental) # ========================================================================== x_fc_security_deposit_type = fields.Selection( [ ('fixed', 'Fixed Amount'), ('percentage', 'Percentage of Rental Price'), ], string='Security Deposit Type', help='How the security deposit is calculated for this rental product.', ) x_fc_security_deposit_amount = fields.Float( string='Security Deposit Amount', digits='Product Price', help='Fixed dollar amount for the security deposit.', ) x_fc_security_deposit_percent = fields.Float( string='Security Deposit (%)', help='Percentage of the rental line price to charge as deposit.', )