Browse Source

APR 30: [ADD] Initial commit 'all_in_one_pos_kit'

pull/195/merge
Cybrosys Technologies 2 months ago
parent
commit
d7df509a86
  1. 45
      all_in_one_pos_kit/README.rst
  2. 24
      all_in_one_pos_kit/__init__.py
  3. 119
      all_in_one_pos_kit/__manifest__.py
  4. 23
      all_in_one_pos_kit/controllers/__init__.py
  5. 50
      all_in_one_pos_kit/controllers/all_in_one_pos_kit.py
  6. 64
      all_in_one_pos_kit/controllers/xlsx_report.py
  7. 7
      all_in_one_pos_kit/doc/RELEASE_NOTES.md
  8. 36
      all_in_one_pos_kit/models/__init__.py
  9. 85
      all_in_one_pos_kit/models/account_move.py
  10. 70
      all_in_one_pos_kit/models/meals_planning.py
  11. 113
      all_in_one_pos_kit/models/mrp_production.py
  12. 48
      all_in_one_pos_kit/models/multi_barcode_product.py
  13. 134
      all_in_one_pos_kit/models/pos_config.py
  14. 54
      all_in_one_pos_kit/models/pos_greetings.py
  15. 439
      all_in_one_pos_kit/models/pos_order.py
  16. 41
      all_in_one_pos_kit/models/pos_order_line.py
  17. 666
      all_in_one_pos_kit/models/pos_report.py
  18. 33
      all_in_one_pos_kit/models/pos_session.py
  19. 111
      all_in_one_pos_kit/models/product_product.py
  20. 74
      all_in_one_pos_kit/models/product_template.py
  21. 63
      all_in_one_pos_kit/models/res_config_settings.py
  22. 47
      all_in_one_pos_kit/models/res_users.py
  23. 58
      all_in_one_pos_kit/models/stock_production_lot.py
  24. 22
      all_in_one_pos_kit/report/__init__.py
  25. 45
      all_in_one_pos_kit/report/pos_order_report.py
  26. 411
      all_in_one_pos_kit/report/pos_order_report.xml
  27. 43
      all_in_one_pos_kit/security/all_in_one_pos_kit_security.xml
  28. 8
      all_in_one_pos_kit/security/ir.model.access.csv
  29. BIN
      all_in_one_pos_kit/static/description/assets/icons/check.png
  30. BIN
      all_in_one_pos_kit/static/description/assets/icons/chevron.png
  31. BIN
      all_in_one_pos_kit/static/description/assets/icons/cogs.png
  32. BIN
      all_in_one_pos_kit/static/description/assets/icons/consultation.png
  33. BIN
      all_in_one_pos_kit/static/description/assets/icons/ecom-black.png
  34. BIN
      all_in_one_pos_kit/static/description/assets/icons/education-black.png
  35. BIN
      all_in_one_pos_kit/static/description/assets/icons/hotel-black.png
  36. BIN
      all_in_one_pos_kit/static/description/assets/icons/license.png
  37. BIN
      all_in_one_pos_kit/static/description/assets/icons/lifebuoy.png
  38. BIN
      all_in_one_pos_kit/static/description/assets/icons/logo.png
  39. BIN
      all_in_one_pos_kit/static/description/assets/icons/manufacturing-black.png
  40. BIN
      all_in_one_pos_kit/static/description/assets/icons/pos-black.png
  41. BIN
      all_in_one_pos_kit/static/description/assets/icons/puzzle.png
  42. BIN
      all_in_one_pos_kit/static/description/assets/icons/restaurant-black.png
  43. BIN
      all_in_one_pos_kit/static/description/assets/icons/service-black.png
  44. BIN
      all_in_one_pos_kit/static/description/assets/icons/trading-black.png
  45. BIN
      all_in_one_pos_kit/static/description/assets/icons/training.png
  46. BIN
      all_in_one_pos_kit/static/description/assets/icons/update.png
  47. BIN
      all_in_one_pos_kit/static/description/assets/icons/user.png
  48. BIN
      all_in_one_pos_kit/static/description/assets/icons/wrench.png
  49. BIN
      all_in_one_pos_kit/static/description/assets/modules/1.png
  50. BIN
      all_in_one_pos_kit/static/description/assets/modules/2.png
  51. BIN
      all_in_one_pos_kit/static/description/assets/modules/3.png
  52. BIN
      all_in_one_pos_kit/static/description/assets/modules/4.png
  53. BIN
      all_in_one_pos_kit/static/description/assets/modules/5.png
  54. BIN
      all_in_one_pos_kit/static/description/assets/modules/6.png
  55. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/1.png
  56. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/10.png
  57. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/11.png
  58. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/12.png
  59. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/13.png
  60. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/14.png
  61. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/15.png
  62. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/16.png
  63. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/17.png
  64. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/18.png
  65. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/19.png
  66. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/2.png
  67. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/20.png
  68. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/21.png
  69. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/22.png
  70. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/23.png
  71. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/24.png
  72. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/25.png
  73. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/26.png
  74. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/27.png
  75. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/28.png
  76. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/29.png
  77. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/3.png
  78. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/30.png
  79. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/31.png
  80. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/32.png
  81. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/33.png
  82. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/34.png
  83. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/35.png
  84. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/36.png
  85. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/37.png
  86. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/38.png
  87. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/39.png
  88. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/4.png
  89. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/40.png
  90. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/41.png
  91. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/42.png
  92. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/43.png
  93. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/44.png
  94. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/45.png
  95. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/46.png
  96. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/47.png
  97. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/48.png
  98. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/5.png
  99. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/6.png
  100. BIN
      all_in_one_pos_kit/static/description/assets/screenshots/7.png

45
all_in_one_pos_kit/README.rst

@ -0,0 +1,45 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3
All in One POS Kit
==================
* This module combines a variety of POS features.
Configuration
=============
* No additional configurations needed
Company
-------
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__
License
-------
GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL v3)
(https://www.gnu.org/licenses/agpl-3.0-standalone.html)
Credits
-------
* Developer: (V15) Jumana Haseen @cybrosys, Contact: odoo@cybrosys.com
Contacts
--------
* Mail Contact : odoo@cybrosys.com
Bug Tracker
-----------
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported.
Maintainer
==========
.. image:: https://cybrosys.com/images/logo.png
:target: https://cybrosys.com
This module is maintained by Cybrosys Technologies.
For further information, please visit `Our Website <https://cybrosys.com/>`__
Further information
===================
HTML Description: `<static/description/index.html>`__

24
all_in_one_pos_kit/__init__.py

@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import controllers
from . import models
from . import report

119
all_in_one_pos_kit/__manifest__.py

@ -0,0 +1,119 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
{
'name': 'All in One POS Kit',
'version': '15.0.1.0.0',
'category': 'Point of Sale',
'summary': 'This module combines Different POS features',
'description': 'This module combines Different POS features',
'author': 'Cybrosys Techno Solutions',
'company': 'Cybrosys Techno Solutions',
'maintainer': 'Cybrosys Techno Solutions',
'website': "https://www.cybrosys.com",
'depends': ['hr', 'point_of_sale', 'mrp', 'sale', 'account','web','stock'],
'external_dependencies': {'python': ['twilio', 'pandas']},
'data': [
'security/all_in_one_pos_kit_security.xml',
'security/ir.model.access.csv',
'views/pos_report_views.xml',
'views/pos_config_views.xml',
'views/pos_order_views.xml',
'views/dashboard_views.xml',
'views/product_template_views.xml',
'views/res_users_views.xml',
'views/product_product_views.xml',
'views/pos_greetings_views.xml',
'views/meals_planning_views.xml',
'report/pos_order_report.xml',
],
'assets': {
'point_of_sale.assets': [
'web/static/lib/zxing-library/zxing-library.js',
'all_in_one_pos_kit/static/src/advanced_receipt/js/payment.js',
'all_in_one_pos_kit/static/src/custom_tip/js/PaymentScreen.js',
'all_in_one_pos_kit/static/src/exchange_product/js/models.js',
'all_in_one_pos_kit/static/src/exchange_product/js/AllOrderScreen.js',
'all_in_one_pos_kit/static/src/exchange_product/js/OrderButton.js',
'all_in_one_pos_kit/static/src/exchange_product/js/ExchangeOrder.js',
'all_in_one_pos_kit/static/src/exchange_product/scss/pos.scss',
'all_in_one_pos_kit/static/src/multi_barcode/js/search_bar.js',
'all_in_one_pos_kit/static/src/multi_barcode/js/ProductsWidgetControlPanel.js',
'all_in_one_pos_kit/static/src/multi_barcode/js/pos_scan.js',
'all_in_one_pos_kit/static/src/order_item_count/js/*',
'all_in_one_pos_kit/static/src/order_line_image/css/order_line_image.css',
'all_in_one_pos_kit/static/src/pos_auto_lot/js/auto_lot.js',
'all_in_one_pos_kit/static/src/pos_logo/js/pos_image_field.js',
'all_in_one_pos_kit/static/src/pos_logo/js/pos_receipt_image.js',
'all_in_one_pos_kit/static/src/pos_mrp_order/js/models.js',
'all_in_one_pos_kit/static/src/product_creation/js/product_create_popup.js',
'all_in_one_pos_kit/static/src/product_creation/js/product_create_button.js',
'all_in_one_pos_kit/static/src/product_magnify_image/js/MagnifyProductPopup.js',
'all_in_one_pos_kit/static/src/product_magnify_image/js/ProductScreen.js',
'all_in_one_pos_kit/static/src/product_magnify_image/css/pos_magnify_image.css',
'all_in_one_pos_kit/static/src/time_based_product/js/*',
'all_in_one_pos_kit/static/src/age_restricted/js/*',
'all_in_one_pos_kit/static/src/service_charge/js/service_charge_button.js',
'all_in_one_pos_kit/static/src/age_restricted/js/age_restrict.js',
'all_in_one_pos_kit/static/src/age_restricted/js/restrict_popup.js',
],
'web.assets_backend': [
'all_in_one_pos_kit/static/src/category_wise_receipt/js/Screens/ReceiptScreen/OrderReceipt.js',
'all_in_one_pos_kit/static/src/delete_order_line/js/*',
'all_in_one_pos_kit/static/src/dashboard/js/pos_dashboard.js',
'all_in_one_pos_kit/static/src/dashboard/css/pos_dashboard.css',
'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.js',
'all_in_one_pos_kit/static/src/mass_edit/js/pos_mass_edit_popup.js',
'all_in_one_pos_kit/static/src/mass_edit/js/pos_mass_edit_button.js',
'all_in_one_pos_kit/static/src/pos_num_show_hide/js/pos_numpad.js',
'all_in_one_pos_kit/static/src/pos_report/css/*',
'all_in_one_pos_kit/static/src/pos_report/js/*',
],
'web.assets_qweb': [
'all_in_one_pos_kit/static/src/advanced_receipt/xml/order_receipt_templates.xml',
'all_in_one_pos_kit/static/src/category_wise_receipt/xml/Screens/ReceiptScreen/OrderReceipt.xml',
'all_in_one_pos_kit/static/src/custom_tip/xml/PaymentScreen.xml',
'all_in_one_pos_kit/static/src/delete_order_line/xml/*',
'all_in_one_pos_kit/static/src/dashboard/xml/pos_dashboard.xml',
'all_in_one_pos_kit/static/src/exchange_product/xml/AllOrderScreen.xml',
'all_in_one_pos_kit/static/src/exchange_product/xml/ExchangeOrder.xml',
'all_in_one_pos_kit/static/src/exchange_product/xml/OrderButton.xml',
'all_in_one_pos_kit/static/src/mass_edit/xml/pos_mass_edit_popup.xml',
'all_in_one_pos_kit/static/src/mass_edit/xml/pos_mass_edit_button.xml',
'all_in_one_pos_kit/static/src/order_item_count/xml/*',
'all_in_one_pos_kit/static/src/order_line_image/xml/pos_order_line.xml',
'all_in_one_pos_kit/static/src/pos_logo/xml/pos_screen_image_views.xml',
'all_in_one_pos_kit/static/src/pos_logo/xml/pos_ticket_views.xml',
'all_in_one_pos_kit/static/src/pos_num_show_hide/xml/pos.xml',
'all_in_one_pos_kit/static/src/pos_report/xml/*',
'all_in_one_pos_kit/static/src/product_creation/xml/product_create_button.xml',
'all_in_one_pos_kit/static/src/product_creation/xml/product_create_popup.xml',
'all_in_one_pos_kit/static/src/product_magnify_image/xml/*',
'all_in_one_pos_kit/static/src/service_charge/xml/ServiceChargeButton.xml',
'all_in_one_pos_kit/static/src/age_restricted/xml/restrict_popup.xml'
]
},
'images': ['static/description/banner.jpg'],
'license': 'AGPL-3',
'installable': True,
'auto_install': False,
'application': False,
}

23
all_in_one_pos_kit/controllers/__init__.py

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import all_in_one_pos_kit
from . import xlsx_report

50
all_in_one_pos_kit/controllers/all_in_one_pos_kit.py

@ -0,0 +1,50 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import http
from odoo.http import request
class PosProductCreation(http.Controller):
@http.route('/create_product', type="json", auth="none")
def create_product(self, category, name, price, product_reference,
unit_measure, product_categories, barcode):
"""Function to create a product"""
if category == 'Consumable':
product_category = 'consu'
elif category == 'Service':
product_category = 'service'
elif category == 'Stockable':
product_category = 'product'
else:
product_category = ''
request.env['product.template'].sudo().create({
'name': name,
'type': product_category,
'default_code': product_reference,
'list_price': float(price),
'uom_id': int(unit_measure),
'uom_po_id': int(unit_measure),
'categ_id': int(product_categories),
'available_in_pos': True,
'barcode': barcode,
})

64
all_in_one_pos_kit/controllers/xlsx_report.py

@ -0,0 +1,64 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>).
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
import json
from odoo import http
from odoo.http import content_disposition, request
from odoo.addons.web.controllers.main import _serialize_exception
from odoo.tools import html_escape
class TBXLSXReportController(http.Controller):
"""A controller for generating and serving dynamic XLSX reports for POS
(Point of Sale) in Odoo."""
@http.route('/pos_dynamic_xlsx_reports', type='http', auth='user',
methods=['POST'], csrf=False)
def get_report_xlsx(self, model, options, output_format, report_data,
report_name, dfr_data):
"""
Generate and return a dynamic XLSX report based on the provided
parameters.
"""
uid = request.session.uid
report_obj = request.env[model].with_user(uid)
options = options
token = 'dummy-because-api-expects-one'
try:
if output_format == 'xlsx':
response = request.make_response(
None,
headers=[
('Content-Type', 'application/vnd.ms-excel'),
('Content-Disposition',
content_disposition(report_name + '.xlsx'))
]
)
report_obj.get_pos_xlsx_report(options, response, report_data)
response.set_cookie('fileToken', token)
return response
except Exception as e:
se = _serialize_exception(e)
error = {
'code': 200,
'message': 'Odoo Server Error',
'data': se
}
return request.make_response(html_escape(json.dumps(error)))

