232 lines
7.9 KiB
Python
232 lines
7.9 KiB
Python
# -*- 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.',
|
|
)
|
|
|