diff --git a/pos_session_closing_stock_error/__init__.py b/pos_session_closing_stock_error/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/pos_session_closing_stock_error/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/pos_session_closing_stock_error/__manifest__.py b/pos_session_closing_stock_error/__manifest__.py new file mode 100644 index 00000000..b449e013 --- /dev/null +++ b/pos_session_closing_stock_error/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2020 Druidoo - Iván Todorovich +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl). +{ + "name": "Point of Sale - Prevent closing sessions with stock errors", + "summary": "Prevent closing PoS Sessions that have stock errors", + "version": "12.0.1.0.0", + "development_status": "Beta", + "category": "Point of Sale", + "website": "https://github.com/OCA/pos", + "author": "Druidoo, Odoo Community Association (OCA)", + "maintainers": ["ivantodorovich"], + "license": "LGPL-3", + "depends": [ + "point_of_sale", + ], + "data": [ + "views/pos_config_views.xml", + ], +} diff --git a/pos_session_closing_stock_error/models/__init__.py b/pos_session_closing_stock_error/models/__init__.py new file mode 100644 index 00000000..20c47743 --- /dev/null +++ b/pos_session_closing_stock_error/models/__init__.py @@ -0,0 +1,2 @@ +from . import pos_config +from . import pos_session diff --git a/pos_session_closing_stock_error/models/pos_config.py b/pos_session_closing_stock_error/models/pos_config.py new file mode 100644 index 00000000..b5d3b3f5 --- /dev/null +++ b/pos_session_closing_stock_error/models/pos_config.py @@ -0,0 +1,11 @@ +from odoo import models, fields + + +class PosConfig(models.Model): + _inherit = 'pos.config' + + allow_session_closing_with_stock_errors = fields.Boolean( + string="Allow closing sessions with stock errors", + help="If disabled, closing a session that has stock errors " + "will be blocked.", + ) diff --git a/pos_session_closing_stock_error/models/pos_session.py b/pos_session_closing_stock_error/models/pos_session.py new file mode 100644 index 00000000..9354ce24 --- /dev/null +++ b/pos_session_closing_stock_error/models/pos_session.py @@ -0,0 +1,20 @@ +from odoo import models, _ +from odoo.exceptions import ValidationError + + +class PosSession(models.Model): + _inherit = 'pos.session' + + def action_pos_session_close(self): + for rec in self: + if ( + rec.picking_count + and not rec.config_id.allow_session_closing_with_stock_errors + ): + raise ValidationError(_( + "It's not possible to close the session %s because it has " + "%i picking error(s).\nPlease resolve them or enable " + "'Allow closing sessions with stock errors' in " + "PoS Configuration: %s.") % ( + rec.name, rec.picking_count, rec.config_id.name)) + return super().action_pos_session_close() diff --git a/pos_session_closing_stock_error/readme/CONFIGURE.rst b/pos_session_closing_stock_error/readme/CONFIGURE.rst new file mode 100644 index 00000000..d5023ff9 --- /dev/null +++ b/pos_session_closing_stock_error/readme/CONFIGURE.rst @@ -0,0 +1,2 @@ +In any Point of Sale configuration (pos.config), disable "Allow closing sessions with stock errors" +to prevent closing sessions with stock errors. diff --git a/pos_session_closing_stock_error/readme/CONTRIBUTORS.rst b/pos_session_closing_stock_error/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000..25a7ad47 --- /dev/null +++ b/pos_session_closing_stock_error/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Iván Todorovich diff --git a/pos_session_closing_stock_error/readme/DESCRIPTION.rst b/pos_session_closing_stock_error/readme/DESCRIPTION.rst new file mode 100644 index 00000000..a804ff36 --- /dev/null +++ b/pos_session_closing_stock_error/readme/DESCRIPTION.rst @@ -0,0 +1,7 @@ +Prevent closing PoS Sessions that have stock.picking errors. + +These errors usually happen when selecting a wrong serial/lot number in the POS. + +.. image:: ../static/description/session_with_errors.png + +.. image:: ../static/description/error_msg.png diff --git a/pos_session_closing_stock_error/static/description/error_msg.png b/pos_session_closing_stock_error/static/description/error_msg.png new file mode 100644 index 00000000..fde766dd Binary files /dev/null and b/pos_session_closing_stock_error/static/description/error_msg.png differ diff --git a/pos_session_closing_stock_error/static/description/session_with_errors.png b/pos_session_closing_stock_error/static/description/session_with_errors.png new file mode 100644 index 00000000..06c1bc96 Binary files /dev/null and b/pos_session_closing_stock_error/static/description/session_with_errors.png differ diff --git a/pos_session_closing_stock_error/tests/__init__.py b/pos_session_closing_stock_error/tests/__init__.py new file mode 100644 index 00000000..abf4b32c --- /dev/null +++ b/pos_session_closing_stock_error/tests/__init__.py @@ -0,0 +1 @@ +from . import test_pos diff --git a/pos_session_closing_stock_error/tests/test_pos.py b/pos_session_closing_stock_error/tests/test_pos.py new file mode 100644 index 00000000..494d5a48 --- /dev/null +++ b/pos_session_closing_stock_error/tests/test_pos.py @@ -0,0 +1,64 @@ +from odoo import fields +from odoo.exceptions import ValidationError +from odoo.tests.common import TransactionCase + + +class TestPos(TransactionCase): + + def setUp(self): + super().setUp() + self.pos_config = self.env.ref('point_of_sale.pos_config_main').copy({ + 'name': 'Block PoS Session with Stock Error', + 'allow_session_closing_with_stock_errors': False, + }) + self.product_tracking = self.env.ref( + 'point_of_sale.desk_organizer' + ).copy({ + 'name': 'Product with Tracking', + 'tracking': 'serial', + }) + + def test_session_closing_with_errors(self): + pos_session = self.env['pos.session'].create({ + 'config_id': self.pos_config.id, + }) + # We create an order that will generate errors + # (the product requires a serial number) + pos_order = self.env['pos.order'].create({ + 'session_id': pos_session.id, + 'lines': [(0, 0, { + 'name': 'OL/0001', + 'product_id': self.product_tracking.id, + 'tax_ids': False, + 'qty': 1.0, + 'price_unit': 1000, + 'price_subtotal': 1000, + 'price_subtotal_incl': 1000, + })], + 'amount_total': 1000.0, + 'amount_tax': 0.0, + 'amount_paid': 1000.0, + 'amount_return': 0.0, + }) + # Register order payment + pos_order.add_payment({ + 'amount': 1000, + 'payment_date': fields.Datetime.now(), + 'statement_id': pos_session.statement_ids[0].id, + 'payment_name': 'PAY', + 'journal': pos_session.statement_ids[0].journal_id.id, + }) + # Set ending balance in statement + pos_session.statement_ids[0].write({ + 'balance_end_real': pos_session.statement_ids[0].balance_end + }) + pos_order.action_pos_order_paid() + # Blocked because we have errors + with self.assertRaises(ValidationError): + pos_session.action_pos_session_close() + # Enable closing with errors + self.pos_config.write({ + 'allow_session_closing_with_stock_errors': True, + }) + # Should be possible to close now + pos_session.action_pos_session_close() diff --git a/pos_session_closing_stock_error/views/pos_config_views.xml b/pos_session_closing_stock_error/views/pos_config_views.xml new file mode 100644 index 00000000..1f3b0b6e --- /dev/null +++ b/pos_session_closing_stock_error/views/pos_config_views.xml @@ -0,0 +1,22 @@ + + + + pos.config + + +
+
+
+ +
+
+
+
+
+
+
+