diff --git a/hr_expense_petty_cash/README.rst b/hr_expense_petty_cash/README.rst new file mode 100644 index 000000000..01c634e49 --- /dev/null +++ b/hr_expense_petty_cash/README.rst @@ -0,0 +1,134 @@ +========== +Petty Cash +========== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:b0ac8e37253106681f342556f00a62710fa4e139ec09806af5b472e7466e9aa5 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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--expense-lightgray.png?logo=github + :target: https://github.com/OCA/hr-expense/tree/16.0/hr_expense_petty_cash + :alt: OCA/hr-expense +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/hr-expense-16-0/hr-expense-16-0-hr_expense_petty_cash + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/hr-expense&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module work about expense that paid by petty cash. + +General Process: + +#. Create Petty Cash Holder +#. Transfer cash to Petty Cash Holder (by using Vendor Bill) +#. You can create expense paid by petty cash and select petty cash holder. +#. Then balance of petty cash holder less than amount of expense, you must transfer cash to petty cash holder before Submit Report to Manager. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +**Create a Petty Cash Account** + +#. Go to Invoicing > Accounting > Petty Cash +#. Create a new petty cash account +#. Type = Bank and Cash or Account type that is asset + +Note: + +* You will need the "Show Full Accounting Features" to see accounting data + +Usage +===== + +**Create a Petty Cash Holder** + +#. Go to Invoicing > Accounting > Petty Cash +#. Create a new petty cash holder +#. Select Petty Cash Account and Journal (optional) + +**Add Balance for Petty Cash Holder** + +#. Go to Invoicing > Vendor > Bill +#. Create a new vendor bill +#. Select Vendor (Petty Cash Holder) +#. Check Petty Cash will auto line with Unit Price = Max Limit - Balance + +**Create an Expense paid by Petty Cash** + +#. Go to Expenses > My Expenses +#. Create a new expense +#. Select Paid by = Petty Cash and Select Petty Cash Holder + +**Create an Expense Report paid by Petty Cash** + +#. Go to Expenses > My Expense Reports +#. Create a new expense report +#. Select or Create expenses Paid by Petty Cash and same Petty Cash Holder + +Note : Bill and Expense will default journal from petty cash holder, +if you configure journal in petty cash holder. + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Ecosoft + +Contributors +~~~~~~~~~~~~ + +* `Ecosoft `__: + + * Pimolnat Suntian + * Saran Lim. + +* `Trinityroots `__: + + * Santi Techatoo + +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-expense `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_expense_petty_cash/__init__.py b/hr_expense_petty_cash/__init__.py new file mode 100644 index 000000000..4fb530fd1 --- /dev/null +++ b/hr_expense_petty_cash/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/hr_expense_petty_cash/__manifest__.py b/hr_expense_petty_cash/__manifest__.py new file mode 100644 index 000000000..6eb928aae --- /dev/null +++ b/hr_expense_petty_cash/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Petty Cash", + "version": "16.0.1.0.0", + "category": "Human Resources", + "author": "Ecosoft, Odoo Community Association (OCA)", + "license": "AGPL-3", + "website": "https://github.com/OCA/hr-expense", + "depends": ["hr_expense"], + "data": [ + "security/ir.model.access.csv", + "security/petty_cash_security.xml", + "views/account_move_views.xml", + "views/hr_expense_sheet_views.xml", + "views/hr_expense_views.xml", + "views/petty_cash_views.xml", + ], + "installable": True, +} diff --git a/hr_expense_petty_cash/i18n/es.po b/hr_expense_petty_cash/i18n/es.po new file mode 100644 index 000000000..e7a9b68c3 --- /dev/null +++ b/hr_expense_petty_cash/i18n/es.po @@ -0,0 +1,233 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_petty_cash +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-10-30 19:36+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: none\n" +"Language: es\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 4.17\n" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "%s is not a petty cash holder" +msgstr "%s no es titular de una caja menor" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Account on invoice line should be {name}." +msgstr "La cuenta en la línea de factura debe ser {name}." + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__active +msgid "Active" +msgstr "Activo" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_form +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Archived" +msgstr "Archivado" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__petty_cash_balance +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_list +msgid "Balance" +msgstr "Saldo" + +#. module: hr_expense_petty_cash +#: model_terms:ir.actions.act_window,help:hr_expense_petty_cash.action_petty_cash +msgid "Create a new petty cash holder" +msgstr "Crear un nuevo titular de caja para gastos menores" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__display_name +msgid "Display Name" +msgstr "Mostrar Nombre" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_hr_expense +msgid "Expense" +msgstr "Gasto" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Informe de Gastos" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Group By" +msgstr "Agrupar Por" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__id +msgid "ID" +msgstr "ID(identificación)" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__journal_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Journal" +msgstr "Dario" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_account_move +msgid "Journal Entry" +msgstr "Entrada Diaria" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash____last_update +msgid "Last Modified on" +msgstr "Última Modificación el" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__write_uid +msgid "Last Updated by" +msgstr "Última Actualización por" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__write_date +msgid "Last Updated on" +msgstr "Última Actualización el" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_list +msgid "Limit" +msgstr "Límite" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__petty_cash_limit +msgid "Max Limit" +msgstr "Límite Máximo" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense_sheet.py:0 +#, python-format +msgid "" +"Not enough money in petty cash holder.\n" +"You are requesting {amount_company}{symbol}, but the balance is {balance}{symbol}." +msgstr "" +"No hay suficiente dinero en la caja para gastos menores.\n" +"Está solicitando {amount_company}{symbol}, pero el saldo es " +"{balance}{symbol}." + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense__payment_mode +msgid "Paid By" +msgstr "Pagado Por" + +#. module: hr_expense_petty_cash +#: model:ir.actions.act_window,name:hr_expense_petty_cash.action_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_bank_statement_line__is_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_move__is_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_payment__is_petty_cash +#: model:ir.model.fields.selection,name:hr_expense_petty_cash.selection__hr_expense__payment_mode__petty_cash +#: model:ir.ui.menu,name:hr_expense_petty_cash.menu_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_sheet_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_form +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash" +msgstr "Caja Menor" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__account_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash Account" +msgstr "Cuenta de Caja Menor" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__partner_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_sheet_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash Holder" +msgstr "Tenedor de Caja Menor" + +#. module: hr_expense_petty_cash +#: model:ir.model.constraint,message:hr_expense_petty_cash.constraint_petty_cash_partner_uniq +msgid "Petty Cash Holder must be unique!" +msgstr "¡El titular de la caja Menor debe ser único!" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "" +"Petty Cash balance is {balance} {symbol}.\n" +"Max amount to add is {max_amount} {symbol}." +msgstr "" +"El saldo de caja menor es {balance} {symbol}.\n" +"La cantidad máxima a añadir es {max_amount} {symbol}." + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense__petty_cash_id +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense_sheet__petty_cash_id +msgid "Petty cash holder" +msgstr "Tenedor de caja menor" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Please check Petty Cash on {name}." +msgstr "Por favor, compruebe la caja menor en {name}." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Please select petty cash holder" +msgstr "Por favor, seleccione el titular de la caja menor" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You can not create report without category." +msgstr "No puede crear un informe sin categoría." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#: code:addons/hr_expense_petty_cash/models/hr_expense_sheet.py:0 +#, python-format +msgid "You cannot create report from many petty cash holders." +msgstr "" +"No se puede crear un informe a partir de muchos titulares de caja pequeña." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You cannot report expenses for different employees in the same report." +msgstr "" +"No se pueden declarar gastos de diferentes empleados en el mismo informe." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You cannot report twice the same line!" +msgstr "¡No se puede informar dos veces de la misma línea!" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "{name} with petty cash checked must contain only 1 line." +msgstr "{name} con caja chica comprobada debe contener sólo 1 línea." diff --git a/hr_expense_petty_cash/i18n/hr_expense_petty_cash.pot b/hr_expense_petty_cash/i18n/hr_expense_petty_cash.pot new file mode 100644 index 000000000..b3e98b0ad --- /dev/null +++ b/hr_expense_petty_cash/i18n/hr_expense_petty_cash.pot @@ -0,0 +1,223 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_petty_cash +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.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_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "%s is not a petty cash holder" +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Account on invoice line should be {name}." +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__active +msgid "Active" +msgstr "" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_form +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Archived" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__petty_cash_balance +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_list +msgid "Balance" +msgstr "" + +#. module: hr_expense_petty_cash +#: model_terms:ir.actions.act_window,help:hr_expense_petty_cash.action_petty_cash +msgid "Create a new petty cash holder" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__create_uid +msgid "Created by" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__create_date +msgid "Created on" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__display_name +msgid "Display Name" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_hr_expense +msgid "Expense" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_hr_expense_sheet +msgid "Expense Report" +msgstr "" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Group By" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__id +msgid "ID" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__journal_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Journal" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_account_move +msgid "Journal Entry" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash____last_update +msgid "Last Modified on" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__write_date +msgid "Last Updated on" +msgstr "" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_list +msgid "Limit" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__petty_cash_limit +msgid "Max Limit" +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense_sheet.py:0 +#, python-format +msgid "" +"Not enough money in petty cash holder.\n" +"You are requesting {amount_company}{symbol}, but the balance is {balance}{symbol}." +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense__payment_mode +msgid "Paid By" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.actions.act_window,name:hr_expense_petty_cash.action_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_bank_statement_line__is_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_move__is_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_payment__is_petty_cash +#: model:ir.model.fields.selection,name:hr_expense_petty_cash.selection__hr_expense__payment_mode__petty_cash +#: model:ir.ui.menu,name:hr_expense_petty_cash.menu_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_sheet_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_form +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__account_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash Account" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__partner_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_sheet_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash Holder" +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.constraint,message:hr_expense_petty_cash.constraint_petty_cash_partner_uniq +msgid "Petty Cash Holder must be unique!" +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "" +"Petty Cash balance is {balance} {symbol}.\n" +"Max amount to add is {max_amount} {symbol}." +msgstr "" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense__petty_cash_id +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense_sheet__petty_cash_id +msgid "Petty cash holder" +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Please check Petty Cash on {name}." +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Please select petty cash holder" +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You can not create report without category." +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#: code:addons/hr_expense_petty_cash/models/hr_expense_sheet.py:0 +#, python-format +msgid "You cannot create report from many petty cash holders." +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You cannot report expenses for different employees in the same report." +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You cannot report twice the same line!" +msgstr "" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "{name} with petty cash checked must contain only 1 line." +msgstr "" diff --git a/hr_expense_petty_cash/i18n/it.po b/hr_expense_petty_cash/i18n/it.po new file mode 100644 index 000000000..bb79751f1 --- /dev/null +++ b/hr_expense_petty_cash/i18n/it.po @@ -0,0 +1,232 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * hr_expense_petty_cash +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2023-07-12 15:12+0000\n" +"Last-Translator: Francesco Foresti \n" +"Language-Team: none\n" +"Language: it\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 4.17\n" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "%s is not a petty cash holder" +msgstr "%s non è un titolare piccola cassa" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Account on invoice line should be {name}." +msgstr "Il conto sulla riga fattura dovrebbe essere {name}." + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__active +msgid "Active" +msgstr "Attivo" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_form +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Archived" +msgstr "In archivio" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__petty_cash_balance +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_list +msgid "Balance" +msgstr "Saldo" + +#. module: hr_expense_petty_cash +#: model_terms:ir.actions.act_window,help:hr_expense_petty_cash.action_petty_cash +msgid "Create a new petty cash holder" +msgstr "Crea un nuovo titolare piccola cassa" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__create_uid +msgid "Created by" +msgstr "Creato da" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__create_date +msgid "Created on" +msgstr "Creato il" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__display_name +msgid "Display Name" +msgstr "Nome visualizzato" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_hr_expense +msgid "Expense" +msgstr "Spesa" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_hr_expense_sheet +msgid "Expense Report" +msgstr "Nota spese" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Group By" +msgstr "Raggruppa per" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__id +msgid "ID" +msgstr "ID" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__journal_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Journal" +msgstr "Registro" + +#. module: hr_expense_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_account_move +msgid "Journal Entry" +msgstr "Registrazione contabile" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash____last_update +msgid "Last Modified on" +msgstr "Ultima modifica il" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__write_uid +msgid "Last Updated by" +msgstr "Ultimo aggiornamento di" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__write_date +msgid "Last Updated on" +msgstr "Ultimo aggiornamento il" + +#. module: hr_expense_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_list +msgid "Limit" +msgstr "Limite" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__petty_cash_limit +msgid "Max Limit" +msgstr "Limite massimo" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense_sheet.py:0 +#, python-format +msgid "" +"Not enough money in petty cash holder.\n" +"You are requesting {amount_company}{symbol}, but the balance is {balance}" +"{symbol}." +msgstr "" +"Denaro insufficiente nella piccola cassa del titolare.\n" +"Si richiede {amount_company}{symbol}, ma il saldo è {balance}{symbol}." + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense__payment_mode +msgid "Paid By" +msgstr "Pagato da" + +#. module: hr_expense_petty_cash +#: model:ir.actions.act_window,name:hr_expense_petty_cash.action_petty_cash +#: model:ir.model,name:hr_expense_petty_cash.model_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_bank_statement_line__is_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_move__is_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_account_payment__is_petty_cash +#: model:ir.model.fields.selection,name:hr_expense_petty_cash.selection__hr_expense__payment_mode__petty_cash +#: model:ir.ui.menu,name:hr_expense_petty_cash.menu_petty_cash +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_sheet_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_form +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash" +msgstr "Piccola cassa" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__account_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash Account" +msgstr "Conto piccola cassa" + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_petty_cash__partner_id +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_sheet_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.hr_expense_view_search_inherit +#: model_terms:ir.ui.view,arch_db:hr_expense_petty_cash.view_petty_cash_search +msgid "Petty Cash Holder" +msgstr "Titolare piccola cassa" + +#. module: hr_expense_petty_cash +#: model:ir.model.constraint,message:hr_expense_petty_cash.constraint_petty_cash_partner_uniq +msgid "Petty Cash Holder must be unique!" +msgstr "Il titolare piccola cassa deve essere univoco!" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "" +"Petty Cash balance is {balance} {symbol}.\n" +"Max amount to add is {max_amount} {symbol}." +msgstr "" +"Il saldo dlela piccola cassa è {balance} {symbol}.\n" +"Il valora massimo da aggiungere è {max_amount} {symbol}." + +#. module: hr_expense_petty_cash +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense__petty_cash_id +#: model:ir.model.fields,field_description:hr_expense_petty_cash.field_hr_expense_sheet__petty_cash_id +msgid "Petty cash holder" +msgstr "Titolare piccola cassa" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Please check Petty Cash on {name}." +msgstr "Verificare la piccola cassa su {name}." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "Please select petty cash holder" +msgstr "Selezionare il titolare piccola cassa" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You can not create report without category." +msgstr "Non si puà generare il resoconto senza prodotto." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#: code:addons/hr_expense_petty_cash/models/hr_expense_sheet.py:0 +#, python-format +msgid "You cannot create report from many petty cash holders." +msgstr "Non si può creare un resoconto da più titolari piccola cassa." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You cannot report expenses for different employees in the same report." +msgstr "" +"Non si può fare la nota spese per diversi dipendenti nello stesso resoconto." + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/hr_expense.py:0 +#, python-format +msgid "You cannot report twice the same line!" +msgstr "Non si può rendicontare due volte la stessa riga!" + +#. module: hr_expense_petty_cash +#: code:addons/hr_expense_petty_cash/models/account_move.py:0 +#, python-format +msgid "{name} with petty cash checked must contain only 1 line." +msgstr "{name} con la piccola cassa selezionata deve contenere solo una riga." diff --git a/hr_expense_petty_cash/models/__init__.py b/hr_expense_petty_cash/models/__init__.py new file mode 100644 index 000000000..e60741242 --- /dev/null +++ b/hr_expense_petty_cash/models/__init__.py @@ -0,0 +1,7 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import account_move +from . import hr_expense +from . import hr_expense_sheet +from . import petty_cash diff --git a/hr_expense_petty_cash/models/account_move.py b/hr_expense_petty_cash/models/account_move.py new file mode 100644 index 000000000..8eafc5a44 --- /dev/null +++ b/hr_expense_petty_cash/models/account_move.py @@ -0,0 +1,153 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import _, api, fields, models +from odoo.exceptions import UserError, ValidationError +from odoo.tools import float_compare + + +class AccountMove(models.Model): + _inherit = "account.move" + + is_petty_cash = fields.Boolean( + string="Petty Cash", + readonly=True, + states={"draft": [("readonly", False)]}, + ) + + def action_post(self): + self._check_petty_cash_amount() + return super().action_post() + + @api.constrains("invoice_line_ids", "line_ids") + def _check_petty_cash_amount(self): + petty_cash_env = self.env["petty.cash"].sudo() + for rec in self: + petty_cash = petty_cash_env.search( + [ + ("partner_id", "=", rec.partner_id.id), + ("company_id", "=", rec.company_id.id), + ], + limit=1, + ) + if petty_cash and rec.invoice_line_ids: + account = petty_cash.account_id + # Must check Petty Cash on vender bills + if ( + not rec.is_petty_cash + and account.id in rec.invoice_line_ids.mapped("account_id").ids + ): + raise UserError( + _("Please check Petty Cash on {name}.").format( + name=rec.display_name + ) + ) + if rec.is_petty_cash: + if len(rec.invoice_line_ids) > 1: + raise UserError( + _( + "{name} with petty cash checked must contain " + "only 1 line." + ).format(name=rec.display_name) + ) + if rec.invoice_line_ids.account_id.id != account.id: + raise UserError( + _("Account on invoice line should be {name}.").format( + name=account.display_name + ) + ) + balance = petty_cash.petty_cash_balance + limit = petty_cash.petty_cash_limit + max_amount = limit - balance + amount = sum( + rec.invoice_line_ids.filtered( + lambda l: l.account_id == account + ).mapped("price_subtotal") + ) + company_currency = rec.company_id.currency_id + amount_company = rec.currency_id._convert( + amount, + company_currency, + rec.company_id, + rec.date or fields.Date.today(), + ) + prec = rec.currency_id.rounding + if ( + float_compare( + amount_company, max_amount, precision_rounding=prec + ) + == 1 + ): + raise ValidationError( + _( + "Petty Cash balance is {balance} {symbol}.\n" + "Max amount to add is {max_amount} {symbol}." + ).format( + balance="{:,.2f}".format(balance), + symbol=company_currency.symbol, + max_amount="{:,.2f}".format(max_amount), + ) + ) + + def _add_petty_cash_invoice_line(self, petty_cash): + self.ensure_one() + # Get suggested currency amount + amount = petty_cash.petty_cash_limit - petty_cash.petty_cash_balance + company_currency = self.company_id.currency_id + amount_doc_currency = company_currency._convert( + amount, + self.currency_id, + self.company_id, + self.date or fields.Date.today(), + ) + + inv_line = self.env["account.move.line"].new( + { + "name": petty_cash.account_id.name, + "account_id": petty_cash.account_id.id, + "partner_id": petty_cash.partner_id.id, + "price_unit": amount_doc_currency, + "quantity": 1, + "tax_ids": [], # no tax need + } + ) + return inv_line + + @api.onchange("is_petty_cash", "partner_id") + def _onchange_is_petty_cash(self): + self.line_ids = False + self.invoice_line_ids = False + if self.is_petty_cash: + if not self.partner_id: + raise ValidationError(_("Please select petty cash holder")) + # Selected partner must be petty cash holder + petty_cash = self.env["petty.cash"].search( + [ + ("partner_id", "=", self.partner_id.id), + ("company_id", "=", self.company_id.id), + ], + limit=1, + ) + if not petty_cash: + raise ValidationError( + _("%s is not a petty cash holder") % self.partner_id.name + ) + self.invoice_line_ids = self._add_petty_cash_invoice_line(petty_cash) + + if petty_cash.journal_id: + # Prevent inconsistent journal_id + if ( + ( + self.move_type in self.get_sale_types(include_receipts=True) + and petty_cash.journal_id.type == "sale" + ) + or ( + self.move_type in self.get_purchase_types(include_receipts=True) + and petty_cash.journal_id.type == "purchase" + ) + or ( + self.move_type == "entry" + and petty_cash.journal_id.type == "general" + ) + ): + self.journal_id = petty_cash.journal_id.id diff --git a/hr_expense_petty_cash/models/hr_expense.py b/hr_expense_petty_cash/models/hr_expense.py new file mode 100644 index 000000000..5de8731c6 --- /dev/null +++ b/hr_expense_petty_cash/models/hr_expense.py @@ -0,0 +1,117 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import Command, _, fields, models +from odoo.exceptions import UserError, ValidationError +from odoo.tools import float_compare +from odoo.tools.misc import format_date + + +class HrExpense(models.Model): + _inherit = "hr.expense" + + payment_mode = fields.Selection(selection_add=[("petty_cash", "Petty Cash")]) + petty_cash_id = fields.Many2one( + string="Petty cash holder", + comodel_name="petty.cash", + ondelete="restrict", + check_company=True, + readonly=True, + states={"draft": [("readonly", False)]}, + ) + + def _get_default_expense_sheet_values(self): + """Core Odoo filter own_account and company only. + this function overwrite for petty cash""" + petty_cash = self.filtered(lambda x: x.payment_mode == "petty_cash") + todo = self - petty_cash + if petty_cash: + # If there is an expense with total_amount_company == 0, + # it means that expense has not been processed by OCR yet + expenses_with_amount = self.filtered( + lambda expense: not float_compare( + expense.total_amount_company, + 0.0, + precision_rounding=expense.company_currency_id.rounding, + ) + == 0 + ) + if any( + expense.state != "draft" or expense.sheet_id + for expense in expenses_with_amount + ): + raise UserError(_("You cannot report twice the same line!")) + if not expenses_with_amount: + raise UserError(_("You cannot report the expenses without amount!")) + if len(expenses_with_amount.mapped("employee_id")) != 1: + raise UserError( + _( + "You cannot report expenses for different employees in the same report." + ) + ) + if any(not expense.product_id for expense in expenses_with_amount): + raise UserError(_("You can not create report without category.")) + if len(self.company_id) != 1: + raise UserError( + _( + "You cannot report expenses for different companies in the same report." + ) + ) + if len(petty_cash) == 1: + sheet_name = petty_cash.name + else: + dates = petty_cash.mapped("date") + min_date = format_date(self.env, min(dates)) + max_date = format_date(self.env, max(dates)) + if min_date == max_date: + sheet_name = min_date + else: + sheet_name = _( + "%(date_from)s - %(date_to)s", + date_from=min_date, + date_to=max_date, + ) + # check expense petty cash can't create holder more than 1 + if len(petty_cash.petty_cash_id) != 1: + raise ValidationError( + _("You cannot create report from many petty cash holders.") + ) + values = { + "company_id": self.company_id.id, + "employee_id": self[0].employee_id.id, + "name": sheet_name, + "expense_line_ids": [Command.set(petty_cash.ids)], + "state": "draft", + } + # default journal from petty cash (if any) + journal_petty_cash = self[0].petty_cash_id.journal_id + if journal_petty_cash: + values["journal_id"] = journal_petty_cash.id + return values + return super(HrExpense, todo)._get_default_expense_sheet_values() + + def _get_petty_cash_move_line( + self, + move_line_name, + partner_id, + total_amount, + total_amount_currency, + account=False, + ): + account_date = ( + self.date + or self.sheet_id.accounting_date + or fields.Date.context_today(self) + ) + ml_dict = { + "name": move_line_name, + "debit": total_amount if total_amount > 0.0 else 0.0, + "credit": -total_amount if total_amount < 0.0 else 0.0, + "account_id": account and account.id or self.account_id.id, + "date_maturity": account_date, + "amount_currency": total_amount_currency, + "currency_id": self.currency_id.id, + "expense_id": self.id, + "partner_id": partner_id, + } + return ml_dict diff --git a/hr_expense_petty_cash/models/hr_expense_sheet.py b/hr_expense_petty_cash/models/hr_expense_sheet.py new file mode 100644 index 000000000..a539e641c --- /dev/null +++ b/hr_expense_petty_cash/models/hr_expense_sheet.py @@ -0,0 +1,146 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import Command, _, api, fields, models +from odoo.exceptions import ValidationError +from odoo.tools import float_compare +from odoo.tools.misc import clean_context + + +class HrExpenseSheet(models.Model): + _inherit = "hr.expense.sheet" + + petty_cash_id = fields.Many2one( + string="Petty cash holder", + comodel_name="petty.cash", + ondelete="restrict", + compute="_compute_petty_cash", + store=True, + ) + + @api.depends("expense_line_ids", "payment_mode") + def _compute_petty_cash(self): + for rec in self: + rec.petty_cash_id = False + if rec.payment_mode == "petty_cash": + set_petty_cash_ids = set() + for line in rec.expense_line_ids: + set_petty_cash_ids.add(line.petty_cash_id.id) + if len(set_petty_cash_ids) == 1: + rec.petty_cash_id = rec.env["petty.cash"].browse( + set_petty_cash_ids.pop() + ) + journal_petty_cash = rec.petty_cash_id.journal_id + if journal_petty_cash: + rec.journal_id = journal_petty_cash + else: + raise ValidationError( + _("You cannot create report from many petty cash holders.") + ) + + @api.constrains("expense_line_ids", "total_amount") + def _check_petty_cash_amount(self): + for rec in self: + if rec.payment_mode == "petty_cash": + petty_cash = rec.petty_cash_id + balance = petty_cash.petty_cash_balance + amount = rec.total_amount + company_currency = rec.company_id.currency_id + amount_company = rec.currency_id._convert( + amount, + company_currency, + rec.company_id, + rec.accounting_date or fields.Date.today(), + ) + prec = rec.currency_id.rounding + if float_compare(amount_company, balance, precision_rounding=prec) == 1: + raise ValidationError( + _( + "Not enough money in petty cash holder.\n" + "You are requesting {amount_company}{symbol}, " + "but the balance is {balance}{symbol}." + ).format( + amount_company="{:,.2f}".format(amount_company), + symbol=company_currency.symbol, + balance="{:,.2f}".format(balance), + ) + ) + + def _do_create_moves(self): + petty_cash_account_sheets = self.filtered( + lambda sheet: sheet.payment_mode == "petty_cash" + ) + self_without_petty_cash = self - petty_cash_account_sheets + if petty_cash_account_sheets: + self = self.with_context( + **clean_context(self.env.context) + ) # remove default_* + moves = self.env["account.move"].create( + [sheet._prepare_bill_vals() for sheet in petty_cash_account_sheets] + ) + moves.action_post() + self.activity_update() + return moves + return super(HrExpenseSheet, self_without_petty_cash)._do_create_moves() + + def action_open_account_move(self): + self.ensure_one() + if self.payment_mode == "petty_cash": + return { + "name": self.account_move_id.name, + "type": "ir.actions.act_window", + "view_mode": "form", + "views": [[False, "form"]], + "res_model": "account.move", + "res_id": self.account_move_id.id, + } + return super().action_open_account_move() + + def _get_petty_cash_move_line_vals(self): + self.ensure_one() + move_line_vals = [] + for expense in self.expense_line_ids: + move_line_name = ( + expense.employee_id.name + ": " + expense.name.split("\n")[0][:64] + ) + partner_id = ( + expense.employee_id.sudo().address_home_id.commercial_partner_id.id + ) + # source move line + move_line_src = expense._get_petty_cash_move_line( + move_line_name, + partner_id, + expense.total_amount_company, + expense.total_amount, + ) + move_line_values = [move_line_src] + + # destination move line + move_line_dst = expense._get_petty_cash_move_line( + move_line_name, + expense.petty_cash_id.partner_id.id, + -expense.total_amount_company, + -expense.total_amount, + expense.petty_cash_id.account_id, + ) + move_line_values.append(move_line_dst) + move_line_vals.extend(move_line_values) + return move_line_vals + + def _prepare_bill_vals(self): + """create journal entry instead of bills when clearing document""" + self.ensure_one() + res = super()._prepare_bill_vals() + if self.payment_mode == "petty_cash": + res["move_type"] = "entry" + move_line_vals = self._get_petty_cash_move_line_vals() + res["line_ids"] = [Command.create(x) for x in move_line_vals] + return res + + def action_sheet_move_create(self): + res = super().action_sheet_move_create() + paid_petty_cash = self.filtered(lambda m: m.payment_mode == "petty_cash") + paid_petty_cash.write( + {"state": "done", "amount_residual": 0.0, "payment_state": "paid"} + ) + return res diff --git a/hr_expense_petty_cash/models/petty_cash.py b/hr_expense_petty_cash/models/petty_cash.py new file mode 100644 index 000000000..8adcae0b0 --- /dev/null +++ b/hr_expense_petty_cash/models/petty_cash.py @@ -0,0 +1,64 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class PettyCash(models.Model): + _name = "petty.cash" + _description = "Petty Cash" + _rec_name = "partner_id" + _check_company_auto = True + + active = fields.Boolean(default=True) + partner_id = fields.Many2one( + comodel_name="res.partner", + string="Petty Cash Holder", + required=True, + check_company=True, + ) + account_id = fields.Many2one( + comodel_name="account.account", + string="Petty Cash Account", + required=True, + check_company=True, + ) + petty_cash_limit = fields.Float( + string="Max Limit", + required=True, + ) + petty_cash_balance = fields.Float( + string="Balance", + compute="_compute_petty_cash_balance", + ) + journal_id = fields.Many2one( + comodel_name="account.journal", + check_company=True, + ) + company_id = fields.Many2one( + comodel_name="res.company", + required=True, + default=lambda self: self.env.company, + ) + + _sql_constraints = [ + ( + "partner_uniq", + "unique(partner_id, company_id)", + "Petty Cash Holder must be unique!", + ), + ] + + @api.depends("partner_id", "account_id") + def _compute_petty_cash_balance(self): + aml_env = self.env["account.move.line"] + for rec in self: + aml = aml_env.search( + [ + ("partner_id", "=", rec.partner_id.id), + ("account_id", "=", rec.account_id.id), + ("parent_state", "=", "posted"), + ] + ) + balance = sum(line.debit - line.credit for line in aml) + rec.petty_cash_balance = balance diff --git a/hr_expense_petty_cash/readme/CONFIGURE.rst b/hr_expense_petty_cash/readme/CONFIGURE.rst new file mode 100644 index 000000000..2d49b7ac0 --- /dev/null +++ b/hr_expense_petty_cash/readme/CONFIGURE.rst @@ -0,0 +1,9 @@ +**Create a Petty Cash Account** + +#. Go to Invoicing > Accounting > Petty Cash +#. Create a new petty cash account +#. Type = Bank and Cash or Account type that is asset + +Note: + +* You will need the "Show Full Accounting Features" to see accounting data diff --git a/hr_expense_petty_cash/readme/CONTRIBUTORS.rst b/hr_expense_petty_cash/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..7ad5c5105 --- /dev/null +++ b/hr_expense_petty_cash/readme/CONTRIBUTORS.rst @@ -0,0 +1,8 @@ +* `Ecosoft `__: + + * Pimolnat Suntian + * Saran Lim. + +* `Trinityroots `__: + + * Santi Techatoo diff --git a/hr_expense_petty_cash/readme/DESCRIPTION.rst b/hr_expense_petty_cash/readme/DESCRIPTION.rst new file mode 100644 index 000000000..9aad21d9a --- /dev/null +++ b/hr_expense_petty_cash/readme/DESCRIPTION.rst @@ -0,0 +1,8 @@ +This module work about expense that paid by petty cash. + +General Process: + +#. Create Petty Cash Holder +#. Transfer cash to Petty Cash Holder (by using Vendor Bill) +#. You can create expense paid by petty cash and select petty cash holder. +#. Then balance of petty cash holder less than amount of expense, you must transfer cash to petty cash holder before Submit Report to Manager. diff --git a/hr_expense_petty_cash/readme/USAGE.rst b/hr_expense_petty_cash/readme/USAGE.rst new file mode 100644 index 000000000..69344bc22 --- /dev/null +++ b/hr_expense_petty_cash/readme/USAGE.rst @@ -0,0 +1,27 @@ +**Create a Petty Cash Holder** + +#. Go to Invoicing > Accounting > Petty Cash +#. Create a new petty cash holder +#. Select Petty Cash Account and Journal (optional) + +**Add Balance for Petty Cash Holder** + +#. Go to Invoicing > Vendor > Bill +#. Create a new vendor bill +#. Select Vendor (Petty Cash Holder) +#. Check Petty Cash will auto line with Unit Price = Max Limit - Balance + +**Create an Expense paid by Petty Cash** + +#. Go to Expenses > My Expenses +#. Create a new expense +#. Select Paid by = Petty Cash and Select Petty Cash Holder + +**Create an Expense Report paid by Petty Cash** + +#. Go to Expenses > My Expense Reports +#. Create a new expense report +#. Select or Create expenses Paid by Petty Cash and same Petty Cash Holder + +Note : Bill and Expense will default journal from petty cash holder, +if you configure journal in petty cash holder. diff --git a/hr_expense_petty_cash/security/ir.model.access.csv b/hr_expense_petty_cash/security/ir.model.access.csv new file mode 100644 index 000000000..b2407a62a --- /dev/null +++ b/hr_expense_petty_cash/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_hr_expense_petty_cash,access_hr_expense_petty_cash,model_petty_cash,account.group_account_user,1,1,1,1 diff --git a/hr_expense_petty_cash/security/petty_cash_security.xml b/hr_expense_petty_cash/security/petty_cash_security.xml new file mode 100644 index 000000000..94ca0b39c --- /dev/null +++ b/hr_expense_petty_cash/security/petty_cash_security.xml @@ -0,0 +1,10 @@ + + + Petty Cash multi-company + + + + ['|',('company_id','=',False),('company_id','in',company_ids)] + + + diff --git a/hr_expense_petty_cash/static/description/icon.png b/hr_expense_petty_cash/static/description/icon.png new file mode 100644 index 000000000..3a0328b51 Binary files /dev/null and b/hr_expense_petty_cash/static/description/icon.png differ diff --git a/hr_expense_petty_cash/static/description/index.html b/hr_expense_petty_cash/static/description/index.html new file mode 100644 index 000000000..1d4275db1 --- /dev/null +++ b/hr_expense_petty_cash/static/description/index.html @@ -0,0 +1,484 @@ + + + + + + +Petty Cash + + + +
+