7
all_in_one_pos_kit/doc/RELEASE_NOTES.md

@ -0,0 +1,7 @@
## Module <all_in_one_pos_kit>
#### 21.03.2025
#### Version 15.0.1.0.0
#### ADD
- Initial commit for All in One POS Kit

36
all_in_one_pos_kit/models/__init__.py

@ -0,0 +1,36 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from . import account_move
from . import meals_planning
from . import mrp_production
from . import multi_barcode_product
from . import pos_config
from . import pos_greetings
from . import pos_order
from . import pos_order_line
from . import pos_report
from . import pos_session
from . import product_product
from . import product_template
from . import res_config_settings
from . import res_users
from . import stock_production_lot

85
all_in_one_pos_kit/models/account_move.py

@ -0,0 +1,85 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU LESSER
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3.
#
# 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 LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details.
#
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE
# (LGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
# #############################################################################
import math
import re
from odoo import api, fields, models
class AccountMove(models.Model):
"""Extends the 'account.move' model to include additional fields and
methods for managing account barcodes."""
_inherit = "account.move"
account_barcode = fields.Char(string='Account Barcode',
help='Enter the barcode associated with this'
'account.')
@api.model
def create(self, vals):
"""Changed variable name from res.account_barcode to
self.account_barcode"""
res = super(AccountMove, self).create(vals)
ean = self.generate_ean(str(res.id))
self.account_barcode = ean
return res
def ean_checksum(self, ean_code):
"""Returns the checksum of an ean string of length 13, returns -1 if
the string has the wrong length."""
if len(ean_code) != 13:
return -1
odd_sum = 0
even_sum = 0
ean_value = ean_code
reverse_value = ean_value[::-1]
final_ean = reverse_value[1:]
for i in range(len(final_ean)):
if i % 2 == 0:
odd_sum += int(final_ean[i])
else:
even_sum += int(final_ean[i])
total = (odd_sum * 3) + even_sum
check = int(10 - math.ceil(total % 10.0)) % 10
return check
def check_ean(self, ean_code):
"""Returns True if ean_code is a valid ean13 string, or null"""
if not ean_code:
return True
if len(ean_code) != 13:
return False
try:
int(ean_code)
except:
return False
return self.ean_checksum(ean_code) == int(ean_code[-1])
def generate_ean(self, ean):
"""Creates and returns a valid ean13 from an invalid one"""
if not ean:
return "0000000000000"
ean_code = re.sub("[A-Za-z]", "0", ean)
ean_code = re.sub("[^0-9]", "", ean_code)
ean_code = ean_code[:13]
if len(ean_code) < 13:
ean_code = ean_code + '0' * (13 - len(ean_code))
return ean_code[:-1] + str(self.ean_checksum(ean_code))

70
all_in_one_pos_kit/models/meals_planning.py

@ -0,0 +1,70 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class MealsPlanning(models.Model):
"""By using this model user can specify the time range and pos session"""
_name = 'meals.planning'
_description = "Product Planning"
_inherit = ['mail.thread', 'mail.activity.mixin']
name = fields.Char(string='Name', required=True,
help='Name for Product Planning', copy=False)
pos_ids = fields.Many2many('pos.config', string='Shops', copy=False,
help='Choose PoS Sessions', required=True)
time_from = fields.Float(string='From', required=True,
help='Add from time(24hr)')
time_to = fields.Float(string='To', required=True, help='Add to time(24hr)')
product_ids = fields.Many2many('product.product', string='Product',
domain=[('available_in_pos', '=', True)],
help='Choose products that should be '
'displayed')
state = fields.Selection([('activated', 'Activated'),
('deactivated', 'Deactivated')],
default='deactivated', string='Status',
help='Status of meals planning')
company_id = fields.Many2one('res.company', string='Company',
help='Choose the company',
default=lambda self: self.env.company)
@api.constrains('time_from', 'time_to')
def _check_time_range(self):
"""Validation for from time and to time"""
if self.time_from >= self.time_to:
raise ValidationError(_('From time must be less than to time!'))
if self.time_from > 24.0 or self.time_to > 24.0:
raise ValidationError(_(
'Time value greater than 24 is not valid!'))
def action_activate_meals_plan(self):
"""Change state to activate"""
self.write({
'state': 'activated'
})
def action_deactivate_meals_plan(self):
"""Change state to deactivate"""
self.write({
'state': 'deactivated'
})

113
all_in_one_pos_kit/models/mrp_production.py

@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import models
class MrpProduction(models.Model):
# Inheriting model mrp_production
_inherit = 'mrp.production'
def create_mrp_from_pos(self, products):
""" Function to create mrp order from pos"""
product_ids = []
if products:
for product in products:
flag = 1
if product_ids:
for product_id in product_ids:
if product_id['id'] == product['id']:
product_id['qty'] += product['qty']
flag = 0
if flag:
product_ids.append(product)
for prod in product_ids:
if prod['qty'] > 0:
bom_count = self.env['mrp.bom'].search(
[('product_tmpl_id', '=', prod['product_tmpl_id'])])
if bom_count:
bom_temp = self.env['mrp.bom'].search(
[('product_tmpl_id', '=', prod['product_tmpl_id']),
('product_id', '=', False)])
bom_prod = self.env['mrp.bom'].search(
[('product_id', '=', prod['id'])])
if bom_prod:
bom = bom_prod[0]
elif bom_temp:
bom = bom_temp[0]
else:
bom = []
if bom:
vals = {
'origin': 'POS-' + prod['pos_reference'],
'state': 'confirmed',
'product_id': prod['id'],
'product_tmpl_id': prod['product_tmpl_id'],
'product_uom_id': prod['uom_id'],
'product_qty': prod['qty'],
'bom_id': bom.id,
}
mrp_order = self.sudo().create(vals)
list_value = []
for bom_line in mrp_order.bom_id.bom_line_ids:
list_value.append((0, 0, {
'raw_material_production_id': mrp_order.id,
'name': mrp_order.name,
'product_id': bom_line.product_id.id,
'product_uom': bom_line.product_uom_id.id,
'product_uom_qty': (bom_line.product_qty
* mrp_order.product_qty) /
self.env[
'mrp.bom'].search([(
"product_tmpl_id", "=", prod[
'product_tmpl_id'])]).product_qty,
'location_id': mrp_order.location_src_id.id,
'location_dest_id': bom_line.product_id.
with_company(
self.company_id.id).
property_stock_production.id,
'company_id': mrp_order.company_id.id,
'state': 'draft',
'quantity_done': 0,
'operation_id': False
}))
finished_vals = {
'product_id': prod['id'],
'product_uom_qty': prod['qty'],
'product_uom': prod['uom_id'],
'name': mrp_order.name,
'date_deadline': mrp_order.date_deadline,
'picking_type_id': mrp_order.picking_type_id.id,
'location_id': mrp_order.location_src_id.id,
'location_dest_id': mrp_order.location_dest_id.id,
'company_id': mrp_order.company_id.id,
'production_id': mrp_order.id,
'warehouse_id': mrp_order.location_dest_id.
warehouse_id.id,
'origin': mrp_order.name,
'group_id': mrp_order.procurement_group_id.id,
'propagate_cancel': mrp_order.propagate_cancel,
}
mrp_order.update({'move_raw_ids': list_value,
'move_finished_ids': [
(0, 0, finished_vals)]
})
return True

48
all_in_one_pos_kit/models/multi_barcode_product.py

