@ -0,0 +1,47 @@ |
|||
.. image:: https://img.shields.io/badge/license-LGPL--3-green.svg |
|||
:target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html |
|||
:alt: License: LGPL-3 |
|||
|
|||
Advanced Loyalty Management |
|||
=========================== |
|||
* In this module we can deduct the loyalty points when order is refunded, we can convert the change into loyalty points, History of claimed rewards can be seen in customer's page.And introduced new reward type , in which we can redeem our points. |
|||
|
|||
Configuration |
|||
============ |
|||
- www.odoo.com/documentation/16.0/setup/install.html |
|||
- Install our custom addon |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (LGPL v3). |
|||
(https://www.gnu.org/licenses/lgpl-3.0-standalone.html) |
|||
|
|||
Credits |
|||
------- |
|||
* Developer: (V16) NIHALA KP, Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://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 |
|||
-------- |
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit https://www.cybrosys.com |
|||
|
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com" |
|||
|
|||
Further Information |
|||
----------- |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import models |
@ -0,0 +1,68 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
{ |
|||
'name': 'Advanced Loyalty Management', |
|||
'version': '16.0.1.0.0', |
|||
'category': 'Point of Sale', |
|||
'summary': 'Loyalty deduction on order refund', |
|||
'description': """When an order is refunded, any loyalty points gained from |
|||
that purchase are also revoked. This means that the points earned through |
|||
the refunded transaction will be deducted from the customer's loyalty points |
|||
balance.""", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'images': [ |
|||
'static/description/banner.png', |
|||
], |
|||
'depends': ['point_of_sale','pos_loyalty','base','sale_management'], |
|||
'data': [ |
|||
'views/res_partner_views.xml', |
|||
'views/loyalty_rewards_views.xml', |
|||
'views/loyalty_program_views.xml', |
|||
'views/loyalty_kanban_view.xml', |
|||
'views/pos_order_line_views.xml', |
|||
], |
|||
"assets": |
|||
{"point_of_sale.assets": |
|||
[ |
|||
'advanced_loyalty_management/static/src/xml/pos_loyalty_deduction.xml', |
|||
'advanced_loyalty_management/static/src/js/pos_loyalty_deduction.js', |
|||
'advanced_loyalty_management/static/src/xml/pos_loyalty_receipt.xml', |
|||
'advanced_loyalty_management/static/src/js/pos_loyalty_deduction_receipt.js', |
|||
'advanced_loyalty_management/static/src/xml/pos_loyalty_change.xml', |
|||
'advanced_loyalty_management/static/src/js/pos_ticketscreen.js', |
|||
'advanced_loyalty_management/static/src/js/pos_payment_screen.js', |
|||
'advanced_loyalty_management/static/src/xml/pos_change_popup.xml', |
|||
'advanced_loyalty_management/static/src/js/pos_change_popup.js', |
|||
'advanced_loyalty_management/static/src/js/pos_order.js', |
|||
'advanced_loyalty_management/static/src/js/rewardbutton.js', |
|||
'advanced_loyalty_management/static/src/xml/reward_popup.xml', |
|||
'advanced_loyalty_management/static/src/js/reward_popup.js', |
|||
] |
|||
}, |
|||
'license': 'LGPL-3', |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False, |
|||
} |
@ -0,0 +1,6 @@ |
|||
## Module <advanced_loyalty_management> |
|||
|
|||
#### 06.04.2024 |
|||
#### Version 16.0.1.0.0 |
|||
#### ADD |
|||
- Initial Commit Advanced Loyalty Management |
@ -0,0 +1,27 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import loyalty_program |
|||
from . import loyalty_reward |
|||
from . import res_partner |
|||
from . import pos_order_line |
|||
from . import pos_refund |
|||
from . import pos_session |
@ -0,0 +1,52 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import api, models, fields |
|||
|
|||
|
|||
class LoyaltyProgram(models.Model): |
|||
"""to load more fields in loyalty program and loyalty model""" |
|||
_inherit = 'loyalty.program' |
|||
|
|||
point_rate = fields.Integer(string='Point Rate', |
|||
help="Points corresponding to each unit", |
|||
default=1, required=True) |
|||
change_rate = fields.Monetary(string='Change Rate', default=1, |
|||
readonly=True, |
|||
help="Unit of money per points cost") |
|||
currency_id = fields.Many2one('res.currency', string='Currency', |
|||
default=lambda |
|||
self: self.env.user.company_id.currency_id, |
|||
help="Symbol of Currency") |
|||
|
|||
@api.model |
|||
def convert_loyalty(self, program_id, coupon_id, added_loyalty, partner_id): |
|||
"""Change is converted to loyalty""" |
|||
if int(coupon_id) < 0: |
|||
self.env['loyalty.card'].create({ |
|||
'program_id': program_id, |
|||
'partner_id': partner_id, |
|||
'points': added_loyalty |
|||
}) |
|||
else: |
|||
loyalty_card = self.env['loyalty.card'].search( |
|||
[('id', '=', int(coupon_id))]) |
|||
loyalty_card.points += added_loyalty |
@ -0,0 +1,78 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import api, models, fields |
|||
|
|||
|
|||
class LoyaltyReward(models.Model): |
|||
"""To add a new reward type in loyalty reward""" |
|||
_inherit = 'loyalty.reward' |
|||
|
|||
reward_type = fields.Selection(selection_add=[ |
|||
('redemption', 'Redemption') |
|||
], ondelete={'redemption': 'set default'}, |
|||
help="A new reward type is added") |
|||
redemption_point = fields.Integer(string='Redemption Point', default=1.0, |
|||
readonly=True, |
|||
help="No of points redeemed according " |
|||
"to the redemption amount") |
|||
redemption_amount = fields.Float(string='Maximum Redemption per Point', |
|||
default=1.00, required=True, |
|||
help="Maximum Redemption per redemption " |
|||
"point") |
|||
max_redemption_type = fields.Selection( |
|||
[('amount', 'Amount'), ('percent', 'Percentage'), ('points', 'Points')], |
|||
default="amount", |
|||
required=True, |
|||
help="Redemption type based on Fixed Amount,percentage or Point wise") |
|||
max_redemption_amount = fields.Float(string='Max Redemption Amount', |
|||
default=10, required=True, |
|||
help="Maximum redemption amount " |
|||
"given to an order") |
|||
redemption_frequency = fields.Integer(string='Redemption Frequency', |
|||
default=1, required=True, |
|||
help="Number of times this reward " |
|||
"can be claimed") |
|||
redemption_frequency_unit = fields.Selection([('day', 'Daily'), |
|||
('week', 'Weekly'), |
|||
('month', 'Monthly'), |
|||
('year', 'Yearly')], |
|||
default='day', required=True, |
|||
string='Redemption Frequency' |
|||
'Unit', |
|||
help="Choose the frequency " |
|||
"for claiming the reward") |
|||
redemption_eligibility = fields.Float(string="Redemption Eligibility", |
|||
default=200, |
|||
help="points required for claiming " |
|||
"the reward") |
|||
|
|||
@api.depends('reward_type', 'reward_product_id', 'discount_mode', |
|||
'discount', 'currency_id', 'discount_applicability', |
|||
'all_discount_product_ids') |
|||
def _compute_description(self): |
|||
"""compute description when the reward type is redemption""" |
|||
for reward in self: |
|||
if reward.reward_type == 'redemption': |
|||
reward.description = 'Redemption' |
|||
else: |
|||
res = super(LoyaltyReward, self)._compute_description() |
|||
return res |
@ -0,0 +1,34 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import models |
|||
|
|||
|
|||
class PosOrderLine(models.Model): |
|||
"""information about points cost is updated to the front end""" |
|||
_inherit = 'pos.order.line' |
|||
|
|||
def _export_for_ui(self, orderline): |
|||
"""information about points cost is updated to the front end""" |
|||
result = super()._export_for_ui(orderline) |
|||
result['points_cost'] = orderline.points_cost |
|||
result['coupon_id'] = orderline.coupon_id.id |
|||
return result |
@ -0,0 +1,81 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import models, fields |
|||
|
|||
|
|||
class PosOrder(models.Model): |
|||
"""To update the loyalty points when order is refunded""" |
|||
_inherit = 'pos.order' |
|||
|
|||
check = fields.Boolean(string="check", |
|||
help="to check whether any quantity is refunded") |
|||
|
|||
def _compute_order_name(self): |
|||
"""Compute the loyalty points when order is refunded""" |
|||
res = super()._compute_order_name() |
|||
partner_id = self.partner_id |
|||
li = [line.mapped('price_subtotal_incl') for line |
|||
in self.lines.filtered(lambda x: not x.is_reward_line)] |
|||
reward_line = self.refunded_order_ids.lines.filtered( |
|||
lambda x: x.is_reward_line) |
|||
points_cost = [] |
|||
for line in reward_line: |
|||
dict = {} |
|||
dict.update({ |
|||
line.coupon_id.id: line.points_cost |
|||
}) |
|||
points_cost.append(dict) |
|||
if self.refunded_order_ids: |
|||
cards = self.env['loyalty.card'].search( |
|||
[('partner_id', '=', partner_id.id)]) |
|||
for program in cards: |
|||
if not self.refunded_order_ids.check: |
|||
for point in points_cost: |
|||
for key, values in point.items(): |
|||
if program.id == key: |
|||
program.points += point[key] |
|||
self.refunded_order_ids.check = True |
|||
for rule in program.program_id.rule_ids: |
|||
if rule.reward_point_mode == 'money': |
|||
points_granted = rule.reward_point_amount |
|||
reward_points = [sum(sublist) * points_granted for |
|||
sublist in li] |
|||
program.points += reward_points[0] |
|||
elif rule.reward_point_mode == 'order': |
|||
reward_points = rule.reward_point_amount |
|||
reward_line_ids = len(reward_line) |
|||
ordered_qty = sum(self.refunded_order_ids.lines.mapped( |
|||
'qty')) - reward_line_ids |
|||
refunded_qty = sum( |
|||
self.refunded_order_ids.lines.filtered( |
|||
lambda x: not x.is_reward_line).mapped( |
|||
'refunded_qty')) |
|||
if ordered_qty == refunded_qty: |
|||
program.points -= reward_points |
|||
elif rule.reward_point_mode == 'unit': |
|||
points_granted = rule.reward_point_amount |
|||
qty = sum( |
|||
self.lines.filtered( |
|||
lambda x: not x.is_reward_line).mapped('qty')) |
|||
reward_points = qty * points_granted |
|||
program.points += reward_points |
|||
return res |
@ -0,0 +1,43 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import models |
|||
|
|||
|
|||
class PosSession(models.Model): |
|||
"""to load more fields in loyalty program and loyalty model""" |
|||
_inherit = 'pos.session' |
|||
|
|||
def _loader_params_loyalty_program(self): |
|||
"""To load more fields in the model loyalty.program""" |
|||
result = super()._loader_params_loyalty_program() |
|||
result['search_params']['fields'].extend(['point_rate']) |
|||
return result |
|||
|
|||
def _loader_params_loyalty_reward(self): |
|||
"""to load more fields in the model loyalty.reward""" |
|||
result = super()._loader_params_loyalty_reward() |
|||
result['search_params']['fields'].extend( |
|||
['redemption_point', 'redemption_amount', 'max_redemption_amount', |
|||
'redemption_frequency', |
|||
'redemption_frequency_unit', 'redemption_eligibility', |
|||
'max_redemption_type']) |
|||
return result |
@ -0,0 +1,64 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2024-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import api, models |
|||
|
|||
|
|||
class ResPartner(models.Model): |
|||
"""Add loyalty card in res.partner form""" |
|||
_inherit = 'res.partner' |
|||
|
|||
def action_view_loyalty(self): |
|||
"""customers can view their own loyalty points""" |
|||
action = self.env['ir.actions.act_window']._for_xml_id( |
|||
'loyalty.loyalty_card_action') |
|||
all_child = self.with_context(active_test=False).search( |
|||
[('id', 'child_of', self.ids)]) |
|||
action['domain'] = [('partner_id', 'in', all_child.ids)] |
|||
action['context'] = {'search_default_active': True} |
|||
return action |
|||
|
|||
def view_claimed_rewards(self): |
|||
"""a smart button is created to view the claimed the rewards""" |
|||
order_id = self.env['pos.order'].search( |
|||
[('partner_id', '=', self.id)]).ids |
|||
return { |
|||
'type': 'ir.actions.act_window', |
|||
'name': 'Claimed Rewards', |
|||
'view_mode': 'tree,form', |
|||
'res_model': 'pos.order.line', |
|||
'domain': [('is_reward_line', '=', 'true'), |
|||
('order_id', 'in', order_id)], |
|||
'context': "{'create': False}" |
|||
} |
|||
|
|||
@api.model |
|||
def check_redemption(self, pid): |
|||
"""to check number of times the reward is claimed""" |
|||
order = self.env['pos.order'].search([('partner_id', '=', pid)]) |
|||
data = [] |
|||
date = [] |
|||
order_line = self.env['pos.order.line'].search( |
|||
[('is_reward_line', '=', 'true'), ('order_id', 'in', order.ids)]) |
|||
for line in order_line: |
|||
data.append(line.order_id.id) |
|||
date.append(line.create_date.date()) |
|||
return data, date |
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: 66 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 72 KiB |
After Width: | Height: | Size: 88 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 91 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 73 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 65 KiB |
After Width: | Height: | Size: 71 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 129 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 78 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 138 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 125 KiB |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 105 KiB |
After Width: | Height: | Size: 69 KiB |
After Width: | Height: | Size: 79 KiB |
After Width: | Height: | Size: 90 KiB |
After Width: | Height: | Size: 103 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 376 KiB |
After Width: | Height: | Size: 74 KiB |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1,784 @@ |
|||
<div style="background-color: #714B67; height: 810px; width: 100%; padding: 15px; position: relative;"> |
|||
<!-- TITLE BAR --> |
|||
<div class="d-flex align-items-center justify-content-between" |
|||
style="border-bottom: 1px solid #875A7B; padding: 15px; display: flex; justify-content: space-between; align-items: center;"> |
|||
<img src="assets/misc/cybrosys-logo.png" width="42" height="42" |
|||
style="width: 42px; height: 42px;"/> |
|||
<div> |
|||
<div |
|||
style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
|||
class="mr-2"> |
|||
<i class="fa fa-check mr-1"></i>Community |
|||
</div> |
|||
<div |
|||
style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
|||
class="mr-2"> |
|||
<i class="fa fa-check mr-1"></i>Enterprise |
|||
</div> |
|||
<div |
|||
style="color: #7C7BAD; font-size: 14px; font-family: 'Montserrat', sans-serif; font-weight: bold; background-color: white; display: inline-block; padding: 3px 10px; border-radius: 50px;" |
|||
class="mr-2"> |
|||
<i class="fa fa-check mr-1"></i>Odoo.sh |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF TITLE BAR --> |
|||
<div class="container"> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-12 col-lg-12"> |
|||
<!-- APP HERO --> |
|||
<h1 style="color: #FFFFFF; font-weight: bolder; font-size: 50px; text-align: center; margin-top: 50px;"> |
|||
Advanced Loyalty Management</h1> |
|||
<p style="color:#FFFFFF; padding: 8px 15px; text-align: center; font-size: 24px;"> |
|||
In this module, we have incorporated new features such as |
|||
deducting loyalty points upon refund, converting change into |
|||
loyalty points, displaying claimed rewards history, and |
|||
introducing a new type of reward |
|||
<img src="assets/screenshots/v16-hero.gif" |
|||
class="img-responsive" |
|||
style="width: 100%; margin-left: auto; margin-right: auto;"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
|
|||
</div> |
|||
|
|||
<!-- NAVIGATION SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px; margin-top: 300px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/compass.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Explore This |
|||
Module</h2> |
|||
</div> |
|||
<div class="row my-4" style="font-family: 'Montserrat', sans-serif;"> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#overview"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Overview</span> |
|||
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">Learn |
|||
more about this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#features"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Features</span> |
|||
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
|||
features of this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6 my-3"> |
|||
<a href="#screenshots"> |
|||
<div class="d-flex justify-content-between align-items-center" |
|||
style="background-color: #f5f5f5; padding: 30px; width: 100%;"> |
|||
<div> |
|||
<span style="color: #714B67; font-size: 24px; font-weight: 500; display: block;">Screenshots</span> |
|||
<span style="color: #714B67; font-size: 16px; font-weight: 400; color:#282F33; display: block;">View |
|||
screenshots for this |
|||
module</span> |
|||
</div> |
|||
<img src="assets/misc/right-arrow.png" width="36" height="36"/> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<!-- END OF NAVIGATION SECTION --> |
|||
|
|||
<!-- OVERVIEW SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="overview"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/pie-chart.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Overview |
|||
</h2> |
|||
</div> |
|||
<div class="row" |
|||
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
|||
<div class="col-sm-12 py-4"> |
|||
In this module, we have implemented the functionality to deduct loyalty |
|||
points when an order is refunded. Additionally, we offer the capability |
|||
to convert change into loyalty points. Customers can conveniently view |
|||
their claimed rewards history on their respective pages. Furthermore, |
|||
we've introduced a new reward type, allowing users to redeem their |
|||
accumulated points. |
|||
</div> |
|||
</div> |
|||
<!-- END OF OVERVIEW SECTION --> |
|||
|
|||
<!-- FEATURES SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" id="features"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/features.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Features |
|||
</h2> |
|||
</div> |
|||
<div class="row" |
|||
style="font-family: 'Montserrat', sans-serif; font-weight: 400; font-size: 14px; line-height: 200%;"> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div class="d-flex align-items-center" |
|||
style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">We can deduct the Loyalty Points of the customer if the order is refunded.</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" |
|||
style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">We can convert the change into Loyalty Points.</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" |
|||
style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">History of claimed Rewards can be seen in the customer's page</span> |
|||
</div> |
|||
<div class="d-flex align-items-center" |
|||
style="margin-top: 30px; margin-bottom: 30px"> |
|||
<img src="assets/misc/check-box.png" class="mr-2"/> |
|||
<span style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">A new loyalty reward type is introduced, where we can redeem the points</span> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<!-- END OF FEATURES SECTION --> |
|||
|
|||
<!-- SCREENSHOTS SECTION --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;" |
|||
id="screenshots"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/pictures.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Screenshots |
|||
</h2> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
We have the capability to create three distinct loyalty |
|||
programs, each with its own set of conditions. |
|||
</h3> |
|||
<img src="assets/screenshots/1.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/2.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/3.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Let's explore how it operates on the point of sale (POS) screen |
|||
and Receipt |
|||
</h3> |
|||
<img src="assets/screenshots/4.png" class="img-thumbnail"> |
|||
<br/> |
|||
<img src="assets/screenshots/5.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Now lets check on the customer's page and see its updated on the |
|||
customer's loyalty cards |
|||
</h3> |
|||
<img src="assets/screenshots/7.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Now Lets see How it deducts on refund, We can see the point is |
|||
deducted from loyalty 1 and loyalty 3 programs , |
|||
The point is not deducted from the loyalty program (loyalty 2), |
|||
Because the condition is based on per order, the points from |
|||
that point will be deducted only when the whole order is |
|||
refunded |
|||
</h3> |
|||
<img src="assets/screenshots/8.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Now lets check on the receipt and customer's page and see how it |
|||
is updated. |
|||
</h3> |
|||
<img src="assets/screenshots/9.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/10.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Now lets refund the whole order and check how the loyalty points |
|||
are deducted. |
|||
</h3> |
|||
<img src="assets/screenshots/11.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/12.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/13.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/14.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Now lets claim a reward and check How the points are effected. |
|||
</h3> |
|||
<img src="assets/screenshots/15.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/16.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Now lets refund the order and check How the points are effected. |
|||
Here we given a warning to prevent the refund of reward line,to |
|||
avoid the conflict in calculations of loyalty points. |
|||
</h3> |
|||
<img src="assets/screenshots/17.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/18.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
We can refund the whole quantity and check the how the points |
|||
reduced |
|||
</h3> |
|||
<img src="assets/screenshots/19.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/20.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/21.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/22.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
We can check the next case, that is converting the change to |
|||
loyalty points |
|||
</h3> |
|||
<img src="assets/screenshots/21.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Given a button under the change to show the list of loyalty |
|||
programs to which converted loyalty points should added |
|||
</h3> |
|||
<img src="assets/screenshots/22.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
When the popup is clicked, users can select the loyalty program |
|||
to which they wish to allocate their converted points. Upon |
|||
selection, the change amount resets to zero. |
|||
</h3> |
|||
<img src="assets/screenshots/23.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/24.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/25.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Now let's check new reward type and how its works |
|||
</h3> |
|||
<img src="assets/screenshots/26.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
In this reward type , the customer can only claim the reward if |
|||
the customer is doing the purchase for the second time </h3> |
|||
<img src="assets/screenshots/27.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
If the same customer do the purchase for the second time, they are eligible for claiming the reward </h3> |
|||
<img src="assets/screenshots/28.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Upon selecting the reward a popup showing how many points we can redeem </h3> |
|||
<img src="assets/screenshots/29.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
Then we can see that it is applied to reward line </h3> |
|||
<img src="assets/screenshots/30.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/31.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
If the same customer come again he cannot claim it again, While configuring the rewards , we give the condition Daily 1</h3> |
|||
<img src="assets/screenshots/26.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/32.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
If maximum redemption amount type is given in percentage, We can redeem the 10% of the order's subtotal</h3> |
|||
<img src="assets/screenshots/33.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/34.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;"> |
|||
If maximum redemption amount type is given in Points, We can redeem that much points as maximum redemption points</h3> |
|||
<img src="assets/screenshots/36.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/35.png" class="img-thumbnail"> |
|||
</div> |
|||
<div style="display: block; margin: 30px auto;"> |
|||
<h3 style="font-family: 'Montserrat', sans-serif; font-size: 18px; font-weight: bold;">I |
|||
Next we can see the history of claimed rewards in the customer's page.</h3> |
|||
<img src="assets/screenshots/37.png" class="img-thumbnail"> |
|||
<img src="assets/screenshots/38.png" class="img-thumbnail"> |
|||
</div> |
|||
|
|||
</div> |
|||
</div> |
|||
<!-- END OF SCREENSHOTS SECTION --> |
|||
|
|||
<!-- RELATED PRODUCTS --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/categories.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Related |
|||
Products |
|||
</h2> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12"> |
|||
<div id="demo1" class="row carousel slide" data-ride="carousel"> |
|||
<!-- The slideshow --> |
|||
<div class="carousel-inner" style="padding: 30px;"> |
|||
<div class="carousel-item" style="min-height: 198.656px;"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/advanced_chatter_view/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/1.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/hide_chatter/#" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/2.jpg"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/mail_push_notification/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/3.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
<div class="carousel-item active" |
|||
style="min-height: 198.656px;"> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/activity_reminder/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/4.png"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/camera_in_discuss/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/5.jpg"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
<div class="col-xs-12 col-sm-4 col-md-4 mb16 mt16" |
|||
style="float:left"> |
|||
<a href="https://apps.odoo.com/apps/modules/16.0/autosuggestion_in_discuss/" |
|||
target="_blank"> |
|||
<div style="border-radius:10px"> |
|||
<img class="img img-responsive center-block" |
|||
style="border-radius: 0px;" |
|||
src="assets/modules/6.jpg"> |
|||
</div> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- Left and right controls --> |
|||
<a class="carousel-control-prev" href="#demo1" data-slide="prev" |
|||
style="width:35px; color:#000"> <span |
|||
class="carousel-control-prev-icon"><i |
|||
class="fa fa-chevron-left" |
|||
style="font-size:24px"></i></span> |
|||
</a> <a class="carousel-control-next" href="#demo1" |
|||
data-slide="next" style="width:35px; color:#000"> |
|||
<span class="carousel-control-next-icon"><i |
|||
class="fa fa-chevron-right" |
|||
style="font-size:24px"></i></span> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF RELATED PRODUCTS --> |
|||
|
|||
<!-- OUR SERVICES --> |
|||
|
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/star.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Our Services |
|||
</h2> |
|||
</div> |
|||
|
|||
<div class="container my-5"> |
|||
<div class="row"> |
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #1dd1a1 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/cogs.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Customization</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #ff6b6b !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/wrench.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Implementation</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #6462CD !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/lifebuoy.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Support</h6> |
|||
</div> |
|||
|
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #ffa801 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/user.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Hire |
|||
Odoo |
|||
Developer</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #54a0ff !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/puzzle.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Integration</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #6d7680 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/update.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Migration</h6> |
|||
</div> |
|||
|
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #786fa6 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/consultation.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Consultancy</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #f8a5c2 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/training.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Implementation</h6> |
|||
</div> |
|||
|
|||
<div class="col-lg-4 d-flex flex-column justify-content-center align-items-center my-4"> |
|||
<div class="d-flex justify-content-center align-items-center mx-3 my-3" |
|||
style="background-color: #e6be26 !important; border-radius: 15px !important; height: 80px; width: 80px;"> |
|||
<img src="assets/icons/license.png" class="img-responsive" |
|||
height="48px" width="48px"> |
|||
</div> |
|||
<h6 class="text-center" |
|||
style="font-family: Montserrat, 'sans-serif' !important; font-weight: bold;"> |
|||
Odoo |
|||
Licensing Consultancy</h6> |
|||
</div> |
|||
</div> |
|||
|
|||
</div> |
|||
|
|||
<!-- END OF OUR SERVICES --> |
|||
|
|||
<!-- OUR INDUSTRIES --> |
|||
|
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/corporate.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Our |
|||
Industries |
|||
</h2> |
|||
</div> |
|||
|
|||
<div class="container my-5"> |
|||
<div class="row"> |
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/trading-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Trading |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Easily procure |
|||
and |
|||
sell your products</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/pos-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
POS |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Easy |
|||
configuration |
|||
and convivial experience</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/education-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Education |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
A platform for |
|||
educational management</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/manufacturing-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Manufacturing |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Plan, track and |
|||
schedule your operations</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/ecom-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
E-commerce & Website |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Mobile |
|||
friendly, |
|||
awe-inspiring product pages</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/service-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Service Management |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Keep track of |
|||
services and invoice</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/restaurant-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Restaurant |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
Run your bar or |
|||
restaurant methodically</p> |
|||
</div> |
|||
</div> |
|||
|
|||
<div class="col-lg-3"> |
|||
<div class="my-4 d-flex flex-column justify-content-center" |
|||
style="background-color: #f6f8f9 !important; border-radius: 0px; padding: 2rem !important; height: 250px !important;"> |
|||
<img src="assets/icons/hotel-black.png" |
|||
class="img-responsive mb-3" height="48px" width="48px"> |
|||
<h5 style="font-family: Montserrat, sans-serif !important; color: #000 !important; font-weight: bold;"> |
|||
Hotel Management |
|||
</h5> |
|||
<p style="font-family: Montserrat, sans-serif !important; font-size: 0.9rem !important;"> |
|||
An |
|||
all-inclusive |
|||
hotel management application</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<!-- END OF OUR INDUSTRIES --> |
|||
|
|||
<!-- SUPPORT --> |
|||
<div class="d-flex align-items-center" |
|||
style="border-bottom: 2px solid #714B67; padding: 15px 0px;"> |
|||
<div class="d-flex justify-content-center align-items-center mr-2" |
|||
style="background-color: #F5F5F5; border-radius: 0px; width: 40px; height: 40px;"> |
|||
<img src="assets/misc/customer-support.png"/> |
|||
</div> |
|||
<h2 class="mt-2" |
|||
style="font-family: 'Montserrat', sans-serif; font-size: 24px; font-weight: bold;"> |
|||
Support |
|||
</h2> |
|||
</div> |
|||
<div class="container mt-5"> |
|||
<div class="row"> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
|||
<div class="mr-4 d-flex justify-content-center align-items-center" |
|||
style="background-color: #714B67; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
|||
<img src="assets/misc/support.png" height="48" width="48" |
|||
style="width: 42px; height: 42px;"/> |
|||
</div> |
|||
<div> |
|||
<h4>Need Help?</h4> |
|||
<p style="line-height: 100%;">Got questions or need help? |
|||
Get in touch.</p> |
|||
<a href="mailto:odoo@cybrosys.com"> |
|||
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;"> |
|||
odoo@cybrosys.com</p> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="col-sm-12 col-md-6"> |
|||
<div style="background-color: #F6F8F9; padding: 30px; display: flex; align-items: center;"> |
|||
<div class="mr-4 d-flex justify-content-center align-items-center" |
|||
style="background-color: #2AC44D; display: inline-block; height: 70px; width: 70px; display: flex; align-items: center; justify-content: center;"> |
|||
<img src="assets/misc/whatsapp.png" height="52" width="52" |
|||
style="width: 52px; height: 52px;"/> |
|||
</div> |
|||
<div> |
|||
<h4>WhatsApp</h4> |
|||
<p style="line-height: 100%;">Say hi to us on WhatsApp!</p> |
|||
<a href="https://api.whatsapp.com/send?phone=918606827707"> |
|||
<p style="font-weight: 400; font-size: 28px; line-height: 80%; color: #714B67;"> |
|||
+91 86068 |
|||
27707</p> |
|||
</a> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div class="row"> |
|||
<div class="col-sm-12 my-5 d-flex justify-content-center align-items-center"> |
|||
<img src="assets/misc/logo.png" width="144" height="31" |
|||
style="width:144px; height: 31px; margin-top: 40px;"/> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<!-- END OF SUPPORT --> |
@ -0,0 +1,36 @@ |
|||
/** @odoo-module **/ |
|||
|
|||
import AbstractAwaitablePopup from 'point_of_sale.AbstractAwaitablePopup'; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
import { useService } from "@web/core/utils/hooks"; |
|||
var rpc = require('web.rpc'); |
|||
|
|||
class LoyaltyPrograms extends AbstractAwaitablePopup { |
|||
convertToLoyalty(props, programId,ev){ |
|||
//-----Change is converted to loyalty----
|
|||
var change = props.change |
|||
const loyalty = props.Loyalty.filter(point => point.program.id ==programId)[0] |
|||
var addedLoyalty = change * loyalty.program.point_rate |
|||
props.Order.programToAdd = programId |
|||
props.Order.convertToLoyalty = addedLoyalty |
|||
props.Order.changeConverted = true |
|||
const partner_id = props.Order.partner.id |
|||
props.Order.getLoyaltyPoints() |
|||
var convertLoyalty = rpc.query({ |
|||
model:'loyalty.program', |
|||
method:'convert_loyalty', |
|||
args:[programId,loyalty.couponId,addedLoyalty,partner_id] |
|||
}) |
|||
ev.confirm() |
|||
} |
|||
|
|||
} |
|||
LoyaltyPrograms.template = 'LoyaltyPrograms'; |
|||
LoyaltyPrograms.defaultProps = { |
|||
confirmText: 'Confirm', |
|||
cancelText: 'Cancel', |
|||
title: 'Loyalty Programs', |
|||
body: '', |
|||
}; |
|||
Registries.Component.add(LoyaltyPrograms); |
|||
return LoyaltyPrograms; |
@ -0,0 +1,64 @@ |
|||
/** @odoo-module **/ |
|||
|
|||
import OrderSummary from 'point_of_sale.OrderSummary'; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
import { round_decimals,round_precision } from 'web.utils'; |
|||
|
|||
export const PosLoyaltyDeduction = (OrderSummary) => |
|||
class PosLoyaltyDeduction extends OrderSummary { |
|||
deductLoyaltyPoints() { |
|||
//----to deduct the loyalty points when the order is refunded---
|
|||
const order = this.env.pos.get_order(); |
|||
let refundedLines = order.orderlines.filter((line) => line.refunded_orderline_id != undefined); |
|||
let pointsReduced = []; |
|||
let newBalance = []; |
|||
let programName= []; |
|||
let valsList = []; |
|||
if(order.couponPointChanges != undefined){ |
|||
let rewardPoints = JSON.parse(localStorage.getItem('pointsCost')) |
|||
for (const pointChange of Object.values(order.getLoyaltyPoints())) { |
|||
const { couponId,bal, points, program } = pointChange; |
|||
if(couponId > 0){ |
|||
let loyaltyCard = this.env.pos.couponCache[couponId] |
|||
let programs = this.env.pos.program_by_id[loyaltyCard.program_id]; |
|||
programName.push(programs.name) |
|||
let balance = loyaltyCard.balance |
|||
let res = 0; |
|||
let ruleId = []; |
|||
programs.rules.forEach(rule => { |
|||
ruleId.push(rule.id) |
|||
let totalQuantity = 0; |
|||
for (let line of refundedLines) { |
|||
const refundedQty = line.pos.toRefundLines[line.refunded_orderline_id]?.orderline?.refundedQty - line.get_quantity() |
|||
switch (rule.reward_point_mode) { |
|||
case 'money': |
|||
res -= round_precision(rule.reward_point_amount * line.get_price_with_tax(), 0.01); |
|||
break; |
|||
case 'unit': |
|||
res -= rule.reward_point_amount * line.get_quantity(); |
|||
break; |
|||
default: |
|||
totalQuantity += line.pos.toRefundLines[line.refunded_orderline_id]?.orderline?.qty || 0; |
|||
res += totalQuantity === refundedQty ? rule.reward_point_amount : 0; |
|||
} |
|||
} |
|||
}) |
|||
for(var line of refundedLines){ |
|||
if(line.pos.toRefundLines[line.refunded_orderline_id]?.orderline?.refundedQty === 0 && rewardPoints.length != 0){ |
|||
for(var pointscost of rewardPoints){ |
|||
if (pointscost[couponId]){ |
|||
res -= pointscost[couponId] |
|||
} |
|||
} |
|||
} |
|||
} |
|||
let currentBalance = balance - res; |
|||
valsList.push({lostPoint: res, newPoint: currentBalance.toFixed(2), programName : programs.name, ruleId:ruleId }); |
|||
} |
|||
} |
|||
} |
|||
this.env.pos.lostPoints = valsList; |
|||
return valsList; |
|||
} |
|||
} |
|||
Registries.Component.extend(OrderSummary, PosLoyaltyDeduction) |
@ -0,0 +1,16 @@ |
|||
odoo.define('advanced_loyalty_management.pos_loyalty_deduction_receipt', function (require) { |
|||
"use strict"; |
|||
var { Order } = require('point_of_sale.models'); |
|||
const Registries = require('point_of_sale.Registries'); |
|||
|
|||
const DeductedLoyalty = (Order) => class DeductedLoyalty extends Order { |
|||
export_for_printing() { |
|||
//----to show the deducted points in receipt
|
|||
var line = super.export_for_printing(...arguments); |
|||
var points = this.pos.lostPoints |
|||
line.lostPoints = points |
|||
return line; |
|||
} |
|||
} |
|||
Registries.Model.extend(Order, DeductedLoyalty); |
|||
}) |
@ -0,0 +1,168 @@ |
|||
/** @odoo-module **/ |
|||
import { Order, Orderline, PosGlobalState} from 'point_of_sale.models'; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
|
|||
var utils = require('web.utils'); |
|||
var round_pr = utils.round_precision; |
|||
|
|||
function _newRandomRewardCode() { |
|||
return (Math.random() + 1).toString(36).substring(3); |
|||
} |
|||
|
|||
const PosLoyaltyOrder = (Order) => class PosLoyaltyOrder extends Order { |
|||
getLoyaltyPoints() { |
|||
//------change is added to loyalty points---
|
|||
// map: couponId -> LoyaltyPoints
|
|||
const loyaltyPoints = {}; |
|||
for (const pointChange of Object.values(this.couponPointChanges)) { |
|||
const { coupon_id, points, program_id } = pointChange; |
|||
const program = this.pos.program_by_id[program_id]; |
|||
if (program.program_type !== "loyalty") { |
|||
continue; |
|||
} |
|||
const loyaltyCard = this.pos.couponCache[coupon_id] || /* or new card */ { |
|||
id: coupon_id, |
|||
balance: 0, |
|||
}; |
|||
let [won, spent, total] = [0, 0, 0]; |
|||
var balance = loyaltyCard.balance; |
|||
if(this.pos.get_order().convertToLoyalty == undefined){ |
|||
won += points - this._getPointsCorrection(program); |
|||
} |
|||
else{ |
|||
won += points - this._getPointsCorrection(program); |
|||
if(program_id === this.pos.get_order().programToAdd){ |
|||
won += this.pos.get_order().convertToLoyalty |
|||
} |
|||
} |
|||
if (coupon_id !== 0) { |
|||
for (const line of this._get_reward_lines()) { |
|||
if (line.coupon_id === coupon_id) { |
|||
spent += line.points_cost; |
|||
} |
|||
} |
|||
} |
|||
total = balance + won - spent; |
|||
const name = program.portal_visible ? program.portal_point_name : _t("Points"); |
|||
loyaltyPoints[coupon_id] = { |
|||
won: parseFloat(won.toFixed(2)), |
|||
spent: parseFloat(spent.toFixed(2)), |
|||
total: parseFloat(total.toFixed(2)), |
|||
balance: parseFloat(balance.toFixed(2)), |
|||
name, |
|||
program, |
|||
}; |
|||
} |
|||
return Object.entries(loyaltyPoints).map(([couponId, points]) => ({ |
|||
couponId, |
|||
points, |
|||
program: points.program, |
|||
})); |
|||
} |
|||
|
|||
_getRewardLineValues(args) { |
|||
//----a new reward type is added
|
|||
const reward = args['reward']; |
|||
if (reward.reward_type === 'discount') { |
|||
return this._getRewardLineValuesDiscount(args); |
|||
} else if (reward.reward_type === 'product') { |
|||
return this._getRewardLineValuesProduct(args); |
|||
} |
|||
else if (reward.reward_type === 'redemption'){ |
|||
return this._getRewardLineValuesRedemption(args) |
|||
} |
|||
return []; |
|||
} |
|||
|
|||
_getRewardLineValuesRedemption(args){ |
|||
//---configured new reward product
|
|||
const reward = args["reward"]; |
|||
const coupon_id = args["coupon_id"]; |
|||
const rewardAppliesTo = reward.discount_applicability; |
|||
let getDiscountable; |
|||
getDiscountable = this._getDiscountableOnOrder.bind(this); |
|||
let { discountable, discountablePerTax } = getDiscountable(reward); |
|||
discountable = Math.min(this.get_total_with_tax(), discountable); |
|||
const discount = reward.pointsToRedeem * reward.redemption_amount |
|||
const discountProduct = reward.discount_line_product_id; |
|||
const rewardCode = _newRandomRewardCode(); |
|||
const points = this._getRealCouponPoints(args["coupon_id"]) |
|||
const cost = reward.clear_wallet ? points :reward.pointsToRedeem |
|||
return[ |
|||
{ |
|||
product: discountProduct, |
|||
price: -Math.min(discount), |
|||
quantity: 1, |
|||
reward_id: reward.id, |
|||
is_reward_line: true, |
|||
coupon_id: coupon_id, |
|||
points_cost: cost, |
|||
reward_identifier_code: rewardCode, |
|||
merge: false, |
|||
tax_ids: [], |
|||
}, |
|||
] |
|||
} |
|||
get_change(paymentline) { |
|||
//----change is converted to loyalty points
|
|||
if (!paymentline) { |
|||
if(this.changeConverted == undefined){ |
|||
var change = |
|||
this.get_total_paid() - this.get_total_with_tax() - this.get_rounding_applied(); |
|||
} |
|||
else{ |
|||
var change = 0 |
|||
} |
|||
} else { |
|||
var change = -this.get_total_with_tax(); |
|||
var lines = this.paymentlines; |
|||
for (var i = 0; i < lines.length; i++) { |
|||
change += lines[i].get_amount(); |
|||
if (lines[i] === paymentline) { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
return round_pr(Math.max(0,change), this.pos.currency.rounding); |
|||
} |
|||
export_as_JSON() { |
|||
//---amount returned is changed
|
|||
var orderLines, paymentLines; |
|||
orderLines = []; |
|||
this.orderlines.forEach(item => { |
|||
return orderLines.push([0, 0, item.export_as_JSON()]); |
|||
}); |
|||
paymentLines = []; |
|||
this.paymentlines.forEach(_.bind( function(item) { |
|||
return paymentLines.push([0, 0, item.export_as_JSON()]); |
|||
}, this)); |
|||
var json = { |
|||
name: this.get_name(), |
|||
amount_paid: this.get_total_paid() - this.get_change(), |
|||
amount_total: this.get_total_with_tax(), |
|||
amount_tax: this.get_total_tax(), |
|||
amount_return: this.get_total_paid() - this.get_total_with_tax() - this.get_rounding_applied(), |
|||
lines: orderLines, |
|||
statement_ids: paymentLines, |
|||
pos_session_id: this.pos_session_id, |
|||
pricelist_id: this.pricelist ? this.pricelist.id : false, |
|||
partner_id: this.get_partner() ? this.get_partner().id : false, |
|||
user_id: this.pos.user.id, |
|||
uid: this.uid, |
|||
sequence_number: this.sequence_number, |
|||
creation_date: this.validation_date || this.creation_date, // todo: rename creation_date in master
|
|||
fiscal_position_id: this.fiscal_position ? this.fiscal_position.id : false, |
|||
server_id: this.server_id ? this.server_id : false, |
|||
to_invoice: this.to_invoice ? this.to_invoice : false, |
|||
to_ship: this.to_ship ? this.to_ship : false, |
|||
is_tipped: this.is_tipped || false, |
|||
tip_amount: this.tip_amount || 0, |
|||
access_token: this.access_token || '', |
|||
}; |
|||
if (!this.is_paid && this.user_id) { |
|||
json.user_id = this.user_id; |
|||
} |
|||
return json; |
|||
} |
|||
} |
|||
Registries.Model.extend(Order, PosLoyaltyOrder); |
@ -0,0 +1,25 @@ |
|||
/** @odoo-module **/ |
|||
import PaymentScreenStatus from 'point_of_sale.PaymentScreenStatus'; |
|||
import Registries from 'point_of_sale.Registries' ; |
|||
const { Gui } = require('point_of_sale.Gui'); |
|||
|
|||
const ChangeLoyalty = (PaymentScreenStatus) => |
|||
class extends PaymentScreenStatus { |
|||
convertLoyalty(){ |
|||
//----A pop is added to choose to which loyalty card the program is added
|
|||
var order = this.env.pos.get_order() |
|||
var loyaltyPoints = order.getLoyaltyPoints() |
|||
var change = order.get_change() |
|||
if(loyaltyPoints.length != 0){ |
|||
Gui.showPopup("LoyaltyPrograms", { |
|||
title: this.env._t('Convert Change'), |
|||
cancelText: this.env._t("Cancel"), |
|||
confirmText:this.env._t("Confirm"), |
|||
Order:order, |
|||
change:change, |
|||
Loyalty :loyaltyPoints, |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
Registries.Component.extend(PaymentScreenStatus, ChangeLoyalty) |
@ -0,0 +1,83 @@ |
|||
odoo.define('advanced_loyalty_management.TicketScreen', function (require) { |
|||
'use strict'; |
|||
const TicketScreen = require('point_of_sale.TicketScreen'); |
|||
const Registries = require('point_of_sale.Registries'); |
|||
const { parse } = require('web.field_utils'); |
|||
const {_t} = require('web.core'); |
|||
const NumberBuffer = require('point_of_sale.NumberBuffer'); |
|||
const LoyaltyRefundTicketScreen = (TicketScreen) => |
|||
class extends TicketScreen { |
|||
async _onDoRefund() { |
|||
//----points cost is stored to local storage when the parent order has reward lines
|
|||
var res = super._onDoRefund() |
|||
let pointsCost = [] |
|||
var refundOrder = this._state.syncedOrders.cache[this._state.ui.selectedSyncedOrderId] |
|||
var rewardLines = refundOrder._get_reward_lines() |
|||
for(var line in rewardLines ){ |
|||
var dict = {} |
|||
dict[rewardLines[line].coupon_id] = rewardLines[line].points_cost |
|||
pointsCost.push(dict) |
|||
} |
|||
localStorage.setItem("pointsCost", JSON.stringify(pointsCost)) |
|||
return res |
|||
} |
|||
|
|||
_onUpdateSelectedOrderline({ detail }) { |
|||
//----to prevent the return of reward lines
|
|||
const buffer = detail.buffer; |
|||
const order = this.getSelectedSyncedOrder(); |
|||
if (!order) return NumberBuffer.reset(); |
|||
let pointsCost = [] |
|||
var reward_product_id = [] |
|||
var refundOrder = this._state.syncedOrders.cache[this._state.ui.selectedSyncedOrderId] |
|||
var rewardLines = refundOrder._get_reward_lines() |
|||
for(var line in rewardLines ){ |
|||
var dict = {} |
|||
dict[rewardLines[line].coupon_id] = rewardLines[line].points_cost |
|||
pointsCost.push(dict) |
|||
reward_product_id.push(rewardLines[line].product.id) |
|||
} |
|||
const selectedOrderlineId = this.getSelectedOrderlineId(); |
|||
const orderline = order.orderlines.find((line) => line.id == selectedOrderlineId); |
|||
if (!orderline) return NumberBuffer.reset(); |
|||
const toRefundDetail = this._getToRefundDetail(orderline); |
|||
// When already linked to an order, do not modify the to refund quantity.
|
|||
if (toRefundDetail.destinationOrderUid) return NumberBuffer.reset(); |
|||
const refundableQty = toRefundDetail.orderline.qty - toRefundDetail.orderline.refundedQty; |
|||
if (refundableQty <= 0) return NumberBuffer.reset(); |
|||
if (buffer == null || buffer == '') { |
|||
toRefundDetail.qty = 0; |
|||
} else { |
|||
const quantity = Math.abs(parse.float(buffer)); |
|||
if(orderline.is_reward_line == true || orderline.product.id == reward_product_id[0] -1){ |
|||
if(quantity > 0){ |
|||
this.showPopup('ErrorPopup', { |
|||
title: _t("REFUND NOT POSSIBLE"), |
|||
body: _t( |
|||
"You cannot refund a rewarded line", |
|||
), |
|||
}); |
|||
} |
|||
} |
|||
else{ |
|||
if (quantity > refundableQty) { |
|||
NumberBuffer.reset(); |
|||
this.showPopup('ErrorPopup', { |
|||
title: this.env._t('Maximum Exceeded'), |
|||
body: _.str.sprintf( |
|||
this.env._t( |
|||
'The requested quantity to be refunded is higher than the ordered quantity. %s is requested while only %s can be refunded.' |
|||
), |
|||
quantity, |
|||
refundableQty |
|||
), |
|||
}); |
|||
} else { |
|||
toRefundDetail.qty = quantity; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Registries.Component.extend(TicketScreen, LoyaltyRefundTicketScreen); |
|||
}); |
@ -0,0 +1,59 @@ |
|||
/** @odoo-module **/ |
|||
|
|||
import AbstractAwaitablePopup from 'point_of_sale.AbstractAwaitablePopup'; |
|||
import Registries from 'point_of_sale.Registries'; |
|||
const { useState,useRef } = owl; |
|||
import { _t } from 'web.core'; |
|||
|
|||
|
|||
class RewardPopup extends AbstractAwaitablePopup { |
|||
setup(){ |
|||
this.state = useState({ |
|||
value:'' , |
|||
redeemPoints:'' |
|||
}) |
|||
this.points = useRef("points"); |
|||
} |
|||
|
|||
toRedeem(ev){ |
|||
ev.state.redeemPoints = ev.points.el.value |
|||
} |
|||
|
|||
save(props,ev){ |
|||
//---after giving the points to redeem, the reward is added to orderline
|
|||
if(isNaN(ev.state.redeemPoints)){ |
|||
ev.showPopup('ErrorPopup', { |
|||
title: ev.env._t('Error'), |
|||
body: ev.env._t('Please enter a valid number'), |
|||
}); |
|||
}else if(ev.props.max_redemption_points < ev.state.redeemPoints){ |
|||
ev.showPopup('ErrorPopup', { |
|||
title: ev.env._t('Error'), |
|||
body: ev.env._t('Points to redeem should be less than Maximum Redemption Point.'), |
|||
}); |
|||
} |
|||
else{ |
|||
const selectedReward = props.selected_reward |
|||
const pointsWon = props.order.couponPointChanges[selectedReward.coupon_id].points |
|||
const order = props.order.access_token |
|||
selectedReward.reward.pointsToRedeem = parseInt(ev.state.redeemPoints) |
|||
ev.confirm() |
|||
props.order.selectedCoupon = selectedReward.coupon_id |
|||
props.order.pointsCost = parseInt(ev.state.redeemPoints) |
|||
return props.property._applyReward( |
|||
selectedReward.reward, |
|||
selectedReward.coupon_id, |
|||
selectedReward.potentialQty |
|||
); |
|||
} |
|||
} |
|||
} |
|||
RewardPopup.template = 'RewardPopup'; |
|||
RewardPopup.defaultProps = { |
|||
confirmText: 'Confirm', |
|||
cancelText: 'Cancel', |
|||
title: 'Loyalty Programs', |
|||
body: '', |
|||
}; |
|||
Registries.Component.add(RewardPopup); |
|||
return RewardPopup; |
@ -0,0 +1,171 @@ |
|||
/** @odoo-module **/ |
|||
|
|||
import { patch } from "@web/core/utils/patch"; |
|||
import { RewardButton} from "@pos_loyalty/js/ControlButtons/RewardButton"; |
|||
import { useListener } from "@web/core/utils/hooks"; |
|||
const { Gui } = require('point_of_sale.Gui'); |
|||
const { useState } = owl; |
|||
var rpc = require('web.rpc'); |
|||
|
|||
patch(RewardButton.prototype, "pos_loyalty_reward", { |
|||
setup() { |
|||
useListener('click', this.onClick); |
|||
this.state = useState({ |
|||
frequency : 0, |
|||
}); |
|||
}, |
|||
_mergeFreeProductRewards(freeProductRewards, potentialFreeProductRewards,redemption) { |
|||
const result = [] |
|||
for (const reward of potentialFreeProductRewards) { |
|||
if (!freeProductRewards.find(item => item.reward.id === reward.reward.id)) { |
|||
result.push(reward); |
|||
} |
|||
} |
|||
for (const rew of redemption){ |
|||
result.push(rew) |
|||
} |
|||
return freeProductRewards.concat(result); |
|||
}, |
|||
_getPotentialRewards() { |
|||
const order = this.env.pos.get_order(); |
|||
// Claimable rewards excluding those from eWallet programs.
|
|||
// eWallet rewards are handled in the eWalletButton.
|
|||
var pointCheck = false |
|||
for (const pointChange of Object.values(order.couponPointChanges)){ |
|||
if(pointChange.coupon_id > 0){ |
|||
pointCheck = true |
|||
} |
|||
} |
|||
let rewards = []; |
|||
if (order) { |
|||
const claimableRewards = order.getClaimableRewards(); |
|||
rewards = claimableRewards.filter(({ reward }) => reward.program_id.program_type !== 'ewallet'); |
|||
} |
|||
const discountRewards = rewards.filter(({ reward }) => reward.reward_type == 'discount'); |
|||
const freeProductRewards = rewards.filter(({ reward }) => reward.reward_type == 'product'); |
|||
const redemption = rewards.filter(({ reward }) => reward.reward_type == 'redemption' && pointCheck == true |
|||
&& reward.redemption_frequency > this.state.frequency); |
|||
if(order.partner != null){ |
|||
var checkFrequency = this.check(rewards) |
|||
} |
|||
const potentialFreeProductRewards = order.getPotentialFreeProductRewards(); |
|||
return discountRewards.concat(this._mergeFreeProductRewards(freeProductRewards, potentialFreeProductRewards,redemption)); |
|||
}, |
|||
async check(rewards){ |
|||
//---to check how many times the reward is claimed
|
|||
let count = 0; |
|||
const partner_id = this.env.pos.get_order().partner.id |
|||
var checkRedemption = await rpc.query({ |
|||
model:'res.partner', |
|||
method:'check_redemption', |
|||
args:[partner_id] |
|||
}).then((result)=>{ |
|||
const today = new Date() |
|||
const year = today.getFullYear(); |
|||
const month = String(today.getMonth() + 1).padStart(2, '0'); |
|||
const day = String(today.getDate()).padStart(2, '0'); |
|||
const formattedDate = `${year}-${month}-${day}`; |
|||
const currentWeekStart = new Date(today.getFullYear(), today.getMonth(), today.getDate() - today.getDay()); |
|||
const currentWeekEnd = new Date(today.getFullYear(), today.getMonth(), today.getDate() + (6 - today.getDay())); |
|||
const formattedCurrentWeekStart = currentWeekStart.toISOString().split('T')[0]; |
|||
const formattedCurrentWeekEnd = currentWeekEnd.toISOString().split('T')[0]; |
|||
const currentMonthStart = new Date(today.getFullYear(), today.getMonth(), 1); |
|||
const currentMonthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0); // Last day of current month
|
|||
const formattedCurrentMonthStart = currentMonthStart.toISOString().split('T')[0]; |
|||
const formattedCurrentMonthEnd = currentMonthEnd.toISOString().split('T')[0]; |
|||
const currentYearStart = new Date(today.getFullYear(), 0, 1); |
|||
const currentYearEnd = new Date(today.getFullYear(), 11, 31); |
|||
const formattedCurrentYearStart = currentYearStart.toISOString().split('T')[0]; |
|||
const formattedCurrentYearEnd = currentYearEnd.toISOString().split('T')[0]; |
|||
for (const reward of rewards){ |
|||
if(reward.reward.redemption_frequency_unit === 'day'){ |
|||
for (let i = 0; i < result[1].length; i++) { |
|||
if (result[1][i] === formattedDate) { |
|||
count ++; |
|||
} |
|||
} |
|||
} |
|||
else if(reward.reward.redemption_frequency_unit === 'week'){ |
|||
for (let i = 0; i < result[1].length; i++) { |
|||
const date =(result[1][i]); |
|||
if (date >= formattedCurrentWeekStart && date <= formattedCurrentWeekEnd) { |
|||
count++; |
|||
} |
|||
} |
|||
} |
|||
else if(reward.reward.redemption_frequency_unit === 'month'){ |
|||
for (let i = 0; i < result[1].length; i++) { |
|||
const date =(result[1][i]); |
|||
if (date >= formattedCurrentMonthStart && date <= formattedCurrentMonthEnd) { |
|||
count++; |
|||
} |
|||
} |
|||
} |
|||
else if(reward.reward.redemption_frequency_unit === 'year'){ |
|||
for (let i = 0; i < result[1].length; i++) { |
|||
const date =(result[1][i]); |
|||
if (date >= formattedCurrentYearStart && date <= formattedCurrentYearEnd) { |
|||
count ++ |
|||
|
|||
} |
|||
} |
|||
} |
|||
} |
|||
return count |
|||
}) |
|||
this.state.frequency = checkRedemption |
|||
}, |
|||
|
|||
async onClick() { |
|||
//----change in work flow when reward type is redemption
|
|||
const rewards = this._getPotentialRewards(); |
|||
const order = this.env.pos.get_order(); |
|||
if (rewards.length === 0) { |
|||
await this.showPopup('ErrorPopup', { |
|||
title: this.env._t('No rewards available.'), |
|||
body: this.env._t('There are no rewards claimable for this customer.') |
|||
}); |
|||
return false; |
|||
|
|||
} else { |
|||
const rewardsList = rewards.map((reward) => ({ |
|||
id: reward.reward.id, |
|||
label: reward.reward.description, |
|||
item: reward, |
|||
})); |
|||
const { confirmed, payload: selectedReward } = await this.showPopup('SelectionPopup', { |
|||
title: this.env._t('Please select a reward'), |
|||
list: rewardsList, |
|||
}); |
|||
if (confirmed) { |
|||
if(selectedReward.reward.reward_type == "redemption"){ |
|||
var points = [] |
|||
if(selectedReward.reward.max_redemption_type == 'points'){ |
|||
points.push(selectedReward.reward.max_redemption_amount/selectedReward.reward.redemption_amount) |
|||
} |
|||
else if(selectedReward.reward.max_redemption_type == 'amount'){ |
|||
points.push(selectedReward.reward.max_redemption_amount/selectedReward.reward.redemption_amount) |
|||
} |
|||
else if(selectedReward.reward.max_redemption_type == 'percent'){ |
|||
var totalAmount = order.get_total_with_tax() |
|||
var maxRedemption = totalAmount * selectedReward.reward.max_redemption_amount / 100 |
|||
points.push(maxRedemption/selectedReward.reward.redemption_amount) |
|||
} |
|||
await Gui.showPopup("RewardPopup", { |
|||
title: this.env._t("Redeem Points"), |
|||
cancelText: this.env._t("Cancel"), |
|||
confirmText:this.env._t("Confirm"), |
|||
rewards: rewards, |
|||
selected_reward: selectedReward, |
|||
order: order, |
|||
max_redemption_points : points[0], |
|||
property: this |
|||
}); |
|||
}else{ |
|||
return this._applyReward(selectedReward.reward, selectedReward.coupon_id, selectedReward.potentialQty); |
|||
} |
|||
} |
|||
} |
|||
return false; |
|||
} |
|||
}) |