From 2e854b88b484cbd88de13b5853676fb002727f0d Mon Sep 17 00:00:00 2001 From: Sylvain LE GAL Date: Thu, 11 Jul 2019 15:03:22 +0200 Subject: [PATCH] [ADD] module from 8.0 serie --- pos_invoicing/README.rst | 93 ++++++++++++++++++ pos_invoicing/__init__.py | 1 + pos_invoicing/__openerp__.py | 27 +++++ pos_invoicing/demo/res_groups.xml | 13 +++ pos_invoicing/i18n/fr.po | 50 ++++++++++ pos_invoicing/models/__init__.py | 5 + pos_invoicing/models/account_invoice.py | 39 ++++++++ pos_invoicing/models/account_voucher.py | 28 ++++++ pos_invoicing/models/pos_order.py | 18 ++++ pos_invoicing/models/pos_session.py | 70 +++++++++++++ pos_invoicing/readme/CONTRIBUTORS.rst | 2 + pos_invoicing/readme/DESCRIPTION.rst | 25 +++++ pos_invoicing/readme/ROADMAP.rst | 4 + .../description/account_invoice_form.png | Bin 0 -> 32949 bytes pos_invoicing/static/description/icon.png | Bin 0 -> 4113 bytes pos_invoicing/tests/__init__.py | 2 + pos_invoicing/tests/test_module.py | 92 +++++++++++++++++ pos_invoicing/views/view_account_invoice.xml | 21 ++++ 18 files changed, 490 insertions(+) create mode 100644 pos_invoicing/README.rst create mode 100644 pos_invoicing/__init__.py create mode 100644 pos_invoicing/__openerp__.py create mode 100644 pos_invoicing/demo/res_groups.xml create mode 100644 pos_invoicing/i18n/fr.po create mode 100644 pos_invoicing/models/__init__.py create mode 100644 pos_invoicing/models/account_invoice.py create mode 100644 pos_invoicing/models/account_voucher.py create mode 100644 pos_invoicing/models/pos_order.py create mode 100644 pos_invoicing/models/pos_session.py create mode 100644 pos_invoicing/readme/CONTRIBUTORS.rst create mode 100644 pos_invoicing/readme/DESCRIPTION.rst create mode 100644 pos_invoicing/readme/ROADMAP.rst create mode 100644 pos_invoicing/static/description/account_invoice_form.png create mode 100644 pos_invoicing/static/description/icon.png create mode 100644 pos_invoicing/tests/__init__.py create mode 100644 pos_invoicing/tests/test_module.py create mode 100644 pos_invoicing/views/view_account_invoice.xml diff --git a/pos_invoicing/README.rst b/pos_invoicing/README.rst new file mode 100644 index 00000000..c8b17163 --- /dev/null +++ b/pos_invoicing/README.rst @@ -0,0 +1,93 @@ +========================= +Point Of Sale - Invoicing +========================= + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! 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-grap%2Fgrap--odoo--incubator-lightgray.png?logo=github + :target: https://github.com/grap/grap-odoo-incubator/tree/8.0/pos_invoicing + :alt: grap/grap-odoo-incubator + +|badge1| |badge2| |badge3| + +When you pay a pos_order, and then create an invoice : + +* you mustn't register a payment against the invoice as the payment + already exists in POS +* The POS payment will be reconciled with the invoice when the session + is closed +* You mustn't modify the invoice because the amount could become + different from the one registered in POS. Thus we have to + automatically validate the created invoice + +Functionality +------------- +About the invoices created from POS after payment: + +* automatically validate them and don't allow modifications +* Disable the Pay Button +* Don't display them in the Customer Payment tool + +Technically +----------- + +add a ``pos_pending_payment`` field on the ``account.invoice`` to mark the +items that shouldn't be paid. + +.. figure:: https://raw.githubusercontent.com/grap/grap-odoo-incubator/8.0/pos_invoicing/static/description/account_invoice_form.png + +**Table of contents** + +.. contents:: + :local: + +Known issues / Roadmap +====================== + +* This module reconcile invoiced orders only if a customer has one invoice per + session. + +* It should be great to use the OCA module ``pos_autoreconcile``. + +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 +~~~~~~~ + +* GRAP + +Contributors +~~~~~~~~~~~~ + +* Sylvain LE GAL +* Julien WESTE + +Maintainers +~~~~~~~~~~~ + + + +This module is part of the `grap/grap-odoo-incubator `_ project on GitHub. + + +You are welcome to contribute. diff --git a/pos_invoicing/__init__.py b/pos_invoicing/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/pos_invoicing/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_invoicing/__openerp__.py b/pos_invoicing/__openerp__.py new file mode 100644 index 00000000..52e877d7 --- /dev/null +++ b/pos_invoicing/__openerp__.py @@ -0,0 +1,27 @@ +# coding: utf-8 +# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) +# @author: Julien WESTE +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': 'Point Of Sale - Invoicing', + 'summary': 'Handle invoicing from Point Of Sale', + 'version': '8.0.3.0.0', + 'category': 'Point of Sale', + 'author': 'GRAP', + 'website': 'http://www.grap.coop', + 'license': 'AGPL-3', + 'depends': [ + 'point_of_sale', + ], + 'data': [ + 'views/view_account_invoice.xml', + ], + 'demo': [ + 'demo/res_groups.xml', + ], + 'images': [ + 'static/description/account_invoice_form.png', + ], + 'installable': False, +} diff --git a/pos_invoicing/demo/res_groups.xml b/pos_invoicing/demo/res_groups.xml new file mode 100644 index 00000000..2c5779c4 --- /dev/null +++ b/pos_invoicing/demo/res_groups.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/pos_invoicing/i18n/fr.po b/pos_invoicing/i18n/fr.po new file mode 100644 index 00000000..675f5b7c --- /dev/null +++ b/pos_invoicing/i18n/fr.po @@ -0,0 +1,50 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_invoicing +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-08-02 13:14+0000\n" +"PO-Revision-Date: 2018-08-02 13:14+0000\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_invoicing +#: model:ir.model,name:pos_invoicing.model_account_voucher +msgid "Accounting Voucher" +msgstr "Justificatif comptable" + +#. module: pos_invoicing +#: help:account.invoice,pos_pending_payment:0 +msgid "Indicates an invoice for which there are pending payments in the Point of Sale. \n" +"The invoice will be marked as paid when the session will be closed." +msgstr "La case est cochée si il y a des paiements en cours dans le point de vente. \n" +"La facture sera marquée comme payée quand la session sera fermée." + +#. module: pos_invoicing +#: model:ir.model,name:pos_invoicing.model_account_invoice +msgid "Invoice" +msgstr "Facture" + +#. module: pos_invoicing +#: view:account.invoice:pos_invoicing.view_account_invoice_form +#: field:account.invoice,pos_pending_payment:0 +msgid "PoS - Pending Payment" +msgstr "PdV - Paiement en cours" + +#. module: pos_invoicing +#: model:ir.model,name:pos_invoicing.model_pos_order +msgid "Point of Sale" +msgstr "Point de Vente" + +#. module: pos_invoicing +#: code:addons/pos_invoicing/models/account_invoice.py:37 +#, python-format +msgid "You can not realize this action on the invoice(s) %s because there are pending payments in the Point of Sale." +msgstr "Vous ne pouvez pas réaliser cette action sur la / les facture(s) %s car il y a des paiements en cours dans le point de vente." diff --git a/pos_invoicing/models/__init__.py b/pos_invoicing/models/__init__.py new file mode 100644 index 00000000..e64dd454 --- /dev/null +++ b/pos_invoicing/models/__init__.py @@ -0,0 +1,5 @@ +# coding: utf-8 +from . import pos_order +from . import pos_session +from . import account_invoice +from . import account_voucher diff --git a/pos_invoicing/models/account_invoice.py b/pos_invoicing/models/account_invoice.py new file mode 100644 index 00000000..3c0d9285 --- /dev/null +++ b/pos_invoicing/models/account_invoice.py @@ -0,0 +1,39 @@ +# coding: utf-8 +# Copyright (C) 2018 - Today: GRAP (http://www.grap.coop) +# @author: Julien WESTE +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import _, api, fields, models +from openerp.exceptions import Warning as UserError + + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + pos_pending_payment = fields.Boolean( + string='PoS - Pending Payment', readonly=True, + oldname='forbid_payment', + help="Indicates an invoice for which there are pending payments in the" + " Point of Sale. \nThe invoice will be marked as paid when the session" + " will be closed.") + + # Overload Section + @api.multi + def action_cancel(self): + self._check_pos_pending_payment() + return super(AccountInvoice, self).action_cancel() + + @api.multi + def invoice_pay_customer(self): + self._check_pos_pending_payment() + return super(AccountInvoice, self).invoice_pay_customer() + + @api.multi + def _check_pos_pending_payment(self): + invoices = self.filtered(lambda x: x.pos_pending_payment) + if invoices: + raise UserError(_( + "You can not realize this action on the invoice(s) %s because" + " there are pending payments in the Point of Sale.") % ( + ', '.join(invoices.mapped('name')))) diff --git a/pos_invoicing/models/account_voucher.py b/pos_invoicing/models/account_voucher.py new file mode 100644 index 00000000..216979d7 --- /dev/null +++ b/pos_invoicing/models/account_voucher.py @@ -0,0 +1,28 @@ +# coding: utf-8 +# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) +# @author: Julien WESTE +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import api, models + + +class AccountVoucher(models.Model): + _inherit = 'account.voucher' + + # Override section + @api.multi + def recompute_voucher_lines( + self, partner_id, journal_id, price, currency_id, ttype, date): + + move_line_obj = self.env['account.move.line'] + + res = super(AccountVoucher, self).recompute_voucher_lines( + partner_id, journal_id, price, currency_id, ttype, date) + + for voucher_type in ['line_dr_ids', 'line_cr_ids']: + for voucher_line in res['value'][voucher_type]: + move_line = move_line_obj.browse(voucher_line['move_line_id']) + if move_line.invoice.pos_pending_payment: + res['value'][voucher_type].remove(voucher_line) + return res diff --git a/pos_invoicing/models/pos_order.py b/pos_invoicing/models/pos_order.py new file mode 100644 index 00000000..545f6b55 --- /dev/null +++ b/pos_invoicing/models/pos_order.py @@ -0,0 +1,18 @@ +# coding: utf-8 +# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) +# @author: Julien WESTE +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp import api, models + + +class PosOrder(models.Model): + _inherit = 'pos.order' + + @api.multi + def action_invoice(self): + res = super(PosOrder, self).action_invoice() + self.mapped('invoice_id').write({'pos_pending_payment': True}) + self.mapped('invoice_id').signal_workflow('invoice_open') + return res diff --git a/pos_invoicing/models/pos_session.py b/pos_invoicing/models/pos_session.py new file mode 100644 index 00000000..8f74195f --- /dev/null +++ b/pos_invoicing/models/pos_session.py @@ -0,0 +1,70 @@ +# coding: utf-8 +# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) +# @author: Julien WESTE +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import logging + +from openerp import api, models + +_logger = logging.getLogger(__name__) + + +class PosSession(models.Model): + _inherit = 'pos.session' + + @api.multi + def wkf_action_close(self): + move_line_obj = self.env['account.move.line'] + + res = super(PosSession, self).wkf_action_close() + + # Get All Pos Order invoiced during the current Sessions + orders = self.order_ids.filtered(lambda x: x.invoice_id) + + for order in orders: + # Get accounting partner + partner = order.partner_id.parent_id or order.partner_id + + # Search all Sale Move Lines to reconcile in Sale Journal + sale_move_lines = [] + sale_total = 0 + + for move_line in order.invoice_id.move_id.line_id: + if (move_line.partner_id.id == partner.id and + move_line.account_id.type == 'receivable'): + sale_move_lines.append(move_line) + sale_total += move_line.debit - move_line.credit + + # Search all move Line to reconcile in Payment Journals + payment_move_lines = [] + payment_total = 0 + + statement_ids = order.mapped('statement_ids.statement_id').ids + move_lines = move_line_obj.search([ + ('statement_id', 'in', statement_ids), + ('partner_id', '=', partner.id), + ('reconcile_id', '=', False)]) + for move_line in move_lines: + if (move_line.account_id.type == 'receivable'): + payment_move_lines.append(move_line) + payment_total += move_line.debit - move_line.credit + + # Try to reconcile + if payment_total != - sale_total: + # Unable to reconcile + _logger.warning( + "Unable to reconcile the payment of %s #%d." + "(partner : %s)" % ( + order.name, order.id, partner.name)) + else: + # Reconcile move lines + move_lines = move_line_obj.browse( + [x.id for x in sale_move_lines] + + [x.id for x in payment_move_lines]) + move_lines.reconcile('manual', False, False, False) + # Unflag the invoice as 'PoS Pending Payment' + order.invoice_id.pos_pending_payment = False + + return res diff --git a/pos_invoicing/readme/CONTRIBUTORS.rst b/pos_invoicing/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..7754037b --- /dev/null +++ b/pos_invoicing/readme/CONTRIBUTORS.rst @@ -0,0 +1,2 @@ +* Sylvain LE GAL +* Julien WESTE diff --git a/pos_invoicing/readme/DESCRIPTION.rst b/pos_invoicing/readme/DESCRIPTION.rst new file mode 100644 index 00000000..6875c12b --- /dev/null +++ b/pos_invoicing/readme/DESCRIPTION.rst @@ -0,0 +1,25 @@ +When you pay a pos_order, and then create an invoice : + +* you mustn't register a payment against the invoice as the payment + already exists in POS +* The POS payment will be reconciled with the invoice when the session + is closed +* You mustn't modify the invoice because the amount could become + different from the one registered in POS. Thus we have to + automatically validate the created invoice + +Functionality +------------- +About the invoices created from POS after payment: + +* automatically validate them and don't allow modifications +* Disable the Pay Button +* Don't display them in the Customer Payment tool + +Technically +----------- + +add a ``pos_pending_payment`` field on the ``account.invoice`` to mark the +items that shouldn't be paid. + +.. figure:: ../static/description/account_invoice_form.png diff --git a/pos_invoicing/readme/ROADMAP.rst b/pos_invoicing/readme/ROADMAP.rst new file mode 100644 index 00000000..e09f972b --- /dev/null +++ b/pos_invoicing/readme/ROADMAP.rst @@ -0,0 +1,4 @@ +* This module reconcile invoiced orders only if a customer has one invoice per + session. + +* It should be great to use the OCA module ``pos_autoreconcile``. diff --git a/pos_invoicing/static/description/account_invoice_form.png b/pos_invoicing/static/description/account_invoice_form.png new file mode 100644 index 0000000000000000000000000000000000000000..1ee85da2a9c1cc044967c45f2456d1847d12ae1e GIT binary patch literal 32949 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 literal 0 HcmV?d00001 diff --git a/pos_invoicing/static/description/icon.png b/pos_invoicing/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..7f52ccf0893dba796c5615289afe3f93c66a07ff GIT binary patch literal 4113 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 H>m2tVih0@a literal 0 HcmV?d00001 diff --git a/pos_invoicing/tests/__init__.py b/pos_invoicing/tests/__init__.py new file mode 100644 index 00000000..17b82062 --- /dev/null +++ b/pos_invoicing/tests/__init__.py @@ -0,0 +1,2 @@ +# coding: utf-8 +from . import test_module diff --git a/pos_invoicing/tests/test_module.py b/pos_invoicing/tests/test_module.py new file mode 100644 index 00000000..a7dc96ae --- /dev/null +++ b/pos_invoicing/tests/test_module.py @@ -0,0 +1,92 @@ +# coding: utf-8 +# Copyright (C) 2013 - Today: GRAP (http://www.grap.coop) +# @author: Julien WESTE +# @author: Sylvain LE GAL (https://twitter.com/legalsylvain) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import time +from openerp.tests.common import TransactionCase + + +class TestPosInvoicing(TransactionCase): + """Tests for POS Invoicing Module""" + + def setUp(self): + super(TestPosInvoicing, self).setUp() + + # Get Registry + self.session_obj = self.env['pos.session'] + self.order_obj = self.env['pos.order'] + + # Get Object + self.config = self.env.ref('point_of_sale.pos_config_main') + self.partner_A = self.env.ref('base.res_partner_2') + self.partner_B = self.env.ref('base.res_partner_12') + self.product = self.env.ref('product.product_product_48') + self.payment_journal = self.env.ref('account.cash_journal') + + # Test Section + def test_01_invoice_with_payment(self): + """Test the workflow: Draft Order -> Payment -> Invoice""" + # Opening Session + session = self.session_obj.create({'config_id': self.config.id}) + + # TODO FIXME, for the time being, reconciliation is not + # set if a customer make many invoices in the same pos session. + # self._create_order(session, self.partner_A, 100, True) + # self._create_order(session, self.partner_A, 200, True) + self._create_order(session, self.partner_B, 400, True) + + # The Invoice must be unpayable but in 'open' state + # Invoice created by order should be in open state + invoiced_orders = session.mapped('order_ids').filtered( + lambda x: x.state == 'invoiced') + invoices = invoiced_orders.mapped('invoice_id') + + self.assertEquals( + [x for x in invoices.mapped('state') if x != 'open'], [], + "All invoices generated from PoS should be in the 'open' state" + " when session is opened") + + self.assertEquals( + [x for x in invoices.mapped('pos_pending_payment') if not x], + [], + "All invoices generated from PoS should be marked as PoS Pending" + " Payment when session is opened") + + # Close Session + session.signal_workflow('close') + + self.assertEquals( + [x for x in invoices.mapped('state') if x != 'paid'], [], + "All invoices generated from PoS should be in the 'paid' state" + " when session is closed") + + self.assertEquals( + [x for x in invoices.mapped('pos_pending_payment') if x], [], + "Invoices generated from PoS should not be marked as PoS Pending" + " Payment when session is closed") + + # Private Section + def _create_order(self, session, partner, amount, with_invoice): + # create Pos Order + order = self.order_obj.create({ + 'session_id': session.id, + 'partner_id': partner.id, + 'lines': [[0, False, { + 'product_id': self.product.id, + 'qty': 1, + 'price_unit': amount, + }]], + }) + # Finish Payment + self.order_obj.add_payment(order.id, { + 'journal': self.payment_journal.id, + 'payment_date': time.strftime('%Y-%m-%d'), + 'amount': amount, + }) + # Mark as Paid + order.signal_workflow('paid') + if with_invoice: + order.action_invoice() + return order diff --git a/pos_invoicing/views/view_account_invoice.xml b/pos_invoicing/views/view_account_invoice.xml new file mode 100644 index 00000000..13a9fba2 --- /dev/null +++ b/pos_invoicing/views/view_account_invoice.xml @@ -0,0 +1,21 @@ + + + + + + account.invoice + + + + + + + + + +