@ -0,0 +1,48 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import fields, models
class MultiProductBarcode(models.Model):
"""
A model for managing multiple barcodes for products in Odoo.
"""
_name = 'multi.barcode.product'
multi_barcode = fields.Char(string="Barcode",
help="Provide alternate barcodes "
"for this product")
product_multi = fields.Many2one('product.product')
template_multi = fields.Many2one('product.template')
def get_barcode_val(self, product):
"""
Retrieve the barcode value along with the associated product.
Args:
product (recordset): The product record associated with the barcode.
Returns:
tuple: A tuple containing the barcode value (str) and the product
(recordset).
"""
return self.multi_barcode, product

134
all_in_one_pos_kit/models/pos_config.py

@ -0,0 +1,134 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import api, fields, models
class PosConfig(models.Model):
"""Inherited POS Configuration to add field's and functions"""
_inherit = 'pos.config'
is_qr_code = fields.Boolean(string='Order QRCode',
help='Enable this field to show the '
'qr code to pos receipt')
is_invoice_number = fields.Boolean(string='Invoice Number',
help='Enable this field to show the '
'invoice number to pos receipt')
is_customer_details = fields.Boolean(string='Customer Details',
help='Enable this field to show the '
'customer number to pos receipt')
is_customer_name = fields.Boolean(string='Customer Name',
help='Enable this field to show '
'the customer name to pos receipt')
is_customer_address = fields.Boolean(string='Customer Address',
help='Enable this field to show '
'the customer address code to '
'pos receipt')
is_customer_mobile = fields.Boolean(string='Customer Mobile',
help='Enable this field to show the '
'customer mobile to pos receipt')
is_customer_phone = fields.Boolean(string='Customer Phone',
help='Enable this field to show the '
'customer phone to pos receipt')
is_customer_email = fields.Boolean(string='Customer Email',
help='Enable this field to show the '
'customer email to pos receipt')
is_customer_vat = fields.Boolean(string='Customer VAT',
help='Enable this field to show the '
'customer vat to pos receipt')
custom_tip_percentage = fields.Float(string="Custom Percentage",
help="Enter the percentage custom tips"
"you want to apply")
image = fields.Binary(string='Image', help='Add logo for pos session')
is_session = fields.Boolean(string="Session",
compute='_compute_check_session',
help="Check it is for sessions", )
is_service_charges = fields.Boolean(string="Service Charges",
help="Enable to add service charge")
charge_type = fields.Selection(
[('amount', 'Amount'),
('percentage', 'Percentage')],
string='Type', default='amount',
help="Can choose charge percentage or amount")
service_charge = fields.Float(string='Service Charge',
help="Charge need to apply")
service_product_id = fields.Many2one('product.product',
string='Service Product',
domain="[('available_in_pos', '=', "
"True),"
"('sale_ok', '=', True), "
"('type', '=', 'service')]",
help="Service Product")
enable_service_charge = fields.Boolean(
string="Service Charges",
help="Enable to add service charge")
visibility = fields.Selection(
[('global', 'Global'), ('session', 'Session')],
default='global', string="Visibility",
help='Setup the Service charge globally or per session')
global_selection = fields.Selection([
('amount', 'Amount'),
('percentage', 'Percentage')],
string='Type', default='amount',
help='Set the service charge as a amount or percentage')
global_charge = fields.Float(
string='Service Charge',
help='Set a default service charge globally')
global_product_id = fields.Many2one(
'product.product', string='Service Product',
domain="[('available_in_pos', '=', True),('sale_ok', '=', True),"
"('type', '=', 'service')]",
help='Set a service product globally')
customer_msg = fields.Boolean('POS Greetings',
Help='Create an account if you ever create '
'an account')
auth_token = fields.Char('Auth Token',
Help='Copy the token from your twilio console '
'window adn paste here')
account_sid = fields.Char('Account SID')
twilio_number = fields.Char('Twilio Number',
Help='The number provided by twilio used to '
'send text messages')
sms_body = fields.Text('Body')
def _compute_check_session(self):
"""To check the service charge is set up for session wise or globally"""
check_session = self.env['ir.config_parameter'].sudo().get_param(
'service_charges_pos.visibility')
if check_session == 'session':
self.is_session = True
else:
self.is_session = False
@api.onchange('is_service_charges')
def onchange_is_service_charges(self):
"""When the service charge is enable set service product
and amount by default per session"""
if self.is_service_charges:
if not self.service_product_id:
domain = [('available_in_pos', '=', True),
('sale_ok', '=', True), ('type', '=', 'service')]
self.service_product_id = self.env[
'product.product'].search(
domain, limit=1)
self.service_charge = 10.0
else:
self.service_product_id = False
self.service_charge = 0.0

54
all_in_one_pos_kit/models/pos_greetings.py

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import fields, models
class POSGreetings(models.Model):
"""Model representing POS Greetings."""
_name = 'pos.greetings'
_description = 'POS Greetings'
_rec_name = 'order_id'
customer_id = fields.Many2one(
comodel_name='res.partner', string='Customer', help="Customer"
)
order_id = fields.Many2one(
comodel_name='pos.order', string='Order', help="Order"
)
auth_token = fields.Char(
string='Token', help='Token'
)
twilio_number = fields.Char(
string='Twilio Number', help='Twilio Number'
)
to_number = fields.Char(
string='Customer Number', help='Customer Number'
)
sms_body = fields.Char(
string='Body', required=True, help='Body'
)
session_id = fields.Many2one(
comodel_name='pos.session', string='Session', help='Session'
)
send_sms = fields.Boolean(
string='Send SMS', default=False, help='Send SMS'
)

439
all_in_one_pos_kit/models/pos_order.py

