@ -0,0 +1,45 @@ |
|||
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html |
|||
:alt: License: AGPL-3 |
|||
|
|||
All in One POS Kit |
|||
================== |
|||
* This module combines a variety of POS features. |
|||
|
|||
Installation |
|||
============ |
|||
- www.odoo.com/documentation/16.0/setup/install.html |
|||
- Install our custom addon |
|||
|
|||
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 @cybrosys, Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For 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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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,101 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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': '16.0.1.0.0', |
|||
'category': 'Point of Sale', |
|||
'summary': 'This module combines Different POS features', |
|||
'description': 'This module combines Different POS features', |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': "https://www.cybrosys.com", |
|||
'depends': ['hr', 'point_of_sale', 'mrp'], |
|||
'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': [ |
|||
'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_magnify_image/css/pos_magnify_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/product_magnify_image/js/*', |
|||
'all_in_one_pos_kit/static/src/pos_mrp_order/js/models.js', |
|||
'all_in_one_pos_kit/static/src/pos_num_show_hide/js/pos_numpad.js', |
|||
'all_in_one_pos_kit/static/src/order_item_count/js/*', |
|||
'all_in_one_pos_kit/static/src/product_creation/js/*', |
|||
'all_in_one_pos_kit/static/src/pos_auto_lot/js/auto_lot.js', |
|||
'all_in_one_pos_kit/static/src/advanced_receipt/js/payment.js', |
|||
'all_in_one_pos_kit/static/src/category_wise_receipt/js/pos_receipt.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/product_magnify_image/xml/*', |
|||
'all_in_one_pos_kit/static/src/pos_num_show_hide/xml/pos.xml', |
|||
'all_in_one_pos_kit/static/src/order_item_count/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/category_wise_receipt/xml/pos_receipt.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/js/pos_dashboard.js', |
|||
'all_in_one_pos_kit/static/src/pos_report/js/*', |
|||
'all_in_one_pos_kit/static/src/dashboard/xml/pos_dashboard.xml', |
|||
'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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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 werkzeug |
|||
from odoo import http |
|||
from odoo.http import request |
|||
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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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,7 @@ |
|||
## Module <all_in_one_pos_kit> |
|||
|
|||
#### 3.10.2023 |
|||
#### Version 16.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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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,73 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Afra MP (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') |
|||
pos_ids = fields.Many2many('pos.config', string='Shops', |
|||
help='Choose PoS Sessions', required=True) |
|||
time_from = fields.Float(string='From', required=True, |
|||
help='Add from time(24hr)') |
|||
time_to = fields.Float(string='To', required=True, help='Add to time(24hr)') |
|||
product_ids = fields.Many2many('product.product', string='Product', |
|||
domain=[('available_in_pos', '=', True)], |
|||
help='Select the products available for' |
|||
' the menu.') |
|||
state = fields.Selection([('activated', 'Activated'), |
|||
('deactivated', 'Deactivated')], |
|||
string='State', |
|||
default='deactivated', |
|||
help='Select the state of the item (activated or ' |
|||
'deactivated).') |
|||
company_id = fields.Many2one('res.company', string='Company', |
|||
default=lambda self: self.env.company, |
|||
help='Select the company associated with the ' |
|||
'item.') |
|||
|
|||
@api.constrains('time_from', 'time_to') |
|||
def _check_time_range(self): |
|||
"""Validation for from time and to time""" |
|||
if self.time_from >= self.time_to: |
|||
raise ValidationError('From time must be less than to time!') |
|||
if self.time_from > 24.0 or self.time_to > 24.0: |
|||
raise ValidationError( |
|||
'Time value greater than 24 is not valid!') |
|||
|
|||
def action_activate_meals_plan(self): |
|||
"""Change state to activate""" |
|||
self.write({ |
|||
'state': 'activated' |
|||
}) |
|||
|
|||
def action_deactivate_meals_plan(self): |
|||
"""Change state to deactivate""" |
|||
self.write({ |
|||
'state': 'deactivated' |
|||
}) |
@ -0,0 +1,92 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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): |
|||
"""Inherit the class mrp_production to add new function""" |
|||
_inherit = 'mrp.production' |
|||
|
|||
def create_mrp_from_pos(self, products): |
|||
"""Fetch value from js and create manufacturing order for the product |
|||
and return true""" |
|||
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: |
|||
if self.env['mrp.bom'].search([('product_tmpl_id', '=', |
|||
prod['product_tmpl_id'])]): |
|||
if self.env['mrp.bom'].search( |
|||
[('product_id', '=', prod['id'])]): |
|||
bom = self.env['mrp.bom'].search( |
|||
[('product_id', '=', prod['id'])])[0] |
|||
elif self.env['mrp.bom'].search([( |
|||
'product_tmpl_id', '=', |
|||
prod[ |
|||
'product_tmpl_id']), |
|||
('product_id', '=', |
|||
False)]): |
|||
bom = self.env['mrp.bom'].search([( |
|||
'product_tmpl_id', |
|||
'=', prod[ |
|||
'product_tmpl_id']), |
|||
( |
|||
'product_id', '=', |
|||
False)])[0] |
|||
else: |
|||
bom = [] |
|||
if bom: |
|||
mrp_order = self.sudo().create({ |
|||
'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}) |
|||
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, |
|||
'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, |
|||
})) |
|||
mrp_order.update({'move_raw_ids': list_value}) |
|||
return True |
@ -0,0 +1,46 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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.product' |
|||
_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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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( |
|||
'service_charges_pos.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,51 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class POSGreetings(models.Model): |
|||
"""Model representing POS Greetings.""" |
|||
_name = 'pos.greetings' |
|||
_description = 'POS Greetings' |
|||
_rec_name = 'order_id' |
|||
|
|||
partner_id = fields.Many2one('res.partner', string='Customer', |
|||
help='Select the customer for whom the SMS ' |
|||
'will be sent.') |
|||
order_id = fields.Many2one('pos.order', string='Order', |
|||
help='Select the order associated with the SMS') |
|||
auth_token = fields.Char(string='Token', |
|||
help='Enter the authentication token for the SMS ' |
|||
'service provider.') |
|||
twilio_number = fields.Char(string='Twilio Number', |
|||
help='Enter the Twilio phone number used to' |
|||
' send the SMS.') |
|||
to_number = fields.Char(string='Customer Number', |
|||
help='Enter the recipients phone number to send ' |
|||
'the SMS to.') |
|||
sms_body = fields.Char(string='Body', |
|||
help='Enter the content or message of the SMS.') |
|||
session_id = fields.Many2one('pos.session', string='Session', |
|||
help='Select the session associated with ' |
|||
'the SMS.') |
|||
send_sms = fields.Boolean(string='Send SMS',help='Check this box to send ' |
|||
'the SMS when saving the record.') |
@ -0,0 +1,264 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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 get_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]) |
|||
return { |
|||
'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, |
|||
} |
|||
|
|||
@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. |
|||
This method is called to create POS orders based on the provided |
|||
data from the user interface. |
|||
:param orders: A list of dictionaries representing the POS orders. |
|||
:param draft: Set to True if the orders should be created in the |
|||
draft state. |
|||
:returns: A list of dictionaries containing the created order |
|||
details. |
|||
""" |
|||
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() |
|||
if params.get_param( |
|||
'pos.customer_msg') and pos_order.partner_id.phone: |
|||
try: |
|||
# Download the helper library from https://www.twilio.com/docs/python/install |
|||
Client(params.get_param('pos.account_sid'), |
|||
params.get_param( |
|||
'pos.auth_token')).messages.create( |
|||
body=params.get_param('pos.sms_body'), |
|||
from_=params.get_param('pos.twilio_number'), |
|||
to=str(pos_order.partner_id.phone)) |
|||
self.env['pos.greetings'].create({ |
|||
'partner_id': pos_order.partner_id.id, |
|||
'order_id': pos_order.id, |
|||
'auth_token': params.get_param('pos.auth_token'), |
|||
'twilio_number': params.get_param( |
|||
'pos.twilio_number'), |
|||
'to_number': str(pos_order.partner_id.phone), |
|||
'session_id': pos_order.session_id.id, |
|||
'sms_body': params.get_param('pos.sms_body'), |
|||
'send_sms': True, |
|||
}) |
|||
except Exception: |
|||
pass |
|||
return res |
|||
|
|||
|
|||
class PosOrderLine(models.Model): |
|||
"""Inherit the class pos_order_line""" |
|||
_inherit = "pos.order.line" |
|||
|
|||
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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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 PosReportGenerator(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.env['pos.report'].search([('id', '=', option[0])]) |
|||
data = {'report_type': report_values.report_type,'model': self} |
|||
if report_values.date_from: |
|||
data.update({'date_from': report_values.date_from}) |
|||
if report_values.date_to: |
|||
data.update({'date_to': report_values.date_to}) |
|||
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.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,108 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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.product', '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', |
|||
'barcode_type'], }, } |
|||
|
|||
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_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_product(self, params): |
|||
""""Return the model multi_barcode_product""" |
|||
return self.env['multi.barcode.product'].with_context( |
|||
**params['context']).search_read(**params['search_params']) |
|||
|
|||
def _loader_params_multi_barcode_product(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""" |
|||
data = [rec.id for rec in self.env['meals.planning'].search( |
|||
[('state', '=', 'activated'), |
|||
('pos_ids', 'in', self.config_id.id)])] |
|||
|
|||
return {'search_params': {'domain': [('id', '=', data)], |
|||
'fields': ['name', 'product_ids', |
|||
'time_from', 'time_to', 'state', |
|||
'pos_ids']}} |
|||
|
|||
def _get_pos_ui_meals_planning(self, params): |
|||
""""Return the model meals_planning""" |
|||
return self.env['meals.planning'].search_read(**params['search_params']) |
@ -0,0 +1,58 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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.product', |
|||
'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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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.product', |
|||
'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,146 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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') |
|||
barcode_type = fields.Selection( |
|||
selection=[('barcode', 'Barcode'),('qr_code', 'QRCode')], |
|||
string='Barcode Type', |
|||
config_parameter='all_in_one_pos_kit.barcode_type', |
|||
help='Select the type of barcode to be displayed (Barcode or QRCode)') |
|||
customer_msg = fields.Boolean(string='POS Greetings', |
|||
config_parameter='pos.customer_msg', |
|||
help='Create an account if you ever create an' |
|||
'account') |
|||
auth_token = fields.Char(string='Auth Token', |
|||
config_parameter='pos.auth_token', |
|||
help='Copy the token from your twilio console ' |
|||
'window adn paste here') |
|||
account_sid = fields.Char(string='Account SID', |
|||
config_parameter='pos.account_sid', |
|||
help='Enter the Account SID provided by Twilio ' |
|||
'for authentication.') |
|||
twilio_number = fields.Char(string='Twilio Number', |
|||
config_parameter='pos.twilio_number', |
|||
help='Enter the Twilio phone number used to ' |
|||
'send the SMS.') |
|||
sms_body = fields.Text(string='Body', |
|||
help='Enter the content or message of the SMS to be' |
|||
'sent.') |
|||
|
|||
@api.onchange('enable_service_charge') |
|||
def _onchange_enable_service_charge(self): |
|||
"""When the service charge is enabled set service product and amount |
|||
by default in globally""" |
|||
if self.enable_service_charge and not self.global_product_id: |
|||
self.global_product_id = self.env['product.product'].search( |
|||
[('available_in_pos', '=', True), ('sale_ok', '=', True), |
|||
('type', '=', 'service')], limit=1) |
|||
self.global_charge = 10.0 |
|||
else: |
|||
self.global_product_id = False |
|||
self.global_charge = 0.0 |
|||
|
|||
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,47 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################### |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (odoo@cybrosys.com) |
|||
# |
|||
# You can modify it under the terms of the GNU AFFERO |
|||
# GENERAL PUBLIC LICENSE (AGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU AFFERO GENERAL PUBLIC LICENSE (AGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE |
|||
# (AGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################### |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class ResUsers(models.Model): |
|||
"""Inherit the class res_users to add field""" |
|||
_inherit = "res.users" |
|||
|
|||
pos_conf_id = fields.Many2one('pos.config', string="POS Configuration", |
|||
help='select POS for the user') |
|||
pos_config_ids = fields.Many2many('pos.config', string='Allowed Pos', |
|||
help='Allowed Pos for this user') |
|||
show_users = fields.Boolean(string="Show users of pos", default=True, |
|||
help='Show users in dashboard ( for pos ' |
|||
'administrators only)') |
|||
|
|||
@api.model |
|||
def create(self, vals): |
|||
"""This method creates a new record for the ResUsers model with the |
|||
provided values. It clears the caches before creating the record.""" |
|||
self.clear_caches() |
|||
return super(ResUsers, self).create(vals) |
|||
|
|||
def write(self, vals): |
|||
"""For clearing out existing values and update with new values""" |
|||
self.clear_caches() |
|||
return super(ResUsers, self).write(vals) |
@ -0,0 +1,58 @@ |
|||
# -*- coding: utf-8 -*- |
|||
################################################################################ |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>). |
|||
# Author: Afra MP (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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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) 2023-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Afra MP (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"> |
|||
<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"> |
|||
<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"> |
|||
<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"> |
|||
<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"> |
|||
<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"> |
|||
<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 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: 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.2 KiB |
After Width: | Height: | Size: 673 B |
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: 1.5 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 4.4 KiB |
After Width: | Height: | Size: 589 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 967 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 678 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 180 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 990 KiB |
After Width: | Height: | Size: 465 KiB |
After Width: | Height: | Size: 165 KiB |
After Width: | Height: | Size: 636 KiB |
After Width: | Height: | Size: 114 KiB |
After Width: | Height: | Size: 650 KiB |
After Width: | Height: | Size: 139 KiB |
After Width: | Height: | Size: 134 KiB |
After Width: | Height: | Size: 179 KiB |
After Width: | Height: | Size: 150 KiB |
After Width: | Height: | Size: 112 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 111 KiB |
After Width: | Height: | Size: 652 KiB |
After Width: | Height: | Size: 157 KiB |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 440 KiB |
After Width: | Height: | Size: 149 KiB |
After Width: | Height: | Size: 137 KiB |
After Width: | Height: | Size: 632 KiB |
After Width: | Height: | Size: 178 KiB |
After Width: | Height: | Size: 371 KiB |
After Width: | Height: | Size: 667 KiB |
After Width: | Height: | Size: 643 KiB |
After Width: | Height: | Size: 669 KiB |
After Width: | Height: | Size: 657 KiB |
After Width: | Height: | Size: 524 KiB |
After Width: | Height: | Size: 522 KiB |
After Width: | Height: | Size: 661 KiB |
After Width: | Height: | Size: 652 KiB |
After Width: | Height: | Size: 124 KiB |
After Width: | Height: | Size: 189 KiB |