"""Batch send follow-ups to selected partners (or all overdue).""" from datetime import date from odoo import _, api, fields, models from odoo.exceptions import UserError class FusionBatchFollowupWizard(models.TransientModel): _name = "fusion.batch.followup.wizard" _description = "Batch Send Follow-ups Wizard" scope = fields.Selection([ ('selected', 'Selected partners only'), ('all_overdue', 'All overdue partners'), ], required=True, default='selected') partner_ids = fields.Many2many('res.partner', default=lambda self: self._default_partner_ids()) force = fields.Boolean(string='Force (override pause + manual review)', default=False) auto_resolve_level = fields.Boolean( string='Auto-resolve level', default=True, help="If True, engine picks the appropriate level per partner. " "If False, use the chosen override level for all.") override_level_id = fields.Many2one('fusion.followup.level') # Results state = fields.Selection([('draft', 'Draft'), ('done', 'Done')], default='draft') sent_count = fields.Integer(readonly=True) skipped_count = fields.Integer(readonly=True) error_count = fields.Integer(readonly=True) summary = fields.Text(readonly=True) @api.model def _default_partner_ids(self): ctx = self.env.context if ctx.get('active_model') == 'res.partner': return ctx.get('active_ids', []) return [] def action_run(self): self.ensure_one() if self.scope == 'selected' and not self.partner_ids: raise UserError(_("No partners selected.")) partners = self.partner_ids if self.scope == 'all_overdue': Line = self.env['account.move.line'].sudo() overdue_partner_ids = Line.search([ ('parent_state', '=', 'posted'), ('account_id.account_type', '=', 'asset_receivable'), ('reconciled', '=', False), ('amount_residual', '>', 0), ('date_maturity', '<', date.today()), ('company_id', '=', self.env.company.id), ]).mapped('partner_id').ids partners = self.env['res.partner'].sudo().browse(overdue_partner_ids) engine = self.env['fusion.followup.engine'] sent = 0 skipped = 0 errors = [] for partner in partners: try: with self.env.cr.savepoint(): level = self.override_level_id if not self.auto_resolve_level else None result = engine.send_followup_email( partner, level=level, force=self.force) status = result.get('status', '') if status == 'sent': sent += 1 else: skipped += 1 except Exception as e: errors.append(f"{partner.name}: {e}") self.write({ 'state': 'done', 'sent_count': sent, 'skipped_count': skipped, 'error_count': len(errors), 'summary': '\n'.join(errors[:20]) if errors else False, }) return { 'type': 'ir.actions.act_window', 'res_model': self._name, 'res_id': self.id, 'view_mode': 'form', 'target': 'new', }