@ -0,0 +1,439 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
import pytz
from twilio.rest import Client
from odoo import api, fields, models
from datetime import datetime
try:
import qrcode
except ImportError:
qrcode = None
try:
import base64
except ImportError:
base64 = None
class PosOrder(models.Model):
"""Inherited the pos_order class to add filed and function to calculate pos
order details in the dashboard menu"""
_inherit = 'pos.order'
is_exchange = fields.Boolean(string='Exchange',
help='Will enable when create an exchange'
'order')
@api.model
def pos_exchange_order(self, order_id):
"""Mark order a exchanged"""
self.browse(order_id).is_exchange = True
@api.model
def get_invoice(self, id):
"""Retrieve the corresponding invoice details based on the provided ID.
Args:
id (int): The ID of the invoice.
Returns:
dict: A dictionary containing the invoice details.
"""
pos_id = self.search([('pos_reference', '=', id)])
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
invoice_id = self.env['account.move'].search(
[('invoice_origin', '=', pos_id.name)])
return {
'invoice_id': invoice_id.id,
'invoice_name': invoice_id.name,
'base_url': base_url,
'is_qr_code': invoice_id.account_barcode}
@api.model
def get_department(self, option):
"""
Retrieve sales data based on the specified option for POS (Point of Sale) orders.
This method generates a sales report based on the given option, which can be
'pos_hourly_sales', 'pos_monthly_sales', or default to annual sales grouped by month.
The report is generated for the current company and for the current month or year
as appropriate.
Args:
option (str): The type of sales report to generate. Possible values are:
- 'pos_hourly_sales': Hourly sales data for the current month.
- 'pos_monthly_sales': Daily sales data for the current month.
- Any other value: Monthly sales data for the current year.
Returns:
list: A list containing three elements:
- order (list): A list of total sales amounts.
- today (list): A list of time labels corresponding to the sales amounts
(hours, days, or months).
- label (str): A string indicating the type of time label used ('HOURS', 'DAYS', 'MONTHS').
Raises:
Exception: If an error occurs while executing the database query.
"""
company_id = self.env.company.id
if option == 'pos_hourly_sales':
user_tz = self.env.user.tz if self.env.user.tz else pytz.UTC
query = ('''select EXTRACT(hour FROM date_order at time zone
'utc' at time zone '{}') as date_month,sum(amount_total) from
pos_order where EXTRACT(month FROM date_order::date) =
EXTRACT(month FROM CURRENT_DATE) AND pos_order.company_id = '''
+ str(company_id) + ''' group by date_month ''')
query = query.format(user_tz)
label = 'HOURS'
elif option == 'pos_monthly_sales':
query = '''select date_order::date as date_month,sum(amount_total)
from pos_order where EXTRACT(month FROM date_order::date) = EXTRACT
(month FROM CURRENT_DATE) AND pos_order.company_id = ''' + str(
company_id) + ''' group by date_month '''
label = 'DAYS'
else:
query = '''select TO_CHAR(date_order,'MON')date_month,
sum(amount_total) from pos_order where EXTRACT(year FROM
date_order::date) = EXTRACT(year FROM CURRENT_DATE) AND
pos_order.company_id = ''' + str( company_id) + ''' group by
date_month'''
label = 'MONTHS'
self._cr.execute(query)
docs = self._cr.dictfetchall()
order = []
for record in docs:
order.append(record.get('sum'))
today = []
for record in docs:
today.append(record.get('date_month'))
final = [order, today, label]
return final
@api.model
def get_details(self):
"""
Retrieve various details related to POS (Point of Sale) operations for the current company.
This method fetches payment details, salesperson performance, and session statuses
from the POS system. The details include the total amount collected per payment method,
the total sales and order count per salesperson, and the status of active POS sessions.
Returns:
dict: A dictionary containing the following keys:
- 'payment_details': A list of tuples, each containing the name of the payment
method and the total amount collected, formatted with the
company currency symbol.
- 'salesperson': A list of tuples, each containing the name of the salesperson,
the total sales amount formatted with the company currency
symbol, and the number of orders processed.
- 'selling_product': A list of dictionaries, each representing a POS session with
its name and status (e.g., 'Closed', 'Opened').
Raises:
Exception: If an error occurs during database queries or data formatting.
"""
company_id = self.env.company.id
cr = self._cr
cr.execute(
"""select pos_payment_method.name,sum(amount) from pos_payment inner
join pos_payment_method on pos_payment_method.id=pos_payment.
payment_method_id group by pos_payment_method.name ORDER
BY sum(amount) DESC; """)
payment_details = cr.fetchall()
cr.execute(
'''select hr_employee.name,sum(pos_order.amount_paid) as total,
count(pos_order.amount_paid) as orders from pos_order inner join
hr_employee on pos_order.user_id = hr_employee.user_id
where pos_order.company_id =''' + str(
company_id) + '''GROUP BY hr_employee.name order by total
DESC;''')
salesperson = cr.fetchall()
total_sales = []
for rec in salesperson:
rec = list(rec)
sym_id = rec[1]
company = self.env.company
if company.currency_id.position == 'after':
rec[1] = "%s %s" % (sym_id, company.currency_id.symbol)
else:
rec[1] = "%s %s" % (company.currency_id.symbol, sym_id)
rec = tuple(rec)
total_sales.append(rec)
cr.execute(
'''select DISTINCT(product_template.name) as product_name,sum(qty)
as total_quantity from pos_order_line inner join product_product on
product_product.id=pos_order_line.product_id inner join
product_template on product_product.product_tmpl_id = product_template.id
where pos_order_line.company_id =''' + str(
company_id) + ''' group by product_template.id ORDER
BY total_quantity DESC Limit 10 ''')
sessions = self.env['pos.config'].search([])
sessions_list = []
dict = {
'closing_control': 'Closed',
'opened': 'Opened',
'new_session': 'New Session',
'opening_control': "Opening Control"
}
for session in sessions:
sessions_list.append({
'session': session.name,
'status': dict.get(session.pos_session_state)
})
payments = []
for rec in payment_details:
rec = list(rec)
sym_id = rec[1]
company = self.env.company
if company.currency_id.position == 'after':
rec[1] = "%s %s" % (sym_id, company.currency_id.symbol)
else:
rec[1] = "%s %s" % (company.currency_id.symbol, sym_id)
rec = tuple(rec)
payments.append(rec)
return {
'payment_details': payments,
'salesperson': total_sales,
'selling_product': sessions_list,
}
@api.model
def get_refund_details(self):
"""
Retrieve details about sales, refunds, and POS sessions for the current date.
This method calculates various metrics related to POS (Point of Sale) operations,
including the total sales amount, number of orders, refund counts, and session
information. It also formats the total sales with appropriate magnitude suffixes
(e.g., K for thousand, M for million) for easier readability.
Returns:
dict: A dictionary containing the following keys:
- 'total_sale': A formatted string representing the total sales amount with
an appropriate suffix (e.g., '1.2K' for 1200).
- 'total_order_count': An integer representing the total number of orders.
- 'total_refund_count': An integer representing the total number of refund orders.
- 'total_session': An integer representing the total number of POS sessions.
- 'today_refund_total': An integer representing the number of refunds made today.
- 'today_sale': An integer representing the number of sales made today.
Raises:
Exception: If an error occurs while querying the database or calculating the metrics.
"""
default_date = datetime.today().date()
pos_order = self.env['pos.order'].search([])
total = 0
today_refund_total = 0
total_order_count = 0
total_refund_count = 0
today_sale = 0
a = 0
for rec in pos_order:
if rec.amount_total < 0.0 and rec.date_order.date() == default_date:
today_refund_total = today_refund_total + 1
total_sales = rec.amount_total
total = total + total_sales
total_order_count = total_order_count + 1
if rec.date_order.date() == default_date:
today_sale = today_sale + 1
if rec.amount_total < 0.0:
total_refund_count = total_refund_count + 1
magnitude = 0
while abs(total) >= 1000:
magnitude += 1
total /= 1000.0
# add more suffixes if you need them
val = '%.2f%s' % (total, ['', 'K', 'M', 'G', 'T', 'P'][magnitude])
pos_session = self.env['pos.session'].search([])
total_session = 0
for record in pos_session:
total_session = total_session + 1
return {
'total_sale': val,
'total_order_count': total_order_count,
'total_refund_count': total_refund_count,
'total_session': total_session,
'today_refund_total': today_refund_total,
'today_sale': today_sale,
}
@api.model
def get_the_top_customer(self):
"""
Retrieve the top 10 customers based on the total amount spent in POS (Point of Sale) orders.
This method fetches the top customers who have made the highest total payments for
POS orders in the current company. It returns a list of the top 10 customers along
with the corresponding total amounts they have spent.
Returns:
list: A list containing two elements:
- order (list): A list of total amounts spent by the top 10 customers.
- day (list): A list of customer names corresponding to the total amounts.
Raises:
Exception: If an error occurs during the database query execution.
"""
company_id = self.env.company.id
query = '''select res_partner.name as customer,pos_order.partner_id,
sum(pos_order.amount_paid) as amount_total from pos_order inner join
res_partner on res_partner.id = pos_order.partner_id where pos_order
.company_id = ''' + str(
company_id) + ''' GROUP BY pos_order.partner_id,
res_partner.name ORDER BY amount_total DESC LIMIT 10;'''
self._cr.execute(query)
docs = self._cr.dictfetchall()
order = []
for record in docs:
order.append(record.get('amount_total'))
day = []
for record in docs:
day.append(record.get('customer'))
final = [order, day]
return final
@api.model
def get_the_top_products(self):
"""
Retrieve the top 10 products based on the total quantity sold in POS (Point of Sale) orders.
This method fetches the top-selling products in terms of quantity for the current company
and returns a list of the top 10 products along with their corresponding total quantities sold.
Returns:
list: A list containing two elements:
- total_quantity (list): A list of quantities for the top 10 products.
- product_name (list): A list of product names corresponding to the quantities.
Raises:
Exception: If an error occurs during the database query execution.
"""
company_id = self.env.company.id
query = '''select DISTINCT(product_template.name) as product_name,
sum(qty) as total_quantity from
pos_order_line inner join product_product on product_product.
id=pos_order_line.product_id inner join
product_template on product_product.product_tmpl_id =
product_template.id where pos_order_line.company_id = ''' + str(
company_id) + ''' group by product_template.id ORDER
BY total_quantity DESC Limit 10 '''
self._cr.execute(query)
top_product = self._cr.dictfetchall()
total_quantity = []
for record in top_product:
total_quantity.append(record.get('total_quantity'))
product_name = []
for record in top_product:
product_name.append(record.get('product_name'))
final = [total_quantity, product_name]
return final
@api.model
def get_the_top_categories(self):
"""
Retrieve the top product categories based on the total quantity sold in POS (Point of Sale) orders.
This method fetches the top-selling product categories for the current company and returns
a list containing the total quantities sold for each category, sorted in descending order.
Returns:
list: A list containing two elements:
- total_quantity (list): A list of quantities for the top-selling product categories.
- product_categ (list): A list of category names corresponding to the quantities.
Raises:
Exception: If an error occurs during the database query execution.
"""
company_id = self.env.company.id
query = ('''select DISTINCT(product_category.complete_name) as
product_category,sum(qty) as total_quantity from pos_order_line inner
join product_product on product_product.id=pos_order_line.product_id
inner join product_template on product_product.product_tmpl_id =
product_template.id inner join product_category on product_category.id
= product_template.categ_id where pos_order_line.company_id = ''' +
str(company_id) + ''' group by product_category ORDER BY
total_quantity DESC ''')
self._cr.execute(query)
top_product = self._cr.dictfetchall()
total_quantity = []
for record in top_product:
total_quantity.append(record.get('total_quantity'))
product_categ = []
for record in top_product:
product_categ.append(record.get('product_category'))
final = [total_quantity, product_categ]
return final
@api.model
def create_from_ui(self, orders, draft=False):
"""
Create POS orders from the UI and send SMS notifications to customers if configured.
This method extends the default behavior of creating POS orders from the user interface.
After the orders are created, it checks for configurations that allow SMS notifications
and sends an SMS to the customer using Twilio if applicable. The SMS message details
are also logged in the 'pos.greetings' model.
Args:
orders (list): A list of order dictionaries coming from the POS UI.
Returns:
list: The result from the parent 'create_from_ui' method, representing the
created orders.
Raises:
Exception: If an error occurs while sending the SMS, it is caught and suppressed.
"""
res = super(PosOrder, self).create_from_ui(orders, draft)
ids = []
for line in res:
if line['id']:
ids.append(line['id'])
backend_order = self.search([('id', 'in', ids)])
if backend_order:
for pos_order in backend_order:
config_id = pos_order.session_id.config_id
if config_id.customer_msg:
if pos_order.partner_id.phone:
try:
customer_phone = str(pos_order.partner_id.phone)
twilio_number = config_id.twilio_number
auth_token = config_id.auth_token
account_sid = config_id.account_sid
body = config_id.sms_body
client = Client(account_sid, auth_token)
client.messages.create(
body=body,
from_=twilio_number,
to=customer_phone
)
pos_greeting_obj = self.env['pos.greetings']
pos_greeting_obj.create({
'customer_id': pos_order.partner_id.id,
'order_id': pos_order.id,
'auth_token': auth_token,
'twilio_number': twilio_number,
'to_number': customer_phone,
'session_id': pos_order.session_id.id,
'sms_body': body,
'send_sms': True,
})
except:
pass
return res

41
all_in_one_pos_kit/models/pos_order_line.py

@ -0,0 +1,41 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import api, models
class PosOrderLine(models.Model):
"""Inherited pos order line to get the product details"""
_inherit = "pos.order.line"
@api.model
def get_product_details(self, ids):
"""To get the product details"""
lines = self.env['pos.order.line'].browse(ids)
res = []
for rec in lines:
res.append({
'product_id': rec.product_id.id,
'name': rec.product_id.name,
'qty': rec.qty,
'order_id': rec.order_id.id,
})
return res

666
all_in_one_pos_kit/models/pos_report.py

