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'): vals['sequence_name'] = self.env['ir.sequence'].next_by_code('hr.expense.sequence') 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()