from __future__ import annotations import logging from datetime import date, timedelta from ..tools.odoo_client import OdooClient logger = logging.getLogger(__name__) class AccountingTools: def __init__(self, odoo: OdooClient): self._o = odoo async def get_journal_entries(self, journal_id: int = None, date_from: str = None, date_to: str = None, state: str = 'posted', limit: int = 50) -> list: domain = [('move_type', '=', 'entry'), ('state', '=', state)] if journal_id: domain.append(('journal_id', '=', journal_id)) if date_from: domain.append(('date', '>=', date_from)) if date_to: domain.append(('date', '<=', date_to)) fields = ['name', 'date', 'journal_id', 'ref', 'state', 'amount_total', 'line_ids'] return await self._o.search_read('account.move', domain, fields, limit=limit) async def get_chart_of_accounts(self, account_type: str = None, limit: int = 100) -> list: domain = [('deprecated', '=', False)] if account_type: domain.append(('account_type', '=', account_type)) fields = ['code', 'name', 'account_type', 'balance', 'currency_id'] return await self._o.search_read('account.account', domain, fields, limit=limit) async def get_account_balance(self, account_id: int) -> dict: records = await self._o.search_read( 'account.account', [('id', '=', account_id)], ['code', 'name', 'balance', 'account_type'], limit=1, ) return records[0] if records else {} async def get_trial_balance(self, date_from: str = None, date_to: str = None) -> list: today = date.today() df = date_from or today.replace(day=1).isoformat() dt = date_to or today.isoformat() domain = [ ('move_id.state', '=', 'posted'), ('date', '>=', df), ('date', '<=', dt), ] fields = ['account_id', 'debit', 'credit', 'balance'] lines = await self._o.search_read('account.move.line', domain, fields, limit=500) by_account: dict = {} for line in lines: aid = line['account_id'][0] if isinstance(line['account_id'], list) else line['account_id'] aname = line['account_id'][1] if isinstance(line['account_id'], list) else str(aid) if aid not in by_account: by_account[aid] = {'account_id': aid, 'account_name': aname, 'debit': 0.0, 'credit': 0.0} by_account[aid]['debit'] += line.get('debit', 0.0) by_account[aid]['credit'] += line.get('credit', 0.0) result = list(by_account.values()) for r in result: r['balance'] = r['debit'] - r['credit'] return result async def get_tax_summary(self, date_from: str = None, date_to: str = None) -> dict: today = date.today() df = date_from or today.replace(day=1).isoformat() dt = date_to or today.isoformat() domain = [ ('move_id.state', '=', 'posted'), ('date', '>=', df), ('date', '<=', dt), ('tax_ids', '!=', False), ] fields = ['tax_ids', 'debit', 'credit', 'balance'] lines = await self._o.search_read('account.move.line', domain, fields, limit=500) total_tax = sum(abs(line.get('balance', 0)) for line in lines) return {'period_from': df, 'period_to': dt, 'total_tax_lines': len(lines), 'total_tax_amount': total_tax} async def flag_for_review(self, model: str, record_id: int, reason: str, severity: str = 'medium') -> bool: note = f'[AI FLAG - {severity.upper()}] {reason}' await self._o.call(model, 'message_post', [[record_id]], {'body': note, 'message_type': 'comment'}) logger.info('Flagged %s:%s (%s) for review', model, record_id, severity) return True async def post_chatter_note(self, model: str, record_id: int, note: str) -> bool: await self._o.call(model, 'message_post', [[record_id]], {'body': note, 'message_type': 'comment'}) return True