@ -0,0 +1,666 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import models, fields, api
import io
import json
try:
from odoo.tools.misc import xlsxwriter
except ImportError:
import xlsxwriter
class PosReport(models.Model):
"""
Inheriting POS Report
"""
_name = "pos.report"
pos_report = fields.Char(string="PoS Report", help="PoS Report")
date_from = fields.Datetime(string="Date From", help="Date From")
date_to = fields.Datetime(string="Date to", help="Date to")
report_type = fields.Selection([('report_by_order', 'Report By Order'),
('report_by_order_detail',
'Report By Order Detail'),
('report_by_product', 'Report By Product'),
('report_by_categories',
'Report By Categories'),
(
'report_by_salesman', 'Report By Salesman'),
('report_by_payment', 'Report By Payment')],
default='report_by_order',
string="Report Type", help="Report Type")
@api.model
def pos_report(self, option):
"""
Generate a Point of Sale (POS) report based on the provided options.
This method retrieves the POS report details for the specified report
ID and
gathers the necessary data for generating the report, including date range
and report type. It then retrieves filters and report lines to be used in
displaying the report.
Args:
option (list): A list containing the ID of the POS report to be
generated.
Returns:
dict: A dictionary containing the report details, including:
- name (str): The name of the report.
- type (str): The type of action for the report.
- tag (str): The tag to identify the report action.
- orders (dict): The report data, including report type and date
range.
- filters (dict): Filters applied to the report.
- report_lines (list): The detailed report lines.
- report_main_line (list): The main summary line of the report.
Raises:
Exception: If an error occurs during report generation or data
retrieval.
"""
report_values = self.env['pos.report'].search([('id', '=', option[0])])
data = {
'report_type': report_values.report_type,
'model': self,
}
if report_values.date_from:
data.update({
'date_from': report_values.date_from,
})
if report_values.date_to:
data.update({
'date_to': report_values.date_to,
})
filters = self.get_filter(option)
lines = self._get_report_values(data).get('POS')
main_line = self._get_report_values(data).get('pos_main')
return {
'name': "PoS Orders",
'type': 'ir.actions.client',
'tag': 'pos_r',
'orders': data,
'filters': filters,
'report_lines': lines,
'report_main_line': main_line,
}
def get_filter(self, option):
"""
Retrieve the filters for generating a report based on the specified
options.
This method analyzes the report type obtained from the filter data and
constructs a dictionary of filters that will be applied to the report.
Args:
option (list): A list containing options that influence the report
generation.
Returns:
dict: A dictionary containing the report filters, including:
- report_type (str): A description of the report type being
generated.
"""
data = self.get_filter_data(option)
filters = {}
if data.get('report_type') == 'report_by_order':
filters['report_type'] = 'Report By Order'
elif data.get('report_type') == 'report_by_order_detail':
filters['report_type'] = 'Report By Order Detail'
elif data.get('report_type') == 'report_by_product':
filters['report_type'] = 'Report By Product'
elif data.get('report_type') == 'report_by_categories':
filters['report_type'] = 'Report By Categories'
elif data.get('report_type') == 'report_by_salesman':
filters['report_type'] = 'Report By Salesman'
elif data.get('report_type') == 'report_by_payment':
filters['report_type'] = 'Report By Payment'
else:
filters['report_type'] = 'report_by_order'
return filters
def get_filter_data(self, option):
"""
Retrieve filter data for the specified report option.
This method searches for a POS report using the provided option and
returns a dictionary containing the report type and any default filters
associated with the report.
Args:
option (list): A list containing options, where the first element
is expected to be the ID of the POS report.
Returns:
dict: A dictionary containing the filter data for the report,
including:
- report_type (str): The type of report retrieved from the
database.
"""
report = self.env['pos.report'].search([('id', '=', option[0])])
default_filters = {}
filter_dict = {
'report_type': report.report_type,
}
filter_dict.update(default_filters)
return filter_dict
@api.model
def create(self, vals):
"""
Create a new POS report record with the provided values.
This method overrides the default create method to extend
the functionality of creating a POS report. It calls the
super method to handle the actual record creation and
returns the resulting record.
Args:
vals (dict): A dictionary of field values to create the new
POS report record.
Returns:
recordset: The created POS report record.
.
"""
res = super(PosReport, self).create(vals)
return res
def write(self, vals):
"""
Update existing POS report records with the provided values.
This method overrides the default write method to extend
the functionality of updating POS report records. It calls
the super method to handle the actual record update and
returns the result of the operation.
Args:
vals (dict): A dictionary of field values to update the
existing POS report records.
Returns:
bool: True if the update was successful, False otherwise.
"""
res = super(PosReport, self).write(vals)
return res
def _get_report_sub_lines(self, data, report, date_from, date_to):
report_sub_lines = []
if data.get('report_type') == 'report_by_order':
query = '''
select l.name,l.date_order,l.partner_id,l.amount_total,
l.note,l.user_id,res_partner.name,l.name as shop,
pos_session.name as session,
res_users.partner_id as user_partner,
sum(pos_order_line.qty),l.id as id,
(SELECT res_partner.name as salesman FROM res_partner
WHERE res_partner.id = res_users.partner_id)
from pos_order as l
left join pos_session on l.session_id = pos_session.id
left join res_partner on l.partner_id = res_partner.id
left join res_users on l.user_id = res_users.id
left join pos_order_line on l.id = pos_order_line.order_id
'''
term = 'Where '
if data.get('date_from'):
query += "Where l.date_order >= '%s' " % data.get('date_from')
term = 'AND '
if data.get('date_to'):
query += term + "l.date_order <= '%s' " % data.get('date_to')
query += ("group by l.user_id,res_users.partner_id,"
"res_partner.name,l.partner_id,l.date_order,pos_session."
"name,l.session_id,l.name,l.amount_total,l.note,l.id")
self._cr.execute(query)
report_by_order = self._cr.dictfetchall()
report_sub_lines.append(report_by_order)
elif data.get('report_type') == 'report_by_order_detail':
query = '''
select l.name,l.date_order,l.partner_id,l.amount_total,l.note,
l.user_id,res_partner.name,l.name as shop,pos_session.name as
session,
res_users.partner_id as user_partner,sum(pos_order_line.qty),
pos_order_line.full_product_name, pos_order_line.price_unit,
pos_order_line.price_subtotal,pos_order_line.price_subtotal_incl
,pos_order_line.product_id,product_product.default_code,
(SELECT res_partner.name as salesman FROM res_partner WHERE
res_partner.id = res_users.partner_id)
from pos_order as l
left join pos_session on l.session_id = pos_session.id
left join res_partner on l.partner_id = res_partner.id
left join res_users on l.user_id = res_users.id
left join pos_order_line on l.id = pos_order_line.order_id
left join product_product on pos_order_line.product_id =
product_product.id
'''
term = 'Where '
if data.get('date_from'):
query += "Where l.date_order >= '%s' " % data.get('date_from')
term = 'AND '
if data.get('date_to'):
query += term + "l.date_order <= '%s' " % data.get('date_to')
query += ("group by l.user_id,res_users.partner_id,res_partner.name"
",l.partner_id,l.date_order,pos_session.name,"
"l.session_id,l.name,l.amount_total,l.note,"
"pos_order_line.full_product_name,pos_order_line"
".price_unit,pos_order_line.price_subtotal,pos_order_line"
".price_subtotal_incl,pos_order_line.product_id,"
"product_product.default_code")
self._cr.execute(query)
report_by_order_details = self._cr.dictfetchall()
report_sub_lines.append(report_by_order_details)
elif data.get('report_type') == 'report_by_product':
query = '''
select l.amount_total,l.amount_paid,sum(pos_order_line.qty) as
qty, pos_order_line.full_product_name, pos_order_line.price_unit
,product_product.default_code,product_category.name
from pos_order as l
left join pos_order_line on l.id = pos_order_line.order_id
left join product_product on pos_order_line.product_id =
product_product.id
left join product_template on pos_order_line.product_id =
product_template.id
left join product_category on product_category.id =
product_template.categ_id
'''
term = 'Where '
if data.get('date_from'):
query += "Where l.date_order >= '%s' " % data.get('date_from')
term = 'AND '
if data.get('date_to'):
query += term + "l.date_order <= '%s' " % data.get('date_to')
query += ("group by l.amount_total,l.amount_paid,pos_order_line."
"full_product_name,pos_order_line.price_unit,"
"pos_order_line.product_id,product_product.default_code,"
"product_template.categ_id,product_category.name")
self._cr.execute(query)
report_by_product = self._cr.dictfetchall()
report_sub_lines.append(report_by_product)
elif data.get('report_type') == 'report_by_categories':
query = '''
select product_category.name,sum(l.qty) as qty,sum(l.price_subtotal)
as amount_total,sum(price_subtotal_incl) as total_incl
from pos_order_line as l
left join product_template on l.product_id = product_template.id
left join product_category on product_category.id =
product_template.categ_id
left join pos_order on l.order_id = pos_order.id
'''
term = 'Where '
if data.get('date_from'):
query += "Where pos_order.date_order >= '%s' " % data.get(
'date_from')
term = 'AND '
if data.get('date_to'):
query += term + "pos_order.date_order <= '%s' " % data.get(
'date_to')
query += "group by product_category.name"
self._cr.execute(query)
report_by_categories = self._cr.dictfetchall()
report_sub_lines.append(report_by_categories)
elif data.get('report_type') == 'report_by_salesman':
query = '''
select res_partner.name,sum(pos_order_line.qty) as
qty,sum(pos_order_line.price_subtotal) as amount,count(l.id) as order
from pos_order as l
left join res_users on l.user_id = res_users.id
left join res_partner on res_users.partner_id = res_partner.id
left join pos_order_line on l.id = pos_order_line.order_id
'''
term = 'Where '
if data.get('date_from'):
query += "Where l.date_order >= '%s' " % data.get('date_from')
term = 'AND '
if data.get('date_to'):
query += term + "l.date_order <= '%s' " % data.get('date_to')
query += "group by res_partner.name"
self._cr.execute(query)
report_by_salesman = self._cr.dictfetchall()
report_sub_lines.append(report_by_salesman)
elif data.get('report_type') == 'report_by_payment':
query = '''
select pos_payment_method.name,sum(l.amount_total),pos_session.name
as session,pos_config.name as config
from pos_order as l
left join pos_payment on l.id = pos_payment.pos_order_id
left join pos_payment_method on pos_payment.payment_method_id =
pos_payment_method.id
left join pos_session on l.session_id = pos_session.id
left join pos_config on pos_session.config_id = pos_config.id
'''
term = 'Where '
if data.get('date_from'):
query += "Where l.date_order >= '%s' " % data.get('date_from')
term = 'AND '
if data.get('date_to'):
query += term + "l.date_order <= '%s' " % data.get('date_to')
query += ("group by pos_payment_method.name,pos_session.name,"
"pos_config.name")
self._cr.execute(query)
report_by_payment = self._cr.dictfetchall()
report_sub_lines.append(report_by_payment)
return report_sub_lines
def _get_report_total_value(self, data, report):
"""
Generate detailed sub-reports based on the provided report type and date range.
This method constructs and executes SQL queries to fetch sub-report data
according to the specified report type. It gathers information about orders,
products, categories, salesmen, and payment methods, grouping the results
accordingly.
Args:
data (dict): A dictionary containing filter parameters, such as
'report_type' and date range (date_from, date_to).
report (recordset): The report record for which sub-lines are being
fetched.
date_from (str): The start date for filtering the records.
date_to (str): The end date for filtering the records.
Returns:
list: A list of dictionaries, where each dictionary represents
the result of a sub-report query based on the specified report
type.
"""
report_main_lines = []
if data.get('report_type') == 'report_by_order':
self._cr.execute('''
select count(l.id) as order,sum(l.amount_total) as amount
from pos_order as l
''')
report_by_order = self._cr.dictfetchall()
report_main_lines.append(report_by_order)
elif data.get('report_type') == 'report_by_order_detail':
self._cr.execute('''
select count(line.id) as order,sum(line.price_subtotal)
as total,sum(line.price_subtotal_incl)
from pos_order_line as line
''')
report_by_order_detail = self._cr.dictfetchall()
report_main_lines.append(report_by_order_detail)
elif data.get('report_type') == 'report_by_product':
self._cr.execute('''
select count(l.product_id) as order,sum(l.price_subtotal) as amount
from pos_order_line as l
''')
report_by_product = self._cr.dictfetchall()
report_main_lines.append(report_by_product)
else:
report_main_lines = False
return report_main_lines
def _get_report_values(self, data):
"""
Retrieve report values based on the specified report type and date range.
This method processes the given report type and calls the appropriate
sub-methods to gather the report data and total values.
Args:
data (dict): A dictionary containing report parameters, such as
'model', 'report_type', 'date_from', and 'date_to'.
Returns:
dict: A dictionary containing the report data, including document
IDs, model data, sub-report values (POS), and total values
(pos_main).
"""
docs = data['model']
date_from = data.get('date_from')
date_to = data.get('date_to')
if data['report_type'] == 'report_by_order_detail':
report = ['Report By Order Detail']
elif data['report_type'] == 'report_by_product':
report = ['Report By Product']
elif data['report_type'] == 'report_by_categories':
report = ['Report By Categories']
elif data['report_type'] == 'report_by_salesman':
report = ['Report By Salesman']
elif data['report_type'] == 'report_by_payment':
report = ['Report By Payment']
else:
report = ['Report By Order']
report_res_total = self._get_report_total_value(data, report)
if data.get('report_type'):
report_res = \
self._get_report_sub_lines(data, report, date_from, date_to)[0]
else:
report_res = self._get_report_sub_lines(data, report, date_from,
date_to)
if data.get('report_type') == 'report_by_order':
report_res_total = self._get_report_total_value(data, report)[0]
return {
'doc_ids': self.ids,
'docs': docs,
'POS': report_res,
'pos_main': report_res_total,
}
def get_pos_xlsx_report(self, data, response, report_data):
"""
Generate an Excel report for the Point of Sale (PoS) based on the
provided filters and report data.
Args:
data (str): JSON string containing report filters.
response: The HTTP response object to stream the output.
report_data (str): JSON string containing the main report data.
Returns:
None: Writes the generated report directly to the response stream.
"""
report_data_main = json.loads(report_data)
output = io.BytesIO()
filters = json.loads(data)
workbook = xlsxwriter.Workbook(output, {'in_memory': True})
sheet = workbook.add_worksheet()
head = workbook.add_format({'align': 'center', 'bold': True,
'font_size': '20px'})
heading = workbook.add_format(
{'align': 'center', 'bold': True, 'font_size': '10px',
'border': 2,
'border_color': 'black'})
txt_l = workbook.add_format(
{'font_size': '10px', 'border': 1, 'bold': True})
sheet.merge_range('A2:H3',
'Point of Sale Report',
head)
if filters.get('report_type') == 'report_by_order':
sheet.merge_range('B5:D5', 'Report Type: ' +
filters.get('report_type'), txt_l)
sheet.write('A7', 'PoS', heading)
sheet.write('B7', 'Order', heading)
sheet.write('C7', 'Date Order', heading)
sheet.write('D7', 'Customer', heading)
sheet.write('E7', 'Salesman', heading)
sheet.write('F7', 'Total Qty', heading)
sheet.write('G7', 'Amount Total', heading)
sheet.write('H7', 'Note', heading)
lst = []
for rec in report_data_main[0]:
lst.append(rec)
row = 6
col = 0
sheet.set_column(3, 0, 15)
sheet.set_column(4, 1, 15)
sheet.set_column(5, 2, 15)
sheet.set_column(6, 3, 15)
sheet.set_column(7, 4, 15)
sheet.set_column(8, 5, 15)
sheet.set_column(9, 6, 15)
for rec_data in report_data_main:
row += 1
sheet.write(row, col, rec_data['shop'], txt_l)
sheet.write(row, col + 1, rec_data['session'], txt_l)
sheet.write(row, col + 2, rec_data['date_order'], txt_l)
sheet.write(row, col + 3, rec_data['name'], txt_l)
sheet.write(row, col + 4, rec_data['salesman'], txt_l)
sheet.write(row, col + 5, rec_data['sum'], txt_l)
sheet.write(row, col + 6, rec_data['amount_total'], txt_l)
sheet.write(row, col + 7, rec_data['note'], txt_l)
if filters.get('report_type') == 'report_by_order_detail':
sheet.merge_range('B5:D5', 'Report Type: ' +
filters.get('report_type'), txt_l)
sheet.write('A7', 'PoS', heading)
sheet.write('B7', 'Order', heading)
sheet.write('C7', 'Date Order', heading)
sheet.write('D7', 'Customer', heading)
sheet.write('E7', 'Salesman', heading)
sheet.write('F7', 'Product Code', heading)
sheet.write('G7', 'Product Name', heading)
sheet.write('H7', 'Price unit', heading)
sheet.write('I7', 'Qty', heading)
sheet.write('J7', 'Price Subtotal', heading)
sheet.write('K7', 'Price Subtotal Incl', heading)
lst = []
for rec in report_data_main[0]:
lst.append(rec)
row = 6
col = 0
sheet.set_column(3, 0, 15)
sheet.set_column(4, 1, 15)
sheet.set_column(5, 2, 15)
sheet.set_column(6, 3, 15)
sheet.set_column(7, 4, 15)
sheet.set_column(8, 5, 15)
sheet.set_column(9, 6, 15)
sheet.set_column(10, 7, 15)
sheet.set_column(11, 8, 15)
sheet.set_column(12, 9, 15)
for rec_data in report_data_main:
row += 1
sheet.write(row, col, rec_data['shop'], txt_l)
sheet.write(row, col + 1, rec_data['session'], txt_l)
sheet.write(row, col + 2, rec_data['date_order'], txt_l)
sheet.write(row, col + 3, rec_data['name'], txt_l)
sheet.write(row, col + 4, rec_data['salesman'], txt_l)
sheet.write(row, col + 5, rec_data['default_code'], txt_l)
sheet.write(row, col + 6, rec_data['full_product_name'], txt_l)
sheet.write(row, col + 7, rec_data['price_unit'], txt_l)
sheet.write(row, col + 8, rec_data['sum'], txt_l)
sheet.write(row, col + 9, rec_data['price_subtotal'], txt_l)
sheet.write(row, col + 10, rec_data['price_subtotal_incl'],
txt_l)
if filters.get('report_type') == 'report_by_product':
sheet.merge_range('B5:D5', 'Report Type: ' +
filters.get('report_type'), txt_l)
sheet.write('A7', 'Category', heading)
sheet.write('B7', 'Product Code', heading)
sheet.write('C7', 'Product Name', heading)
sheet.write('D7', 'Qty', heading)
sheet.write('E7', 'Amount Total', heading)
sheet.write('F7', 'Amount Total Incl', heading)
lst = []
for rec in report_data_main[0]:
lst.append(rec)
row = 6
col = 0
sheet.set_column(3, 0, 15)
sheet.set_column(4, 1, 15)
sheet.set_column(5, 2, 15)
sheet.set_column(6, 3, 15)
sheet.set_column(7, 4, 15)
sheet.set_column(8, 5, 15)
for rec_data in report_data_main:
row += 1
sheet.write(row, col, rec_data['name'], txt_l)
sheet.write(row, col + 1, rec_data['default_code'], txt_l)
sheet.write(row, col + 2, rec_data['full_product_name'], txt_l)
sheet.write(row, col + 3, rec_data['qty'], txt_l)
sheet.write(row, col + 4, rec_data['amount_total'], txt_l)
sheet.write(row, col + 5, rec_data['amount_paid'], txt_l)
if filters.get('report_type') == 'report_by_categories':
sheet.merge_range('B5:D5', 'Report Type: ' +
filters.get('report_type'), txt_l)
sheet.write('A7', 'Category', heading)
sheet.write('B7', 'Qty', heading)
sheet.write('C7', 'Amount Total', heading)
sheet.write('D7', 'Amount Total Incl', heading)
lst = []
for rec in report_data_main[0]:
lst.append(rec)
row = 6
col = 0
sheet.set_column(3, 0, 15)
sheet.set_column(4, 1, 15)
sheet.set_column(5, 2, 15)
sheet.set_column(6, 3, 15)
for rec_data in report_data_main:
row += 1
sheet.write(row, col, rec_data['name'], txt_l)
sheet.write(row, col + 1, rec_data['qty'], txt_l)
sheet.write(row, col + 2, rec_data['amount_total'], txt_l)
sheet.write(row, col + 3, rec_data['total_incl'], txt_l)
if filters.get('report_type') == 'report_by_salesman':
sheet.merge_range('B5:D5', 'Report Type: ' +
filters.get('report_type'), txt_l)
sheet.write('A7', 'Salesman', heading)
sheet.write('B7', 'Total Order', heading)
sheet.write('C7', 'Total Qty', heading)
sheet.write('D7', 'Total Amount', heading)
lst = []
for rec in report_data_main[0]:
lst.append(rec)
row = 6
col = 0
sheet.set_column(3, 0, 15)
sheet.set_column(4, 1, 15)
sheet.set_column(5, 2, 15)
sheet.set_column(6, 3, 15)
for rec_data in report_data_main:
row += 1
sheet.write(row, col, rec_data['name'], txt_l)
sheet.write(row, col + 1, rec_data['order'], txt_l)
sheet.write(row, col + 2, rec_data['qty'], txt_l)
sheet.write(row, col + 3, rec_data['amount'], txt_l)
if filters.get('report_type') == 'report_by_payment':
sheet.merge_range('B5:D5', 'Report Type: ' +
filters.get('report_type'), txt_l)
sheet.write('A7', 'Point of Sale', heading)
sheet.write('B7', 'PoS Session', heading)
sheet.write('C7', 'Payment', heading)
sheet.write('D7', 'Total Amount', heading)
lst = []
for rec in report_data_main[0]:
lst.append(rec)
row = 6
col = 0
sheet.set_column(3, 0, 15)
sheet.set_column(4, 1, 15)
sheet.set_column(5, 2, 15)
sheet.set_column(6, 3, 15)
for rec_data in report_data_main:
row += 1
sheet.write(row, col, rec_data['config'], txt_l)
sheet.write(row, col + 1, rec_data['session'], txt_l)
sheet.write(row, col + 2, rec_data['name'], txt_l)
sheet.write(row, col + 3, rec_data['sum'], txt_l)
workbook.close()
output.seek(0)
response.stream.write(output.read())
output.close()

