hr_expense_account_split/models/hr_expense.py

127 lines
5.4 KiB
Python

from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_round
class HrExpense(models.Model):
_inherit = 'hr.expense'
@api.depends('product_id', 'company_id', 'payment_mode')
def _compute_account_id(self):
super()._compute_account_id()
for expense in self:
if not expense.product_id:
continue
# Use specific accounts based on payment mode if configured
product = expense.product_id.with_company(expense.company_id)
if expense.payment_mode == 'own_account':
if product.property_account_expense_employee_id:
expense.account_id = product.property_account_expense_employee_id
elif expense.payment_mode == 'company_account':
if product.property_account_expense_company_id:
expense.account_id = product.property_account_expense_company_id
sequence_name = fields.Char(string='Sequence', readonly=True, copy=False, default=lambda self: _('New'))
realization_total_amount = fields.Monetary(
string='Realization Total',
compute='_compute_realization_total_amount',
store=True,
currency_field='currency_id',
help="Total amount from all physical receipts realized for this expense."
)
receipt_due_date = fields.Date(
string="Receipt Due Date",
readonly=True,
help="Date the employee must submit the receipt."
)
receipt_received = fields.Boolean(
string="Receipt Received",
default=False,
tracking=True,
help="Mark if original receipt has been received."
)
receipt_overdue = fields.Boolean(
string="Receipt Overdue",
compute='_compute_receipt_overdue',
store=True,
help="True if receipt is not received and past due date."
)
realization_ids = fields.One2many('hr.expense.realization', 'expense_id', string='Realizations')
realization_count = fields.Integer(string='Realization Count', compute='_compute_realization_count')
@api.depends('realization_ids')
def _compute_realization_count(self):
for expense in self:
expense.realization_count = len(expense.realization_ids)
@api.depends('realization_ids.total_amount')
def _compute_realization_total_amount(self):
for expense in self:
expense.realization_total_amount = sum(expense.realization_ids.mapped('total_amount'))
@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
if vals.get('sequence_name', _('New')) == _('New'):
payment_mode = vals.get('payment_mode', 'own_account')
seq_code = 'hr.expense.sequence.reimbursement' if payment_mode == 'own_account' else 'hr.expense.sequence.kasbon'
vals['sequence_name'] = self.env['ir.sequence'].next_by_code(seq_code) or _('New')
return super().create(vals_list)
def action_create_realization(self):
self.ensure_one()
if self.payment_mode != 'company_account':
raise UserError(_("Realization is only for company-paid expenses."))
# Check if already has a realization
if self.realization_count > 0:
return self.action_view_realizations()
return {
'name': _('Create Realization'),
'type': 'ir.actions.act_window',
'res_model': 'hr.expense.realization',
'view_mode': 'form',
'context': {
'default_expense_id': self.id,
'default_employee_id': self.employee_id.id,
},
'target': 'current',
}
def action_view_realizations(self):
self.ensure_one()
action = self.env["ir.actions.actions"]._for_xml_id("hr_expense_account_split.action_hr_expense_realization")
if self.realization_count > 1:
action['domain'] = [('expense_id', '=', self.id)]
elif self.realization_count == 1:
res = self.env['hr.expense.realization'].search([('expense_id', '=', self.id)], limit=1)
action['views'] = [(self.env.ref('hr_expense_account_split.hr_expense_realization_view_form').id, 'form')]
action['res_id'] = res.id
return action
@api.depends('receipt_due_date', 'receipt_received')
def _compute_receipt_overdue(self):
today = fields.Date.today()
for expense in self:
if expense.receipt_due_date and not expense.receipt_received and expense.receipt_due_date < today:
expense.receipt_overdue = True
else:
expense.receipt_overdue = False
def _prepare_move_lines_vals(self):
res = super()._prepare_move_lines_vals()
if res.get('price_unit'):
# Round the price to the currency's decimal places to avoid floating point artifacts (e.g. ...0001)
# We use precision_digits=2 which is the standard for IDR/USD etc.
res['price_unit'] = float_round(res['price_unit'], precision_digits=self.currency_id.decimal_places or 2)
return res
def action_submit_expenses(self):
for expense in self:
if expense.payment_mode == 'own_account' and expense.nb_attachment == 0:
raise ValidationError(_("You must attach at least one receipt for reimbursement expenses (Paid By: Employee)."))
return super().action_submit_expenses()