mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-23 00:32:03 +02:00
210 lines
8.3 KiB
Python
210 lines
8.3 KiB
Python
# Copyright 2017 Tecnativa - Vicent Cubells
|
|
# Copyright 2020 Tecnativa - David Vidal
|
|
# Copyright 2021 Tecnativa - Víctor Martínez
|
|
# Copyright 2015-2024 Tecnativa - Pedro M. Baeza
|
|
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
|
|
|
from odoo import Command, _, api, fields, models
|
|
from odoo.exceptions import UserError
|
|
|
|
|
|
class HrExpense(models.Model):
|
|
_inherit = "hr.expense"
|
|
|
|
sheet_id_state = fields.Selection(related="sheet_id.state", string="Sheet state")
|
|
invoice_id = fields.Many2one(
|
|
comodel_name="account.move",
|
|
string="Vendor Bill",
|
|
domain=[
|
|
("move_type", "=", "in_invoice"),
|
|
("state", "=", "posted"),
|
|
("payment_state", "=", "not_paid"),
|
|
("expense_ids", "=", False),
|
|
],
|
|
copy=False,
|
|
)
|
|
transfer_move_ids = fields.One2many(
|
|
comodel_name="account.move",
|
|
inverse_name="source_invoice_expense_id",
|
|
)
|
|
|
|
def _prepare_invoice_values(self):
|
|
invoice_lines = [
|
|
(
|
|
0,
|
|
0,
|
|
{
|
|
"product_id": self.product_id.id,
|
|
"name": self.name,
|
|
"price_unit": self.untaxed_amount,
|
|
"quantity": self.quantity,
|
|
"account_id": self.account_id.id,
|
|
"analytic_distribution": self.analytic_distribution,
|
|
"tax_ids": [(6, 0, self.tax_ids.ids)],
|
|
},
|
|
)
|
|
]
|
|
return {
|
|
"name": "/",
|
|
"ref": self.reference,
|
|
"move_type": "in_invoice",
|
|
"invoice_date": self.date,
|
|
"invoice_line_ids": invoice_lines,
|
|
}
|
|
|
|
def action_move_create(self):
|
|
"""Don't let super to create any move:
|
|
- Paid by company: there's already the invoice.
|
|
- Paid by employee: we create here a journal entry transferring the AP
|
|
balance from the invoice partner to the employee.
|
|
"""
|
|
expenses_with_invoice = self.filtered("invoice_id")
|
|
res = super(HrExpense, self - expenses_with_invoice).action_move_create()
|
|
# Create AP transfer entry for expenses paid by employees
|
|
for expense in expenses_with_invoice:
|
|
if expense.payment_mode == "own_account":
|
|
move = self.env["account.move"].create(
|
|
expense._prepare_own_account_transfer_move_vals()
|
|
)
|
|
move.action_post()
|
|
# reconcile with the invoice
|
|
ap_lines = expense.invoice_id.line_ids.filtered(
|
|
lambda x: x.display_type == "payment_term"
|
|
)
|
|
transfer_line = move.line_ids.filtered(
|
|
lambda x: x.partner_id == self.invoice_id.partner_id
|
|
)
|
|
(ap_lines + transfer_line).reconcile()
|
|
return res
|
|
|
|
def _prepare_own_account_transfer_move_vals(self):
|
|
self.ensure_one()
|
|
self = self.with_company(self.company_id)
|
|
# TODO: Allow to select a specific journal
|
|
journal = self.env["account.journal"].search(
|
|
[
|
|
("company_id", "=", self.company_id.id),
|
|
("type", "=", "general"),
|
|
],
|
|
limit=1,
|
|
)
|
|
employee_partner = self.employee_id.sudo().address_home_id.commercial_partner_id
|
|
invoice_partner = self.invoice_id.partner_id
|
|
ap_lines = self.invoice_id.line_ids.filtered(
|
|
lambda x: x.display_type == "payment_term"
|
|
)
|
|
amount_invoice = sum(ap_lines.mapped("credit"))
|
|
return {
|
|
"journal_id": journal.id,
|
|
"move_type": "entry",
|
|
"name": "/",
|
|
"date": self.date,
|
|
"ref": self.name,
|
|
"source_invoice_expense_id": self.id,
|
|
"line_ids": [
|
|
Command.create(
|
|
{
|
|
"account_id": ap_lines.account_id[:1].id,
|
|
"partner_id": invoice_partner.id,
|
|
"debit": amount_invoice,
|
|
}
|
|
),
|
|
Command.create(
|
|
{
|
|
"account_id": employee_partner.property_account_payable_id.id,
|
|
"partner_id": employee_partner.id,
|
|
"credit": amount_invoice,
|
|
}
|
|
),
|
|
],
|
|
}
|
|
|
|
def action_expense_create_invoice(self):
|
|
invoice = self.env["account.move"].create(self._prepare_invoice_values())
|
|
attachments = self.env["ir.attachment"].search(
|
|
[("res_model", "=", self._name), ("res_id", "in", self.ids)]
|
|
)
|
|
for attachment in attachments:
|
|
attachment.copy({"res_model": invoice._name, "res_id": invoice.id})
|
|
# Normalize data in the expense for avoiding mismatchings. Examples: the tax
|
|
# reported is not correct, there's more than one concept, etc...
|
|
self.write(
|
|
{
|
|
"invoice_id": invoice.id,
|
|
"quantity": 1,
|
|
"tax_ids": False,
|
|
"unit_amount": invoice.amount_total,
|
|
}
|
|
)
|
|
return True
|
|
|
|
@api.constrains("invoice_id")
|
|
def _check_invoice_id(self):
|
|
for expense in self: # Only non binding expense
|
|
if (
|
|
not expense.sheet_id
|
|
and expense.invoice_id
|
|
and expense.invoice_id.state != "posted"
|
|
):
|
|
raise UserError(_("Vendor bill state must be Posted"))
|
|
|
|
@api.onchange("invoice_id")
|
|
def _onchange_invoice_id(self):
|
|
"""Assure quantity is 1 if an invoice is set for having proper totals, and
|
|
the rest of the fields that are not computed writable, avoiding to ud
|
|
"""
|
|
if self.invoice_id:
|
|
self.quantity = 1
|
|
self.reference = self.invoice_id.name
|
|
self.date = self.invoice_id.date
|
|
if self.invoice_id.company_id != self.company_id:
|
|
# for avoiding to trigger dependent computes
|
|
self.company_id = self.invoice_id.company_id.id
|
|
|
|
# tax_ids put as dependency for assuring this is computed after setting tax_ids
|
|
@api.depends("invoice_id", "tax_ids")
|
|
def _compute_unit_amount(self):
|
|
with_invoice = self.filtered("invoice_id")
|
|
for record in with_invoice:
|
|
record.unit_amount = record.invoice_id.amount_total
|
|
return super(HrExpense, self - with_invoice)._compute_unit_amount()
|
|
|
|
# tax_ids put as dependency for assuring this is computed after setting tax_ids
|
|
@api.depends("invoice_id", "tax_ids")
|
|
def _compute_amount(self):
|
|
with_invoice = self.filtered("invoice_id")
|
|
for record in with_invoice:
|
|
record.total_amount = record.invoice_id.amount_total
|
|
return super(HrExpense, self - with_invoice)._compute_amount()
|
|
|
|
@api.depends("invoice_id")
|
|
def _compute_currency_id(self):
|
|
with_invoice = self.filtered("invoice_id")
|
|
for record in with_invoice:
|
|
record.currency_id = record.invoice_id.currency_id.id
|
|
return super(HrExpense, self - with_invoice)._compute_currency_id()
|
|
|
|
@api.depends("invoice_id")
|
|
def _compute_tax_ids(self):
|
|
with_invoice = self.filtered("invoice_id")
|
|
for record in with_invoice:
|
|
record.tax_ids = [(5,)]
|
|
return super(HrExpense, self - with_invoice)._compute_tax_ids()
|
|
|
|
@api.depends(
|
|
"transfer_move_ids.line_ids.amount_residual",
|
|
"transfer_move_ids.line_ids.amount_residual_currency",
|
|
)
|
|
def _compute_amount_residual(self):
|
|
"""Compute the amount residual for expenses paid by employee with invoices."""
|
|
applicable_expenses = self.filtered(lambda x: x.transfer_move_ids)
|
|
for rec in applicable_expenses:
|
|
if not rec.currency_id or rec.currency_id == rec.company_currency_id:
|
|
residual_field = "amount_residual"
|
|
else:
|
|
residual_field = "amount_residual_currency"
|
|
payment_term_lines = rec.transfer_move_ids.sudo().line_ids.filtered(
|
|
lambda x: x.account_type in ("asset_receivable", "liability_payable")
|
|
)
|
|
rec.amount_residual = -sum(payment_term_lines.mapped(residual_field))
|
|
return super(HrExpense, self - applicable_expenses)._compute_amount_residual()
|