mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-24 04:52:01 +02:00
19.0 vanilla
This commit is contained in:
parent
a1137a1456
commit
e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions
|
|
@ -1,15 +1,16 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_employee
|
||||
from . import hr_employee_public
|
||||
from . import account_move
|
||||
from . import account_move_line
|
||||
from . import account_payment
|
||||
from . import account_tax
|
||||
from . import hr_department
|
||||
from . import hr_expense
|
||||
from . import ir_attachment
|
||||
from . import product_product
|
||||
from . import product_template
|
||||
from . import res_config_settings
|
||||
from . import account_journal_dashboard
|
||||
from . import res_company
|
||||
from . import analytic
|
||||
from . import ir_actions_report
|
||||
|
|
|
|||
|
|
@ -1,77 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
from odoo.tools.misc import formatLang
|
||||
from odoo.addons.account.models.account_journal_dashboard import group_by_journal
|
||||
|
||||
|
||||
class AccountJournal(models.Model):
|
||||
_inherit = "account.journal"
|
||||
|
||||
def _get_expenses_to_pay_query(self):
|
||||
"""
|
||||
Returns a tuple containing as it's first element the SQL query used to
|
||||
gather the expenses in reported state data, and the arguments
|
||||
dictionary to use to run it as it's second.
|
||||
"""
|
||||
query = """SELECT total_amount as amount_total, currency_id AS currency
|
||||
FROM hr_expense_sheet
|
||||
WHERE state IN ('approve', 'post')
|
||||
and journal_id = %(journal_id)s"""
|
||||
return (query, {'journal_id': self.id})
|
||||
|
||||
def get_journal_dashboard_datas(self):
|
||||
res = super(AccountJournal, self).get_journal_dashboard_datas()
|
||||
#add the number and sum of expenses to pay to the json defining the accounting dashboard data
|
||||
(query, query_args) = self._get_expenses_to_pay_query()
|
||||
self.env.cr.execute(query, query_args)
|
||||
query_results_to_pay = self.env.cr.dictfetchall()
|
||||
(number_to_pay, sum_to_pay) = self._count_results_and_sum_amounts(query_results_to_pay, self.company_id.currency_id)
|
||||
res['number_expenses_to_pay'] = number_to_pay
|
||||
res['sum_expenses_to_pay'] = formatLang(self.env, sum_to_pay or 0.0, currency_obj=self.currency_id or self.company_id.currency_id)
|
||||
return res
|
||||
|
||||
def _prepare_expense_sheet_data_domain(self):
|
||||
return [
|
||||
('state', '=', 'post'),
|
||||
('journal_id', 'in', self.ids),
|
||||
]
|
||||
|
||||
def _get_expense_to_pay_query(self):
|
||||
return self.env['hr.expense.sheet']._where_calc(self._prepare_expense_sheet_data_domain())
|
||||
|
||||
def _fill_sale_purchase_dashboard_data(self, dashboard_data):
|
||||
super(AccountJournal, self)._fill_sale_purchase_dashboard_data(dashboard_data)
|
||||
sale_purchase_journals = self.filtered(lambda journal: journal.type in ('sale', 'purchase'))
|
||||
if not sale_purchase_journals:
|
||||
return
|
||||
field_list = [
|
||||
"hr_expense_sheet.journal_id",
|
||||
"hr_expense_sheet.total_amount AS amount_total",
|
||||
"hr_expense_sheet.currency_id AS currency",
|
||||
]
|
||||
query, params = sale_purchase_journals._get_expense_to_pay_query().select(*field_list)
|
||||
self.env.cr.execute(query, params)
|
||||
query_results_to_pay = group_by_journal(self.env.cr.dictfetchall())
|
||||
curr_cache = {}
|
||||
for journal in sale_purchase_journals:
|
||||
currency = journal.currency_id or journal.company_id.currency_id
|
||||
(number_expenses_to_pay, sum_expenses_to_pay) = self._count_results_and_sum_amounts(query_results_to_pay[journal.id], currency, curr_cache=curr_cache)
|
||||
dashboard_data[journal.id].update({
|
||||
'number_expenses_to_pay': number_expenses_to_pay,
|
||||
'sum_expenses_to_pay': currency.format(sum_expenses_to_pay),
|
||||
})
|
||||
|
||||
def open_expenses_action(self):
|
||||
action = self.env['ir.actions.act_window']._for_xml_id('hr_expense.action_hr_expense_sheet_all_all')
|
||||
action['context'] = {
|
||||
'search_default_approved': 1,
|
||||
'search_default_to_post': 1,
|
||||
'search_default_journal_id': self.id,
|
||||
'default_journal_id': self.id,
|
||||
}
|
||||
action['view_mode'] = 'tree,form'
|
||||
action['views'] = [(k,v) for k,v in action['views'] if v in ['tree', 'form']]
|
||||
action['domain'] = self._prepare_expense_sheet_data_domain()
|
||||
return action
|
||||
|
|
@ -1,101 +1,112 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
from markupsafe import Markup
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo import Command, models, fields, api, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools.misc import frozendict
|
||||
|
||||
|
||||
class AccountMove(models.Model):
|
||||
_inherit = "account.move"
|
||||
|
||||
expense_sheet_id = fields.One2many('hr.expense.sheet', 'account_move_id')
|
||||
expense_ids = fields.One2many(comodel_name='hr.expense', inverse_name='account_move_id')
|
||||
nb_expenses = fields.Integer(compute='_compute_nb_expenses', string='Number of Expenses', compute_sudo=True)
|
||||
|
||||
@api.depends('partner_id', 'expense_sheet_id', 'company_id')
|
||||
def _compute_nb_expenses(self):
|
||||
for move in self:
|
||||
move.nb_expenses = len(move.expense_ids)
|
||||
|
||||
@api.depends('partner_id', 'expense_ids', 'company_id')
|
||||
def _compute_commercial_partner_id(self):
|
||||
own_expense_moves = self.filtered(lambda move: move.sudo().expense_sheet_id.payment_mode == 'own_account')
|
||||
own_expense_moves = self.filtered(lambda move: any(expense.payment_mode == 'own_account' for expense in move.sudo().expense_ids))
|
||||
for move in own_expense_moves:
|
||||
if move.expense_sheet_id.payment_mode == 'own_account':
|
||||
move.commercial_partner_id = (
|
||||
move.partner_id.commercial_partner_id
|
||||
if move.partner_id.commercial_partner_id != move.company_id.partner_id
|
||||
else move.partner_id
|
||||
)
|
||||
move.commercial_partner_id = (
|
||||
move.partner_id.commercial_partner_id
|
||||
if move.partner_id.commercial_partner_id != move.company_id.partner_id
|
||||
else move.partner_id
|
||||
)
|
||||
super(AccountMove, self - own_expense_moves)._compute_commercial_partner_id()
|
||||
|
||||
def action_open_expense_report(self):
|
||||
@api.constrains('expense_ids')
|
||||
def _check_expense_ids(self):
|
||||
for move in self:
|
||||
expense_payment_modes = move.expense_ids.mapped('payment_mode')
|
||||
if 'company_account' in expense_payment_modes and len(move.expense_ids) > 1 :
|
||||
raise ValidationError(_("Each expense paid by the company must have a distinct and dedicated journal entry."))
|
||||
|
||||
def action_open_expense(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': self.expense_sheet_id.name,
|
||||
linked_expenses = self.expense_ids
|
||||
if len(linked_expenses) > 1:
|
||||
return {
|
||||
'name': _("Expenses"),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'list,form',
|
||||
'views': [(False, 'list'), (False, 'form')],
|
||||
'res_model': 'hr.expense',
|
||||
'domain': [('id', 'in', linked_expenses.ids)],
|
||||
}
|
||||
return {
|
||||
'name': linked_expenses.name,
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'hr.expense.sheet',
|
||||
'res_id': self.expense_sheet_id.id
|
||||
'views': [(False, 'form')],
|
||||
'res_model': 'hr.expense',
|
||||
'res_id': linked_expenses.id
|
||||
}
|
||||
|
||||
# Expenses can be written on journal other than purchase, hence don't include them in the constraint check
|
||||
def _check_journal_move_type(self):
|
||||
return super(AccountMove, self.filtered(lambda x: not x.expense_sheet_id))._check_journal_move_type()
|
||||
return super(AccountMove, self.filtered(lambda x: not x.expense_ids))._check_journal_move_type()
|
||||
|
||||
def _creation_message(self):
|
||||
if self.expense_sheet_id:
|
||||
return _("Expense entry Created")
|
||||
if self.expense_ids:
|
||||
if len(self.expense_ids) == 1:
|
||||
return _("Journal entry created from this expense: %(link)s", link=self.expense_ids._get_html_link())
|
||||
links = self.expense_ids[0]._get_html_link()
|
||||
for additional_expense in self.expense_ids[1:]: # ', ' Destroys Markup, and each part here is safe
|
||||
links += ', ' + additional_expense._get_html_link()
|
||||
return _("Journal entry created from these expenses: %(links)s", links=links)
|
||||
return super()._creation_message()
|
||||
|
||||
@api.depends('expense_sheet_id.payment_mode')
|
||||
def _compute_payment_state(self):
|
||||
company_paid = self.filtered(lambda m: m.expense_sheet_id.payment_mode == 'company_account')
|
||||
for move in company_paid:
|
||||
move.payment_state = 'paid'
|
||||
super(AccountMove, self - company_paid)._compute_payment_state()
|
||||
|
||||
@api.depends('expense_sheet_id')
|
||||
@api.depends('expense_ids')
|
||||
def _compute_needed_terms(self):
|
||||
# EXTENDS account
|
||||
# We want to set the account destination based on the 'payment_mode'.
|
||||
super()._compute_needed_terms()
|
||||
for move in self:
|
||||
if move.expense_sheet_id and move.expense_sheet_id.payment_mode == 'company_account':
|
||||
if move.expense_ids and 'company_account' in move.expense_ids.mapped('payment_mode'):
|
||||
term_lines = move.line_ids.filtered(lambda l: l.display_type != 'payment_term')
|
||||
move.needed_terms = {
|
||||
frozendict(
|
||||
{
|
||||
"move_id": move.id,
|
||||
"date_maturity": move.expense_sheet_id.accounting_date
|
||||
or fields.Date.context_today(move.expense_sheet_id),
|
||||
"date_maturity": fields.Date.context_today(move.expense_ids),
|
||||
}
|
||||
): {
|
||||
"balance": -sum(term_lines.mapped("balance")),
|
||||
"amount_currency": -sum(term_lines.mapped("amount_currency")),
|
||||
"name": "",
|
||||
"account_id": move.expense_sheet_id.expense_line_ids[0]._get_expense_account_destination(),
|
||||
"name": move.payment_reference or "",
|
||||
"account_id": move.expense_ids._get_expense_account_destination(),
|
||||
}
|
||||
}
|
||||
|
||||
def _reverse_moves(self, default_values_list=None, cancel=False):
|
||||
# Extends account
|
||||
# Reversing vendor bills that represent employee reimbursements should clear them from the expense sheet such that another
|
||||
# can be generated in place.
|
||||
own_account_moves = self.filtered(lambda move: move.expense_sheet_id.payment_mode == 'own_account')
|
||||
own_account_moves.expense_sheet_id.sudo().write({
|
||||
'state': 'approve',
|
||||
'account_move_id': False,
|
||||
})
|
||||
own_account_moves.ref = False # else, when restarting the expense flow we get duplicate issue on vendor.bill
|
||||
def _prepare_product_base_line_for_taxes_computation(self, product_line):
|
||||
# EXTENDS 'account'
|
||||
results = super()._prepare_product_base_line_for_taxes_computation(product_line)
|
||||
if product_line.expense_id.payment_mode == 'own_account':
|
||||
results['special_mode'] = 'total_included'
|
||||
return results
|
||||
|
||||
def _reverse_moves(self, default_values_list=None, cancel=False):
|
||||
# EXTENDS account
|
||||
self.filtered('expense_ids').write({'expense_ids': [Command.clear()]})
|
||||
return super()._reverse_moves(default_values_list=default_values_list, cancel=cancel)
|
||||
|
||||
def unlink(self):
|
||||
if self.expense_sheet_id:
|
||||
self.expense_sheet_id.write({
|
||||
'state': 'approve',
|
||||
'account_move_id': False, # cannot change to delete='set null' in stable
|
||||
})
|
||||
return super().unlink()
|
||||
|
||||
def button_draft(self):
|
||||
def button_cancel(self):
|
||||
# EXTENDS account
|
||||
employee_expense_sheets = self.expense_sheet_id.filtered(
|
||||
lambda expense_sheet: expense_sheet.payment_mode == 'own_account'
|
||||
)
|
||||
employee_expense_sheets.state = 'post'
|
||||
return super().button_draft()
|
||||
# We need to override this method to remove the link with the move, else we cannot reimburse them anymore.
|
||||
# And cancelling the move != cancelling the expense
|
||||
res = super().button_cancel()
|
||||
self.filtered('expense_ids').write({'expense_ids': [Command.clear()]})
|
||||
return res
|
||||
|
|
|
|||
|
|
@ -1,62 +1,42 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools.misc import frozendict
|
||||
from odoo.tools import SQL
|
||||
|
||||
|
||||
class AccountMoveLine(models.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
expense_id = fields.Many2one('hr.expense', string='Expense', copy=True)
|
||||
expense_id = fields.Many2one('hr.expense', string='Expense', copy=True, index='btree_not_null') # copy=True, else we don't know price is tax incl.
|
||||
|
||||
def _compute_partner_id(self):
|
||||
# EXTENDS account to ensure the partner is correctly set on all the move lines, preventing wrong bank accounts on payments
|
||||
expense_lines = self.filtered('move_id.expense_ids') # Can't use expense_id because the payment terms line may not have it set
|
||||
super(AccountMoveLine, self - expense_lines)._compute_partner_id()
|
||||
for line in expense_lines:
|
||||
line.partner_id = line.move_id.partner_id # The employee partner is correctly set on the move
|
||||
|
||||
@api.constrains('account_id', 'display_type')
|
||||
def _check_payable_receivable(self):
|
||||
super(AccountMoveLine, self.filtered(lambda line: line.move_id.expense_sheet_id.payment_mode != 'company_account'))._check_payable_receivable()
|
||||
|
||||
def reconcile(self):
|
||||
# OVERRIDE
|
||||
not_paid_expenses = self.move_id.expense_sheet_id.expense_line_ids.filtered(lambda expense: expense.state != 'done')
|
||||
res = super().reconcile()
|
||||
# Do not update expense or expense sheet states when reversing journal entries
|
||||
not_paid_expense_sheets = not_paid_expenses.sheet_id.filtered(lambda sheet: sheet.account_move_id.payment_state != 'reversed')
|
||||
paid_expenses = not_paid_expenses.filtered(lambda expense: expense.currency_id.is_zero(expense.amount_residual))
|
||||
paid_expenses.write({'state': 'done'})
|
||||
not_paid_expense_sheets.filtered(lambda sheet: all(expense.state == 'done' for expense in sheet.expense_line_ids)).set_to_paid()
|
||||
return res
|
||||
super(AccountMoveLine, self.filtered(lambda line: line.expense_id.payment_mode != 'company_account'))._check_payable_receivable()
|
||||
|
||||
def _get_attachment_domains(self):
|
||||
attachment_domains = super(AccountMoveLine, self)._get_attachment_domains()
|
||||
if self.expense_id:
|
||||
attachment_domains.append([('res_model', '=', 'hr.expense'), ('res_id', '=', self.expense_id.id)])
|
||||
attachment_domains.append([('res_model', '=', 'hr.expense'), ('res_id', 'in', self.expense_id.ids)])
|
||||
return attachment_domains
|
||||
|
||||
def _compute_tax_key(self):
|
||||
super()._compute_tax_key()
|
||||
for line in self:
|
||||
if line.expense_id:
|
||||
line.tax_key = frozendict(**line.tax_key, expense_id=line.expense_id.id)
|
||||
|
||||
def _compute_all_tax(self):
|
||||
expense_lines = self.filtered('expense_id')
|
||||
super(AccountMoveLine, expense_lines.with_context(force_price_include=True))._compute_all_tax()
|
||||
super(AccountMoveLine, self - expense_lines)._compute_all_tax()
|
||||
for line in expense_lines:
|
||||
for key in list(line.compute_all_tax.keys()):
|
||||
new_key = frozendict(**key, expense_id=line.expense_id.id)
|
||||
line.compute_all_tax[new_key] = line.compute_all_tax.pop(key)
|
||||
@api.model
|
||||
def _get_attachment_by_record(self, id_model2attachments, move_line):
|
||||
return (
|
||||
super()._get_attachment_by_record(id_model2attachments, move_line)
|
||||
or id_model2attachments.get(('hr.expense', move_line.expense_id.id))
|
||||
)
|
||||
|
||||
def _compute_totals(self):
|
||||
expenses = self.filtered('expense_id')
|
||||
super(AccountMoveLine, expenses.with_context(force_price_include=True))._compute_totals()
|
||||
super(AccountMoveLine, self - expenses)._compute_totals()
|
||||
|
||||
def _convert_to_tax_base_line_dict(self):
|
||||
result = super()._convert_to_tax_base_line_dict()
|
||||
if self.expense_id:
|
||||
result.setdefault('extra_context', {})
|
||||
result['extra_context']['force_price_include'] = True
|
||||
return result
|
||||
|
||||
def _get_extra_query_base_tax_line_mapping(self):
|
||||
return ' AND (base_line.expense_id IS NULL OR account_move_line.expense_id = base_line.expense_id)'
|
||||
def _get_extra_query_base_tax_line_mapping(self) -> SQL:
|
||||
return SQL(' AND (base_line.expense_id IS NULL OR account_move_line.expense_id = base_line.expense_id)')
|
||||
|
|
|
|||
|
|
@ -1,60 +1,51 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, _
|
||||
from odoo.exceptions import UserError
|
||||
from odoo import api, models, fields, _
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class AccountPayment(models.Model):
|
||||
_inherit = "account.payment"
|
||||
|
||||
def action_cancel(self):
|
||||
expense_ids = fields.One2many(related='move_id.expense_ids')
|
||||
|
||||
def _compute_outstanding_account_id(self):
|
||||
# EXTENDS account
|
||||
for payment in self:
|
||||
if payment.expense_sheet_id.payment_mode != 'own_account':
|
||||
continue
|
||||
payment.with_context(skip_account_move_synchronization=True).expense_sheet_id.write({
|
||||
'state': 'approve',
|
||||
'account_move_id': False,
|
||||
})
|
||||
expense_company_payments = self.filtered(lambda payment: payment.expense_ids.payment_mode == 'company_account')
|
||||
for payment in expense_company_payments:
|
||||
payment.outstanding_account_id = payment.expense_ids._get_expense_account_destination()
|
||||
super(AccountPayment, self - expense_company_payments)._compute_outstanding_account_id()
|
||||
|
||||
return super().action_cancel()
|
||||
def _compute_show_require_partner_bank(self):
|
||||
expense_payments = self.filtered(lambda pay: pay.move_id.expense_ids)
|
||||
super()._compute_show_require_partner_bank()
|
||||
expense_payments.require_partner_bank_account = False
|
||||
|
||||
def action_draft(self):
|
||||
employee_expense_sheets = self.reconciled_bill_ids.expense_sheet_id.filtered(
|
||||
lambda expense_sheet: expense_sheet.payment_mode == 'own_account'
|
||||
)
|
||||
employee_expense_sheets.state = 'post'
|
||||
return super().action_draft()
|
||||
def write(self, vals):
|
||||
trigger_fields = {
|
||||
'date', 'amount', 'payment_type', 'partner_type', 'payment_reference',
|
||||
'currency_id', 'partner_id', 'destination_account_id', 'partner_bank_id', 'journal_id'
|
||||
'ref', 'payment_method_line_id'
|
||||
}
|
||||
if self.expense_ids and any(field_name in trigger_fields for field_name in vals):
|
||||
raise UserError(_("You cannot do this modification since the payment is linked to an expense."))
|
||||
return super().write(vals)
|
||||
|
||||
def action_open_expense_report(self):
|
||||
def action_open_expense(self):
|
||||
self.ensure_one()
|
||||
return {
|
||||
'name': self.expense_sheet_id.name,
|
||||
'name': self.expense_ids.name,
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
'views': [(False, 'form')],
|
||||
'res_model': 'hr.expense.sheet',
|
||||
'res_id': self.expense_sheet_id.id
|
||||
'res_model': 'hr.expense',
|
||||
'res_id': self.expense_ids.id,
|
||||
}
|
||||
|
||||
def _synchronize_from_moves(self, changed_fields):
|
||||
# EXTENDS account
|
||||
if self.expense_sheet_id:
|
||||
# Constraints bypass when entry is linked to an expense.
|
||||
# Context is not enough, as we want to be able to delete
|
||||
# and update those entries later on.
|
||||
return
|
||||
return super()._synchronize_from_moves(changed_fields)
|
||||
|
||||
def _synchronize_to_moves(self, changed_fields):
|
||||
# EXTENDS account
|
||||
if self.expense_sheet_id:
|
||||
raise UserError(_("You cannot do this modification since the payment is linked to an expense report."))
|
||||
return super()._synchronize_to_moves(changed_fields)
|
||||
|
||||
def _creation_message(self):
|
||||
# EXTENDS mail
|
||||
self.ensure_one()
|
||||
if self.move_id.expense_sheet_id:
|
||||
return _("Payment created for: %s", self.move_id.expense_sheet_id._get_html_link())
|
||||
if self.move_id.expense_ids:
|
||||
return _("Payment created for: %s", self.move_id.expense_ids._get_html_link())
|
||||
return super()._creation_message()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountTax(models.Model):
|
||||
_inherit = "account.tax"
|
||||
|
||||
def _hook_compute_is_used(self, taxes_to_compute):
|
||||
# OVERRIDE in order to fetch taxes used in expenses
|
||||
|
||||
used_taxes = super()._hook_compute_is_used(taxes_to_compute)
|
||||
taxes_to_compute -= used_taxes
|
||||
|
||||
if taxes_to_compute:
|
||||
self.env['hr.expense'].flush_model(['tax_ids'])
|
||||
self.env.cr.execute("""
|
||||
SELECT id
|
||||
FROM account_tax
|
||||
WHERE EXISTS(
|
||||
SELECT 1
|
||||
FROM expense_tax AS exp
|
||||
WHERE tax_id IN %s
|
||||
AND account_tax.id = exp.tax_id
|
||||
)
|
||||
""", [tuple(taxes_to_compute)])
|
||||
|
||||
used_taxes.update([tax[0] for tax in self.env.cr.fetchall()])
|
||||
|
||||
return used_taxes
|
||||
|
||||
def _prepare_base_line_for_taxes_computation(self, record, **kwargs):
|
||||
# EXTENDS 'account'
|
||||
results = super()._prepare_base_line_for_taxes_computation(record, **kwargs)
|
||||
results['expense_id'] = self._get_base_line_field_value_from_record(record, 'expense_id', kwargs, self.env['hr.expense'])
|
||||
return results
|
||||
|
||||
def _prepare_tax_line_for_taxes_computation(self, record, **kwargs):
|
||||
# EXTENDS 'account'
|
||||
results = super()._prepare_tax_line_for_taxes_computation(record, **kwargs)
|
||||
results['expense_id'] = self._get_base_line_field_value_from_record(record, 'expense_id', kwargs, self.env['hr.expense'])
|
||||
return results
|
||||
|
||||
def _prepare_base_line_grouping_key(self, base_line):
|
||||
# EXTENDS 'account'
|
||||
results = super()._prepare_base_line_grouping_key(base_line)
|
||||
results['expense_id'] = base_line['expense_id'].id
|
||||
return results
|
||||
|
||||
def _prepare_tax_line_repartition_grouping_key(self, tax_line):
|
||||
# EXTENDS 'account'
|
||||
results = super()._prepare_tax_line_repartition_grouping_key(tax_line)
|
||||
results['expense_id'] = tax_line['expense_id'].id
|
||||
return results
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo.tools import SQL
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
|
|
@ -16,17 +17,29 @@ class AccountAnalyticApplicability(models.Model):
|
|||
ondelete={'expense': 'cascade'},
|
||||
)
|
||||
|
||||
@api.depends('business_domain')
|
||||
def _compute_display_account_prefix(self):
|
||||
super()._compute_display_account_prefix()
|
||||
for applicability in self.filtered(lambda rec: rec.business_domain == 'expense'):
|
||||
applicability.display_account_prefix = True
|
||||
|
||||
class AnalyticAccount(models.Model):
|
||||
|
||||
class AccountAnalyticAccount(models.Model):
|
||||
_inherit = 'account.analytic.account'
|
||||
|
||||
@api.ondelete(at_uninstall=False)
|
||||
def _unlink_except_account_in_analytic_distribution(self):
|
||||
self.env.cr.execute("""
|
||||
SELECT id FROM hr_expense
|
||||
WHERE analytic_distribution::jsonb ?| array[%s]
|
||||
LIMIT 1
|
||||
""", ([str(id) for id in self.ids],))
|
||||
self.env.cr.execute(
|
||||
SQL(
|
||||
r"""
|
||||
SELECT id FROM hr_expense
|
||||
WHERE %s && %s
|
||||
LIMIT 1
|
||||
""",
|
||||
[str(account_id) for account_id in self.ids],
|
||||
self.env['hr.expense']._query_analytic_accounts(),
|
||||
)
|
||||
)
|
||||
expense_ids = self.env.cr.fetchall()
|
||||
if expense_ids:
|
||||
raise UserError(_("You cannot delete an analytic account that is used in an expense."))
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
|
@ -7,10 +6,11 @@ from odoo import fields, models
|
|||
class HrDepartment(models.Model):
|
||||
_inherit = 'hr.department'
|
||||
|
||||
def _compute_expense_sheets_to_approve(self):
|
||||
expense_sheet_data = self.env['hr.expense.sheet']._read_group([('department_id', 'in', self.ids), ('state', '=', 'submit')], ['department_id'], ['department_id'])
|
||||
result = dict((data['department_id'][0], data['department_id_count']) for data in expense_sheet_data)
|
||||
for department in self:
|
||||
department.expense_sheets_to_approve_count = result.get(department.id, 0)
|
||||
expenses_to_approve_count = fields.Integer(compute='_compute_expenses_to_approve_count', string='Expenses to Approve')
|
||||
|
||||
def _compute_expenses_to_approve_count(self):
|
||||
expense_data = self.env['hr.expense']._read_group([('department_id', 'in', self.ids), ('state', '=', 'submitted')], ['department_id'], ['__count'])
|
||||
result = {department.id: count for department, count in expense_data}
|
||||
for department in self:
|
||||
department.expenses_to_approve_count = result.get(department.id, 0)
|
||||
|
||||
expense_sheets_to_approve_count = fields.Integer(compute='_compute_expense_sheets_to_approve', string='Expenses Reports to Approve')
|
||||
|
|
|
|||
|
|
@ -1,36 +1,62 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models, api
|
||||
from odoo.fields import Domain
|
||||
|
||||
|
||||
class Employee(models.Model):
|
||||
class HrEmployee(models.Model):
|
||||
_inherit = 'hr.employee'
|
||||
|
||||
def _group_hr_expense_user_domain(self):
|
||||
# We return the domain only if the group exists for the following reason:
|
||||
# When a group is created (at module installation), the `res.users` form view is
|
||||
# automatically modifiedto add application accesses. When modifiying the view, it
|
||||
# automatically modified to add application accesses. When modifying the view, it
|
||||
# reads the related field `expense_manager_id` of `res.users` and retrieve its domain.
|
||||
# This is a problem because the `group_hr_expense_user` record has already been created but
|
||||
# This is a problem because the `group_hr_expense_team_approver` record has already been created but
|
||||
# not its associated `ir.model.data` which makes `self.env.ref(...)` fail.
|
||||
group = self.env.ref('hr_expense.group_hr_expense_team_approver', raise_if_not_found=False)
|
||||
return [('groups_id', 'in', group.ids)] if group else []
|
||||
return [
|
||||
'|', ('id', 'parent_of', self.ids), ('all_group_ids', 'in', group.ids)
|
||||
] if group else [('id', 'parent_of', self.ids)]
|
||||
|
||||
expense_manager_id = fields.Many2one(
|
||||
'res.users', string='Expense',
|
||||
domain=_group_hr_expense_user_domain,
|
||||
comodel_name='res.users',
|
||||
string='Expense Approver',
|
||||
compute='_compute_expense_manager', store=True, readonly=False,
|
||||
domain=_group_hr_expense_user_domain,
|
||||
help='Select the user responsible for approving "Expenses" of this employee.\n'
|
||||
'If empty, the approval is done by an Administrator or Approver (determined in settings/users).')
|
||||
'If empty, the approval is done by an Administrator or Approver (determined in settings/users).',
|
||||
)
|
||||
|
||||
filter_for_expense = fields.Boolean(store=False, search='_search_filter_for_expense', groups="hr.group_hr_user")
|
||||
|
||||
def _search_filter_for_expense(self, operator, value):
|
||||
if operator != 'in':
|
||||
return NotImplemented
|
||||
|
||||
domain = Domain.FALSE # Nothing accepted by domain, by default
|
||||
user = self.env.user
|
||||
employee = user.employee_id
|
||||
if user.has_groups('hr_expense.group_hr_expense_user'):
|
||||
domain = Domain('company_id', '=', False) | Domain('company_id', 'child_of', self.env.company.root_id.id) # Then, domain accepts everything
|
||||
elif user.has_groups('hr_expense.group_hr_expense_team_approver') and user.employee_ids:
|
||||
domain = (
|
||||
Domain('department_id.manager_id', '=', employee.id)
|
||||
| Domain('parent_id', '=', employee.id)
|
||||
| Domain('id', '=', employee.id)
|
||||
| Domain('expense_manager_id', '=', user.id)
|
||||
) & Domain('company_id', 'in', [False, employee.company_id.id])
|
||||
elif user.employee_id:
|
||||
domain = Domain('id', '=', employee.id) & Domain('company_id', 'in', [False, employee.company_id.id])
|
||||
return domain
|
||||
|
||||
@api.depends('parent_id')
|
||||
def _compute_expense_manager(self):
|
||||
for employee in self:
|
||||
previous_manager = employee._origin.parent_id.user_id
|
||||
manager = employee.parent_id.user_id
|
||||
if manager and manager.has_group('hr_expense.group_hr_expense_user') and (employee.expense_manager_id == previous_manager or not employee.expense_manager_id):
|
||||
employee.expense_manager_id = manager
|
||||
new_manager = employee.parent_id.user_id
|
||||
if new_manager and (employee.expense_manager_id == previous_manager or not employee.expense_manager_id):
|
||||
employee.expense_manager_id = new_manager
|
||||
elif not employee.expense_manager_id:
|
||||
employee.expense_manager_id = False
|
||||
|
||||
|
|
@ -38,17 +64,7 @@ class Employee(models.Model):
|
|||
return super()._get_user_m2o_to_empty_on_archived_employees() + ['expense_manager_id']
|
||||
|
||||
|
||||
class EmployeePublic(models.Model):
|
||||
class HrEmployeePublic(models.Model):
|
||||
_inherit = 'hr.employee.public'
|
||||
|
||||
expense_manager_id = fields.Many2one('res.users', readonly=True)
|
||||
|
||||
|
||||
class User(models.Model):
|
||||
_inherit = ['res.users']
|
||||
|
||||
expense_manager_id = fields.Many2one(related='employee_id.expense_manager_id', readonly=False)
|
||||
|
||||
@property
|
||||
def SELF_READABLE_FIELDS(self):
|
||||
return super().SELF_READABLE_FIELDS + ['expense_manager_id']
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
from odoo import fields, models
|
||||
from odoo.fields import Domain
|
||||
|
||||
|
||||
class HrEmployee(models.Model):
|
||||
_inherit = 'hr.employee.public'
|
||||
|
||||
filter_for_expense = fields.Boolean(store=False, search='_search_filter_for_expense', groups="hr.group_hr_user")
|
||||
|
||||
def _search_filter_for_expense(self, operator, value):
|
||||
if operator != 'in':
|
||||
return NotImplemented
|
||||
|
||||
domain = Domain.FALSE # Nothing accepted by domain, by default
|
||||
user = self.env.user
|
||||
employee = user.employee_id
|
||||
if user.has_groups('hr_expense.group_hr_expense_user') or user.has_groups('account.group_account_user'):
|
||||
domain = Domain('company_id', '=', False) | Domain('company_id', 'child_of', self.env.company.root_id.id) # Then, domain accepts everything
|
||||
elif user.has_groups('hr_expense.group_hr_expense_team_approver') and user.employee_ids:
|
||||
domain = (
|
||||
Domain('department_id.manager_id', '=', employee.id)
|
||||
| Domain('parent_id', '=', employee.id)
|
||||
| Domain('id', '=', employee.id)
|
||||
| Domain('expense_manager_id', '=', user.id)
|
||||
) & Domain('company_id', 'in', [False, employee.company_id.id])
|
||||
elif user.employee_id:
|
||||
domain = Domain('id', '=', employee.id) & Domain('company_id', 'in', [False, employee.company_id.id])
|
||||
return domain
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,54 @@
|
|||
import io
|
||||
from odoo import models, _
|
||||
from odoo.tools import pdf
|
||||
from odoo.tools.pdf import OdooPdfFileReader, OdooPdfFileWriter, PdfReadError, DependencyError
|
||||
|
||||
|
||||
class IrActionsReport(models.Model):
|
||||
_inherit = 'ir.actions.report'
|
||||
|
||||
def _render_qweb_pdf_prepare_streams(self, report_ref, data, res_ids=None):
|
||||
# OVERRIDE
|
||||
res = super()._render_qweb_pdf_prepare_streams(report_ref, data, res_ids)
|
||||
if not res_ids:
|
||||
return res
|
||||
report = self._get_report(report_ref)
|
||||
if report.report_name == 'hr_expense.report_expense':
|
||||
for expense in self.env['hr.expense'].browse(res_ids):
|
||||
# Will contains the expense
|
||||
stream_list = []
|
||||
stream = res[expense.id]['stream']
|
||||
stream_list.append(stream)
|
||||
attachments = self.env['ir.attachment'].search([('res_id', 'in', expense.ids), ('res_model', '=', 'hr.expense')])
|
||||
expense_report = OdooPdfFileReader(stream, strict=False)
|
||||
output_pdf = OdooPdfFileWriter()
|
||||
output_pdf.appendPagesFromReader(expense_report)
|
||||
for attachment in self._prepare_local_attachments(attachments):
|
||||
if attachment.mimetype == 'application/pdf':
|
||||
attachment_stream = pdf.to_pdf_stream(attachment)
|
||||
else:
|
||||
# In case the attachment is not a pdf we will create a new PDF from the template "report_expense_img"
|
||||
# And then append to the stream. By doing so, the attachment is put on a new page with the name of the expense
|
||||
# associated to the attachment
|
||||
data['attachment'] = attachment
|
||||
attachment_prep_stream = self._render_qweb_pdf_prepare_streams('hr_expense.report_expense_img', data, res_ids=res_ids)
|
||||
attachment_stream = attachment_prep_stream[expense.id]['stream']
|
||||
attachment_reader = OdooPdfFileReader(attachment_stream, strict=False)
|
||||
try:
|
||||
output_pdf.appendPagesFromReader(attachment_reader)
|
||||
except (PdfReadError, DependencyError) as e:
|
||||
expense._message_log(body=_(
|
||||
"The attachment (%(attachment_name)s) has not been added to the report due to the following error: '%(error)s'",
|
||||
attachment_name=attachment.name,
|
||||
error=e
|
||||
))
|
||||
continue
|
||||
stream_list.append(attachment_stream)
|
||||
|
||||
new_pdf_stream = io.BytesIO()
|
||||
output_pdf.write(new_pdf_stream)
|
||||
res[expense.id]['stream'] = new_pdf_stream
|
||||
|
||||
for stream in stream_list:
|
||||
stream.close()
|
||||
return res
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
from odoo import models, api
|
||||
|
||||
|
||||
class IrAttachment(models.Model):
|
||||
_inherit = 'ir.attachment'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
attachments = super().create(vals_list)
|
||||
if self.env.context.get('sync_attachment', True):
|
||||
expenses_attachments = attachments.filtered(lambda att: att.res_model == 'hr.expense')
|
||||
if expenses_attachments:
|
||||
expenses = self.env['hr.expense'].browse(expenses_attachments.mapped('res_id'))
|
||||
for expense in expenses.filtered('sheet_id'):
|
||||
checksums = set(expense.sheet_id.attachment_ids.mapped('checksum'))
|
||||
for attachment in expense.attachment_ids.filtered(lambda att: att.checksum not in checksums):
|
||||
attachment.copy({
|
||||
'res_model': 'hr.expense.sheet',
|
||||
'res_id': expense.sheet_id.id,
|
||||
})
|
||||
return attachments
|
||||
|
||||
def unlink(self):
|
||||
if self.env.context.get('sync_attachment', True):
|
||||
attachments_to_unlink = self.env['ir.attachment']
|
||||
expenses_attachments = self.filtered(lambda att: att.res_model == 'hr.expense')
|
||||
if expenses_attachments:
|
||||
expenses = self.env['hr.expense'].browse(expenses_attachments.mapped('res_id'))
|
||||
for expense in expenses.exists().filtered('sheet_id'):
|
||||
checksums = set(expense.attachment_ids.mapped('checksum'))
|
||||
attachments_to_unlink += expense.sheet_id.attachment_ids.filtered(lambda att: att.checksum in checksums)
|
||||
sheets_attachments = self.filtered(lambda att: att.res_model == 'hr.expense.sheet')
|
||||
if sheets_attachments:
|
||||
sheets = self.env['hr.expense.sheet'].browse(sheets_attachments.mapped('res_id'))
|
||||
for sheet in sheets.exists():
|
||||
checksums = set((sheet.attachment_ids & sheets_attachments).mapped('checksum'))
|
||||
attachments_to_unlink += sheet.expense_line_ids.attachment_ids.filtered(lambda att: att.checksum in checksums)
|
||||
super(IrAttachment, attachments_to_unlink).unlink()
|
||||
return super().unlink()
|
||||
|
|
@ -10,19 +10,46 @@ class ProductProduct(models.Model):
|
|||
def _compute_standard_price_update_warning(self):
|
||||
undone_expenses = self.env['hr.expense']._read_group(
|
||||
domain=[('state', '=', 'draft'), ('product_id', 'in', self.ids)],
|
||||
fields=['unit_amount:array_agg'],
|
||||
groupby=['product_id'],
|
||||
groupby=['price_unit'],
|
||||
)
|
||||
mapp = {row['product_id'][0]: row['unit_amount'] for row in undone_expenses}
|
||||
# The following list is composed of all the price_units of expenses that use this product and should NOT trigger a warning.
|
||||
# Those are the amounts of any undone expense using this product and 0.0 which is the default unit_amount.
|
||||
unit_amounts_no_warning = [self.env.company.currency_id.round(row[0]) for row in undone_expenses]
|
||||
for product in self:
|
||||
product.standard_price_update_warning = False
|
||||
if product._origin.id in mapp:
|
||||
# The following list is composed of all the unit_amounts of expenses that use this product and should NOT trigger a warning.
|
||||
# Those are the amounts of any undone expense using this product and 0.0 which is the default unit_amount.
|
||||
unit_amounts_no_warning = {float(unit_amount) for unit_amount in mapp[product._origin.id]}
|
||||
if undone_expenses:
|
||||
rounded_price = self.env.company.currency_id.round(product.standard_price)
|
||||
if rounded_price and (len(unit_amounts_no_warning) > 1 or (len(unit_amounts_no_warning) == 1 and rounded_price not in unit_amounts_no_warning)):
|
||||
product.standard_price_update_warning = _(
|
||||
"There are unsubmitted expenses linked to this category. Updating the category cost will change expense amounts. "
|
||||
"Make sure it is what you want to do."
|
||||
)
|
||||
|
||||
def write(self, vals):
|
||||
result = super().write(vals)
|
||||
if 'standard_price' in vals:
|
||||
expenses_sudo = self.env['hr.expense'].sudo().search([
|
||||
('company_id', '=', self.env.company.id),
|
||||
('product_id', 'in', self.ids),
|
||||
('state', '=', 'draft'),
|
||||
])
|
||||
for expense_sudo in expenses_sudo:
|
||||
expense_product_sudo = expense_sudo.product_id
|
||||
product_has_cost = (
|
||||
expense_product_sudo
|
||||
and not expense_sudo.company_currency_id.is_zero(expense_product_sudo.standard_price)
|
||||
)
|
||||
expense_vals = {
|
||||
'product_has_cost': product_has_cost,
|
||||
}
|
||||
if product_has_cost:
|
||||
expense_vals.update({
|
||||
'price_unit': expense_product_sudo.standard_price,
|
||||
})
|
||||
else:
|
||||
expense_vals.update({
|
||||
'quantity': 1,
|
||||
'price_unit': expense_sudo.total_amount
|
||||
})
|
||||
expense_sudo.write(expense_vals)
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ class ProductTemplate(models.Model):
|
|||
result['supplier_taxes_id'] = False
|
||||
return result
|
||||
|
||||
can_be_expensed = fields.Boolean(string="Can be Expensed", compute='_compute_can_be_expensed',
|
||||
can_be_expensed = fields.Boolean(string="Expenses", compute='_compute_can_be_expensed',
|
||||
store=True, readonly=False, help="Specify whether the product can be selected in an expense.")
|
||||
|
||||
def _auto_init(self):
|
||||
|
|
@ -30,6 +30,12 @@ class ProductTemplate(models.Model):
|
|||
)
|
||||
return super()._auto_init()
|
||||
|
||||
@api.depends('type')
|
||||
@api.depends('type', 'purchase_ok')
|
||||
def _compute_can_be_expensed(self):
|
||||
self.filtered(lambda p: p.type not in ['consu', 'service']).update({'can_be_expensed': False})
|
||||
self.filtered(lambda p: p.type not in ['consu', 'service'] or not p.purchase_ok).update({'can_be_expensed': False})
|
||||
|
||||
@api.depends('can_be_expensed')
|
||||
def _compute_purchase_ok(self):
|
||||
for record in self:
|
||||
if record.can_be_expensed:
|
||||
record.purchase_ok = True
|
||||
|
|
|
|||
|
|
@ -10,13 +10,12 @@ class ResCompany(models.Model):
|
|||
"account.journal",
|
||||
string="Default Expense Journal",
|
||||
check_company=True,
|
||||
domain="[('type', '=', 'purchase'), ('company_id', '=', company_id)]",
|
||||
domain="[('type', '=', 'purchase')]",
|
||||
help="The company's default journal used when an employee expense is created.",
|
||||
)
|
||||
company_expense_journal_id = fields.Many2one(
|
||||
"account.journal",
|
||||
string="Default Company Expense Journal",
|
||||
company_expense_allowed_payment_method_line_ids = fields.Many2many(
|
||||
"account.payment.method.line",
|
||||
string="Payment methods available for expenses paid by company",
|
||||
check_company=True,
|
||||
domain="[('type', 'in', ['cash', 'bank']), ('company_id', '=', company_id)]",
|
||||
help="The company's default journal used when a company expense is created.",
|
||||
domain="[('payment_type', '=', 'outbound'), ('journal_id', '!=', False), ('journal_id.active', '=', True)]",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,38 +1,73 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
expense_alias_prefix = fields.Char(
|
||||
hr_expense_alias_prefix = fields.Char(
|
||||
'Default Alias Name for Expenses',
|
||||
compute='_compute_expense_alias_prefix',
|
||||
compute='_compute_hr_expense_alias_prefix',
|
||||
store=True,
|
||||
readonly=False)
|
||||
use_mailgateway = fields.Boolean(string='Let your employees record expenses by email',
|
||||
config_parameter='hr_expense.use_mailgateway')
|
||||
|
||||
hr_expense_alias_domain_id = fields.Many2one(
|
||||
comodel_name='mail.alias.domain',
|
||||
compute='_compute_hr_expense_alias_domain_id',
|
||||
inverse='_inverse_hr_expense_alias_domain_id',
|
||||
readonly=False)
|
||||
hr_expense_use_mailgateway = fields.Boolean(string='Let your employees record expenses by email',
|
||||
config_parameter='hr_expense.use_mailgateway')
|
||||
module_hr_payroll_expense = fields.Boolean(string='Reimburse Expenses in Payslip')
|
||||
module_hr_expense_extract = fields.Boolean(string='Send bills to OCR to generate expenses')
|
||||
expense_journal_id = fields.Many2one('account.journal', related='company_id.expense_journal_id', readonly=False)
|
||||
company_expense_journal_id = fields.Many2one('account.journal', related='company_id.company_expense_journal_id', readonly=False)
|
||||
module_hr_expense_stripe = fields.Boolean(string='Link your stripe issuing account to manage company credit cards for your employees through Odoo')
|
||||
expense_journal_id = fields.Many2one('account.journal', related='company_id.expense_journal_id', readonly=False, check_company=True, domain="[('type', '=', 'purchase')]")
|
||||
company_expense_allowed_payment_method_line_ids = fields.Many2many(
|
||||
comodel_name='account.payment.method.line',
|
||||
check_company=True,
|
||||
related='company_id.company_expense_allowed_payment_method_line_ids',
|
||||
readonly=False,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def get_values(self):
|
||||
res = super(ResConfigSettings, self).get_values()
|
||||
expense_alias = self.env.ref('hr_expense.mail_alias_expense', raise_if_not_found=False)
|
||||
res.update(
|
||||
expense_alias_prefix=self.env.ref('hr_expense.mail_alias_expense').alias_name,
|
||||
hr_expense_alias_prefix=expense_alias.alias_name if expense_alias else False,
|
||||
hr_expense_alias_domain_id=expense_alias.alias_domain_id if expense_alias else False,
|
||||
)
|
||||
return res
|
||||
|
||||
def set_values(self):
|
||||
super().set_values()
|
||||
alias = self.env.ref('hr_expense.mail_alias_expense', raise_if_not_found=False)
|
||||
if alias and alias.alias_name != self.expense_alias_prefix:
|
||||
alias.alias_name = self.expense_alias_prefix
|
||||
expense_alias = self.env.ref('hr_expense.mail_alias_expense', raise_if_not_found=False)
|
||||
if not expense_alias and self.hr_expense_alias_prefix:
|
||||
# create data again
|
||||
alias = self.env['mail.alias'].sudo().create({
|
||||
'alias_contact': 'employees',
|
||||
'alias_domain_id': self.env.company.alias_domain_id.id,
|
||||
'alias_model_id': self.env['ir.model']._get_id('hr.expense'),
|
||||
'alias_name': self.hr_expense_alias_prefix,
|
||||
})
|
||||
self.env['ir.model.data'].sudo().create({
|
||||
'name': 'mail_alias_expense',
|
||||
'module': 'hr_expense',
|
||||
'model': 'mail.alias',
|
||||
'noupdate': True,
|
||||
'res_id': alias.id,
|
||||
})
|
||||
elif expense_alias and expense_alias.alias_name != self.hr_expense_alias_prefix:
|
||||
expense_alias.alias_name = self.hr_expense_alias_prefix
|
||||
|
||||
@api.depends('use_mailgateway')
|
||||
def _compute_expense_alias_prefix(self):
|
||||
self.filtered(lambda w: not w.use_mailgateway).update({'expense_alias_prefix': False})
|
||||
@api.depends('hr_expense_use_mailgateway')
|
||||
def _compute_hr_expense_alias_prefix(self):
|
||||
self.filtered(lambda w: not w.hr_expense_use_mailgateway).hr_expense_alias_prefix = False
|
||||
|
||||
@api.depends('hr_expense_use_mailgateway')
|
||||
def _compute_hr_expense_alias_domain_id(self):
|
||||
self.filtered(lambda w: not w.hr_expense_use_mailgateway).hr_expense_alias_domain_id = False
|
||||
|
||||
def _inverse_hr_expense_alias_domain_id(self):
|
||||
expense_alias = self.env.ref('hr_expense.mail_alias_expense', raise_if_not_found=False)
|
||||
for record in self:
|
||||
if expense_alias and expense_alias.alias_domain_id != record.hr_expense_alias_domain_id:
|
||||
expense_alias.alias_domain_id = record.hr_expense_alias_domain_id
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue