commit f659ed99ad588e847702f1f2b87c79aad957f502 Author: Suherdy Yacob Date: Fri Mar 20 15:18:52 2026 +0700 first commit diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..0650744 --- /dev/null +++ b/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/__manifest__.py b/__manifest__.py new file mode 100644 index 0000000..836467a --- /dev/null +++ b/__manifest__.py @@ -0,0 +1,18 @@ +{ + 'name': 'POS Shift Close', + 'version': '1.0', + 'category': 'Sales/Point of Sale', + 'summary': 'Bypass POS session close draft order validation for morning shift and transfer orders.', + 'description': """ +This module allows cashiers on the morning shift to close the POS session even if there are unpaid/draft orders. +Those pending orders will automatically be moved to the next session when it is opened. +The afternoon/evening shift will maintain the standard Odoo behavior (blocking closure if pending orders exist). + """, + 'depends': ['point_of_sale'], + 'data': [ + 'views/res_config_settings_views.xml', + ], + 'installable': True, + 'application': False, + 'license': 'LGPL-3', +} diff --git a/__pycache__/__init__.cpython-312.pyc b/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..397fa72 Binary files /dev/null and b/__pycache__/__init__.cpython-312.pyc differ diff --git a/models/__init__.py b/models/__init__.py new file mode 100644 index 0000000..55380fe --- /dev/null +++ b/models/__init__.py @@ -0,0 +1,3 @@ +from . import pos_config +from . import pos_session +from . import res_config_settings diff --git a/models/__pycache__/__init__.cpython-312.pyc b/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..8be57cc Binary files /dev/null and b/models/__pycache__/__init__.cpython-312.pyc differ diff --git a/models/__pycache__/pos_config.cpython-312.pyc b/models/__pycache__/pos_config.cpython-312.pyc new file mode 100644 index 0000000..82441fc Binary files /dev/null and b/models/__pycache__/pos_config.cpython-312.pyc differ diff --git a/models/__pycache__/pos_session.cpython-312.pyc b/models/__pycache__/pos_session.cpython-312.pyc new file mode 100644 index 0000000..d233cf2 Binary files /dev/null and b/models/__pycache__/pos_session.cpython-312.pyc differ diff --git a/models/__pycache__/res_config_settings.cpython-312.pyc b/models/__pycache__/res_config_settings.cpython-312.pyc new file mode 100644 index 0000000..65aa190 Binary files /dev/null and b/models/__pycache__/res_config_settings.cpython-312.pyc differ diff --git a/models/pos_config.py b/models/pos_config.py new file mode 100644 index 0000000..f1c49b1 --- /dev/null +++ b/models/pos_config.py @@ -0,0 +1,10 @@ +from odoo import fields, models + +class PosConfig(models.Model): + _inherit = 'pos.config' + + morning_shift_end_time = fields.Float( + string='Morning Shift End Time', + default=15.0, + help="Time (in hours) after which closing a POS session will run the standard checks for draft orders, preventing closure. Before this time, the check is bypassed so the morning shift can close." + ) diff --git a/models/pos_session.py b/models/pos_session.py new file mode 100644 index 0000000..872b937 --- /dev/null +++ b/models/pos_session.py @@ -0,0 +1,43 @@ +from odoo import models +from odoo.exceptions import UserError +import pytz +from datetime import datetime + +class PosSession(models.Model): + _inherit = 'pos.session' + + def _check_if_no_draft_orders(self): + """ + Bypass standard Odoo draft order check if the current time in the user's timezone + is before the configured morning_shift_end_time on the pos.config. + """ + draft_orders = self.get_session_orders().filtered(lambda order: order.state == 'draft') + if draft_orders and self.config_id.morning_shift_end_time: + user_tz = pytz.timezone(self.env.user.tz or 'UTC') + local_dt = datetime.now(pytz.utc).astimezone(user_tz) + local_hour = local_dt.hour + (local_dt.minute / 60.0) + + if local_hour < self.config_id.morning_shift_end_time: + # It's the morning shift, we bypass the check + return True + + return super()._check_if_no_draft_orders() + + def _set_opening_control_data(self, cashbox_value: int, notes: str): + """ + When a new session is opened, we pull the draft orders from the previous + closed sessions of this same config, so the afternoon shift can handle them. + """ + res = super()._set_opening_control_data(cashbox_value, notes) + + # Find draft orders belonging to closed sessions of the same POS + pending_orders = self.env['pos.order'].search([ + ('session_id.config_id', '=', self.config_id.id), + ('session_id.state', '=', 'closed'), + ('state', '=', 'draft') + ]) + + if pending_orders: + pending_orders.write({'session_id': self.id}) + + return res diff --git a/models/res_config_settings.py b/models/res_config_settings.py new file mode 100644 index 0000000..7adc510 --- /dev/null +++ b/models/res_config_settings.py @@ -0,0 +1,9 @@ +from odoo import fields, models + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + pos_morning_shift_end_time = fields.Float( + related='pos_config_id.morning_shift_end_time', + readonly=False, + ) diff --git a/views/res_config_settings_views.xml b/views/res_config_settings_views.xml new file mode 100644 index 0000000..3b284ea --- /dev/null +++ b/views/res_config_settings_views.xml @@ -0,0 +1,15 @@ + + + + res.config.settings.view.form.inherit.pos_shift_close + res.config.settings + + + + + + + + + +