Petty Cash

+ + +

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

+

This module work about expense that paid by petty cash.

+

General Process:

+
    +
  1. Create Petty Cash Holder
  2. +
  3. Transfer cash to Petty Cash Holder (by using Vendor Bill)
  4. +
  5. You can create expense paid by petty cash and select petty cash holder.
  6. +
  7. Then balance of petty cash holder less than amount of expense, you must transfer cash to petty cash holder before Submit Report to Manager.
  8. +
+

Table of contents

+ +
+

Configuration

+

Create a Petty Cash Account

+
    +
  1. Go to Invoicing > Accounting > Petty Cash
  2. +
  3. Create a new petty cash account
  4. +
  5. Type = Bank and Cash or Account type that is asset
  6. +
+

Note:

+
    +
  • You will need the “Show Full Accounting Features” to see accounting data
  • +
+
+
+

Usage

+

Create a Petty Cash Holder

+
    +
  1. Go to Invoicing > Accounting > Petty Cash
  2. +
  3. Create a new petty cash holder
  4. +
  5. Select Petty Cash Account and Journal (optional)
  6. +
+

Add Balance for Petty Cash Holder

+
    +
  1. Go to Invoicing > Vendor > Bill
  2. +
  3. Create a new vendor bill
  4. +
  5. Select Vendor (Petty Cash Holder)
  6. +
  7. Check Petty Cash will auto line with Unit Price = Max Limit - Balance
  8. +
