diff --git a/pos_store_draft_order/README.rst b/pos_store_draft_order/README.rst new file mode 100644 index 00000000..13f5ca3b --- /dev/null +++ b/pos_store_draft_order/README.rst @@ -0,0 +1,109 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +================================== +Point Of Sale - Store Draft Orders +================================== + +Allow to close a Session even if there are some PoS Orders in draft state. + +By default, in Odoo, All PoS Orders must be in 'paid' or 'invoiced' state to +allow user to close the session. + +This module can be usefull to let Orders in draft 'state' for some customers. + +Feature +------- + +1. New computed field 'is_partial_paid' on PoS Order + +* This field is True, if the PoS order is in a draft state with some + payments; +* Forbid to close a session if there is a partial paid Order, to avoid to have + Account Move Lines that can not be reconciled; +* In the tree view, the partial_paid orders are displayed in red colors; + +.. image:: ./static/description/pos_order_states.png + :alt: Blue, red and black orders depending of payments. + +2. Possibility to close session + +* if a PoS order is in a 'draft' state (without any payment), the PoS Order + will be unassociated to the current session, when closing the session; +* When opening a new session, the PoS Orders in 'draft' state will be + associated to the new session, based on the user_id; + +**Workflow** + +* The PoS order 'Main/0004' is in draft state, before closing the session +.. image:: ./static/description/1_before_closing.png + +* The PoS order is unassociated of the closed session 'POS/2015/09/05/01' +.. image:: ./static/description/2_after_closing.png + +* The PoS order is associated to the new opened session 'POS/2015/09/05/02' +.. image:: ./static/description/3_after_opening.png + +Installation +============ + +This module will allow users to let orders in a draft state, only for orders +created in back-office. +If you want the same feature for the front-office PoS, please install both +modules : 'pos_store_draft_order' and 'pos_order_load'. + +Configuration +============= + +* A new field 'allow_store_draft_order' is available in PoS Config Model, to + allow or block the cashier to let Orders in a draft state when closing + session; + +Usage +===== + +To use this module, you need to: + +* go to ... + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/xxxx/8.0 + +For further information, please visit: + +* https://www.odoo.com/forum/help-1 + +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 +`here `_. + + +Credits +======= + +Contributors +------------ + +* Sylvain LE GAL (https://twitter.com/legalsylvain) +* Julien WESTE + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +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. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/pos_store_draft_order/__init__.py b/pos_store_draft_order/__init__.py new file mode 100644 index 00000000..630e671d --- /dev/null +++ b/pos_store_draft_order/__init__.py @@ -0,0 +1,2 @@ +# -*- encoding: utf-8 -*- +from . import model diff --git a/pos_store_draft_order/__openerp__.py b/pos_store_draft_order/__openerp__.py new file mode 100644 index 00000000..17c9ec22 --- /dev/null +++ b/pos_store_draft_order/__openerp__.py @@ -0,0 +1,41 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Point Of Sale - Store Draft Orders Module for Odoo +# Copyright (C) 2013-Today GRAP (http://www.grap.coop) +# @author Julien WESTE +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Point Of Sale - Store Draft Orders', + 'version': '8.0.1.0.0', + 'category': 'Point of Sale', + 'summary': "Allow users to close session with Draft Orders", + 'author': 'GRAP,Odoo Community Association (OCA)', + 'website': 'http://www.odoo-community.org', + 'license': 'AGPL-3', + 'depends': [ + 'point_of_sale', + ], + 'data': [ + 'view/view.xml', + ], + 'demo': [ + 'demo/pos_config.yml', + ], +} diff --git a/pos_store_draft_order/demo/pos_config.yml b/pos_store_draft_order/demo/pos_config.yml new file mode 100644 index 00000000..fdf683e7 --- /dev/null +++ b/pos_store_draft_order/demo/pos_config.yml @@ -0,0 +1,24 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Point Of Sale - Store Draft Orders Module for Odoo +# Copyright (C) 2015-Today GRAP (http://www.grap.coop) +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +- !record {model: pos.config, id: point_of_sale.pos_config_main}: + allow_store_draft_order: True diff --git a/pos_store_draft_order/i18n/fr.po b/pos_store_draft_order/i18n/fr.po new file mode 100644 index 00000000..cc5efa45 --- /dev/null +++ b/pos_store_draft_order/i18n/fr.po @@ -0,0 +1,48 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_store_draft_order +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-10-19 11:47+0000\n" +"PO-Revision-Date: 2015-10-19 11:47+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_store_draft_order +#: field:pos.config,allow_store_draft_order:0 +msgid "Allow to Store Draft Orders" +msgstr "Autoriser les ardoises" + +#. module: pos_store_draft_order +#: help:pos.config,allow_store_draft_order:0 +msgid "If you check this field, users will have the possibility to let some PoS orders in a draft state, and allow the customer to paid later.\n" +"Order in a draft state will not generate entries during the close of the session." +msgstr "Si vous cochez cette case, les utilisateurs auront la possibilité de laisser certaines ventes à l'état de brouillon, et autorisera le client à payer plus tard.\n" +"Ces ventes à l'état de brouillon ne génèrent pas d'écritures comptables lors de la clôture de la session." + +#. module: pos_store_draft_order +#: field:pos.order,is_partial_paid:0 +msgid "Is Partially Paid" +msgstr "Est partiellement payée" + +#. module: pos_store_draft_order +#: model:ir.model,name:pos_store_draft_order.model_pos_order +msgid "Point of Sale" +msgstr "Point de Vente" + +#. module: pos_store_draft_order +#: code:addons/pos_store_draft_order/model/pos_session.py:63 +#, python-format +msgid "You cannot confirm this session, because '%s' is still in 'draft' state with associated payments.\n" +"\n" +" Please finish to pay this Order first." +msgstr "Vous ne pouvez pas fermer cette session car '%s' est en brouillon avec des paiments.\n" +"\n" +"Merci de compléter les paiments." diff --git a/pos_store_draft_order/i18n/pos_store_draft_order.pot b/pos_store_draft_order/i18n/pos_store_draft_order.pot new file mode 100644 index 00000000..ac2c51a8 --- /dev/null +++ b/pos_store_draft_order/i18n/pos_store_draft_order.pot @@ -0,0 +1,51 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * pos_store_draft_order +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 8.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2015-10-19 11:52+0000\n" +"PO-Revision-Date: 2015-10-19 11:52+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_store_draft_order +#: field:pos.config,allow_store_draft_order:0 +msgid "Allow to Store Draft Orders" +msgstr "" + +#. module: pos_store_draft_order +#: help:pos.config,allow_store_draft_order:0 +msgid "If you check this field, users will have the possibility to let some PoS orders in a draft state, and allow the customer to paid later.\n" +"Order in a draft state will not generate entries during the close of the session." +msgstr "" + +#. module: pos_store_draft_order +#: field:pos.order,is_partial_paid:0 +msgid "Is Partially Paid" +msgstr "" + +#. module: pos_store_draft_order +#: model:ir.model,name:pos_store_draft_order.model_pos_order +msgid "Point of Sale" +msgstr "" + +#. module: pos_store_draft_order +#: code:addons/pos_store_draft_order/model/pos_session.py:63 +#, python-format +msgid "You cannot confirm this session, because '%s' is still in 'draft' state with associated payments.\n" +"\n" +" Please finish to pay this Order first." +msgstr "" + +#. module: pos_store_draft_order +#: view:pos.order:pos_store_draft_order.pos_order_view_form +msgid "red: is_partial_paid==True; blue: state=='draft'; gray: state in ('done','cancel'); black: state not in ('done','cancel')" +msgstr "" + diff --git a/pos_store_draft_order/model/__init__.py b/pos_store_draft_order/model/__init__.py new file mode 100644 index 00000000..fc11e61f --- /dev/null +++ b/pos_store_draft_order/model/__init__.py @@ -0,0 +1,4 @@ +# -*- encoding: utf-8 -*- +from . import pos_order +from . import pos_session +from . import pos_config diff --git a/pos_store_draft_order/model/pos_config.py b/pos_store_draft_order/model/pos_config.py new file mode 100644 index 00000000..0e3bf4ed --- /dev/null +++ b/pos_store_draft_order/model/pos_config.py @@ -0,0 +1,36 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Point Of Sale - Store Draft Orders Module for Odoo +# Copyright (C) 2013-Today GRAP (http://www.grap.coop) +# @author Julien WESTE +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import fields, models + + +class PosConfig(models.Model): + _inherit = 'pos.config' + + # Column Section + allow_store_draft_order = fields.Boolean( + string='Allow to Store Draft Orders', help="If you check this field," + " users will have the possibility to let some PoS orders in a draft" + " state, and allow the customer to paid later.\n" + "Order in a draft state will not generate entries during the close" + " of the session.") diff --git a/pos_store_draft_order/model/pos_order.py b/pos_store_draft_order/model/pos_order.py new file mode 100644 index 00000000..4ff1aa75 --- /dev/null +++ b/pos_store_draft_order/model/pos_order.py @@ -0,0 +1,40 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Point Of Sale - Store Draft Orders Module for Odoo +# Copyright (C) 2013-Today GRAP (http://www.grap.coop) +# @author Julien WESTE +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import api, fields, models + + +class PosOrder(models.Model): + _inherit = 'pos.order' + + # Column Section + is_partial_paid = fields.Boolean( + string='Is Partially Paid', compute='compute_is_partial_paid', + store=True) + + # Compute Section + @api.one + @api.depends('state', 'statement_ids') + def compute_is_partial_paid(self): + self.is_partial_paid =\ + (self.state == 'draft') and len(self.statement_ids) != 0 diff --git a/pos_store_draft_order/model/pos_session.py b/pos_store_draft_order/model/pos_session.py new file mode 100644 index 00000000..5cf5be85 --- /dev/null +++ b/pos_store_draft_order/model/pos_session.py @@ -0,0 +1,67 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Point Of Sale - Store Draft Orders Module for Odoo +# Copyright (C) 2013-Today GRAP (http://www.grap.coop) +# @author Julien WESTE +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import api, models +from openerp.exceptions import Warning +from openerp.tools.translate import _ + + +class PosSession(models.Model): + _inherit = 'pos.session' + + # Overload Section + @api.model + def create(self, vals): + """Recover all PoS Order in 'draft' state and associate them to the new + Pos Session""" + order_obj = self.env['pos.order'] + + res = super(PosSession, self).create(vals) + draftOrders = order_obj.search([ + ('state', '=', 'draft'), ('user_id', '=', self._uid)]) + draftOrders.write({'session_id': res.id}) + + return res + + @api.multi + def wkf_action_closing_control(self): + """Remove all PoS Orders in 'draft' to the sessions we want + to close. + Check if there is any Partial Paid Orders""" + self._remove_session_from_draft_orders() + return super(PosSession, self).wkf_action_closing_control() + + # Custom Section + @api.one + def _remove_session_from_draft_orders(self): + for order in self.order_ids: + # Check if there is a partial payment + if order.is_partial_paid: + raise Warning(_( + "You cannot confirm this session, because '%s' is" + " still in 'draft' state with associated payments.\n\n" + " Please finish to pay this Order first." % (order.name))) + # remove session id on the current Order if it is in draft state + if order.state == 'draft' and\ + self.config_id.allow_store_draft_order: + order.write({'session_id': False}) diff --git a/pos_store_draft_order/static/description/1_before_closing.png b/pos_store_draft_order/static/description/1_before_closing.png new file mode 100644 index 00000000..d61aeb02 Binary files /dev/null and b/pos_store_draft_order/static/description/1_before_closing.png differ diff --git a/pos_store_draft_order/static/description/2_after_closing.png b/pos_store_draft_order/static/description/2_after_closing.png new file mode 100644 index 00000000..1caff482 Binary files /dev/null and b/pos_store_draft_order/static/description/2_after_closing.png differ diff --git a/pos_store_draft_order/static/description/3_after_opening.png b/pos_store_draft_order/static/description/3_after_opening.png new file mode 100644 index 00000000..c71499fe Binary files /dev/null and b/pos_store_draft_order/static/description/3_after_opening.png differ diff --git a/pos_store_draft_order/static/description/icon.png b/pos_store_draft_order/static/description/icon.png new file mode 100644 index 00000000..2dbd4d7c Binary files /dev/null and b/pos_store_draft_order/static/description/icon.png differ diff --git a/pos_store_draft_order/static/description/pos_order_states.png b/pos_store_draft_order/static/description/pos_order_states.png new file mode 100644 index 00000000..2880c95a Binary files /dev/null and b/pos_store_draft_order/static/description/pos_order_states.png differ diff --git a/pos_store_draft_order/tests/__init__.py b/pos_store_draft_order/tests/__init__.py new file mode 100644 index 00000000..f818a6d8 --- /dev/null +++ b/pos_store_draft_order/tests/__init__.py @@ -0,0 +1,2 @@ +# -*- encoding: utf-8 -*- +from . import test_pos_store_draft_order diff --git a/pos_store_draft_order/tests/test_pos_store_draft_order.py b/pos_store_draft_order/tests/test_pos_store_draft_order.py new file mode 100644 index 00000000..aaee0bbd --- /dev/null +++ b/pos_store_draft_order/tests/test_pos_store_draft_order.py @@ -0,0 +1,95 @@ +# -*- encoding: utf-8 -*- +############################################################################## +# +# Point Of Sale - Store Draft Orders Module for Odoo +# Copyright (C) 2015-Today GRAP (http://www.grap.coop) +# @author Sylvain LE GAL (https://twitter.com/legalsylvain) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.exceptions import Warning +from openerp.tests.common import TransactionCase + + +class TestPosStoreDraftOrder(TransactionCase): + + def setUp(self): + super(TestPosStoreDraftOrder, self).setUp() + + self.session_obj = self.env['pos.session'] + self.order_obj = self.env['pos.order'] + + self.pos_config_id = self.ref('point_of_sale.pos_config_main') + self.product_id = self.ref('product.product_product_48') + self.cash_journal_id = self.ref('account.cash_journal') + + # Test Section + def test_01_allow_draft_order_unpaid(self): + """Test the possibility to let a PoS Order in a draft state if it is + not paid at all.""" + # Open a new session + session_1 = self.session_obj.create({'config_id': self.pos_config_id}) + + # Create Order + order = self.order_obj.create({ + 'session_id': session_1.id, + 'lines': [[0, False, { + 'discount': 0, + 'price_unit': 10, + 'product_id': self.product_id, + 'qty': 1}]] + }) + + # Close Session + session_1.signal_workflow('close') + + self.assertEquals( + session_1.state, 'closed', + "Unpaid Draft Orders must not block the closing process of the" + " associated session.") + + # Open a second session + session_2 = self.session_obj.create({'config_id': self.pos_config_id}) + + self.assertEquals( + order.session_id.id, session_2.id, + "Draft Orders of a previous session must be associated to the" + " new opened session to allow payment.") + + # Test Section + def test_02_block_draft_order_partial_paid(self): + """Test the unpossibility to let a PoS Order in a draft state if it is + in a partial paid state.""" + # Open a new session + session_1 = self.session_obj.create({'config_id': self.pos_config_id}) + + # Create Order + order = self.order_obj.create({ + 'session_id': session_1.id, + 'lines': [[0, False, { + 'discount': 0, + 'price_unit': 10, + 'product_id': self.product_id, + 'qty': 3}]] + }) + + # Make partial payment + self.order_obj.add_payment( + order.id, {'amount': 1, 'journal': self.cash_journal_id}) + + # Try Close Session (Must fail) + with self.assertRaises(Warning): + session_1.signal_workflow('close') diff --git a/pos_store_draft_order/view/view.xml b/pos_store_draft_order/view/view.xml new file mode 100644 index 00000000..858d50e9 --- /dev/null +++ b/pos_store_draft_order/view/view.xml @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + pos.order + + + + + + + red: is_partial_paid==True; blue: state=='draft'; gray: state in ('done','cancel'); black: state not in ('done','cancel') + + + + + + + pos.config + + + + + + + + + +