# -*- 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 models class ProductProduct(models.Model): _inherit = 'product.product' def get_adp_device_code(self): """Get ADP device code, preferring the linked device code record. Checks in order: 1. Linked Many2one device code record on template 2. x_fc_adp_device_code char field on template 3. Mapped field from fusion settings (legacy) 4. default_code """ self.ensure_one() tmpl = self.product_tmpl_id if tmpl and tmpl.x_fc_adp_device_code_id: return tmpl.x_fc_adp_device_code_id.device_code or '' if tmpl and tmpl.x_fc_adp_device_code: return tmpl.x_fc_adp_device_code ICP = self.env['ir.config_parameter'].sudo() field_name = ICP.get_param('fusion_claims.field_product_code', 'x_fc_adp_device_code') if field_name and field_name != 'x_fc_adp_device_code': if field_name in self._fields: value = getattr(self, field_name, '') or '' if value: return value if tmpl and field_name in tmpl._fields: value = getattr(tmpl, field_name, '') or '' if value: return value return self.default_code or '' def get_adp_price(self): """Get ADP price, preferring the linked device code record. Checks in order: 1. Linked Many2one device code record price on template 2. x_fc_adp_price field on template 3. Mapped field from fusion settings (legacy) 4. list_price """ self.ensure_one() tmpl = self.product_tmpl_id if tmpl and tmpl.x_fc_adp_device_code_id and tmpl.x_fc_adp_device_code_id.adp_price: return tmpl.x_fc_adp_device_code_id.adp_price if tmpl and tmpl.x_fc_adp_price: return tmpl.x_fc_adp_price ICP = self.env['ir.config_parameter'].sudo() field_name = ICP.get_param('fusion_claims.field_product_adp_price', 'x_fc_adp_price') if field_name and field_name != 'x_fc_adp_price': if field_name in self._fields: value = getattr(self, field_name, 0.0) or 0.0 if value: return value if tmpl and field_name in tmpl._fields: value = getattr(tmpl, field_name, 0.0) or 0.0 if value: return value return tmpl.list_price if tmpl else 0.0 def is_non_adp_funded(self): """ Check if this product has a NON-ADP, NON-FUNDED, or UNFUNDED device code. Products with these device codes are not covered by ADP and should have: - ADP portion = 0 - Client portion = full amount - NOT included in ADP invoices (only in client invoices) Returns True if the product is NOT funded by ADP. """ self.ensure_one() # Get the ADP device code adp_code = self.get_adp_device_code() if not adp_code: return False # Check for non-funded codes (case-insensitive) # These product codes indicate items NOT covered by ADP funding: # - NON-ADP, NON-FUNDED, UNFUNDED: Explicitly not ADP funded # - ACS: Accessibility items (client pays 100%) # - ODS: ODSP items (client pays 100%) # - OWP: Ontario Works items (client pays 100%) non_funded_codes = [ 'NON-ADP', 'NON ADP', 'NONADP', 'NON-FUNDED', 'NON FUNDED', 'NONFUNDED', 'UNFUNDED', 'NOT-FUNDED', 'NOT FUNDED', 'NOTFUNDED', 'ACS', 'ODS', 'OWP' ] adp_code_upper = adp_code.upper().strip() for non_funded in non_funded_codes: if adp_code_upper == non_funded or adp_code_upper.startswith(non_funded): return True return False def action_sync_adp_price_from_database(self): """Sync product ADP data from the device codes database. Looks up the product's device code in fusion.adp.device.code and populates the Many2one link, price, and device code string. """ ADPDevice = self.env['fusion.adp.device.code'].sudo() updated = [] not_found = [] no_code = [] for product in self: product_tmpl = product.product_tmpl_id if product_tmpl.x_fc_adp_device_code_id: adp_device = product_tmpl.x_fc_adp_device_code_id device_code = adp_device.device_code else: device_code = product.get_adp_device_code() if not device_code: no_code.append(product.name) continue adp_device = ADPDevice.search([ ('device_code', '=', device_code), ('active', '=', True) ], limit=1) if adp_device: old_price = product_tmpl.x_fc_adp_price or 0 write_vals = { 'x_fc_adp_device_code_id': adp_device.id, 'x_fc_adp_device_code': adp_device.device_code, 'x_fc_adp_price': adp_device.adp_price, 'x_fc_is_adp_product': True, } product_tmpl.sudo().write(write_vals) updated.append({ 'name': product.name, 'code': device_code, 'old_price': old_price, 'new_price': adp_device.adp_price, }) else: not_found.append(f"{product.name} ({device_code})") message_parts = [] if updated: msg = f"Synced {len(updated)} product(s):" message_parts.append(msg) if not_found: message_parts.append( f"Not found in database: {', '.join(not_found)}" ) if no_code: message_parts.append( f"No ADP code: {', '.join(no_code)}" ) if not message_parts: message_parts.append("No products to process.") return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'ADP Price Sync', 'message': '
'.join(message_parts), 'type': 'success' if updated else 'warning', 'sticky': True, } }