+

Create an Expense paid by Petty Cash

+
    +
  1. Go to Expenses > My Expenses
  2. +
  3. Create a new expense
  4. +
  5. Select Paid by = Petty Cash and Select Petty Cash Holder
  6. +
+

Create an Expense Report paid by Petty Cash

+
    +
  1. Go to Expenses > My Expense Reports
  2. +
  3. Create a new expense report
  4. +
  5. Select or Create expenses Paid by Petty Cash and same Petty Cash Holder
  6. +
+

Note : Bill and Expense will default journal from petty cash holder, +if you configure journal in petty cash holder.

+
+
+

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 to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Ecosoft
  • +
+
+
+

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-expense project on GitHub.

+

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

+
+
+
+ + diff --git a/hr_expense_petty_cash/tests/__init__.py b/hr_expense_petty_cash/tests/__init__.py new file mode 100644 index 000000000..9187e19b9 --- /dev/null +++ b/hr_expense_petty_cash/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_hr_expense_petty_cash diff --git a/hr_expense_petty_cash/tests/test_hr_expense_petty_cash.py b/hr_expense_petty_cash/tests/test_hr_expense_petty_cash.py new file mode 100644 index 000000000..b79c37349 --- /dev/null +++ b/hr_expense_petty_cash/tests/test_hr_expense_petty_cash.py @@ -0,0 +1,280 @@ +# Copyright 2019 Ecosoft Co., Ltd. (http://ecosoft.co.th) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import Command, fields +from odoo.exceptions import UserError, ValidationError +from odoo.tests.common import Form, TransactionCase + + +class TestHrExpensePettyCash(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + + # Object + cls.account_obj = cls.env["account.account"] + cls.journal_obj = cls.env["account.journal"] + cls.petty_cash_obj = cls.env["petty.cash"] + cls.move_obj = cls.env["account.move"] + cls.sheet_obj = cls.env["hr.expense.sheet"] + cls.exp_obj = cls.env["hr.expense"] + + # Demo data + cls.employee_1 = cls.env.ref("hr.employee_admin") + cls.employee_2 = cls.env.ref("hr.employee_al") + cls.product = cls.env.ref("hr_expense.expense_product_travel_accommodation") + cls.partner_1 = cls.env.ref("base.res_partner_1") + cls.partner_2 = cls.env.ref("base.res_partner_2") + cls.partner_3 = cls.env.ref("base.res_partner_3") + + cls.account_id = cls.account_obj.create( + { + "code": "111111", + "name": "Payable - Test", + "account_type": "liability_payable", + "reconcile": True, + } + ) + cls.account_revenue_id = cls.account_obj.create( + { + "code": "111112", + "name": "Cost Of Revenue - Test", + "account_type": "expense_direct_cost", + } + ) + cls.petty_cash_journal_id = cls.journal_obj.create( + {"code": "PC", "name": "Petty Cash", "type": "purchase"} + ) + + # Create a Petty Cash Account + cls.petty_cash_account_id = cls.account_obj.create( + { + "code": "000000", + "name": "Petty Cash - Test", + "account_type": "asset_cash", + } + ) + cls.petty_cash_holder = cls._create_petty_cash_holder(cls, cls.partner_1) + cls.petty_cash_holder_2 = cls._create_petty_cash_holder(cls, cls.partner_3) + + def _create_petty_cash_holder(self, partner): + petty_cash_holder = self.petty_cash_obj.create( + { + "partner_id": partner.id, + "account_id": self.petty_cash_account_id.id, + "petty_cash_limit": 1000.0, + } + ) + return petty_cash_holder + + def _create_invoice(self, partner=False): + invoice = self.move_obj.create( + { + "partner_id": partner, + "move_type": "in_invoice", + "invoice_date": fields.Date.today(), + } + ) + return invoice + + def _create_expense( + self, + amount, + employee, + payment_mode="own_account", + petty_cash_holder=False, + ): + with Form(self.exp_obj) as expense: + expense.name = "Expense - Test" + expense.employee_id = employee + expense.product_id = self.product + expense.total_amount = amount + expense.payment_mode = payment_mode + if payment_mode == "petty_cash": + expense.petty_cash_id = petty_cash_holder + expense = expense.save() + expense.tax_ids = False # Test no vat + return expense + + def _create_expense_sheet(self, expenses): + expense_sheet = self.sheet_obj.create( + { + "name": expenses[0].name, + "employee_id": expenses[0].employee_id.id, + "expense_line_ids": [Command.set(expenses.ids)], + } + ) + return expense_sheet + + def _create_multi_invoice_line(self, petty_cash=False): + invoice = self.move_obj.create( + { + "partner_id": self.partner_1.id, + "move_type": "in_invoice", + "invoice_date": fields.Date.today(), + "is_petty_cash": petty_cash, + "invoice_line_ids": [ + Command.create( + { + "name": "Test line 1", + "quantity": 1, + "price_unit": 100, + "account_id": self.account_revenue_id.id, + } + ), + Command.create( + { + "name": "Test line 2", + "quantity": 1, + "price_unit": 100, + "account_id": self.account_revenue_id.id, + } + ), + ], + } + ) + return invoice + + def _check_warning(self): + # no partner and check petty cash + invoice = self._create_invoice() + with self.assertRaises(ValidationError): + with Form(invoice) as inv: + inv.is_petty_cash = True + # partner is not holder. + invoice = self._create_invoice(self.partner_2.id) + with self.assertRaises(ValidationError): + with Form(invoice) as inv: + inv.is_petty_cash = True + invoice = self._create_invoice(self.partner_1.id) + invoice.is_petty_cash = True + invoice._onchange_is_petty_cash() + + self.assertEqual(len(invoice.invoice_line_ids), 1) + self.assertEqual(invoice.invoice_line_ids.price_unit, 1000.0) + # over limit + with self.assertRaises(ValidationError): + invoice.invoice_line_ids.with_context(check_move_validity=False).write( + {"price_unit": 1500.0} + ) + invoice.action_post() + # change account to not petty cash + invoice.invoice_line_ids.account_id = self.account_revenue_id.id + with self.assertRaises(UserError): + invoice.action_post() + # no partner + invoice.invoice_line_ids.account_id = self.petty_cash_account_id.id + with self.assertRaises(UserError): + invoice.write({"partner_id": False}) + invoice.action_post() + # Create line manual and not check petty cash + invoice = self.move_obj.create( + { + "partner_id": self.partner_1.id, + "move_type": "in_invoice", + "invoice_date": fields.Date.today(), + "invoice_line_ids": [ + ( + 0, + 0, + { + "name": "Test line", + "quantity": 1, + "price_unit": 100, + "account_id": self.account_revenue_id.id, + }, + ) + ], + } + ) + invoice.invoice_line_ids.account_id = self.petty_cash_account_id.id + with self.assertRaises(UserError): + invoice.action_post() + + # Create multi line and check petty cash + with self.assertRaises(UserError): + self._create_multi_invoice_line(petty_cash=True) + + def test_01_create_petty_cash_holder(self): + self.assertEqual(self.petty_cash_holder.petty_cash_balance, 0.00) + self._check_warning() + invoice = self._create_invoice(self.partner_1.id) + invoice.is_petty_cash = True + invoice._onchange_is_petty_cash() + invoice.action_post() + self.petty_cash_holder._compute_petty_cash_balance() + self.assertEqual(self.petty_cash_holder.petty_cash_balance, 1000.0) + + def test_02_create_expense_petty_cash(self): + invoice = self._create_invoice(self.partner_1.id) + invoice.is_petty_cash = True + invoice._onchange_is_petty_cash() + invoice.invoice_line_ids.price_unit = 1000.0 + invoice.action_post() + self.petty_cash_holder._compute_petty_cash_balance() + self.assertEqual(self.petty_cash_holder.petty_cash_balance, 1000.0) + # Create expense + expense_own = self._create_expense(400.0, self.employee_1, "own_account") + expense_petty_cash = self._create_expense( + 400.0, self.employee_1, "petty_cash", self.petty_cash_holder + ) + expense_petty_cash_2 = self._create_expense( + 200.0, self.employee_1, "petty_cash", self.petty_cash_holder_2 + ) + expense_petty_cash_3 = self._create_expense( + 100.0, self.employee_2, "petty_cash", self.petty_cash_holder_2 + ) + expense_report = expense_own + expense_petty_cash + expense_petty_cash_2 + # Check expenses must have 1 petty cash holder only + with self.assertRaises(ValidationError): + expense_report.action_submit_expenses() + # check create direct expense sheet and many diff petty cash + expense_diff_holder = expense_petty_cash + expense_petty_cash_2 + with self.assertRaises(ValidationError): + self._create_expense_sheet(expense_diff_holder) + # create expense normal not petty cash + expense_own.sheet_id = False + action = expense_own.action_submit_expenses() + self.assertEqual(action["res_model"], "hr.expense.sheet") + sheet = self._create_expense_sheet(expense_petty_cash) + self.assertEqual(sheet.state, "draft") + with self.assertRaises(ValidationError): + sheet.expense_line_ids.total_amount = 1600.0 + sheet._check_petty_cash_amount() + sheet.expense_line_ids.total_amount = 400.0 + # Submitted to Manager and Approve + sheet.action_submit_sheet() + self.assertEqual(sheet.state, "submit") + sheet.approve_expense_sheets() + self.assertEqual(sheet.state, "approve") + # Check state != draft, many employee and don't have product + with self.assertRaises(UserError): + expense_petty_cash.action_submit_expenses() + expense_test = expense_petty_cash_2 + expense_petty_cash_3 + with self.assertRaises(UserError): + expense_test.action_submit_expenses() + expense_petty_cash_3.product_id = False + with self.assertRaises(UserError): + expense_petty_cash_3.action_submit_expenses() + # Create Expense Entries + sheet.action_sheet_move_create() + self.assertEqual(sheet.state, "done") + self.assertTrue(sheet.account_move_id.id) + self.assertEqual(self.petty_cash_holder.petty_cash_balance, 600.0) + + def test_03_create_expense_petty_cash_with_journal(self): + self.petty_cash_holder.journal_id = self.petty_cash_journal_id + invoice = self._create_invoice(self.partner_1.id) + invoice.is_petty_cash = True + invoice._onchange_is_petty_cash() + invoice.invoice_line_ids.price_unit = 1000.0 + self.assertEqual(invoice.journal_id, self.petty_cash_holder.journal_id) + invoice.action_post() + self.petty_cash_holder._compute_petty_cash_balance() + self.assertEqual(self.petty_cash_holder.petty_cash_balance, 1000.0) + expense_petty_cash = self._create_expense( + 400.0, self.employee_1, "petty_cash", self.petty_cash_holder + ) + expense_petty_cash.action_submit_expenses() + sheet = self._create_expense_sheet(expense_petty_cash) + self.assertEqual(sheet.journal_id, self.petty_cash_holder.journal_id) diff --git a/hr_expense_petty_cash/views/account_move_views.xml b/hr_expense_petty_cash/views/account_move_views.xml new file mode 100644 index 000000000..d85154a47 --- /dev/null +++ b/hr_expense_petty_cash/views/account_move_views.xml @@ -0,0 +1,16 @@ + + + + account.move.form.inherit + account.move + + + + + + + + diff --git a/hr_expense_petty_cash/views/hr_expense_sheet_views.xml b/hr_expense_petty_cash/views/hr_expense_sheet_views.xml new file mode 100644 index 000000000..6f1ad2c29 --- /dev/null +++ b/hr_expense_petty_cash/views/hr_expense_sheet_views.xml @@ -0,0 +1,44 @@ + + + + view.hr.expense.sheet.form.inherit + hr.expense.sheet + + + + + + + {'invisible': [('payment_mode', 'not in', ['own_account', 'petty_cash'])]} + + + + + + hr.expense.sheet.search.inherit + hr.expense.sheet + + + + + + + + + + + diff --git a/hr_expense_petty_cash/views/hr_expense_views.xml b/hr_expense_petty_cash/views/hr_expense_views.xml new file mode 100644 index 000000000..7ea350a64 --- /dev/null +++ b/hr_expense_petty_cash/views/hr_expense_views.xml @@ -0,0 +1,42 @@ + + + + hr.expense.view.form.inherit + hr.expense + + + +
+ +
+
+
+
+ + + hr.expense.search.inherit + hr.expense + + + + + + + + + + +
diff --git a/hr_expense_petty_cash/views/petty_cash_views.xml b/hr_expense_petty_cash/views/petty_cash_views.xml new file mode 100644 index 000000000..fde7e5287 --- /dev/null +++ b/hr_expense_petty_cash/views/petty_cash_views.xml @@ -0,0 +1,139 @@ + + + + petty.cash.form + petty.cash + +
+ + + + + + + + + + + + + + + + + +
+
+
+ + petty.cash.list + petty.cash + + + + + + + + + + + + + petty.cash.kanban + petty.cash + + + + + + +
+
+
+ + + +
+
+ +
+
+
+
+
+
+
+
+ + petty.cash.search + petty.cash + + + + + + + + + + + + + + + + Petty Cash + petty.cash + tree,kanban,form + + + +

+ Create a new petty cash holder +

+
+
+ +
diff --git a/setup/hr_expense_petty_cash/odoo/addons/hr_expense_petty_cash b/setup/hr_expense_petty_cash/odoo/addons/hr_expense_petty_cash new file mode 120000 index 000000000..fa0ee9b95 --- /dev/null +++ b/setup/hr_expense_petty_cash/odoo/addons/hr_expense_petty_cash @@ -0,0 +1 @@ +../../../../hr_expense_petty_cash \ No newline at end of file diff --git a/setup/hr_expense_petty_cash/setup.py b/setup/hr_expense_petty_cash/setup.py new file mode 100644 index 000000000..28c57bb64 --- /dev/null +++ b/setup/hr_expense_petty_cash/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)