oca-hr/odoo-bringout-oca-hr-expense-hr_expense_invoice/hr_expense_invoice/models/account_move.py
Ernad Husremovic dfcda4100c Move all OCA HR modules from oca-technical to dedicated oca-hr submodule
Reorganized 67 HR-related modules for better structure:
- Moved all odoo-bringout-oca-hr-* packages from packages/oca-technical/
- Now organized in dedicated packages/oca-hr/ submodule
- Includes attendance, expense, holiday, employee, and contract modules
- Maintains all module functionality while improving project organization

This creates a cleaner separation between general technical modules
and HR-specific functionality.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-30 17:11:28 +02:00

77 lines
3 KiB
Python

# Copyright 2019 Ecosoft <saranl@ecosoft.co.th>
# Copyright 2021 Tecnativa - Víctor Martínez
# Copyright 2024 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import float_compare
class AccountMove(models.Model):
_inherit = "account.move"
expense_ids = fields.One2many(
comodel_name="hr.expense", inverse_name="invoice_id", string="Expenses"
)
source_invoice_expense_id = fields.Many2one(
comodel_name="hr.expense",
help="Reference to the expense with a linked invoice that generated this"
"transfer journal entry",
)
def write(self, vals):
# Check if the amount of the invoice linked to an invoice is different
# Done here in the write instead of a Python constraint as the computed field
# amount_total is not yet updated on that moment
res = super().write(vals)
# Only need to check expenses amount when the invoice amount changes
if not ("amount_total" in vals or "tax_totals" in vals):
return res
DecimalPrecision = self.env["decimal.precision"]
precision = DecimalPrecision.precision_get("Product Price")
for move in self.filtered("expense_ids"):
expense_amount = sum(move.expense_ids.mapped("total_amount"))
if float_compare(expense_amount, move.amount_total, precision) != 0:
raise ValidationError(
_(
"You can't change the total amount, as there's an expense "
"linked to this invoice."
)
)
return res
def action_view_expense(self):
self.ensure_one()
return {
"type": "ir.actions.act_window",
"view_mode": "form",
"res_model": "hr.expense",
"res_id": self.expense_ids[:1].id,
}
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
@api.constrains("account_id", "display_type")
def _check_payable_receivable(self):
_self = self.filtered("expense_id")
return super(AccountMoveLine, (self - _self))._check_payable_receivable()
def reconcile(self):
"""Mark expenses paid by employee having invoice when reconciling them."""
expenses = self.move_id.source_invoice_expense_id
not_paid_expenses = expenses.filtered(lambda x: x.state != "done")
res = super().reconcile()
not_paid_expense_sheets = not_paid_expenses.sheet_id.filtered(
lambda x: x.state != "done"
)
paid_expenses = not_paid_expenses.filtered(
lambda expense: expense.currency_id.is_zero(expense.amount_residual)
)
paid_expenses.write({"state": "done"})
paid_sheets = not_paid_expense_sheets.filtered(
lambda x: all(expense.state == "done" for expense in x.expense_line_ids)
)
paid_sheets.set_to_paid()
return res