@ -0,0 +1,46 @@ |
|||||
|
.. 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 |
||||
|
|
||||
|
License |
||||
|
------- |
||||
|
GNU AFFERO GENERAL PUBLIC LICENSE Version 3 (AGPL v3) |
||||
|
(https://www.gnu.org/licenses/agpl-3.0-standalone.html) |
||||
|
|
||||
|
Company |
||||
|
------- |
||||
|
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
||||
|
|
||||
|
Credits |
||||
|
------- |
||||
|
* Developer: (V16) Afra MP, |
||||
|
(V17) Ayana KP, Amaya Aravind, |
||||
|
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 support and more information, please visit https://www.cybrosys.com |
||||
|
|
||||
|
Further information |
||||
|
=================== |
||||
|
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,24 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 |
@ -0,0 +1,95 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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': '17.0.1.0.0', |
||||
|
'category': 'Point of Sale', |
||||
|
'summary': """This module combines Different POS features""", |
||||
|
'description': """ All in One POS Kit module contain many features, such as POS dashboard, Dynamic POS Report Maker, Age Restricted Products in POS, Advanced POS Receipt with Customer Details and Invoice Details, |
||||
|
Helps to directly log in to POS, Allows to create multiple barcode for a single Product, etc.""", |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'maintainer': 'Cybrosys Techno Solutions', |
||||
|
'website': "https://www.cybrosys.com", |
||||
|
'depends': ['hr', 'point_of_sale', 'mrp', 'web'], |
||||
|
'external_dependencies': {'python': ['twilio', 'pandas']}, |
||||
|
'data': [ |
||||
|
'security/all_in_one_pos_kit_security.xml', |
||||
|
'security/ir.model.access.csv', |
||||
|
'views/res_config_settings_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_report_views.xml', |
||||
|
'views/pos_greetings_views.xml', |
||||
|
'views/meals_planning_views.xml', |
||||
|
'report/all_in_one_pos_kit_templates.xml', |
||||
|
], |
||||
|
'assets': { |
||||
|
'point_of_sale._assets_pos': [ |
||||
|
'all_in_one_pos_kit/static/src/exchange_product/scss/pos.scss', |
||||
|
'all_in_one_pos_kit/static/src/order_line_image/css/order_line_image.css', |
||||
|
'all_in_one_pos_kit/static/src/product_creation/css/*', |
||||
|
'all_in_one_pos_kit/static/src/mass_edit/js/*', |
||||
|
'all_in_one_pos_kit/static/src/service_charge/js/*', |
||||
|
'all_in_one_pos_kit/static/src/exchange_product/js/*', |
||||
|
'all_in_one_pos_kit/static/src/age_restricted/js/*', |
||||
|
'all_in_one_pos_kit/static/src/multi_barcode/js/pos_scan.js', |
||||
|
'all_in_one_pos_kit/static/src/delete_order_line/js/*', |
||||
|
'all_in_one_pos_kit/static/src/custom_tip/js/PaymentScreen.js', |
||||
|
'all_in_one_pos_kit/static/src/pos_mrp_order/js/*', |
||||
|
'all_in_one_pos_kit/static/src/pos_num_show_hide/js/pos_numpad.js', |
||||
|
'all_in_one_pos_kit/static/src/product_creation/js/*', |
||||
|
'all_in_one_pos_kit/static/src/advanced_receipt/js/*', |
||||
|
'all_in_one_pos_kit/static/src/order_line_image/js/pos_order_line.js', |
||||
|
'all_in_one_pos_kit/static/src/time_based_product/js/*', |
||||
|
'all_in_one_pos_kit/static/src/exchange_product/xml/*', |
||||
|
'all_in_one_pos_kit/static/src/mass_edit/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', |
||||
|
'all_in_one_pos_kit/static/src/order_line_image/xml/pos_order_line.xml', |
||||
|
'all_in_one_pos_kit/static/src/delete_order_line/xml/*', |
||||
|
'all_in_one_pos_kit/static/src/custom_tip/xml/PaymentScreen.xml', |
||||
|
'all_in_one_pos_kit/static/src/pos_num_show_hide/xml/pos.xml', |
||||
|
'all_in_one_pos_kit/static/src/product_creation/xml/*', |
||||
|
'all_in_one_pos_kit/static/src/advanced_receipt/xml/OrderReceipt.xml', |
||||
|
'all_in_one_pos_kit/static/src/pos_logo/xml/*', |
||||
|
], |
||||
|
'web.assets_backend': [ |
||||
|
'all_in_one_pos_kit/static/src/dashboard/css/pos_dashboard.css', |
||||
|
'all_in_one_pos_kit/static/src/pos_report/css/*', |
||||
|
'all_in_one_pos_kit/static/src/dashboard/xml/pos_dashboard.xml', |
||||
|
'all_in_one_pos_kit/static/src/dashboard/js/pos_dashboard.js', |
||||
|
'all_in_one_pos_kit/static/src/pos_report/js/pos_report.js', |
||||
|
'all_in_one_pos_kit/static/src/pos_report/xml/*', |
||||
|
'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.8.0/Chart.bundle.js', |
||||
|
], |
||||
|
}, |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'installable': True, |
||||
|
'application': False, |
||||
|
'auto_install': False, |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 |
@ -0,0 +1,42 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 |
||||
|
import werkzeug |
||||
|
from odoo.addons.web.controllers.main import home |
||||
|
|
||||
|
|
||||
|
class PosScreen(home.Home): |
||||
|
"""The class PosScreen is used to log in pos session directly""" |
||||
|
|
||||
|
@http.route('/web/login', type='http', auth="none") |
||||
|
def web_login(self, redirect=None, **kw): |
||||
|
"""Override to add direct login to POS""" |
||||
|
res = super().web_login(redirect=redirect, **kw) |
||||
|
if request.env.user.pos_conf_id: |
||||
|
if not request.env.user.pos_conf_id.current_session_id: |
||||
|
request.env['pos.session'].sudo().create({ |
||||
|
'user_id': request.env.uid, |
||||
|
'config_id': request.env.user.pos_conf_id.id |
||||
|
}) |
||||
|
return werkzeug.utils.redirect('/pos/ui') |
||||
|
return res |
@ -0,0 +1,57 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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.tools import html_escape |
||||
|
|
||||
|
|
||||
|
class TBXLSXReportController(http.Controller): |
||||
|
"""Controller class for generating and downloading XLSX reports.""" |
||||
|
|
||||
|
@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 download an XLSX report. |
||||
|
:param model: The model name for which the report is generated. |
||||
|
:param options: Options/configuration for the report. |
||||
|
:param output_format: The output format of the report (e.g.,'xlsx') |
||||
|
:param report_data: Data required for generating the report. |
||||
|
:param report_name: The name of the report. |
||||
|
:param dfr_data: Additional data required for the report. |
||||
|
:returns: The HTTP response containing the generated XLSX report""" |
||||
|
report_obj = request.env[model].with_user(request.session.uid) |
||||
|
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, |
||||
|
dfr_data) |
||||
|
response.set_cookie('fileToken', 'dummy-because-api-expects-one') |
||||
|
return response |
||||
|
except Exception as e: |
||||
|
error = {'code': 200, 'message': 'Odoo Server Error', |
||||
|
'data': http.serialize_exception(e)} |
||||
|
return request.make_response(html_escape(json.dumps(error))) |
@ -0,0 +1,5 @@ |
|||||
|
## Module <all_in_one_pos_kit> |
||||
|
#### 07.05.2025 |
||||
|
#### Version 17.0.1.0.0 |
||||
|
#### ADD |
||||
|
- Initial commit for All in One POS Kit |
@ -0,0 +1,35 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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_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_lot |
@ -0,0 +1,80 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 math |
||||
|
import re |
||||
|
from odoo import api, fields, models |
||||
|
|
||||
|
|
||||
|
class AccountMove(models.Model): |
||||
|
"""Inherit the account_move module to add new fields and functions""" |
||||
|
_inherit = "account.move" |
||||
|
|
||||
|
account_barcode = fields.Char(string='Barcode', |
||||
|
help='Barcode associated with the account ' |
||||
|
'move.') |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, vals): |
||||
|
"""Super the create function to It generates an EAN barcode based on |
||||
|
the ID of the created record and assigns it to the `account_barcode` |
||||
|
field.""" |
||||
|
res = super(AccountMove, self).create(vals) |
||||
|
res.account_barcode = self.generate_ean(str(res.id)) |
||||
|
return res |
||||
|
|
||||
|
def ean_checksum(self, eancode): |
||||
|
"""Returns the checksum of an ean string of length 13, returns -1 if |
||||
|
the string has the wrong length""" |
||||
|
if len(eancode) != 13: |
||||
|
return -1 |
||||
|
oddsum = 0 |
||||
|
evensum = 0 |
||||
|
finalean = eancode[::-1][1:] |
||||
|
for i in range(len(finalean)): |
||||
|
if i % 2 == 0: |
||||
|
oddsum += int(finalean[i]) |
||||
|
else: |
||||
|
evensum += int(finalean[i]) |
||||
|
return int(10 - math.ceil((oddsum * 3) + evensum % 10.0)) % 10 |
||||
|
|
||||
|
def check_ean(eancode): |
||||
|
"""Returns True if eancode is a valid ean13 string, or null""" |
||||
|
if not eancode: |
||||
|
return True |
||||
|
if len(eancode) != 13: |
||||
|
return False |
||||
|
try: |
||||
|
int(eancode) |
||||
|
except: |
||||
|
return False |
||||
|
return eancode.ean_checksum(eancode) == int(eancode[-1]) |
||||
|
|
||||
|
def generate_ean(self, ean): |
||||
|
"""Creates and returns a valid ean13 from an invalid one""" |
||||
|
if not ean: |
||||
|
return "0000000000000" |
||||
|
ean = re.sub("[A-Za-z]", "0", ean) |
||||
|
ean = re.sub("[^0-9]", "", ean) |
||||
|
ean = ean[:13] |
||||
|
if len(ean) < 13: |
||||
|
ean = ean + '0' * (13 - len(ean)) |
||||
|
return ean[:-1] + str(self.ean_checksum(ean)) |
@ -0,0 +1,67 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Ayana KP(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.session', 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)') |
||||
|
menu_product_ids = fields.Many2many('product.product', |
||||
|
string='Product', |
||||
|
domain=[('available_in_pos', '=', True)]) |
||||
|
|
||||
|
state = fields.Selection([('activated', 'Activated'), |
||||
|
('deactivated', 'Deactivated')], |
||||
|
default='deactivated') |
||||
|
company_id = fields.Many2one('res.company', string='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!') |
||||
|
elif 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'}) |
@ -0,0 +1,106 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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): |
||||
|
""" Extends MRP Production model for creating manufacturing orders from POS |
||||
|
orders.""" |
||||
|
_inherit = 'mrp.production' |
||||
|
|
||||
|
def create_mrp_from_pos(self, products): |
||||
|
""" Function for creating manufacturing orders.""" |
||||
|
product_ids = [] |
||||
|
if products: |
||||
|
for product in products: |
||||
|
if self.env['product.product'].browse( |
||||
|
int(product['id'])).to_make_mrp: |
||||
|
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, |
||||
|
'picking_type_id': mrp_order.picking_type_id.id, |
||||
|
'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, |
||||
|
})) |
||||
|
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 |
@ -0,0 +1,46 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 ProductMultiBarcode(models.Model): |
||||
|
"""Created new model to add store multi barcode for product""" |
||||
|
_name = 'multi.barcode.products' |
||||
|
_description = 'For creating multiple Barcodes for products' |
||||
|
|
||||
|
multi_barcode = fields.Char(string="Barcode", |
||||
|
help="Provide alternate barcodes for this " |
||||
|
"product") |
||||
|
product_id = fields.Many2one('product.product', |
||||
|
string='Product', |
||||
|
help='Related product name in product.product' |
||||
|
' model') |
||||
|
product_template_id = fields.Many2one('product.template', |
||||
|
string='Product template', |
||||
|
help='Related product name in ' |
||||
|
'product.template model') |
||||
|
_sql_constraints = [('field_unique', 'unique(multi_barcode)', |
||||
|
'Existing barcode is not allowed !'), ] |
||||
|
|
||||
|
def get_barcode_val(self, product): |
||||
|
"""Returns barcode of record in self and product id""" |
||||
|
return self.multi_barcode, product |
@ -0,0 +1,81 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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_session = fields.Boolean(string="Session", |
||||
|
compute='_compute_is_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") |
||||
|
image = fields.Binary(string='Image', help='Add logo for pos session') |
||||
|
user_ids = fields.Many2many('res.users', |
||||
|
compute='_compute_user_ids', string='User', |
||||
|
help="The users who are allowed to access this" |
||||
|
"feature.") |
||||
|
|
||||
|
def _compute_is_session(self): |
||||
|
"""To check the service charge is set up for session wise or |
||||
|
globally""" |
||||
|
if self.env['ir.config_parameter'].sudo().get_param( |
||||
|
'all_in_one_pos_kit.visibility') == '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 enabled set service product |
||||
|
and amount by default per session""" |
||||
|
if self.is_service_charges: |
||||
|
if not self.service_product_id: |
||||
|
self.service_product_id = self.env['product.product'].search( |
||||
|
[('available_in_pos', '=', True), ('sale_ok', '=', True), |
||||
|
('type', '=', 'service')], limit=1) |
||||
|
self.service_charge = 10.0 |
||||
|
else: |
||||
|
self.service_product_id = False |
||||
|
self.service_charge = 0.0 |
||||
|
|
||||
|
def _compute_user_ids(self): |
||||
|
"""Computes the allowed users in pos""" |
||||
|
for record in self: |
||||
|
if record.env.user.show_users: |
||||
|
record.user_ids = self.env['res.users'].search( |
||||
|
[('pos_config_ids', '=', record.id)]) |
||||
|
else: |
||||
|
record.user_ids = None |
@ -0,0 +1,55 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 GNor similar depending on the terminology used in your Odoo instance.U 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 for creating pos greetings details""" |
||||
|
_name = 'pos.greetings' |
||||
|
_description = 'POS Greetings' |
||||
|
_rec_name = 'order_id' |
||||
|
|
||||
|
customer_id = fields.Many2one('res.partner', |
||||
|
help="Select customer for sending greetings", |
||||
|
string='Customer') |
||||
|
order_id = fields.Many2one('pos.order', |
||||
|
help="Pos order details of related to the " |
||||
|
"greeting messages", |
||||
|
string='Order') |
||||
|
twilio_auth_token = fields.Char(string="Token", |
||||
|
help="Authentication token for sending " |
||||
|
"greetings messages") |
||||
|
twilio_number = fields.Char('Twilio Number', |
||||
|
help="Twilio number for sending greetings " |
||||
|
"messages") |
||||
|
to_number = fields.Char('Customer Number', |
||||
|
help="Add the receiver number for sending " |
||||
|
"greetings") |
||||
|
sms_body = fields.Char('Body', required=True, |
||||
|
help="Body of the greetings message") |
||||
|
session_id = fields.Many2one('pos.session', string='Session', |
||||
|
help="Pos session id which the greetings " |
||||
|
"messages related to") |
||||
|
send_sms = fields.Boolean(string='Send SMS', |
||||
|
help="Used for identifying is the sms is send " |
||||
|
"or not ", |
||||
|
default=False) |
@ -0,0 +1,265 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 |
||||
|
|
||||
|
|
||||
|
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' |
||||
|
|
||||
|
exchange = fields.Boolean(string='Exchange', |
||||
|
help='Enable if the order contain is exchange ' |
||||
|
'product') |
||||
|
sale_barcode = fields.Char(string='Barcode', |
||||
|
help='Barcode associated with the pos order.') |
||||
|
|
||||
|
def set_pos_exchange_order(self): |
||||
|
"""Mark order a exchanged""" |
||||
|
self.exchange = True |
||||
|
return |
||||
|
|
||||
|
@api.model |
||||
|
def get_department(self, option): |
||||
|
"""Function to filter the POs sales report chart""" |
||||
|
company_id = self.env.company.id |
||||
|
if option == 'pos_hourly_sales': |
||||
|
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( |
||||
|
self.env.user.tz if self.env.user.tz else pytz.UTC) |
||||
|
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 = [] |
||||
|
today = [] |
||||
|
for record in docs: |
||||
|
order.append(record.get('sum')) |
||||
|
today.append(record.get('date_month')) |
||||
|
return [order, today, label] |
||||
|
|
||||
|
@api.model |
||||
|
def get_details(self): |
||||
|
"""Function to get payment details,session details and sales person |
||||
|
details""" |
||||
|
company_id = self.env.company |
||||
|
self._cr.execute('''select pos_payment_method.name ->>'en_US',sum(amount) |
||||
|
from pos_payment inner join pos_payment_method on |
||||
|
pos_payment_method.id=pos_payment.payment_method_id |
||||
|
where pos_payment.company_id = ''' + str(company_id.id) + " " + ''' |
||||
|
group by pos_payment_method.name ORDER |
||||
|
BY sum(amount) DESC; ''') |
||||
|
payment_details = self._cr.fetchall() |
||||
|
self._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.id) + " " + '''GROUP BY hr_employee.name order by total DESC;''') |
||||
|
salesperson = self._cr.fetchall() |
||||
|
payments = [] |
||||
|
for rec in payment_details: |
||||
|
rec = list(rec) |
||||
|
if company_id.currency_id.position == 'after': |
||||
|
rec[1] = "%s %s" % (rec[1], company_id.currency_id.symbol) |
||||
|
else: |
||||
|
rec[1] = "%s %s" % (company_id.currency_id.symbol, rec[1]) |
||||
|
payments.append(tuple(rec)) |
||||
|
total_sales = [] |
||||
|
for rec in salesperson: |
||||
|
rec = list(rec) |
||||
|
if company_id.currency_id.position == 'after': |
||||
|
rec[1] = "%s %s" % (rec[1], company_id.currency_id.symbol) |
||||
|
else: |
||||
|
rec[1] = "%s %s" % (company_id.currency_id.symbol, rec[1]) |
||||
|
total_sales.append(tuple(rec)) |
||||
|
sessions_list = [] |
||||
|
session = {'opened': 'Opened', 'opening_control': "Opening Control"} |
||||
|
for session_id in self.env['pos.config'].search([]): |
||||
|
if session.get(session_id.pos_session_state) is None: |
||||
|
sessions_list.append({'session': session_id.name, |
||||
|
'status': 'Closed'}) |
||||
|
else: |
||||
|
sessions_list.append({'session': session_id.name, |
||||
|
'status': session.get( |
||||
|
session_id.pos_session_state)}) |
||||
|
return {'payment_details': payments, 'salesperson': total_sales, |
||||
|
'selling_product': sessions_list} |
||||
|
|
||||
|
@api.model |
||||
|
def get_refund_details(self): |
||||
|
"""Function to get total count of orders,session and refund orders""" |
||||
|
total = sum(self.env['pos.order'].search([]).mapped('amount_total')) |
||||
|
today_refund_total = 0 |
||||
|
today_sale = 0 |
||||
|
for pos_order_id in self.env['pos.order'].search([]): |
||||
|
if pos_order_id.date_order.date() == fields.date.today(): |
||||
|
today_sale = today_sale + 1 |
||||
|
if pos_order_id.amount_total < 0.0: |
||||
|
today_refund_total = today_refund_total + 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]) |
||||
|
data= { |
||||
|
'total_sale': val, |
||||
|
'total_order_count': self.env['pos.order'].search_count([]), |
||||
|
'total_refund_count': self.env['pos.order'].search_count( |
||||
|
[('amount_total', '<', 0.0)]), |
||||
|
'total_session': self.env['pos.session'].search_count([]), |
||||
|
'today_refund_total': today_refund_total, |
||||
|
'today_sale': today_sale, |
||||
|
} |
||||
|
return data |
||||
|
|
||||
|
@api.model |
||||
|
def get_the_top_customer(self): |
||||
|
"""Function to get top 10 customer in pos""" |
||||
|
self._cr.execute('''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( |
||||
|
self.env.company.id) + ''' GROUP BY pos_order.partner_id, |
||||
|
res_partner.name ORDER BY amount_total DESC LIMIT 10;''') |
||||
|
top_customer = self._cr.dictfetchall() |
||||
|
order = [] |
||||
|
day = [] |
||||
|
for record in top_customer: |
||||
|
order.append(record.get('amount_total')) |
||||
|
day.append(record.get('customer')) |
||||
|
return [order, day] |
||||
|
|
||||
|
@api.model |
||||
|
def get_the_top_products(self): |
||||
|
"""Function to get top 10 product in """ |
||||
|
|
||||
|
self._cr.execute('''select DISTINCT(product_template.name)->>'en_US' 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( |
||||
|
self.env.company.id) + ''' group by product_template.id ORDER |
||||
|
BY total_quantity DESC Limit 10 ''') |
||||
|
top_product = self._cr.dictfetchall() |
||||
|
total_quantity = [] |
||||
|
product_name = [] |
||||
|
for record in top_product: |
||||
|
total_quantity.append(record.get('total_quantity')) |
||||
|
product_name.append(record.get('product_name')) |
||||
|
return [total_quantity, product_name] |
||||
|
|
||||
|
@api.model |
||||
|
def get_the_top_categories(self): |
||||
|
"""Function to get top categories in pos""" |
||||
|
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( |
||||
|
self.env.company.id) + ''' group by product_category ORDER BY total_quantity DESC ''' |
||||
|
self._cr.execute(query) |
||||
|
top_categories = self._cr.dictfetchall() |
||||
|
total_quantity = [] |
||||
|
product_categ = [] |
||||
|
for record in top_categories: |
||||
|
total_quantity.append(record.get('total_quantity')) |
||||
|
product_categ.append(record.get('product_category')) |
||||
|
return [total_quantity, product_categ] |
||||
|
|
||||
|
@api.model |
||||
|
def get_invoice(self, id): |
||||
|
"""Retrieve invoice information based on a POS reference ID. |
||||
|
This method searches for a POS record with the specified reference ID. It |
||||
|
then retrieves the associated invoice based on the name matching the |
||||
|
reference. The invoice details, including ID, name, base URL, and account |
||||
|
barcode, are returned as a dictionary. |
||||
|
:param id: The POS reference ID to search for. |
||||
|
:return: A dictionary containing the invoice details. |
||||
|
:rtype: dict""" |
||||
|
invoice_id = self.env['account.move'].search( |
||||
|
[('ref', '=', self.search([('pos_reference', '=', id)]).name)]) |
||||
|
return {'invoice_id': invoice_id.id, 'invoice_name': invoice_id.name, |
||||
|
'base_url': self.env['ir.config_parameter'].get_param( |
||||
|
'web.base.url'), 'barcode': invoice_id.account_barcode} |
||||
|
|
||||
|
@api.model |
||||
|
def create_from_ui(self, orders, draft=False): |
||||
|
"""Create POS orders from the user interface and send SMS messages to |
||||
|
customers.This method creates POS orders from the provided data and |
||||
|
sends SMS messages to customers if the 'customer_msg' parameter is |
||||
|
set and the customer has a valid phone number.""" |
||||
|
res = super(PosOrder, self).create_from_ui(orders) |
||||
|
id = [line['id'] for line in res if line['id']] |
||||
|
if backend_order := self.search([('id', 'in', id)]): |
||||
|
for pos_order in backend_order: |
||||
|
params = self.env['ir.config_parameter'].sudo() |
||||
|
customer_msg = params.get_param('all_in_one_pos_kit.customer_msg') |
||||
|
twilio_auth_token = params.get_param('all_in_one_pos_kit.twilio_auth_token') |
||||
|
account_sid = params.get_param('all_in_one_pos_kit.account_sid') |
||||
|
twilio_number = params.get_param('all_in_one_pos_kit.twilio_number') |
||||
|
sms_body = params.get_param('all_in_one_pos_kit.sms_body') |
||||
|
if customer_msg and pos_order.partner_id.phone: |
||||
|
try: |
||||
|
customer_phone = str(pos_order.partner_id.phone) |
||||
|
# Download the helper library from https://www.twilio.com/docs/python/install |
||||
|
client = Client(account_sid, twilio_auth_token) |
||||
|
message = client.messages.create( |
||||
|
body=sms_body, |
||||
|
from_=twilio_number, |
||||
|
to=customer_phone |
||||
|
) |
||||
|
self.env['pos.greetings'].create({ |
||||
|
'customer_id': pos_order.partner_id.id, |
||||
|
'order_id': pos_order.id, |
||||
|
'twilio_auth_token': twilio_auth_token, |
||||
|
'twilio_number': twilio_number, |
||||
|
'to_number': customer_phone, |
||||
|
'session_id': pos_order.session_id.id, |
||||
|
'sms_body': sms_body, |
||||
|
'send_sms': True, |
||||
|
}) |
||||
|
except Exception as e: |
||||
|
pass |
||||
|
return res |
||||
|
|
||||
|
|
||||
|
class PosOrderLine(models.Model): |
||||
|
"""Inherit the class pos_order_line""" |
||||
|
_inherit = "pos.order.line" |
||||
|
|
||||
|
@api.model |
||||
|
def get_product_details(self, ids): |
||||
|
"""Function to get the product details""" |
||||
|
return [{'product_id': rec.product_id.id, 'name': rec.product_id.name, |
||||
|
'qty': rec.qty} |
||||
|
for rec in self.env['pos.order.line'].browse(ids)] |
@ -0,0 +1,438 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 io |
||||
|
import json |
||||
|
from odoo import api, fields, models |
||||
|
try: |
||||
|
from odoo.tools.misc import xlsxwriter |
||||
|
except ImportError: |
||||
|
import xlsxwriter |
||||
|
|
||||
|
|
||||
|
class PosReport(models.Model): |
||||
|
"""Class to generate PDF and XLS report in POS""" |
||||
|
_name = "pos.report" |
||||
|
_description = 'POS Report' |
||||
|
|
||||
|
pos_report = fields.Char(string="PoS Report", help="Enter the name of the " |
||||
|
"PoS report.") |
||||
|
date_from = fields.Datetime(string="Date From", help="Specify the starting" |
||||
|
"date for the report") |
||||
|
date_to = fields.Datetime(string="Date to", help="Specify the ending date" |
||||
|
"for the report.") |
||||
|
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="Select the type of report to generate.") |
||||
|
|
||||
|
@api.model |
||||
|
def pos_report(self, option): |
||||
|
"""The pos_report method is used to generate the PoS report based on the |
||||
|
selected option. It retrieves the necessary data from the database and |
||||
|
returns the report in a specific format.""" |
||||
|
report_values = self.browse(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}) |
||||
|
self._get_report_values(data) |
||||
|
return { |
||||
|
'name': "PoS Orders", |
||||
|
'type': 'ir.actions.client', |
||||
|
'tag': 'pos_r', |
||||
|
'orders': data, |
||||
|
'filters': self.get_filter(option), |
||||
|
'report_lines': self._get_report_values(data).get('POS'), |
||||
|
'report_main_line': self._get_report_values(data).get('pos_main'), |
||||
|
} |
||||
|
|
||||
|
def get_filter(self, option): |
||||
|
"""Get the filter settings for the report""" |
||||
|
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): |
||||
|
"""Get the filter data for the report""" |
||||
|
return {'report_type': self.env['pos.report'].search([ |
||||
|
('id', '=', option[0])]).report_type} |
||||
|
|
||||
|
def _get_report_sub_lines(self, data): |
||||
|
"""Get the sub_lines of the report""" |
||||
|
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_sub_lines.append(self._cr.dictfetchall()) |
||||
|
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_sub_lines.append(self._cr.dictfetchall()) |
||||
|
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.product_id as id, 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_sub_lines.append(self._cr.dictfetchall()) |
||||
|
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_sub_lines.append(self._cr.dictfetchall()) |
||||
|
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_sub_lines.append(self._cr.dictfetchall()) |
||||
|
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_sub_lines.append(self._cr.dictfetchall()) |
||||
|
return report_sub_lines |
||||
|
|
||||
|
def _get_report_total_value(self, data): |
||||
|
"""The _get_report_total_value method retrieves the total values of the |
||||
|
report based on the selected report type and filters.""" |
||||
|
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_main_lines.append(self._cr.dictfetchall()) |
||||
|
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_main_lines.append(self._cr.dictfetchall()) |
||||
|
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_main_lines.append(self._cr.dictfetchall()) |
||||
|
else: |
||||
|
report_main_lines = False |
||||
|
return report_main_lines |
||||
|
|
||||
|
def _get_report_values(self, data): |
||||
|
"""The _get_report_values method generates the report values by calling |
||||
|
_get_report_sub_lines and _get_report_total_value.""" |
||||
|
report_res_total = self._get_report_total_value(data) |
||||
|
if data.get('report_type'): |
||||
|
report_res = self._get_report_sub_lines(data)[0] |
||||
|
else: |
||||
|
report_res = self._get_report_sub_lines(data) |
||||
|
if data.get('report_type') == 'report_by_order': |
||||
|
report_res_total = self._get_report_total_value(data)[0] |
||||
|
return { |
||||
|
'doc_ids': self.ids, |
||||
|
'docs': data['model'], |
||||
|
'POS': report_res, |
||||
|
'pos_main': report_res_total, |
||||
|
|
||||
|
} |
||||
|
|
||||
|
def get_pos_xlsx_report(self, data, response, report_data, dfr_data): |
||||
|
"""Generate an XLSX report for the Point of Sale. |
||||
|
:param data: JSON-encoded data representing the report filters |
||||
|
:param response: HTTP response object |
||||
|
:param report_data: JSON-encoded report datas |
||||
|
:param dfr_data: data for DFR (Data Field Relations)""" |
||||
|
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() |
||||
|
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}) |
||||
|
if filters.get('report_type') == 'report_by_order': |
||||
|
sheet.merge_range('D5:F5', '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 = [rec for rec in report_data_main[0]] |
||||
|
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('E5:G5', '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 = [rec for rec in report_data_main[0]] |
||||
|
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 = [rec for rec in report_data_main[0]] |
||||
|
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:C5', '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 = [rec for rec in report_data_main[0]] |
||||
|
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:C5', '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 = [rec for rec in report_data_main[0]] |
||||
|
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:C5', '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 = [rec for rec in report_data_main[0]] |
||||
|
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: |
||||
|
name = list(rec_data['name'].values())[0] |
||||
|
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, 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() |
@ -0,0 +1,115 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 _pos_ui_models_to_load(self): |
||||
|
"""Supering the function for loading res.config.settings to pos |
||||
|
session""" |
||||
|
result = super()._pos_ui_models_to_load() |
||||
|
result += ['res.config.settings', 'pos.order', 'pos.order.line', |
||||
|
'multi.barcode.products', 'meals.planning'] |
||||
|
return result |
||||
|
|
||||
|
def _loader_params_res_config_settings(self): |
||||
|
"""Returning the field required""" |
||||
|
return { |
||||
|
'search_params': {'fields': ['enable_service_charge', 'visibility', |
||||
|
'global_selection', 'global_charge', |
||||
|
'global_product_id', |
||||
|
'custom_tip_percentage', 'barcode', |
||||
|
'invoice_number', 'customer_details', |
||||
|
'customer_name', 'customer_address', |
||||
|
'customer_mobile', 'customer_phone', |
||||
|
'customer_email', 'customer_vat', |
||||
|
], }, } |
||||
|
|
||||
|
def _get_pos_ui_res_config_settings(self, params): |
||||
|
"""Returns the model""" |
||||
|
return self.env['res.config.settings'].search_read( |
||||
|
**params['search_params']) |
||||
|
|
||||
|
def _loader_params_account_move(self): |
||||
|
return {'search_params': {'fields': ['account_barcode']}} |
||||
|
def _get_pos_ui_res_account_move(self, params): |
||||
|
"""Returns the model""" |
||||
|
return self.env['account.move'].search_read( |
||||
|
**params['search_params']) |
||||
|
|
||||
|
def _loader_params_pos_order(self): |
||||
|
"""pos_order model field load in pos session""" |
||||
|
return {'search_params': { |
||||
|
'domain': [], |
||||
|
'fields': ['name', 'date_order', 'pos_reference', |
||||
|
'partner_id', 'lines', 'exchange']}} |
||||
|
|
||||
|
def _get_pos_ui_pos_order(self, params): |
||||
|
"""Return the model pos_order""" |
||||
|
return self.env['pos.order'].search_read(**params['search_params']) |
||||
|
|
||||
|
def _loader_params_pos_order_line(self): |
||||
|
"""pos_order_line model field load in pos session""" |
||||
|
return {'search_params': {'domain': [], |
||||
|
'fields': ['product_id', 'qty', |
||||
|
'price_subtotal', |
||||
|
'total_cost']}} |
||||
|
|
||||
|
def _get_pos_ui_pos_order_line(self, params): |
||||
|
"""Return the model pos_order_line""" |
||||
|
return self.env['pos.order.line'].search_read( |
||||
|
**params['search_params']) |
||||
|
|
||||
|
def _loader_params_product_product(self): |
||||
|
"""loaded product template field into pos session""" |
||||
|
result = super()._loader_params_product_product() |
||||
|
result['search_params']['fields'].extend( |
||||
|
['is_age_restrict', 'product_multi_barcodes_ids', 'name', 'id']) |
||||
|
return result |
||||
|
|
||||
|
def _get_pos_ui_multi_barcode_products(self, params): |
||||
|
""""Return the model multi_barcode_product""" |
||||
|
return self.env['multi.barcode.products'].with_context( |
||||
|
**params['context']).search_read(**params['search_params']) |
||||
|
|
||||
|
def _loader_params_multi_barcode_products(self): |
||||
|
"""loaded multi_barcode_product field into pos session""" |
||||
|
return {'search_params': {'fields': ['multi_barcode'], }, |
||||
|
'context': {'display_default_code': False}, } |
||||
|
|
||||
|
def _loader_params_meals_planning(self): |
||||
|
"""Returning corresponding data to pos""" |
||||
|
plans = self.env['meals.planning'].search([ |
||||
|
('state', '=', 'activated'), |
||||
|
('pos_ids', 'in', self.id)]) |
||||
|
data = plans.mapped('id') |
||||
|
return { |
||||
|
'search_params': { |
||||
|
'domain': [('id', '=', data)], |
||||
|
'fields': ['name', 'menu_product_ids', 'time_from', 'time_to', |
||||
|
'state', 'pos_ids']}} |
||||
|
|
||||
|
def _get_pos_ui_meals_planning(self, params): |
||||
|
return self.env['meals.planning'].search_read(**params['search_params']) |
@ -0,0 +1,58 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 ProductProduct(models.Model): |
||||
|
"""Inherit the product_product module to add new fields""" |
||||
|
_inherit = 'product.product' |
||||
|
|
||||
|
product_multi_barcodes_ids = fields.One2many('multi.barcode.products', |
||||
|
'product_id', |
||||
|
string='Barcodes', |
||||
|
help='Add multi barcode for ' |
||||
|
'product') |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, vals): |
||||
|
"""Super the create function to update the field |
||||
|
product_multi_barcodes_ids""" |
||||
|
res = super(ProductProduct, self).create(vals) |
||||
|
res.product_multi_barcodes_ids.update({ |
||||
|
'product_template_id': res.product_tmpl_id.id |
||||
|
}) |
||||
|
return res |
||||
|
|
||||
|
def write(self, vals): |
||||
|
"""Super the write function to update the field |
||||
|
product_multi_barcodes_ids""" |
||||
|
res = super(ProductProduct, self).write(vals) |
||||
|
self.product_multi_barcodes_ids.update({ |
||||
|
'product_template_id': self.product_tmpl_id.id |
||||
|
}) |
||||
|
return res |
||||
|
|
||||
|
@api.onchange('to_make_mrp') |
||||
|
def _onchange_to_make_mrp(self): |
||||
|
"""Function to show raise error if the product doesn't have BOM""" |
||||
|
if self.to_make_mrp and not self.bom_count: |
||||
|
raise Warning(_('Please set Bill of Material for this product.')) |
@ -0,0 +1,66 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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") |
||||
|
product_template_ids = fields.One2many('multi.barcode.products', |
||||
|
'product_template_id', |
||||
|
string='Barcodes', |
||||
|
help='Add multi barcode for the ' |
||||
|
'module') |
||||
|
to_make_mrp = fields.Boolean(string='To Create MRP Order', |
||||
|
help="Check if the product should be make mrp " |
||||
|
"order") |
||||
|
|
||||
|
@api.model |
||||
|
def create(self, vals): |
||||
|
"""Super the create function to update the field product_template_ids""" |
||||
|
res = super(ProductTemplate, self).create(vals) |
||||
|
res.product_template_ids.update({ |
||||
|
'product_id': res.product_variant_id.id |
||||
|
}) |
||||
|
return res |
||||
|
|
||||
|
def write(self, vals): |
||||
|
"""Super the write function to update the field product_template_ids""" |
||||
|
res = super(ProductTemplate, self).write(vals) |
||||
|
if self.product_template_ids: |
||||
|
self.product_template_ids.update({ |
||||
|
'product_id': self.product_variant_id.id |
||||
|
}) |
||||
|
return res |
||||
|
|
||||
|
@api.onchange('to_make_mrp') |
||||
|
def _onchange_to_make_mrp(self): |
||||
|
"""Function to show raise error if the product doesn't have BOM""" |
||||
|
if self.to_make_mrp: |
||||
|
if not self.bom_count: |
||||
|
raise ValidationError( |
||||
|
'Please set Bill of Material for this product.') |
@ -0,0 +1,125 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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 ResConfigSettings(models.TransientModel): |
||||
|
"""Inherited Configuration Settings""" |
||||
|
_inherit = "res.config.settings" |
||||
|
|
||||
|
enable_service_charge = fields.Boolean( |
||||
|
string="Service Charges", |
||||
|
config_parameter="all_in_one_pos_kit.enable_service_charge", |
||||
|
help="Enable to add service charge") |
||||
|
visibility = fields.Selection( |
||||
|
[('global', 'Global'), ('session', 'Session')], |
||||
|
default='global', string="Visibility", |
||||
|
config_parameter="all_in_one_pos_kit.visibility", |
||||
|
help='Setup the Service charge globally or per session') |
||||
|
global_selection = fields.Selection([ |
||||
|
('amount', 'Amount'), |
||||
|
('percentage', 'Percentage')], |
||||
|
string='Type', default='amount', |
||||
|
config_parameter="all_in_one_pos_kit.global_selection", |
||||
|
help='Set the service charge as a amount or percentage') |
||||
|
global_charge = fields.Float( |
||||
|
string='Service Charge', |
||||
|
config_parameter="all_in_one_pos_kit.global_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')]", |
||||
|
config_parameter="all_in_one_pos_kit.global_product_id", |
||||
|
help='Set a service product globally') |
||||
|
custom_tip_percentage = fields.Float( |
||||
|
string="Custom Percentage", |
||||
|
config_parameter='all_in_one_pos_kit.custom_tip_percentage', |
||||
|
help="enter the percentage custom tips") |
||||
|
barcode = fields.Boolean(string='Order Barcode', |
||||
|
config_parameter='all_in_one_pos_kit.barcode', |
||||
|
help='Enable or disable the display of order ' |
||||
|
'barcode') |
||||
|
invoice_number = fields.Boolean( |
||||
|
string='Invoice Number', |
||||
|
config_parameter='all_in_one_pos_kit.invoice_number', |
||||
|
help='Enable or disable the display of invoice number') |
||||
|
customer_details = fields.Boolean( |
||||
|
string='Customer Details', |
||||
|
config_parameter='all_in_one_pos_kit.customer_details', |
||||
|
help='Enable or disable the display of customer details') |
||||
|
customer_name = fields.Boolean( |
||||
|
string='Customer Name', |
||||
|
config_parameter='all_in_one_pos_kit.customer_name', |
||||
|
help='Enable or disable the display of customer name') |
||||
|
customer_address = fields.Boolean( |
||||
|
string='Customer Address', |
||||
|
config_parameter='all_in_one_pos_kit.customer_address', |
||||
|
help='Enable or disable the display of customer address') |
||||
|
customer_mobile = fields.Boolean( |
||||
|
string='Customer Mobile', |
||||
|
config_parameter='all_in_one_pos_kit.customer_mobile', |
||||
|
help='Enable or disable the display of customer mobile number') |
||||
|
customer_phone = fields.Boolean( |
||||
|
string='Customer Phone', |
||||
|
config_parameter='all_in_one_pos_kit.customer_phone', |
||||
|
help='Enable or disable the display of customer phone number') |
||||
|
customer_email = fields.Boolean( |
||||
|
string='Customer Email', |
||||
|
config_parameter='all_in_one_pos_kit.customer_email', |
||||
|
help='Enable or disable the display of customer email') |
||||
|
customer_vat = fields.Boolean( |
||||
|
string='Customer VAT', |
||||
|
config_parameter='all_in_one_pos_kit.customer_vat', |
||||
|
help='Enable or disable the display of customer VAT number') |
||||
|
customer_msg = fields.Boolean('POS Greetings', |
||||
|
config_parameter='all_in_one_pos_kit.customer_msg', |
||||
|
Help='Create an account if you ' |
||||
|
'ever create an account') |
||||
|
twilio_auth_token = fields.Char('Auth Token', |
||||
|
config_parameter='all_in_one_pos_kit.twilio_auth_token', |
||||
|
Help='Copy the token from your twilio console ' |
||||
|
'window and paste here') |
||||
|
account_sid = fields.Char('Account SID', |
||||
|
config_parameter='all_in_one_pos_kit.account_sid') |
||||
|
twilio_number = fields.Char('Twilio Number', |
||||
|
config_parameter='all_in_one_pos_kit.twilio_number', |
||||
|
Help='The number provided by ' |
||||
|
'twilio used to send ' |
||||
|
'text messages') |
||||
|
sms_body = fields.Char('Body', config_parameter='all_in_one_pos_kit.sms_body') |
||||
|
|
||||
|
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 |
@ -0,0 +1,35 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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)') |
@ -0,0 +1,58 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
################################################################################ |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
||||
|
# Author: Ayana KP(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.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") |
@ -0,0 +1,22 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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_report |
@ -0,0 +1,42 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################### |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# |
||||
|
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
||||
|
# Author: Ayana KP(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): |
||||
|
"""Model to generate the POS order report.""" |
||||
|
_name = 'report.all_in_one_pos_kit.pos_order_report' |
||||
|
|
||||
|
@api.model |
||||
|
def _get_report_values(self, docids, data=None): |
||||
|
"""Get the report values for generating the POS order report. |
||||
|
:param docids: The IDs of the records to include in the report. |
||||
|
:param data: Additional data for generating the report (optional). |
||||
|
:return: A dictionary containing the report values.""" |
||||
|
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 |
@ -0,0 +1,432 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<template id="pos_order_report"> |
||||
|
<!-- Template to filter the report based on the selected report type --> |
||||
|
<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"> |
||||
|
<!-- Template for generating a report by order. It displays information |
||||
|
such as PoS,order, date order, customer, salesman, total quantity, |
||||
|
total amount, and note. --> |
||||
|
<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></br> |
||||
|
<table class="table table-sm table-reports"> |
||||
|
<thead> |
||||
|
<tr class="text-right"> |
||||
|
<th colspan="6">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" t-key="main.index"> |
||||
|
<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></br> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template id="report_order_detail"> |
||||
|
<!-- Template for generating a detailed report by order. It includes |
||||
|
additional information such as product code, product name, price unit, |
||||
|
quantity, subtotal price, and subtotal price including taxes. --> |
||||
|
<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></br> |
||||
|
<table class="table table-sm table-reports"> |
||||
|
<thead> |
||||
|
<tr class="text-right"> |
||||
|
<th colspan="6">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" t-key="main.index"> |
||||
|
<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></br> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template id="report_product"> |
||||
|
<!-- Template for generating a report by product. It shows information |
||||
|
such as category, product code, product name, quantity, total amount, |
||||
|
and total amount including taxes. --> |
||||
|
<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></br> |
||||
|
<table class="table table-sm table-reports"> |
||||
|
<thead> |
||||
|
<tr class="text-right"> |
||||
|
<th colspan="6">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" t-key="main.index"> |
||||
|
<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></br> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template id="report_category"> |
||||
|
<!-- Template for generating a report by category. It displays |
||||
|
information about categories, quantity, total amount, and total amount |
||||
|
including taxes. --> |
||||
|
<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></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" t-key="pos_category.index"> |
||||
|
<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></br> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template id="report_salesman"> |
||||
|
<!-- Template for generating a report by salesman. It includes |
||||
|
information about the salesman, total orders, total quantity, and |
||||
|
total amount. --> |
||||
|
<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></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" t-key="main.index"> |
||||
|
<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></br> |
||||
|
</div> |
||||
|
</template> |
||||
|
<template id="report_payment"> |
||||
|
<!-- Template for generating a report by payment. It shows details about |
||||
|
the point of sale, PoS session, payment methods, and total amount. --> |
||||
|
<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></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" t-key="main.index"> |
||||
|
<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 t-foreach="main['name']" t-as="nme" t-key="main.index"> |
||||
|
<t t-esc="main['name'][nme]"/> |
||||
|
</t> |
||||
|
</span> |
||||
|
</td> |
||||
|
<td colspan="6"> |
||||
|
<span t-esc="main['sum']"/> |
||||
|
</td> |
||||
|
</tr> |
||||
|
</t> |
||||
|
</tbody> |
||||
|
</table> |
||||
|
</div> |
||||
|
<br></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> |
@ -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"> |
||||
|
<!-- Multi Planning Multi Company Rule --> |
||||
|
<field name="name">multi panning multi company rule</field> |
||||
|
<field name="model_id" ref="model_meals_planning"/> |
||||
|
<field name="domain_force"> |
||||
|
['|',('company_id','=',False),('company_id', 'in', company_ids)] |
||||
|
</field> |
||||
|
</record> |
||||
|
</odoo> |
|
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 80 KiB |
After Width: | Height: | Size: 3.2 KiB |
After Width: | Height: | Size: 565 B |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 38 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 49 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 169 KiB |
After Width: | Height: | Size: 203 KiB |
After Width: | Height: | Size: 43 KiB |
After Width: | Height: | Size: 83 KiB |
After Width: | Height: | Size: 176 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 142 KiB |
After Width: | Height: | Size: 206 KiB |
After Width: | Height: | Size: 57 KiB |
After Width: | Height: | Size: 22 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 140 KiB |
After Width: | Height: | Size: 156 KiB |
After Width: | Height: | Size: 168 KiB |
After Width: | Height: | Size: 191 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 61 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 68 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 55 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 95 KiB |
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 84 KiB |