33
all_in_one_pos_kit/models/pos_session.py

@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import models
class PosSession(models.Model):
"""Inherit POS Session to load model and fields"""
_inherit = 'pos.session'
def _loader_params_product_product(self):
"""loaded the field to pos"""
result = super()._loader_params_product_product()
result['search_params']['fields'].extend(['is_age_restrict'])
return result

111
all_in_one_pos_kit/models/product_product.py

@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import api, fields, models
from odoo.osv import expression
class ProductProduct(models.Model):
"""
Inherit poduct_product model
"""
_inherit = 'product.product'
product_multi_barcodes = fields.One2many(
comodel_name='multi.barcode.product',
inverse_name='product_multi', string='Barcodes'
)
@api.model
def create(self, vals):
"""
Create a new product and update the associated multi barcodes record.
Args:
vals (dict): The values to create the product.
Returns:
recordset: The created product record.
"""
res = super(ProductProduct, self).create(vals)
res.product_multi_barcodes.update({
'template_multi': res.product_tmpl_id.id
})
return res
def write(self, vals):
"""
Update the product and its associated multi barcodes record.
Args:
vals (dict): The values to update the product.
Returns:
bool: True if the update was successful.
"""
res = super(ProductProduct, self).write(vals)
self.product_multi_barcodes.update({
'template_multi': self.product_tmpl_id.id
})
return res
@api.model
def _name_search(self, name, args=None, operator='ilike', limit=100,
name_get_uid=None):
"""
Custom name search for products based on various fields.
Args:
name (str): The name to search for.
args (list): Additional search criteria.
operator (str): The operator for the search (default is 'ilike').
limit (int): The maximum number of records to return.
name_get_uid (int): The UID for access rights.
Returns:
list: List of product IDs that match the search criteria.
"""
args = args or []
domain = []
if name:
domain = ['|', '|', ('name', operator, name),
('default_code', operator, name),
'|', ('barcode', operator, name),
('product_multi_barcodes', operator, name)]
product_id = self._search(expression.AND([domain, args]), limit=limit,
access_rights_uid=name_get_uid)
return product_id
@api.onchange('to_make_mrp')
def onchange_to_make_mrp(self):
"""
Triggered when the `to_make_mrp` field is changed.
If the `to_make_mrp` field is set to True, this method checks if the
product has an associated Bill of Materials (BoM). If no BoM is found,
it raises a warning prompting the user to set a BoM for the product.
Raises:
Warning: If `to_make_mrp` is True and `bom_count` is 0.
"""
if self.to_make_mrp:
if not self.bom_count:
raise Warning('Please set Bill of Material for this product.')

74
all_in_one_pos_kit/models/product_template.py

