# -*- coding: utf-8 -*- # Copyright 2026 Nexa Systems Inc. # License OPL-1 (Odoo Proprietary License v1.0) from odoo import models, fields, api class ResConfigSettings(models.TransientModel): _inherit = 'res.config.settings' fi_case_conversion = fields.Selection([ ('none', 'No Conversion'), ('upper', 'UPPERCASE'), ('sentence', 'Sentence case'), ('capitalized', 'Capitalized Case'), ('lower', 'lowercase'), ], string='Product Name Case', config_parameter='fusion_inventory.case_conversion', default='none', help='Globally convert all product names to the selected case. ' 'Overrides individual product settings and applies to new products.') fi_auto_update_cost = fields.Boolean( string='Auto-Update Cost from Vendor Bills', config_parameter='fusion_inventory.auto_update_cost', default=True, help='Automatically update product cost when a vendor bill is confirmed. ' 'Uses the latest bill line price (skips zero-price lines).') fi_default_margin = fields.Float( string='Default Margin (%)', config_parameter='fusion_inventory.default_margin', default=0, help='Default margin percentage applied to new products.') fi_booking_hold_hours = fields.Integer( string='Booking Hold Duration (hours)', config_parameter='fusion_inventory.booking_hold_hours', default=24, help='How many hours a product booking holds before auto-expiring.') fi_openai_api_key = fields.Char( string='OpenAI API Key', config_parameter='fusion_inventory.openai_api_key', help='API key for OpenAI features (discrepancy analysis, notes parsing). ' 'Falls back to Fusion Digitize key if empty.') @api.model def get_fi_openai_key(self): """Return the effective OpenAI API key, falling back to Fusion Digitize.""" key = self.env['ir.config_parameter'].sudo().get_param( 'fusion_inventory.openai_api_key', '' ) if not key: key = self.env['ir.config_parameter'].sudo().get_param( 'fusion_digitize.openai_api_key', '' ) return key or '' def action_sync_all_costs_from_bills(self): """Pull every product's cost from its latest posted vendor bill line.""" products = self.env['product.template'].sudo().search([]) products._sync_cost_from_latest_bill() return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'Cost Sync Complete', 'message': f'Checked {len(products)} products against vendor bill history.', 'type': 'success', 'sticky': False, }, } def action_apply_case_conversion_all(self): """Apply the global case conversion to all existing products.""" mode = self.fi_case_conversion if not mode or mode == 'none': return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'No Conversion Selected', 'message': 'Select a case conversion option first.', 'type': 'warning', 'sticky': False, }, } products = self.env['product.template'].search([]) count = 0 for product in products: converted = self.env['product.template']._apply_case_conversion( product.name, mode ) if converted and converted != product.name: product.with_context(_fi_converting_case=True).write({ 'name': converted, 'x_fi_case_conversion': mode, }) count += 1 return { 'type': 'ir.actions.client', 'tag': 'display_notification', 'params': { 'title': 'Case Conversion Applied', 'message': f'{count} product names converted to {mode}.', 'type': 'success', 'sticky': False, }, }