diff --git a/oca_dependencies.txt b/oca_dependencies.txt index 0bbc6e4c..9650cd3f 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,3 +1,4 @@ +account-payment queue partner-contact stock-logistics-barcode diff --git a/pos_session_pay_invoice/README.rst b/pos_session_pay_invoice/README.rst new file mode 100644 index 00000000..2105e675 --- /dev/null +++ b/pos_session_pay_invoice/README.rst @@ -0,0 +1,101 @@ +======================= +POS Session Pay invoice +======================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpos-lightgray.png?logo=github + :target: https://github.com/OCA/pos/tree/12.0/pos_session_pay_invoice + :alt: OCA/pos +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/pos-12-0/pos-12-0-pos_session_pay_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/184/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This modules allows to pay an existing Supplier Invoice / Customer Refund, or +to collect payment for an existing Customer Invoice, from within a POS Session. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +#. Go to *Point of Sale / Configuration / Point of Sale* and activate the + 'Cash Control' setting. + +Usage +===== + +#. Go to *Point of Sale / Dashboard* and create and open or access to an + already open POS Session. +#. Press the button **Pay Invoice** to pay a Supplier Invoice or a Customer + Refund. It will be paid using Cash. +#. Select **Collect Payment from Invoice** in to receive a payment of an + existing Customer Invoice or a Supplier Refund. You will need to select + a Journal if the POS Config has defined multiple Payment Methods. + +Known issues / Roadmap +====================== + +* Cannot pay invoices in a different currency than that defined in the journal + associated to the payment method used to pay/collect payment. + +* Should depend on `pos_invoicing` but it requires a refactoring of `pos_invoicing`. + It will be improved when migrating to 13.0 + +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 +~~~~~~~ + +* Creu Blanca + +Contributors +~~~~~~~~~~~~ + +* Enric Tobella +* Jordi Ballester + +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/pos `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/pos_session_pay_invoice/__init__.py b/pos_session_pay_invoice/__init__.py new file mode 100644 index 00000000..bd6911bc --- /dev/null +++ b/pos_session_pay_invoice/__init__.py @@ -0,0 +1,3 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import wizard diff --git a/pos_session_pay_invoice/__manifest__.py b/pos_session_pay_invoice/__manifest__.py new file mode 100644 index 00000000..9fca5f3c --- /dev/null +++ b/pos_session_pay_invoice/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright (C) 2017 Creu Blanca +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +{ + 'name': 'POS Session Pay invoice', + 'version': '12.0.1.0.0', + 'category': 'Point Of Sale', + 'author': "Creu Blanca," + "Odoo Community Association (OCA)", + 'website': 'https://github.com/OCA/pos', + 'summary': 'Pay and receive invoices from PoS Session', + "license": "LGPL-3", + 'depends': [ + "point_of_sale", "account_cash_invoice" + ], + 'data': [ + "wizard/cash_invoice_out.xml", + "wizard/cash_invoice_in.xml", + "views/pos_session.xml", + ], +} diff --git a/pos_session_pay_invoice/i18n/pos_session_pay_invoice.pot b/pos_session_pay_invoice/i18n/pos_session_pay_invoice.pot new file mode 100644 index 00000000..7856fa62 --- /dev/null +++ b/pos_session_pay_invoice/i18n/pos_session_pay_invoice.pot @@ -0,0 +1,65 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_session_pay_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: pos_session_pay_invoice +#: model:ir.ui.view,arch_db:pos_session_pay_invoice.view_pos_session_form +msgid "Collect Payment\n" +" from Invoice" +msgstr "" + +#. module: pos_session_pay_invoice +#: model:ir.ui.view,arch_db:pos_session_pay_invoice.view_pos_session_form +msgid "Pay\n" +" Invoice" +msgstr "" + +#. module: pos_session_pay_invoice +#: code:addons/pos_session_pay_invoice/wizard/cash_invoice_in.py:53 +#: code:addons/pos_session_pay_invoice/wizard/cash_invoice_out.py:50 +#, python-format +msgid "Bank Statement was not found" +msgstr "" + +#. module: pos_session_pay_invoice +#: model:ir.actions.act_window,name:pos_session_pay_invoice.action_pos_invoice_out +msgid "Collect Payment from Invoice" +msgstr "" + +#. module: pos_session_pay_invoice +#: model:ir.actions.act_window,name:pos_session_pay_invoice.action_pos_invoice_in +msgid "Pay invoice" +msgstr "" + +#. module: pos_session_pay_invoice +#: model:ir.ui.view,arch_db:pos_session_pay_invoice.view_pos_config_kanban +msgid "Summary" +msgstr "" + +#. module: pos_session_pay_invoice +#: code:addons/pos_session_pay_invoice/wizard/cash_invoice_in.py:24 +#, python-format +msgid "There is no cash register for this Pos session" +msgstr "" + +#. module: pos_session_pay_invoice +#: model:ir.model,name:pos_session_pay_invoice.model_cash_invoice_in +msgid "cash.invoice.in" +msgstr "" + +#. module: pos_session_pay_invoice +#: model:ir.model,name:pos_session_pay_invoice.model_cash_invoice_out +msgid "cash.invoice.out" +msgstr "" + diff --git a/pos_session_pay_invoice/readme/CONFIGURE.rst b/pos_session_pay_invoice/readme/CONFIGURE.rst new file mode 100644 index 00000000..ecefd6e1 --- /dev/null +++ b/pos_session_pay_invoice/readme/CONFIGURE.rst @@ -0,0 +1,2 @@ +#. Go to *Point of Sale / Configuration / Point of Sale* and activate the + 'Cash Control' setting. diff --git a/pos_session_pay_invoice/readme/CONTRIBUTORS.rst b/pos_session_pay_invoice/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..e250875e --- /dev/null +++ b/pos_session_pay_invoice/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Enric Tobella +* Jordi Ballester diff --git a/pos_session_pay_invoice/readme/DESCRIPTION.rst b/pos_session_pay_invoice/readme/DESCRIPTION.rst new file mode 100644 index 00000000..d66c63af --- /dev/null +++ b/pos_session_pay_invoice/readme/DESCRIPTION.rst @@ -0,0 +1,2 @@ +This modules allows to pay an existing Supplier Invoice / Customer Refund, or +to collect payment for an existing Customer Invoice, from within a POS Session. diff --git a/pos_session_pay_invoice/readme/ROADMAP.rst b/pos_session_pay_invoice/readme/ROADMAP.rst new file mode 100644 index 00000000..e9a91dc5 --- /dev/null +++ b/pos_session_pay_invoice/readme/ROADMAP.rst @@ -0,0 +1,5 @@ +* Cannot pay invoices in a different currency than that defined in the journal + associated to the payment method used to pay/collect payment. + +* Should depend on `pos_invoicing` but it requires a refactoring of `pos_invoicing`. + It will be improved when migrating to 13.0 diff --git a/pos_session_pay_invoice/readme/USAGE.rst b/pos_session_pay_invoice/readme/USAGE.rst new file mode 100644 index 00000000..7f4d8f3c --- /dev/null +++ b/pos_session_pay_invoice/readme/USAGE.rst @@ -0,0 +1,7 @@ +#. Go to *Point of Sale / Dashboard* and create and open or access to an + already open POS Session. +#. Press the button **Pay Invoice** to pay a Supplier Invoice or a Customer + Refund. It will be paid using Cash. +#. Select **Collect Payment from Invoice** in to receive a payment of an + existing Customer Invoice or a Supplier Refund. You will need to select + a Journal if the POS Config has defined multiple Payment Methods. diff --git a/pos_session_pay_invoice/static/description/icon.png b/pos_session_pay_invoice/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/pos_session_pay_invoice/static/description/icon.png differ diff --git a/pos_session_pay_invoice/tests/__init__.py b/pos_session_pay_invoice/tests/__init__.py new file mode 100644 index 00000000..63055abd --- /dev/null +++ b/pos_session_pay_invoice/tests/__init__.py @@ -0,0 +1,3 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import test_pay_invoice diff --git a/pos_session_pay_invoice/tests/test_pay_invoice.py b/pos_session_pay_invoice/tests/test_pay_invoice.py new file mode 100644 index 00000000..908ae202 --- /dev/null +++ b/pos_session_pay_invoice/tests/test_pay_invoice.py @@ -0,0 +1,100 @@ +# Copyright 2017 Creu Blanca +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo.tests import common + + +class TestSessionPayInvoice(common.TransactionCase): + def setUp(self): + super(TestSessionPayInvoice, self).setUp() + self.company = self.env.ref('base.main_company') + partner = self.env.ref('base.partner_demo') + self.invoice_out = self.env['account.invoice'].create({ + 'company_id': self.company.id, + 'partner_id': partner.id, + 'date_invoice': '2016-03-12', + 'type': 'out_invoice', + }) + account = self.env['account.account'].create({ + 'code': 'test_cash_pay_invoice', + 'company_id': self.company.id, + 'name': 'Test', + 'user_type_id': self.env.ref( + 'account.data_account_type_revenue').id + }) + self.env['account.invoice.line'].create({ + 'product_id': self.env.ref('product.product_delivery_02').id, + 'invoice_id': self.invoice_out.id, + 'account_id': account.id, + 'name': 'Producto de prueba', + 'quantity': 1.0, + 'price_unit': 100.0, + }) + self.invoice_out._onchange_invoice_line_ids() + self.invoice_out.action_invoice_open() + self.invoice_out.number = '2999/99999' + self.invoice_in = self.env['account.invoice'].create({ + 'partner_id': partner.id, + 'company_id': self.company.id, + 'type': 'in_invoice', + 'date_invoice': '2016-03-12', + }) + self.env['account.invoice.line'].create({ + 'product_id': self.env.ref('product.product_delivery_02').id, + 'invoice_id': self.invoice_in.id, + 'name': 'Producto de prueba', + 'account_id': account.id, + 'quantity': 1.0, + 'price_unit': 100.0, + }) + self.invoice_in._onchange_invoice_line_ids() + self.invoice_in.action_invoice_open() + self.invoice_in.number = '2999/99999' + self.config = self.env.ref('point_of_sale.pos_config_main') + self.config.cash_control = True + + self.account_cash_differences_id = self.env['account.account'].create({ + 'code': 'test_cash_differences', + 'company_id': self.company.id, + 'name': 'Test Cash Differences', + 'user_type_id': self.env.ref( + 'account.data_account_type_revenue').id + }) + + def test_pos_invoice(self): + self.config.open_session_cb() + session = self.config.current_session_id + self.assertIsNotNone(session.statement_ids) + cash_statements = session.statement_ids.filtered( + lambda x: x.journal_id.type == 'cash') + self.assertEquals(len(cash_statements), 1) + journal = cash_statements[0].journal_id + journal.profit_account_id = self.account_cash_differences_id + journal.loss_account_id = self.account_cash_differences_id + + session.action_pos_session_open() + in_invoice = self.env['cash.invoice.in'].with_context( + active_ids=session.ids, active_model='pos.session' + ).create({ + 'invoice_id': self.invoice_in.id, + 'amount': 100.0 + }) + in_invoice.run() + out_invoice = self.env['cash.invoice.out'].with_context( + active_ids=session.ids, active_model='pos.session' + ).create({ + 'invoice_id': self.invoice_out.id, + 'amount': 75.0 + }) + out_invoice.run() + box = self.env['cash.box.in'].with_context( + active_ids=session.ids, active_model='pos.session' + ).create({ + 'name': "Testing", + 'amount': 25.0 + }) + box.run() + session.action_pos_session_closing_control() + session.action_pos_session_validate() + self.assertEqual(self.invoice_out.residual, 25.) + self.assertEqual(self.invoice_in.residual, 0.) diff --git a/pos_session_pay_invoice/views/pos_session.xml b/pos_session_pay_invoice/views/pos_session.xml new file mode 100644 index 00000000..38e2e71d --- /dev/null +++ b/pos_session_pay_invoice/views/pos_session.xml @@ -0,0 +1,43 @@ + + + + pos.config.kanban.view + pos.config + + + + + + + + + pos.session.form.view + pos.session + + + + + + + + + diff --git a/pos_session_pay_invoice/wizard/__init__.py b/pos_session_pay_invoice/wizard/__init__.py new file mode 100644 index 00000000..953231da --- /dev/null +++ b/pos_session_pay_invoice/wizard/__init__.py @@ -0,0 +1,4 @@ +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from . import cash_invoice_in +from . import cash_invoice_out diff --git a/pos_session_pay_invoice/wizard/cash_invoice_in.py b/pos_session_pay_invoice/wizard/cash_invoice_in.py new file mode 100644 index 00000000..de1e8e4f --- /dev/null +++ b/pos_session_pay_invoice/wizard/cash_invoice_in.py @@ -0,0 +1,55 @@ +# Copyright (C) 2017 Creu Blanca +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class CashInvoiceIn(models.TransientModel): + _inherit = 'cash.invoice.in' + + def default_company(self, active_model, active_ids): + if active_model == 'pos.session': + active = self.env[active_model].browse(active_ids) + return active[0].config_id.company_id + return super(CashInvoiceIn, self).default_company( + active_model, active_ids + ) + + def default_journals(self, active_model, active_ids): + if active_model == 'pos.session': + active = self.env[active_model].browse(active_ids) + if not active.cash_register_id: + raise UserError(_( + "There is no cash register for this Pos session" + )) + return active.cash_register_id.journal_id + return super(CashInvoiceIn, self).default_journals( + active_model, active_ids + ) + + def default_currency(self, active_model, active_ids): + if active_model == 'pos.session': + journal = self._default_journal() + if journal.currency_id: + return journal.currency_id + return super(CashInvoiceIn, self).default_currency( + active_model, active_ids + ) + + @api.multi + def run(self): + active_model = self.env.context.get('active_model', False) + active_ids = self.env.context.get('active_ids', False) + if active_model == 'pos.session': + bank_statements = [ + session.statement_ids.filtered( + lambda r: r.journal_id.id == self.journal_id.id + ) + for session in self.env[active_model].browse(active_ids) + ] + if not bank_statements: + raise UserError(_('Bank Statement was not found')) + return self._run(bank_statements) + else: + return super(CashInvoiceIn, self).run() diff --git a/pos_session_pay_invoice/wizard/cash_invoice_in.xml b/pos_session_pay_invoice/wizard/cash_invoice_in.xml new file mode 100644 index 00000000..fb1483cf --- /dev/null +++ b/pos_session_pay_invoice/wizard/cash_invoice_in.xml @@ -0,0 +1,12 @@ + + + + diff --git a/pos_session_pay_invoice/wizard/cash_invoice_out.py b/pos_session_pay_invoice/wizard/cash_invoice_out.py new file mode 100644 index 00000000..b429be22 --- /dev/null +++ b/pos_session_pay_invoice/wizard/cash_invoice_out.py @@ -0,0 +1,52 @@ +# Copyright (C) 2017 Creu Blanca +# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html). + +from odoo import api, models, _ +from odoo.exceptions import UserError + + +class CashInvoiceOut(models.TransientModel): + _inherit = 'cash.invoice.out' + + def default_company(self, active_model, active_ids): + if active_model == 'pos.session': + active = self.env[active_model].browse(active_ids) + return active[0].config_id.company_id + return super(CashInvoiceOut, self).default_company( + active_model, active_ids + ) + + def default_currency(self, active_model, active_ids): + if active_model == 'pos.session': + journal = self._default_journal() + if journal.currency_id: + return journal.currency_id + return super(CashInvoiceOut, self).default_currency( + active_model, active_ids + ) + + def default_journals(self, active_model, active_ids): + if active_model == 'pos.session': + active = self.env[active_model].browse(active_ids) + return self.env['account.journal'].browse( + [r.journal_id.id for r in active.statement_ids]) + return super(CashInvoiceOut, self).default_journals( + active_model, active_ids + ) + + @api.multi + def run(self): + active_model = self.env.context.get('active_model', False) + active_ids = self.env.context.get('active_ids', False) + if active_model == 'pos.session': + bank_statements = [ + session.statement_ids.filtered( + lambda r: r.journal_id.id == self.journal_id.id + ) + for session in self.env[active_model].browse(active_ids) + ] + if not bank_statements: + raise UserError(_('Bank Statement was not found')) + return self._run(bank_statements) + else: + return super(CashInvoiceOut, self).run() diff --git a/pos_session_pay_invoice/wizard/cash_invoice_out.xml b/pos_session_pay_invoice/wizard/cash_invoice_out.xml new file mode 100644 index 00000000..8a84f40f --- /dev/null +++ b/pos_session_pay_invoice/wizard/cash_invoice_out.xml @@ -0,0 +1,12 @@ + + + +