@ -0,0 +1,74 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import api, fields, models
from odoo.exceptions import ValidationError
class ProductTemplate(models.Model):
"""Inherited product.template to add field"""
_inherit = 'product.template'
is_age_restrict = fields.Boolean(string="Is Age Restricted",
help="Enable if the product is age "
"restricted")
template_multi_barcodes = fields.One2many('multi.barcode.product',
'template_multi',
string='Barcodes')
to_make_mrp = fields.Boolean(string='To Create MRP Order',
help="Check if the product should be make "
"mrp order")
@api.onchange('to_make_mrp')
def onchange_to_make_mrp(self):
"""
Checks if the product has a Bill of Materials set when 'to_make_mrp'
is True.
Raises a ValidationError if no BOM exists.
"""
if self.to_make_mrp:
if not self.bom_count:
raise ValidationError(
'Please set Bill of Material for this product.')
@api.model
def create(self, vals):
"""
Creates a new product template. Updates 'template_multi_barcodes' with
the new product variant ID.
"""
res = super(ProductTemplate, self).create(vals)
res.template_multi_barcodes.update({
'product_multi': res.product_variant_id.id
})
return res
def write(self, vals):
"""
Updates an existing product template. Ensures
'template_multi_barcodes' is updated if present.
"""
res = super(ProductTemplate, self).write(vals)
if self.template_multi_barcodes:
self.template_multi_barcodes.update({
'product_multi': self.product_variant_id.id
})
return res

63
all_in_one_pos_kit/models/res_config_settings.py

@ -0,0 +1,63 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import fields, models
class ResConfigSettings(models.TransientModel):
"""Inherited Configuration Settings"""
_inherit = "res.config.settings"
customer_msg = fields.Boolean(string='POS Greetings',
config_parameter='pos.customer_msg',
help='Create an account if you ever create an'
'account')
auth_token = fields.Char(string='Auth Token',
config_parameter='pos.auth_token',
help='Copy the token from your twilio console '
'window adn paste here')
account_sid = fields.Char(string='Account SID',
config_parameter='pos.account_sid',
help='Enter the Account SID provided by Twilio '
'for authentication.')
twilio_number = fields.Char(string='Twilio Number',
config_parameter='pos.twilio_number',
help='Enter the Twilio phone number used to '
'send the SMS.')
sms_body = fields.Text(string='Body',
help='Enter the content or message of the SMS to be'
'sent.')
def set_values(self):
"""Override method to set configuration values.
:return: Result of the super method"""
res = super(ResConfigSettings, self).set_values()
self.env['ir.config_parameter'].set_param(
'pos.sms_body', self.sms_body)
return res
def get_values(self):
"""Override method to get configuration values.
:return: Dictionary of configuration values"""
res = super(ResConfigSettings, self).get_values()
res.update(sms_body=self.env['ir.config_parameter'].sudo().get_param(
'pos.sms_body'))
return res

47
all_in_one_pos_kit/models/res_users.py

@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
###############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################
from odoo import api, fields, models
class ResUsers(models.Model):
"""Inherit the class res_users to add field"""
_inherit = "res.users"
pos_conf_id = fields.Many2one('pos.config', string="POS Configuration",
help='select POS for the user')
pos_config_ids = fields.Many2many('pos.config', string='Allowed Pos',
help='Allowed Pos for this user')
show_users = fields.Boolean(string="Show users of pos", default=True,
help='Show users in dashboard ( for pos '
'administrators only)')
@api.model
def create(self, vals):
"""This method creates a new record for the ResUsers model with the
provided values. It clears the caches before creating the record."""
self.clear_caches()
return super(ResUsers, self).create(vals)
def write(self, vals):
"""For clearing out existing values and update with new values"""
self.clear_caches()
return super(ResUsers, self).write(vals)

58
all_in_one_pos_kit/models/stock_production_lot.py

@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
################################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
from odoo import api, models
from odoo.tools import float_compare
class StockLot(models.Model):
"""This class inherits from the "stock.lot" model, which represents lots of
products in the inventory.It adds additional methods and fields to enhance
the functionality related to lots."""
_inherit = "stock.production.lot"
@api.model
def get_available_lots_for_pos(self, product_id, ):
"""Get available lots for a product suitable for the Point of Sale
(PoS).This method retrieves the available lots for a specific product
that are suitable for the Point of Sale (PoS) based on the configured
removal strategy. The lots are sorted based on the expiration date or
creation date,depending on the removal strategy."""
company_id = self.env.company.id
removal_strategy_id = (self.env['product.template'].browse(
self.env['product.product'].browse(product_id).product_tmpl_id.id)
.categ_id.removal_strategy_id.method)
if removal_strategy_id == 'fefo':
lots = self.sudo().search(
["&", ["product_id", "=", product_id], "|",
["company_id", "=", company_id],
["company_id", "=", False]],
order='expiration_date asc')
else:
lots = self.sudo().search(
["&", ["product_id", "=", product_id], "|",
["company_id", "=", company_id],
["company_id", "=", False], ],
order='create_date asc')
lots = lots.filtered(lambda l: float_compare(
l.product_qty, 0,
precision_digits=l.product_uom_id.rounding) > 0)[:1]
return lots.mapped("name")

22
all_in_one_pos_kit/report/__init__.py

@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from . import pos_order_report

45
all_in_one_pos_kit/report/pos_order_report.py

@ -0,0 +1,45 @@
# -*- coding: utf-8 -*-
#############################################################################
#
# Cybrosys Technologies Pvt. Ltd.
#
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>)
# Author: Jumana Haseen (odoo@cybrosys.com)
#
# You can modify it under the terms of the GNU AFFERO
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3.
#
# 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 (AGPL v3) for more details.
#
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
# (AGPL v3) along with this program.
# If not, see <http://www.gnu.org/licenses/>.
#
#############################################################################
from odoo import api, models
class PosOrder(models.AbstractModel):
_name = 'report.all_in_one_pos_kit.pos_order_report'
@api.model
def _get_report_values(self, docids, data=None):
"""
Retrieves report values based on the given docids and data.
If the context indicates that this is a POS order report, it processes the report data.
:param docids: List of document IDs for which to generate the report.
:param data: Dictionary containing report data and filters.
:return: Updated data dictionary for report generation.
"""
if self.env.context.get('pos_order_report'):
if data.get('report_data'):
data.update({'report_main_line_data': data.get('report_data')[
'report_lines'],
'Filters': data.get('report_data')['filters'],
'company': self.env.company,
})
return data

411
all_in_one_pos_kit/report/pos_order_report.xml

