import logging from odoo import fields from datetime import timedelta _logger = logging.getLogger(__name__) def get_ap_aging(env, params): today = fields.Date.today() domain = [ ('account_id.account_type', '=', 'liability_payable'), ('parent_state', '=', 'posted'), ('reconciled', '=', False), ('company_id', '=', env.company.id), ] amls = env['account.move.line'].search(domain) buckets = {'current': 0, '1_30': 0, '31_60': 0, '61_90': 0, '90_plus': 0} for aml in amls: amt = abs(aml.amount_residual) if not aml.date_maturity or aml.date_maturity >= today: buckets['current'] += amt else: days = (today - aml.date_maturity).days if days <= 30: buckets['1_30'] += amt elif days <= 60: buckets['31_60'] += amt elif days <= 90: buckets['61_90'] += amt else: buckets['90_plus'] += amt return {'total': sum(buckets.values()), 'buckets': buckets, 'line_count': len(amls)} def find_duplicate_bills(env, params): window_days = int(params.get('window_days', 7)) bills = env['account.move'].search([ ('move_type', 'in', ('in_invoice', 'in_refund')), ('state', '=', 'posted'), ('company_id', '=', env.company.id), ], order='partner_id, amount_total, date') duplicates = [] prev = None for bill in bills: if prev and ( prev.partner_id == bill.partner_id and abs(prev.amount_total - bill.amount_total) < 0.01 and abs((prev.date - bill.date).days) <= window_days ): duplicates.append({ 'bill_1': {'id': prev.id, 'name': prev.name, 'date': str(prev.date)}, 'bill_2': {'id': bill.id, 'name': bill.name, 'date': str(bill.date)}, 'partner': bill.partner_id.name, 'amount': bill.amount_total, }) prev = bill return {'count': len(duplicates), 'duplicates': duplicates[:20]} def match_bill_to_po(env, params): bill_id = int(params['bill_id']) bill = env['account.move'].browse(bill_id) if not bill.exists(): return {'error': 'Bill not found'} matches = [] for line in bill.invoice_line_ids: if line.purchase_line_id: matches.append({ 'bill_line': line.name or '', 'po': line.purchase_line_id.order_id.name, 'po_line': line.purchase_line_id.name, 'po_qty': line.purchase_line_id.product_qty, 'bill_qty': line.quantity, 'match': abs(line.quantity - line.purchase_line_id.product_qty) < 0.01, }) return {'bill': bill.name, 'matches': matches, 'unmatched_lines': len(bill.invoice_line_ids) - len(matches)} def get_unpaid_bills(env, params): domain = [ ('move_type', 'in', ('in_invoice', 'in_refund')), ('state', '=', 'posted'), ('payment_state', 'in', ('not_paid', 'partial')), ('company_id', '=', env.company.id), ] if params.get('partner_id'): domain.append(('partner_id', '=', int(params['partner_id']))) bills = env['account.move'].search(domain, order='invoice_date_due asc', limit=int(params.get('limit', 50))) return { 'count': len(bills), 'total': sum(b.amount_residual for b in bills), 'bills': [{ 'id': b.id, 'name': b.name, 'partner': b.partner_id.name if b.partner_id else '', 'amount_total': b.amount_total, 'amount_residual': b.amount_residual, 'date_due': str(b.invoice_date_due) if b.invoice_date_due else '', } for b in bills], } def verify_bill_taxes(env, params): bill_id = int(params['bill_id']) bill = env['account.move'].browse(bill_id) if not bill.exists(): return {'error': 'Bill not found'} issues = [] for line in bill.invoice_line_ids: if line.product_id and not line.tax_ids: issues.append({ 'line': line.name or line.product_id.name, 'issue': 'No tax applied to product line', }) return {'bill': bill.name, 'issues': issues, 'clean': len(issues) == 0} def get_payment_schedule(env, params): days_ahead = int(params.get('days_ahead', 30)) cutoff = fields.Date.today() + timedelta(days=days_ahead) bills = env['account.move'].search([ ('move_type', '=', 'in_invoice'), ('state', '=', 'posted'), ('payment_state', 'in', ('not_paid', 'partial')), ('invoice_date_due', '<=', cutoff), ('company_id', '=', env.company.id), ], order='invoice_date_due asc') return { 'period': f'Next {days_ahead} days', 'total': sum(b.amount_residual for b in bills), 'bills': [{ 'id': b.id, 'name': b.name, 'partner': b.partner_id.name if b.partner_id else '', 'amount_residual': b.amount_residual, 'date_due': str(b.invoice_date_due) if b.invoice_date_due else '', } for b in bills[:50]], } TOOLS = { 'get_ap_aging': get_ap_aging, 'find_duplicate_bills': find_duplicate_bills, 'match_bill_to_po': match_bill_to_po, 'get_unpaid_bills': get_unpaid_bills, 'verify_bill_taxes': verify_bill_taxes, 'get_payment_schedule': get_payment_schedule, }