206 lines
7.1 KiB
Python
206 lines
7.1 KiB
Python
import logging
|
|
from odoo import fields
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_payroll_entries(env, params):
|
|
payroll_journals = env['account.journal'].search([
|
|
('name', 'ilike', 'payroll'),
|
|
('company_id', '=', env.company.id),
|
|
])
|
|
if not payroll_journals and params.get('journal_id'):
|
|
payroll_journals = env['account.journal'].browse(int(params['journal_id']))
|
|
domain = [
|
|
('journal_id', 'in', payroll_journals.ids),
|
|
('state', '=', 'posted'),
|
|
('company_id', '=', env.company.id),
|
|
]
|
|
if params.get('date_from'):
|
|
domain.append(('date', '>=', params['date_from']))
|
|
if params.get('date_to'):
|
|
domain.append(('date', '<=', params['date_to']))
|
|
entries = env['account.move'].search(domain, order='date desc', limit=50)
|
|
return {
|
|
'count': len(entries),
|
|
'entries': [{
|
|
'id': e.id, 'name': e.name, 'date': str(e.date),
|
|
'amount': e.amount_total, 'ref': e.ref or '',
|
|
} for e in entries],
|
|
}
|
|
|
|
|
|
def compare_payroll_to_bank(env, params):
|
|
date_from = params.get('date_from')
|
|
date_to = params.get('date_to')
|
|
if not date_from or not date_to:
|
|
return {'error': 'date_from and date_to are required'}
|
|
payroll_journals = env['account.journal'].search([
|
|
('name', 'ilike', 'payroll'), ('company_id', '=', env.company.id),
|
|
])
|
|
payroll_entries = env['account.move'].search([
|
|
('journal_id', 'in', payroll_journals.ids),
|
|
('state', '=', 'posted'),
|
|
('date', '>=', date_from), ('date', '<=', date_to),
|
|
])
|
|
bank_lines = env['account.bank.statement.line'].search([
|
|
('date', '>=', date_from), ('date', '<=', date_to),
|
|
('company_id', '=', env.company.id),
|
|
])
|
|
payroll_total = sum(e.amount_total for e in payroll_entries)
|
|
bank_payroll = sum(abs(l.amount) for l in bank_lines if 'payroll' in (l.payment_ref or '').lower())
|
|
return {
|
|
'payroll_journal_total': payroll_total,
|
|
'bank_payroll_total': bank_payroll,
|
|
'difference': payroll_total - bank_payroll,
|
|
}
|
|
|
|
|
|
def verify_source_deductions(env, params):
|
|
return {
|
|
'status': 'info',
|
|
'message': 'Source deduction verification requires CRA rate tables. Use fusion_payroll for full verification.',
|
|
}
|
|
|
|
|
|
def get_cra_remittance_status(env, params):
|
|
cra_accounts = env['account.account'].search([
|
|
('name', 'ilike', 'CRA'),
|
|
('company_id', '=', env.company.id),
|
|
])
|
|
result = []
|
|
for acct in cra_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 {'accounts': result}
|
|
|
|
|
|
def find_unmatched_payroll_cheques(env, params):
|
|
bank_lines = env['account.bank.statement.line'].search([
|
|
('is_reconciled', '=', False),
|
|
('company_id', '=', env.company.id),
|
|
('payment_ref', 'ilike', 'cheque'),
|
|
])
|
|
return {
|
|
'count': len(bank_lines),
|
|
'cheques': [{
|
|
'id': l.id, 'date': str(l.date),
|
|
'ref': l.payment_ref, 'amount': l.amount,
|
|
} for l in bank_lines[:30]],
|
|
}
|
|
|
|
|
|
def parse_payroll_summary(env, params):
|
|
import re
|
|
raw_data = params.get('data', '')
|
|
if not raw_data:
|
|
return {'error': 'No payroll data provided'}
|
|
|
|
lines = raw_data.strip().split('\n')
|
|
entries = []
|
|
totals = {'gross': 0, 'cpp': 0, 'ei': 0, 'tax': 0, 'net': 0}
|
|
|
|
for line in lines:
|
|
amounts = re.findall(r'\$?([\d,]+\.?\d*)', line)
|
|
if len(amounts) >= 2:
|
|
name_part = re.sub(r'\$?[\d,]+\.?\d*', '', line).strip(' \t,|-')
|
|
parsed_amounts = [float(a.replace(',', '')) for a in amounts]
|
|
entry = {'name': name_part or 'Employee', 'amounts': parsed_amounts}
|
|
if len(parsed_amounts) >= 5:
|
|
entry.update({
|
|
'gross': parsed_amounts[0],
|
|
'cpp': parsed_amounts[1],
|
|
'ei': parsed_amounts[2],
|
|
'tax': parsed_amounts[3],
|
|
'net': parsed_amounts[4] if len(parsed_amounts) > 4 else parsed_amounts[0] - sum(parsed_amounts[1:4]),
|
|
})
|
|
for k in ('gross', 'cpp', 'ei', 'tax', 'net'):
|
|
totals[k] += entry.get(k, 0)
|
|
entries.append(entry)
|
|
|
|
return {
|
|
'status': 'parsed',
|
|
'employee_count': len(entries),
|
|
'entries': entries,
|
|
'totals': totals,
|
|
'raw_lines': len(lines),
|
|
}
|
|
|
|
|
|
def create_payroll_journal_entry(env, params):
|
|
journal_id = int(params['journal_id'])
|
|
date = params['date']
|
|
lines_data = params['lines']
|
|
move_vals = {
|
|
'journal_id': journal_id,
|
|
'date': date,
|
|
'ref': params.get('ref', 'Payroll Entry'),
|
|
'line_ids': [(0, 0, {
|
|
'account_id': int(line['account_id']),
|
|
'name': line.get('name', 'Payroll'),
|
|
'debit': float(line.get('debit', 0)),
|
|
'credit': float(line.get('credit', 0)),
|
|
'partner_id': int(line['partner_id']) if line.get('partner_id') else False,
|
|
}) for line in lines_data],
|
|
}
|
|
move = env['account.move'].create(move_vals)
|
|
return {'status': 'created', 'move_id': move.id, 'name': move.name}
|
|
|
|
|
|
def get_payroll_schedule(env, params):
|
|
return {'status': 'info', 'message': 'Payroll schedule available via fusion_payroll module.'}
|
|
|
|
|
|
def match_payroll_cheques(env, params):
|
|
st_line_id = int(params['statement_line_id'])
|
|
move_line_ids = [int(x) for x in params['move_line_ids']]
|
|
st_line = env['account.bank.statement.line'].browse(st_line_id)
|
|
st_line.set_line_bank_statement_line(move_line_ids)
|
|
return {'status': 'matched', 'statement_line_id': st_line_id}
|
|
|
|
|
|
def verify_payroll_deductions(env, params):
|
|
return verify_source_deductions(env, params)
|
|
|
|
|
|
def get_cra_remittance_due(env, params):
|
|
return get_cra_remittance_status(env, params)
|
|
|
|
|
|
def prepare_cra_payment(env, params):
|
|
return create_payroll_journal_entry(env, params)
|
|
|
|
|
|
def generate_t4(env, params):
|
|
return {'status': 'info', 'message': 'T4 generation available via fusion_payroll module.'}
|
|
|
|
|
|
def generate_roe(env, params):
|
|
return {'status': 'info', 'message': 'ROE generation available via fusion_payroll module.'}
|
|
|
|
|
|
def get_payroll_cost_report(env, params):
|
|
return get_payroll_entries(env, params)
|
|
|
|
|
|
TOOLS = {
|
|
'get_payroll_entries': get_payroll_entries,
|
|
'compare_payroll_to_bank': compare_payroll_to_bank,
|
|
'verify_source_deductions': verify_source_deductions,
|
|
'get_cra_remittance_status': get_cra_remittance_status,
|
|
'find_unmatched_payroll_cheques': find_unmatched_payroll_cheques,
|
|
'parse_payroll_summary': parse_payroll_summary,
|
|
'create_payroll_journal_entry': create_payroll_journal_entry,
|
|
'get_payroll_schedule': get_payroll_schedule,
|
|
'match_payroll_cheques': match_payroll_cheques,
|
|
'verify_payroll_deductions': verify_payroll_deductions,
|
|
'get_cra_remittance_due': get_cra_remittance_due,
|
|
'prepare_cra_payment': prepare_cra_payment,
|
|
'generate_t4': generate_t4,
|
|
'generate_roe': generate_roe,
|
|
'get_payroll_cost_report': get_payroll_cost_report,
|
|
}
|