@ -0,0 +1,411 @@
<odoo>
<template id="pos_order_report">
<t t-call="web.html_container">
<t t-call="web.internal_layout">
<t t-if="Filters.get('report_type')=='Report By Order'">
<t t-call="all_in_one_pos_kit.report_order"/>
</t>
<t t-if="Filters.get('report_type')=='Report By Order Detail'">
<t t-call="all_in_one_pos_kit.report_order_detail"/>
</t>
<t t-if="Filters.get('report_type')=='Report By Product'">
<t t-call="all_in_one_pos_kit.report_product"/>
</t>
<t t-if="Filters.get('report_type')=='Report By Categories'">
<t t-call="all_in_one_pos_kit.report_category"/>
</t>
<t t-if="Filters.get('report_type')=='Report By Salesman'">
<t t-call="all_in_one_pos_kit.report_salesman"/>
</t>
<t t-if="Filters.get('report_type')=='Report By Payment'">
<t t-call="all_in_one_pos_kit.report_payment"/>
</t>
</t>
</t>
</template>
<template id="report_order">
<div class="page">
<div class="oe_structure"/>
<span t-if="Filters.get('date_from')">
<strong>From:</strong>
<t t-esc="Filters['date_from']"/>
</span>
<span t-if="Filters.get('date_to')">
<strong>To:</strong>
<t t-esc="Filters['date_to']"/>
</span>
<div>
<div style="width:100%;">
<div style="text-align:centre;" class="row">
<div class="col-2">
<strong>Report Type:</strong>
<t t-esc="Filters.get('report_type')"/>
</div>
</div>
</div>
<br/>
<table class="table table-sm table-reports">
<thead>
<tr class="text-right">
<th>PoS</th>
<th colspan="6">Order</th>
<th colspan="6">Date Order</th>
<th colspan="6">Customer</th>
<th colspan="6">Salesman</th>
<th colspan="6">Total Qty</th>
<th colspan="6">Amount Total</th>
<th colspan="6">Note</th>
</tr>
</thead>
<tbody>
<t t-foreach="report_main_line_data" t-as="main">
<tr style="font-weight: bold;">
<td colspan="6">
<span t-esc="main['shop']"/>
</td>
<td colspan="6">
<span t-esc="main['session']"/>
</td>
<td colspan="6">
<span t-esc="main['date_order']"/>
</td>
<td colspan="6">
<span t-esc="main['name']"/>
</td>
<td colspan="6">
<span t-esc="main['salesman']"/>
</td>
<td colspan="6">
<span t-esc="main['sum']"/>
</td>
<td colspan="6">
<span t-esc="main['amount_total']"/>
</td>
<td colspan="6">
<span t-esc="main['note']"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<br/>
</div>
</template>
<template id="report_order_detail">
<div class="page">
<div class="oe_structure"/>
<span t-if="Filters.get('date_from')">
<strong>From:</strong>
<t t-esc="Filters['date_from']"/>
</span>
<span t-if="Filters.get('date_to')">
<strong>To:</strong>
<t t-esc="Filters['date_to']"/>
</span>
<div>
<div style="width:100%;">
<div style="text-align:centre;" class="row">
<div class="col-2">
<strong>Report Type:</strong>
<t t-esc="Filters.get('report_type')"/>
</div>
</div>
</div>
<br/>
<table class="table table-sm table-reports">
<thead>
<tr class="text-right">
<th>PoS</th>
<th colspan="6">Order</th>
<th colspan="6">Date Order</th>
<th colspan="6">Customer</th>
<th colspan="6">Salesman</th>
<th colspan="6">Product Code</th>
<th colspan="6">Product Name</th>
<th colspan="6">Price unit</th>
<th colspan="6">Qty</th>
<th colspan="6">Price Subtotal</th>
<th colspan="6">Price Subtotal Incl</th>
</tr>
</thead>
<tbody>
<t t-foreach="report_main_line_data" t-as="main">
<tr style="font-weight: bold;">
<td colspan="6">
<span t-esc="main['shop']"/>
</td>
<td colspan="6">
<span t-esc="main['session']"/>
</td>
<td colspan="6">
<span t-esc="main['date_order']"/>
</td>
<td colspan="6">
<span t-esc="main['name']"/>
</td>
<td colspan="6">
<span t-esc="main['salesman']"/>
</td>
<td colspan="6">
<span t-esc="main['default_code']"/>
</td>
<td colspan="6">
<span t-esc="main['full_product_name']"/>
</td>
<td colspan="6">
<span t-esc="main['price_unit']"/>
</td>
<td colspan="6">
<span t-esc="main['sum']"/>
</td>
<td colspan="6">
<span t-esc="main['price_subtotal']"/>
</td>
<td colspan="6">
<span t-esc="main['price_subtotal_incl']"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<br/>
</div>
</template>
<template id="report_product">
<div class="page">
<div class="oe_structure"/>
<span t-if="Filters.get('date_from')">
<strong>From:</strong>
<t t-esc="Filters['date_from']"/>
</span>
<span t-if="Filters.get('date_to')">
<strong>To:</strong>
<t t-esc="Filters['date_to']"/>
</span>
<div>
<div style="width:100%;">
<div style="text-align:centre;" class="row">
<div class="col-2">
<strong>Report Type:</strong>
<t t-esc="Filters.get('report_type')"/>
</div>
</div>
</div>
<br/>
<table class="table table-sm table-reports">
<thead>
<tr class="text-right">
<th>Category</th>
<th colspan="6">Product Code</th>
<th colspan="6">Product Name</th>
<th colspan="6">Qty</th>
<th colspan="6">Amount Total</th>
<th colspan="6">Amount Total Incl</th>
</tr>
</thead>
<tbody>
<t t-foreach="report_main_line_data" t-as="main">
<tr style="font-weight: bold;">
<td colspan="6">
<span t-esc="main['name']"/>
</td>
<td colspan="6">
<span t-esc="main['default_code']"/>
</td>
<td colspan="6">
<span t-esc="main['full_product_name']"/>
</td>
<td colspan="6">
<span t-esc="main['qty']"/>
</td>
<td colspan="6">
<span t-esc="main['amount_total']"/>
</td>
<td colspan="6">
<span t-esc="main['amount_total']"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<br/>
</div>
</template>
<template id="report_category">
<div class="page">
<div class="oe_structure"/>
<span t-if="Filters.get('date_from')">
<strong>From:</strong>
<t t-esc="Filters['date_from']"/>
</span>
<span t-if="Filters.get('date_to')">
<strong>To:</strong>
<t t-esc="Filters['date_to']"/>
</span>
<div>
<div style="width:100%;">
<div style="text-align:centre;" class="row">
<div class="col-2">
<strong>Report Type:</strong>
<t t-esc="Filters.get('report_type')"/>
</div>
</div>
</div>
<br/>
<table class="table table-sm table-reports">
<thead>
<tr>
<th colspan="6">Category</th>
<th colspan="6">Qty</th>
<th colspan="6">Amount Total</th>
<th colspan="6">Amount Total Incl</th>
</tr>
</thead>
<tbody>
<t t-foreach="report_main_line_data" t-as="pos_category">
<tr style="font-weight: bold;">
<td colspan="6">
<span t-esc="pos_category['name']"/>
</td>
<td colspan="6">
<span t-esc="pos_category['qty']"/>
</td>
<td colspan="6">
<span t-esc="pos_category['amount_total']"/>
</td>
<td colspan="6">
<span t-esc="pos_category['total_incl']"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<br/>
</div>
</template>
<template id="report_salesman">
<div class="page">
<div class="oe_structure"/>
<span t-if="Filters.get('date_from')">
<strong>From:</strong>
<t t-esc="Filters['date_from']"/>
</span>
<span t-if="Filters.get('date_to')">
<strong>To:</strong>
<t t-esc="Filters['date_to']"/>
</span>
<div>
<div style="width:100%;">
<div style="text-align:centre;" class="row">
<div class="col-2">
<strong>Report Type:</strong>
<t t-esc="Filters.get('report_type')"/>
</div>
</div>
</div>
<br/>
<table class="table table-sm table-reports">
<thead>
<tr>
<th>Salesman</th>
<th colspan="6">Total Order</th>
<th colspan="6">Total Qty</th>
<th colspan="6">Total Amount</th>
</tr>
</thead>
<tbody>
<t t-foreach="report_main_line_data" t-as="main">
<tr style="font-weight: bold;">
<td>
<span t-esc="main['name']"/>
</td>
<td colspan="6">
<span t-esc="main['order']"/>
</td>
<td colspan="6">
<span t-esc="main['qty']"/>
</td>
<td colspan="6">
<span t-esc="main['amount']"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<br/>
</div>
</template>
<template id="report_payment">
<div class="page">
<div class="oe_structure"/>
<span t-if="Filters.get('date_from')">
<strong>From:</strong>
<t t-esc="Filters['date_from']"/>
</span>
<span t-if="Filters.get('date_to')">
<strong>To:</strong>
<t t-esc="Filters['date_to']"/>
</span>
<div>
<div style="width:100%;">
<div style="text-align:centre;" class="row">
<div class="col-2">
<strong>Report Type:</strong>
<t t-esc="Filters.get('report_type')"/>
</div>
</div>
</div>
<br/>
<table class="table table-sm table-reports">
<thead>
<tr>
<th>Point of Sale</th>
<th colspan="6">PoS Session</th>
<th colspan="6">Payment</th>
<th colspan="6">Total Amount</th>
</tr>
</thead>
<tbody>
<t t-foreach="report_main_line_data" t-as="main">
<tr style="font-weight: bold;">
<td>
<span t-esc="main['config']"/>
</td>
<td colspan="6">
<span t-esc="main['session']"/>
</td>
<td colspan="6">
<span t-esc="main['name']"/>
</td>
<td colspan="6">
<span t-esc="main['sum']"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<br/>
</div>
</template>
<record id="action_report_pos_all_in_one" model="ir.actions.report">
<field name="name">POS All In One Report</field>
<field name="model">pos.report</field>
<field name="report_type">qweb-pdf</field>
<field name="report_name">all_in_one_pos_kit.pos_order_report</field>
<field name="report_file">all_in_one_pos_kit.pos_order_report</field>
</record>
</odoo>

43
all_in_one_pos_kit/security/all_in_one_pos_kit_security.xml

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="restrict_user" model="ir.rule">
<!-- Restrict access to Configurations for Users -->
<field name="name">Config User</field>
<field name="model_id" ref="point_of_sale.model_pos_config"/>
<field name="domain_force">[('id','in',user.pos_config_ids.ids)]</field>
<field name="groups" eval="[(4,ref('point_of_sale.group_pos_user'))]"/>
</record>
<record id="restrict_manager" model="ir.rule">
<!-- Restrict access to Configurations for Managers -->
<field name="name">Config Manager</field>
<field name="model_id" ref="point_of_sale.model_pos_config"/>
<field name="domain_force">[]</field>
<field name="groups"
eval="[(4,ref('point_of_sale.group_pos_manager'))]"/>
</record>
<record id="order_user" model="ir.rule">
<!-- Restrict access to Orders for Users -->
<field name="name">Orders User</field>
<field name="model_id" ref="point_of_sale.model_pos_order"/>
<field name="domain_force">
[('config_id','in',user.pos_config_ids.ids)]
</field>
<field name="groups" eval="[(4,ref('point_of_sale.group_pos_user'))]"/>
</record>
<record id="order_manager" model="ir.rule">
<!-- Orders Manager Rule -->
<field name="name">Orders Manager</field>
<field name="model_id" ref="point_of_sale.model_pos_order"/>
<field name="domain_force">[]</field>
<field name="groups"
eval="[(4,ref('point_of_sale.group_pos_manager'))]"/>
</record>
<record id="meals_panning_rule_company" model="ir.rule">
<field name="name">multi company access for meals planning</field>
<field name="model_id" ref="model_meals_planning"/>
<field name="domain_force">
['|',('company_id','=',False),('company_id',
'in', company_ids)]
</field>
</record>
</odoo>

8
all_in_one_pos_kit/security/ir.model.access.csv

@ -0,0 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_multi_barcode_product,access.multi.barcode.product,model_multi_barcode_product,base.group_user,1,1,1,1
access_mrp_production_pos_user,mrp.production.pos.user,mrp.model_mrp_production,point_of_sale.group_pos_user,1,1,1,0
access_mrp_bom_pos_user,mrp.bom.pos.user,mrp.model_mrp_bom,point_of_sale.group_pos_user,1,0,0,0
access_pos_report,access.pos.report,model_pos_report,base.group_user,1,1,1,1
access_meals_planning_user,access.meals.planning.user,model_meals_planning,point_of_sale.group_pos_user,1,0,0,0
access_meals_planning_manager,access.meals.planning.manager,model_meals_planning,point_of_sale.group_pos_manager,1,1,1,1
access_pos_greetings,access.pos.greetings,model_pos_greetings,base.group_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_multi_barcode_product access.multi.barcode.product model_multi_barcode_product base.group_user 1 1 1 1
3 access_mrp_production_pos_user mrp.production.pos.user mrp.model_mrp_production point_of_sale.group_pos_user 1 1 1 0
4 access_mrp_bom_pos_user mrp.bom.pos.user mrp.model_mrp_bom point_of_sale.group_pos_user 1 0 0 0
5 access_pos_report access.pos.report model_pos_report base.group_user 1 1 1 1
6 access_meals_planning_user access.meals.planning.user model_meals_planning point_of_sale.group_pos_user 1 0 0 0
7 access_meals_planning_manager access.meals.planning.manager model_meals_planning point_of_sale.group_pos_manager 1 1 1 1
8 access_pos_greetings access.pos.greetings model_pos_greetings base.group_user 1 1 1 1

BIN
all_in_one_pos_kit/static/description/assets/icons/check.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

BIN
all_in_one_pos_kit/static/description/assets/icons/chevron.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 B

BIN
all_in_one_pos_kit/static/description/assets/icons/cogs.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
all_in_one_pos_kit/static/description/assets/icons/consultation.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
all_in_one_pos_kit/static/description/assets/icons/ecom-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

BIN
all_in_one_pos_kit/static/description/assets/icons/education-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 733 B

BIN
all_in_one_pos_kit/static/description/assets/icons/hotel-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

BIN
all_in_one_pos_kit/static/description/assets/icons/license.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
all_in_one_pos_kit/static/description/assets/icons/lifebuoy.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
all_in_one_pos_kit/static/description/assets/icons/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
all_in_one_pos_kit/static/description/assets/icons/manufacturing-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 673 B

BIN
all_in_one_pos_kit/static/description/assets/icons/pos-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

BIN
all_in_one_pos_kit/static/description/assets/icons/puzzle.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 653 B

BIN
all_in_one_pos_kit/static/description/assets/icons/restaurant-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 905 B

BIN
all_in_one_pos_kit/static/description/assets/icons/service-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 839 B

BIN
all_in_one_pos_kit/static/description/assets/icons/trading-black.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 427 B

BIN
all_in_one_pos_kit/static/description/assets/icons/training.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 627 B

BIN
all_in_one_pos_kit/static/description/assets/icons/update.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
all_in_one_pos_kit/static/description/assets/icons/user.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 988 B

BIN
all_in_one_pos_kit/static/description/assets/icons/wrench.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
all_in_one_pos_kit/static/description/assets/modules/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
all_in_one_pos_kit/static/description/assets/modules/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
all_in_one_pos_kit/static/description/assets/modules/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
all_in_one_pos_kit/static/description/assets/modules/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
all_in_one_pos_kit/static/description/assets/modules/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
all_in_one_pos_kit/static/description/assets/modules/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/10.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/11.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/12.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/13.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/14.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/15.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/16.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/17.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 175 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/18.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/19.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/20.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/21.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/22.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/23.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/24.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/25.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/26.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/27.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/28.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 513 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/29.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 85 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/30.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/31.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 162 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/32.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/33.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/34.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/35.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/36.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/37.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/38.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/39.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/40.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/41.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/42.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/43.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 521 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/44.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/45.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/46.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/47.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/48.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/5.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/6.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
all_in_one_pos_kit/static/description/assets/screenshots/7.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save