# -*- coding: utf-8 -*- from odoo import models, fields, api, _ from odoo.exceptions import UserError from odoo.fields import Command from markupsafe import Markup import logging _logger = logging.getLogger(__name__) class ModPcaReceivedWizard(models.TransientModel): _name = 'fusion_claims.mod.pca.received.wizard' _description = 'MOD - Record PCA Receipt and Create Invoice(s)' sale_order_id = fields.Many2one('sale.order', required=True, readonly=True) currency_id = fields.Many2one('res.currency', related='sale_order_id.currency_id') order_total = fields.Monetary( related='sale_order_id.amount_untaxed', string='Order Subtotal', readonly=True) # PCA Document pca_document = fields.Binary(string='PCA Document', required=True) pca_filename = fields.Char(string='PCA Filename') # Case details (pre-filled from order, editable) hvmp_reference = fields.Char(string='HVMP Reference #') case_worker_id = fields.Many2one( 'res.partner', string='Case Worker', help='March of Dimes case worker assigned to this case', ) # Approval details approval_type = fields.Selection([ ('full', 'Full Approval'), ('partial', 'Partial Approval'), ], string='Approval Type', required=True, default='full') approved_amount = fields.Monetary( string='MOD Approved Amount', currency_field='currency_id', help='Total amount approved by March of Dimes (before taxes)', ) # Preview lines (computed when partial) preview_line_ids = fields.One2many( 'fusion_claims.mod.funding.approved.wizard.line', 'wizard_id', string='Line Split Preview', ) @api.model def default_get(self, fields_list): res = super().default_get(fields_list) if self.env.context.get('active_id'): order = self.env['sale.order'].browse(self.env.context['active_id']) res['sale_order_id'] = order.id if order.x_fc_case_reference: res['hvmp_reference'] = order.x_fc_case_reference if order.x_fc_case_worker: res['case_worker_id'] = order.x_fc_case_worker.id if order.x_fc_mod_pca_document: res['pca_document'] = order.x_fc_mod_pca_document res['pca_filename'] = order.x_fc_mod_pca_filename if order.x_fc_mod_approved_amount: res['approved_amount'] = order.x_fc_mod_approved_amount res['approval_type'] = order.x_fc_mod_approval_type or 'full' else: res['approved_amount'] = order.amount_untaxed return res @api.onchange('approval_type', 'approved_amount') def _onchange_compute_preview(self): """Compute the proportional split preview when partial approval.""" order = self.sale_order_id if not order: return lines = [] product_lines = order.order_line.filtered( lambda l: not l.display_type and l.product_uom_qty > 0 and l.product_id) if self.approval_type == 'partial' and self.approved_amount and self.approved_amount > 0: subtotal = order.amount_untaxed if subtotal <= 0: return for line in product_lines: ratio = line.price_subtotal / subtotal if subtotal else 0 mod_amount = round(self.approved_amount * ratio, 2) client_amount = round(line.price_subtotal - mod_amount, 2) lines.append((0, 0, { 'product_name': line.product_id.display_name, 'quantity': line.product_uom_qty, 'line_total': line.price_subtotal, 'mod_amount': mod_amount, 'client_amount': client_amount, })) elif self.approval_type == 'full': for line in product_lines: lines.append((0, 0, { 'product_name': line.product_id.display_name, 'quantity': line.product_uom_qty, 'line_total': line.price_subtotal, 'mod_amount': line.price_subtotal, 'client_amount': 0, })) self.preview_line_ids = [(5, 0, 0)] + lines def action_confirm(self): """Record PCA, set approval amounts, and create invoice(s).""" self.ensure_one() order = self.sale_order_id if not self.pca_document: raise UserError(_("Please attach the PCA document.")) if self.approval_type == 'partial': if not self.approved_amount or self.approved_amount <= 0: raise UserError(_("Please enter the approved amount for partial approval.")) if self.approved_amount >= order.amount_untaxed: raise UserError(_( "Approved amount is equal to or greater than the order subtotal. " "Use 'Full Approval' instead.")) client_name = order.partner_id.name or 'Client' # Update sale order vals = { 'x_fc_mod_status': 'contract_received', 'x_fc_mod_pca_received_date': fields.Date.today(), 'x_fc_mod_pca_document': self.pca_document, 'x_fc_mod_pca_filename': self.pca_filename or f'PCA - {client_name}.pdf', 'x_fc_mod_approval_type': self.approval_type, } if self.approval_type == 'partial': vals['x_fc_mod_approved_amount'] = self.approved_amount vals['x_fc_mod_payment_commitment'] = self.approved_amount else: vals['x_fc_mod_approved_amount'] = order.amount_untaxed vals['x_fc_mod_payment_commitment'] = order.amount_total if self.case_worker_id: vals['x_fc_case_worker'] = self.case_worker_id.id if self.hvmp_reference: vals['x_fc_case_reference'] = self.hvmp_reference order.write(vals) # Create invoices mod_partner = order._get_mod_partner() client = order.partner_id if self.approval_type == 'full': mod_invoice = self._create_full_invoice(order, mod_partner) self._log_pca_and_invoices(order, mod_invoice) return self._open_invoice(mod_invoice) else: mod_invoice = self._create_split_mod_invoice(order, mod_partner) client_invoice = self._create_split_client_invoice(order, client) self._log_pca_and_invoices(order, mod_invoice, client_invoice) return self._open_invoice(mod_invoice) # ------------------------------------------------------------------ # Invoice creation helpers # ------------------------------------------------------------------ def _create_full_invoice(self, order, mod_partner): """Create a single MOD invoice for the full order amount.""" line_vals = [] for line in order.order_line: if line.display_type in ('line_section', 'line_note'): line_vals.append(Command.create({ 'display_type': line.display_type, 'name': line.name, 'sequence': line.sequence, })) elif not line.display_type and line.product_uom_qty > 0: inv_line = line._prepare_invoice_line() inv_line['quantity'] = line.product_uom_qty inv_line['sequence'] = line.sequence line_vals.append(Command.create(inv_line)) return order._create_mod_invoice( partner_id=mod_partner.id, invoice_lines=line_vals, portion_type='full', label=' (March of Dimes - Full)', ) def _create_split_mod_invoice(self, order, mod_partner): """Create MOD invoice with proportionally reduced amounts.""" subtotal = order.amount_untaxed approved = self.approved_amount line_vals = [] for line in order.order_line: if line.display_type in ('line_section', 'line_note'): line_vals.append(Command.create({ 'display_type': line.display_type, 'name': line.name, 'sequence': line.sequence, })) elif not line.display_type and line.product_uom_qty > 0: ratio = line.price_subtotal / subtotal if subtotal else 0 mod_line_amount = round(approved * ratio, 2) mod_price_unit = round( mod_line_amount / line.product_uom_qty, 2 ) if line.product_uom_qty else 0 inv_line = line._prepare_invoice_line() inv_line['quantity'] = line.product_uom_qty inv_line['price_unit'] = mod_price_unit inv_line['sequence'] = line.sequence line_vals.append(Command.create(inv_line)) return order._create_mod_invoice( partner_id=mod_partner.id, invoice_lines=line_vals, portion_type='adp', label=' (March of Dimes Portion)', ) def _create_split_client_invoice(self, order, client): """Create Client invoice with the difference amounts.""" subtotal = order.amount_untaxed approved = self.approved_amount line_vals = [] for line in order.order_line: if line.display_type in ('line_section', 'line_note'): line_vals.append(Command.create({ 'display_type': line.display_type, 'name': line.name, 'sequence': line.sequence, })) elif not line.display_type and line.product_uom_qty > 0: ratio = line.price_subtotal / subtotal if subtotal else 0 mod_line_amount = round(approved * ratio, 2) client_line_amount = round(line.price_subtotal - mod_line_amount, 2) client_price_unit = round( client_line_amount / line.product_uom_qty, 2 ) if line.product_uom_qty else 0 inv_line = line._prepare_invoice_line() inv_line['quantity'] = line.product_uom_qty inv_line['price_unit'] = client_price_unit inv_line['sequence'] = line.sequence if client_line_amount <= 0: inv_line['price_unit'] = 0 inv_line['name'] = f'{line.name}\n[Covered by March of Dimes]' line_vals.append(Command.create(inv_line)) return order._create_mod_invoice( partner_id=client.id, invoice_lines=line_vals, portion_type='client', label=' (Client Portion)', ) # ------------------------------------------------------------------ # Logging and navigation # ------------------------------------------------------------------ def _log_pca_and_invoices(self, order, mod_invoice, client_invoice=None): """Log PCA receipt and invoice creation to chatter.""" parts = [f'PCA Received and Invoice(s) Created'] parts.append(f'Date: {fields.Date.today().strftime("%B %d, %Y")}') if self.hvmp_reference: parts.append(f'HVMP Reference: {self.hvmp_reference}') if self.case_worker_id: parts.append(f'Case Worker: {self.case_worker_id.name}') type_label = 'Full Approval' if self.approval_type == 'full' else 'Partial Approval' parts.append(f'Approval Type: {type_label}') if self.approval_type == 'partial': parts.append(f'MOD Approved: ${self.approved_amount:,.2f}') parts.append( f'Client Portion: ${order.amount_untaxed - self.approved_amount:,.2f}') inv_links = [ f'MOD Invoice: ' f'{mod_invoice.name or "Draft"}' ] if client_invoice: inv_links.append( f'Client Invoice: ' f'{client_invoice.name or "Draft"}' ) parts.extend(inv_links) order.message_post( body=Markup('
' + '
'.join(parts) + '
'), message_type='notification', subtype_xmlid='mail.mt_note', ) def _open_invoice(self, invoice): return { 'type': 'ir.actions.act_window', 'name': 'Invoice', 'res_model': 'account.move', 'view_mode': 'form', 'res_id': invoice.id, }