diff --git a/hr_expense_invoice/README.rst b/hr_expense_invoice/README.rst new file mode 100644 index 00000000000..b6c57ccbc1d --- /dev/null +++ b/hr_expense_invoice/README.rst @@ -0,0 +1,94 @@ +================================ +Supplier invoices on HR expenses +================================ + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr-lightgray.png?logo=github + :target: https://github.com/OCA/hr/tree/12.0/hr_expense_invoice + :alt: OCA/hr +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/hr-12-0/hr-12-0-hr_expense_invoice + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/116/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module should be used when a supplier invoice is paid by an employee. It +allows to set a supplier invoice for each expense line, adding the +corresponding journal items to transfer the debt to the employee. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +* Create an expense sheet. +* Add an expense line to sheet with an invoice_id selected or create one new. +* Process expense sheet. +* On paying expense sheet, you are reconciling supplier invoice too. + +Known issues / Roadmap +====================== + +* Multiple payment terms for a supplier invoice are not handled correctly. +* Partial reconcile supplier invoices are also not correctly handled. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Tecnativa + +Contributors +~~~~~~~~~~~~ + +* `Tecnativa `_: + + * Pedro M. Baeza + * Vicent Cubells + +* Kitti Upariphutthiphong + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/hr `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_expense_invoice/__init__.py b/hr_expense_invoice/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/hr_expense_invoice/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/hr_expense_invoice/__manifest__.py b/hr_expense_invoice/__manifest__.py new file mode 100644 index 00000000000..48e5df6d476 --- /dev/null +++ b/hr_expense_invoice/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2015 Pedro M. Baeza +# Copyright 2017 Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Supplier invoices on HR expenses', + 'version': '12.0.1.0.0', + 'category': 'Human Resources', + 'author': 'Tecnativa, ' + 'Odoo Community Association (OCA)', + 'license': 'AGPL-3', + 'website': 'https://github.com/OCA/hr', + 'depends': [ + 'hr_expense', + ], + 'data': [ + 'views/hr_expense_views.xml', + ], + 'installable': True, +} diff --git a/hr_expense_invoice/i18n/cs_CZ.po b/hr_expense_invoice/i18n/cs_CZ.po new file mode 100644 index 00000000000..38ec83c703d --- /dev/null +++ b/hr_expense_invoice/i18n/cs_CZ.po @@ -0,0 +1,46 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +# Translators: +# Lukáš Spurný , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-23 03:45+0000\n" +"PO-Revision-Date: 2018-02-23 03:45+0000\n" +"Last-Translator: Lukáš Spurný , 2018\n" +"Language-Team: Czech (Czech Republic) (https://www.transifex.com/oca/" +"teams/23907/cs_CZ/)\n" +"Language: cs_CZ\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "" +"Cannot reconcile supplier invoice payable with generated line. Please check " +"amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" +"Nelze sladit dodavatelskou fakturu se zaplacenou generovanou linkou. " +"Zkontrolujte částky a zjistěte, zda již faktura již byla přidána nebo " +"zaplacena. Faktura: %s" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "Výdaje" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Zpráva o výdajích" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "Faktura" diff --git a/hr_expense_invoice/i18n/de.po b/hr_expense_invoice/i18n/de.po new file mode 100644 index 00000000000..a7542629d09 --- /dev/null +++ b/hr_expense_invoice/i18n/de.po @@ -0,0 +1,41 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2018-12-18 13:07+0000\n" +"Last-Translator: Maria Sparenberg \n" +"Language-Team: none\n" +"Language: de\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 3.3\n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "Cannot reconcile supplier invoice payable with generated line. Please check amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" +"Der zu zahlende Restbetrag der Rechnung kann nicht mit der erstellten Zeile " +"abgeglichen werden. Bitte prüfen Sie die Mengen und ob die Rechnung schon " +"hinzugefügt oder bezahlt wurde. Rechnung: %s" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "Spesen" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Spesen-Bericht" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "Rechnung" diff --git a/hr_expense_invoice/i18n/es.po b/hr_expense_invoice/i18n/es.po new file mode 100644 index 00000000000..dafe690117c --- /dev/null +++ b/hr_expense_invoice/i18n/es.po @@ -0,0 +1,43 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-09-27 16:34+0000\n" +"PO-Revision-Date: 2017-09-27 16:34+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "" +"Cannot reconcile supplier invoice payable with generated line. Please check " +"amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" +"No se puede conciliar el saldo a pagar de la factura con la línea generada. " +"Compruebe por favor las cantidades y vea si la factura ya está añadida " +"previamente o pagada. Factura: %s" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "Gasto" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Informe de gastos" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "Factura" diff --git a/hr_expense_invoice/i18n/fr.po b/hr_expense_invoice/i18n/fr.po new file mode 100644 index 00000000000..879019d9ee0 --- /dev/null +++ b/hr_expense_invoice/i18n/fr.po @@ -0,0 +1,45 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +# Translators: +# guillaume bauer , 2017 +# Alexandre Fayolle , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-23 03:45+0000\n" +"PO-Revision-Date: 2018-02-23 03:45+0000\n" +"Last-Translator: Alexandre Fayolle , 2018\n" +"Language-Team: French (https://www.transifex.com/oca/teams/23907/fr/)\n" +"Language: fr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "" +"Cannot reconcile supplier invoice payable with generated line. Please check " +"amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" +"Impossible de réconcilier la facture fournisseur avec la ligne générée. " +"Vérifiez le montant et si la facture est déjà ajoutée ou payée. Facture: %s" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "Note de frais" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Rapport de dépenses" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "Facture" diff --git a/hr_expense_invoice/i18n/hr.po b/hr_expense_invoice/i18n/hr.po new file mode 100644 index 00000000000..28e1a256676 --- /dev/null +++ b/hr_expense_invoice/i18n/hr.po @@ -0,0 +1,45 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +# Translators: +# Bole , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-02-23 03:45+0000\n" +"PO-Revision-Date: 2018-02-23 03:45+0000\n" +"Last-Translator: Bole , 2017\n" +"Language-Team: Croatian (https://www.transifex.com/oca/teams/23907/hr/)\n" +"Language: hr\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "" +"Cannot reconcile supplier invoice payable with generated line. Please check " +"amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" +"Nije moguće zatvaranje računa dobavljača sa generiranom stavkom. Molimo " +"provjerite iznose i plaćanja: %s" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "Trošak" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Izvještaj troškova" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "Račun" diff --git a/hr_expense_invoice/i18n/hr_expense_invoice.pot b/hr_expense_invoice/i18n/hr_expense_invoice.pot new file mode 100644 index 00000000000..db4996e63e1 --- /dev/null +++ b/hr_expense_invoice/i18n/hr_expense_invoice.pot @@ -0,0 +1,36 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "Cannot reconcile supplier invoice payable with generated line. Please check amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "" + diff --git a/hr_expense_invoice/i18n/nl_NL.po b/hr_expense_invoice/i18n/nl_NL.po new file mode 100644 index 00000000000..3c5188dd318 --- /dev/null +++ b/hr_expense_invoice/i18n/nl_NL.po @@ -0,0 +1,43 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +# Translators: +# Peter Hageman , 2017 +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-11-28 01:43+0000\n" +"PO-Revision-Date: 2017-11-28 01:43+0000\n" +"Last-Translator: Peter Hageman , 2017\n" +"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/" +"teams/23907/nl_NL/)\n" +"Language: nl_NL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "" +"Cannot reconcile supplier invoice payable with generated line. Please check " +"amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Onkostennota" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "" diff --git a/hr_expense_invoice/i18n/pt.po b/hr_expense_invoice/i18n/pt.po new file mode 100644 index 00000000000..0bd60eba19a --- /dev/null +++ b/hr_expense_invoice/i18n/pt.po @@ -0,0 +1,43 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_invoice +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 10.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2018-10-10 12:08+0000\n" +"Last-Translator: Pedro Castro Silva \n" +"Language-Team: none\n" +"Language: pt\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 3.1.1\n" + +#. module: hr_expense_invoice +#: code:addons/hr_expense_invoice/models/hr_expense_sheet.py:40 +#, python-format +msgid "" +"Cannot reconcile supplier invoice payable with generated line. Please check " +"amounts and see if the invoice is already added or paid. Invoice: %s" +msgstr "" +"Não foi possível reconciliar a fatura a pagar ao fornecedor com a linha " +"gerada. Por favor, verifique os montantes e veja se a fatura já está " +"adicionada ou paga. Fatura: %s" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense +msgid "Expense" +msgstr "Despesa" + +#. module: hr_expense_invoice +#: model:ir.model,name:hr_expense_invoice.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Relatório de Despesas" + +#. module: hr_expense_invoice +#: model:ir.model.fields,field_description:hr_expense_invoice.field_hr_expense_invoice_id +msgid "Invoice" +msgstr "Fatura" diff --git a/hr_expense_invoice/models/__init__.py b/hr_expense_invoice/models/__init__.py new file mode 100644 index 00000000000..945a814c7ef --- /dev/null +++ b/hr_expense_invoice/models/__init__.py @@ -0,0 +1,2 @@ +from . import hr_expense +from . import hr_expense_sheet diff --git a/hr_expense_invoice/models/hr_expense.py b/hr_expense_invoice/models/hr_expense.py new file mode 100644 index 00000000000..a830fcd135a --- /dev/null +++ b/hr_expense_invoice/models/hr_expense.py @@ -0,0 +1,61 @@ +# Copyright 2015 Pedro M. Baeza +# Copyright 2017 Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class HrExpense(models.Model): + _inherit = 'hr.expense' + + invoice_id = fields.Many2one( + comodel_name='account.invoice', + string='Vendor Bill', + domain="[('type', '=', 'in_invoice'), ('state', '=', 'open')]", + oldname='invoice', + ) + + @api.onchange('invoice_id') + def onchange_invoice_id(self): + self.date = self.invoice_id.date_invoice + self.name = self.invoice_id.number + self.reference = self.invoice_id.number or self.invoice_id.reference + self.analytic_account_id = False + self.unit_amount = self.invoice_id.residual + self.quantity = 1.0 + self.total_amount = self.unit_amount + self.description = self.invoice_id.reference + + def _check_vals(self, vals): + if vals.get('invoice_id'): + # Rewrite values because readonly fields are not stored + invoice = self.env['account.invoice'].browse(vals['invoice_id']) + vals['date'] = invoice.date_invoice + vals['analytic_account_id'] = False + vals['unit_amount'] = invoice.residual + vals['total_amount'] = invoice.residual + vals['quantity'] = 1.0 + + @api.model + def create(self, vals): + self._check_vals(vals) + return super(HrExpense, self).create(vals) + + @api.multi + def write(self, vals): + self._check_vals(vals) + return super(HrExpense, self).write(vals) + + @api.multi + def _get_account_move_line_values(self): + move_line_values_by_expense = super()._get_account_move_line_values() + for expense_id, move_lines in move_line_values_by_expense.items(): + expense = self.browse(expense_id) + if not expense.invoice_id: + return move_line_values_by_expense + for move_line in move_lines: + if move_line['debit']: + move_line['partner_id'] = \ + expense.invoice_id.partner_id.commercial_partner_id.id + move_line['account_id'] = expense.invoice_id.account_id.id + return move_line_values_by_expense diff --git a/hr_expense_invoice/models/hr_expense_sheet.py b/hr_expense_invoice/models/hr_expense_sheet.py new file mode 100644 index 00000000000..a0646d58848 --- /dev/null +++ b/hr_expense_invoice/models/hr_expense_sheet.py @@ -0,0 +1,45 @@ +# Copyright 2015 Pedro M. Baeza +# Copyright 2017 Vicent Cubells +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, models +from odoo.exceptions import UserError +from odoo.tools import float_compare + + +class HrExpenseSheet(models.Model): + _inherit = "hr.expense.sheet" + + @api.multi + def action_sheet_move_create(self): + DecimalPrecision = self.env['decimal.precision'] + precision = DecimalPrecision.precision_get('Product Price') + expense_line_ids = \ + self.mapped('expense_line_ids').filtered('invoice_id') + res = super(HrExpenseSheet, self).action_sheet_move_create() + move_lines = self.env['account.move'].search( + [('ref', 'in', self.mapped('name'))], + ).mapped('line_ids') + for line in expense_line_ids: + partner = line.invoice_id.partner_id.commercial_partner_id + c_move_lines = move_lines.filtered( + lambda x: + x.partner_id == partner and + x.debit == line.invoice_id.residual and + not x.reconciled + ) + if len(c_move_lines) > 1: + c_move_lines = c_move_lines[0] + residual = line.invoice_id.residual + c_move_lines |= line.invoice_id.move_id.line_ids.filtered( + lambda x: + x.account_id == line.invoice_id.account_id and + float_compare(x.credit, residual, precision) == 0) + if len(c_move_lines) != 2: + raise UserError( + _('Cannot reconcile supplier invoice payable with ' + 'generated line. Please check amounts and see ' + 'if the invoice is already added or paid. ' + 'Invoice: %s') % line.invoice_id.number) + c_move_lines.reconcile() + return res diff --git a/hr_expense_invoice/readme/CONTRIBUTORS.rst b/hr_expense_invoice/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..036f2b53f9d --- /dev/null +++ b/hr_expense_invoice/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* `Tecnativa `_: + + * Pedro M. Baeza + * Vicent Cubells + +* Kitti Upariphutthiphong diff --git a/hr_expense_invoice/readme/DESCRIPTION.rst b/hr_expense_invoice/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..15395cc77fb --- /dev/null +++ b/hr_expense_invoice/readme/DESCRIPTION.rst @@ -0,0 +1,3 @@ +This module should be used when a supplier invoice is paid by an employee. It +allows to set a supplier invoice for each expense line, adding the +corresponding journal items to transfer the debt to the employee. diff --git a/hr_expense_invoice/readme/ROADMAP.rst b/hr_expense_invoice/readme/ROADMAP.rst new file mode 100644 index 00000000000..bf2442348c8 --- /dev/null +++ b/hr_expense_invoice/readme/ROADMAP.rst @@ -0,0 +1,2 @@ +* Multiple payment terms for a supplier invoice are not handled correctly. +* Partial reconcile supplier invoices are also not correctly handled. diff --git a/hr_expense_invoice/readme/USAGE.rst b/hr_expense_invoice/readme/USAGE.rst new file mode 100644 index 00000000000..894cbb1a443 --- /dev/null +++ b/hr_expense_invoice/readme/USAGE.rst @@ -0,0 +1,4 @@ +* Create an expense sheet. +* Add an expense line to sheet with an invoice_id selected or create one new. +* Process expense sheet. +* On paying expense sheet, you are reconciling supplier invoice too. diff --git a/hr_expense_invoice/static/description/icon.png b/hr_expense_invoice/static/description/icon.png new file mode 100644 index 00000000000..8e5a7f402d9 Binary files /dev/null and b/hr_expense_invoice/static/description/icon.png differ diff --git a/hr_expense_invoice/static/description/index.html b/hr_expense_invoice/static/description/index.html new file mode 100644 index 00000000000..ecad36c2fe9 --- /dev/null +++ b/hr_expense_invoice/static/description/index.html @@ -0,0 +1,444 @@ + + + + + + +Supplier invoices on HR expenses + + + +
+

