import logging from odoo import fields _logger = logging.getLogger(__name__) def get_close_checklist(env, params): from .bank_reconciliation import get_unreconciled_bank_lines from .journal_review import find_draft_entries, find_sequence_gaps from .hst_management import calculate_hst_balance period = params.get('period', str(fields.Date.today())[:7]) date_from = f'{period}-01' import calendar year, month = int(period[:4]), int(period[5:7]) last_day = calendar.monthrange(year, month)[1] date_to = f'{period}-{last_day:02d}' p = {'date_from': date_from, 'date_to': date_to} bank = get_unreconciled_bank_lines(env, p) drafts = find_draft_entries(env, {'min_age_days': '0'}) gaps = find_sequence_gaps(env, p) hst = calculate_hst_balance(env, p) checklist = [ {'item': 'Bank Reconciliation', 'status': 'ok' if bank['count'] == 0 else 'attention', 'detail': f"{bank['count']} unreconciled lines"}, {'item': 'Draft Entries', 'status': 'ok' if drafts['count'] == 0 else 'attention', 'detail': f"{drafts['count']} draft entries"}, {'item': 'Sequence Gaps', 'status': 'ok' if gaps['count'] == 0 else 'warning', 'detail': f"{gaps['count']} gaps found"}, {'item': 'HST Balance', 'status': 'info', 'detail': f"Net HST: ${hst['net_hst']:.2f}"}, ] return {'period': period, 'checklist': checklist} def get_unreconciled_counts(env, params): accounts = env['account.account'].search([ ('reconcile', '=', True), ('company_ids', 'in', env.company.id), ]) result = [] for acct in accounts: count = env['account.move.line'].search_count([ ('account_id', '=', acct.id), ('reconciled', '=', False), ('parent_state', '=', 'posted'), ]) if count > 0: result.append({ 'account_id': acct.id, 'code': acct.code, 'name': acct.name, 'unreconciled_count': count, }) return {'accounts': sorted(result, key=lambda x: -x['unreconciled_count'])} def find_entries_in_locked_period(env, params): company = env.company lock_date = company.fiscalyear_lock_date if not lock_date: return {'status': 'no_lock_date', 'entries': []} entries = env['account.move'].search([ ('date', '<=', lock_date), ('state', '=', 'draft'), ('company_id', '=', company.id), ]) return { 'lock_date': str(lock_date), 'count': len(entries), 'entries': [{'id': e.id, 'name': e.name, 'date': str(e.date)} for e in entries[:20]], } def get_accrual_status(env, params): accrual_codes = params.get('account_codes', ['2100', '2110', '2120']) result = [] for code in accrual_codes: accounts = env['account.account'].search([ ('code', '=like', f'{code}%'), ('company_ids', 'in', env.company.id), ]) for acct in accounts: balance = sum(env['account.move.line'].search([ ('account_id', '=', acct.id), ('parent_state', '=', 'posted'), ]).mapped('balance')) result.append({'code': acct.code, 'name': acct.name, 'balance': balance}) return {'accruals': result} def run_hash_integrity_check(env, params): try: result = env.company._check_hash_integrity() return { 'status': 'completed', 'results': result.get('results', []), 'printing_date': result.get('printing_date', ''), } except Exception as e: return {'error': str(e)} def get_period_summary(env, params): date_from = params.get('date_from') date_to = params.get('date_to') try: report = env.ref('account_reports.trial_balance_report') except Exception: report = env.ref('account.trial_balance_report', raise_if_not_found=False) if not report: return {'error': 'Trial balance report not found'} options = report.get_options({'date': {'date_from': date_from, 'date_to': date_to}}) lines = report._get_lines(options) return { 'period': f'{date_from} to {date_to}', 'lines': [{ 'name': l.get('name', ''), 'columns': [c.get('no_format', c.get('name', '')) for c in l.get('columns', [])], } for l in lines[:100]], } TOOLS = { 'get_close_checklist': get_close_checklist, 'get_unreconciled_counts': get_unreconciled_counts, 'find_entries_in_locked_period': find_entries_in_locked_period, 'get_accrual_status': get_accrual_status, 'run_hash_integrity_check': run_hash_integrity_check, 'get_period_summary': get_period_summary, }