Supplier invoices on HR expenses

+ + +

Beta License: AGPL-3 OCA/hr Translate me on Weblate Try me on Runbot

+

This module should be used when a supplier invoice is paid by an employee. It +allows to set a supplier invoice for each expense line, adding the +corresponding journal items to transfer the debt to the employee.

+

Table of contents

+ +
+

Usage

+
    +
  • Create an expense sheet.
  • +
  • Add an expense line to sheet with an invoice_id selected or create one new.
  • +
  • Process expense sheet.
  • +
  • On paying expense sheet, you are reconciling supplier invoice too.
  • +
+
+
+

Known issues / Roadmap

+
    +
  • Multiple payment terms for a supplier invoice are not handled correctly.
  • +
  • Partial reconcile supplier invoices are also not correctly handled.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Tecnativa
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/hr project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/hr_expense_invoice/tests/__init__.py b/hr_expense_invoice/tests/__init__.py new file mode 100644 index 00000000000..571c2e78e8e --- /dev/null +++ b/hr_expense_invoice/tests/__init__.py @@ -0,0 +1 @@ +from . import test_hr_expense_invoice diff --git a/hr_expense_invoice/tests/test_hr_expense_invoice.py b/hr_expense_invoice/tests/test_hr_expense_invoice.py new file mode 100644 index 00000000000..be5deeb43e8 --- /dev/null +++ b/hr_expense_invoice/tests/test_hr_expense_invoice.py @@ -0,0 +1,131 @@ +# Copyright 2017 Vicent Cubells - +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo.tests import common + + +class TestHrExpenseInvoice(common.SavepointCase): + @classmethod + def setUpClass(cls): + super(TestHrExpenseInvoice, cls).setUpClass() + + partner = cls.env['res.partner'].create({ + 'name': 'Test partner', + 'supplier': True, + }) + employee_home = cls.env['res.partner'].create({ + 'name': 'Employee Home Address', + }) + receivable = cls.env.ref('account.data_account_type_receivable').id + expenses = cls.env.ref('account.data_account_type_expenses').id + invoice_account = cls.env['account.account'].search( + [('user_type_id', '=', receivable)], limit=1).id + invoice_line_account = cls.env['account.account'].search( + [('user_type_id', '=', expenses)], limit=1).id + product = cls.env['product.product'].create({ + 'name': 'Product test', + 'type': 'service', + }) + employee = cls.env['hr.employee'].create({ + 'name': 'Employee A', + 'address_home_id': employee_home.id, + }) + cls.invoice = cls.env['account.invoice'].create({ + 'partner_id': partner.id, + 'account_id': invoice_account, + 'type': 'in_invoice', + 'invoice_line_ids': [(0, 0, { + 'product_id': product.id, + 'quantity': 1.0, + 'price_unit': 100.0, + 'name': 'product that cost 100', + 'account_id': invoice_line_account, + })] + }) + cls.invoice2 = cls.invoice.copy({ + 'invoice_line_ids': [(0, 0, { + 'product_id': product.id, + 'quantity': 1.0, + 'price_unit': 100.0, + 'name': 'product that cost 100', + 'account_id': invoice_line_account, + })] + }) + cls.sheet = cls.env['hr.expense.sheet'].create({ + 'name': 'Test expense sheet', + 'employee_id': employee.id, + }) + cls.expense = cls.env['hr.expense'].create({ + 'name': 'Expense test', + 'employee_id': employee.id, + 'product_id': product.id, + 'unit_amount': 50.0, + }) + cls.expense2 = cls.expense.copy() + + def test_0_hr_test_no_invoice(self): + # There is not expense lines in sheet + self.assertEqual(len(self.sheet.expense_line_ids), 0) + # We add an expense + self.sheet.expense_line_ids = [(6, 0, [self.expense.id])] + self.assertEqual(len(self.sheet.expense_line_ids), 1) + self.assertAlmostEqual(self.expense.total_amount, 50.0) + # We approve sheet, no invoice + self.sheet.approve_expense_sheets() + self.assertEqual(self.sheet.state, 'approve') + self.assertFalse(self.sheet.account_move_id) + # We post journal entries + self.sheet.action_sheet_move_create() + self.assertEqual(self.sheet.state, 'post') + self.assertTrue(self.sheet.account_move_id) + + def test_1_hr_test_invoice(self): + # There is not expense lines in sheet + self.assertEqual(len(self.sheet.expense_line_ids), 0) + # We add an expense + self.sheet.expense_line_ids = [(6, 0, [self.expense.id])] + self.assertEqual(len(self.sheet.expense_line_ids), 1) + # We add invoice to expense + self.invoice.action_invoice_open() + self.expense.invoice_id = self.invoice.id + self.expense.onchange_invoice_id() + self.assertAlmostEqual(self.expense.total_amount, 100.0) + # We approve sheet + self.sheet.approve_expense_sheets() + self.assertEqual(self.sheet.state, 'approve') + self.assertFalse(self.sheet.account_move_id) + self.assertEqual(self.invoice.state, 'open') + # We post journal entries + self.sheet.action_sheet_move_create() + self.assertEqual(self.sheet.state, 'post') + self.assertTrue(self.sheet.account_move_id) + # Invoice is now paid + self.assertEqual(self.invoice.state, 'paid') + + def test_2_hr_test_multi_invoices(self): + # There is not expense lines in sheet + self.assertEqual(len(self.sheet.expense_line_ids), 0) + # We add 2 expenses + self.sheet.expense_line_ids = [(6, 0, [self.expense.id, + self.expense2.id])] + self.assertEqual(len(self.sheet.expense_line_ids), 2) + # We add invoices to expenses + self.invoice.action_invoice_open() + self.invoice2.action_invoice_open() + self.expense.invoice_id = self.invoice.id + self.expense2.invoice_id = self.invoice2.id + self.expense.onchange_invoice_id() + self.expense2.onchange_invoice_id() + self.assertAlmostEqual(self.expense.total_amount, 100.0) + self.assertAlmostEqual(self.expense2.total_amount, 100.0) + # We approve sheet + self.sheet.approve_expense_sheets() + self.assertEqual(self.sheet.state, 'approve') + self.assertFalse(self.sheet.account_move_id) + self.assertEqual(self.invoice.state, 'open') + # We post journal entries + self.sheet.action_sheet_move_create() + self.assertEqual(self.sheet.state, 'post') + self.assertTrue(self.sheet.account_move_id) + # Invoice is now paid + self.assertEqual(self.invoice.state, 'paid') diff --git a/hr_expense_invoice/views/hr_expense_views.xml b/hr_expense_invoice/views/hr_expense_views.xml new file mode 100644 index 00000000000..0e93c0e8bad --- /dev/null +++ b/hr_expense_invoice/views/hr_expense_views.xml @@ -0,0 +1,33 @@ + + + + + hr.expense.form + hr.expense + + + + + + + {'readonly': [('invoice_id', '!=', False)]} + + + {'readonly': [('invoice_id', '!=', False)]} + + + {'readonly': [('invoice_id', '!=', False)]} + + + {'readonly': [('invoice_id', '!=', False)]} + + + {'readonly': [('invoice_id', '!=', False)]} + + + {'readonly': [('invoice_id', '!=', False)]} + + + + +