diff --git a/sale_promotion/README.rst b/sale_promotion/README.rst new file mode 100644 index 000000000..16b495679 --- /dev/null +++ b/sale_promotion/README.rst @@ -0,0 +1,48 @@ +================== +Sale Promotion v11 +================== +Add option to give promotion for your products. + +Contacts +======== +* Cybrosys Techno Solutions + +Depends +======= +[sale] addon Odoo +[account_invoicing] addon Odoo + +Tech +==== +* [Python] - Models +* [XML] - Odoo views + +Installation +============ +- www.odoo.com/documentation/11.0/setup/install.html +- Install our custom addon + +License +======= + GNU Affero General Public License + (http://www.gnu.org/licenses/agpl.html) + +Bug Tracker +=========== + +Contact odoo@cybrosys.com + +Authors +------- +* Developer v10: Anusha @ Cybrosys, anusha@cybrosys.in +* Developer v11: Niyas Raphy @ Cybrosys, niyas@cybrosys.in + +Maintainer +---------- + +This module is maintained by Cybrosys Technologies. + +For support and more information, please visit https://www.cybrosys.com. + + + diff --git a/sale_promotion/__init__.py b/sale_promotion/__init__.py new file mode 100644 index 000000000..3e82b33e0 --- /dev/null +++ b/sale_promotion/__init__.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: Anusha P P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# 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 +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import models + + + diff --git a/sale_promotion/__manifest__.py b/sale_promotion/__manifest__.py new file mode 100644 index 000000000..53d6a9eb0 --- /dev/null +++ b/sale_promotion/__manifest__.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: Anusha P P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# 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 +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +{ + 'name': "Sale Promotion", + 'version': '11.0.1.0.0', + 'summary': """Create Promotion Offers For Sales""", + 'description': """This Module Allows to Set Promotion Offers On Products And Product Categories.""", + 'author': "Cybrosys Techno Solutions", + 'maintainer': 'Cybrosys Techno Solutions', + 'company': "Cybrosys Techno Solutions", + 'website': "https://www.cybrosys.com", + 'category': 'Sales', + 'depends': ['sale', 'account_invoicing'], + 'data': [ + 'security/ir.model.access.csv', + 'views/promotion_product.xml', + 'views/sale_promotion_rule.xml', + 'views/sale_order.xml', + ], + 'images': ['static/description/banner.jpg'], + 'license': 'AGPL-3', + 'installable': True, + 'auto_install': False, +} + + diff --git a/sale_promotion/models/__init__.py b/sale_promotion/models/__init__.py new file mode 100644 index 000000000..6d5cbb328 --- /dev/null +++ b/sale_promotion/models/__init__.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: Anusha P P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# 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 +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from . import promotion_product +from . import sale_promotion_rule +from . import sale_order + + + diff --git a/sale_promotion/models/promotion_product.py b/sale_promotion/models/promotion_product.py new file mode 100644 index 000000000..152e9615d --- /dev/null +++ b/sale_promotion/models/promotion_product.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: Anusha P P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# 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 +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo import models, fields + + +class PromotionProduct(models.Model): + _inherit = 'product.template' + + is_promotion_product = fields.Boolean(string="Promotion Product", default=False,help='This is a promotion product') + + + diff --git a/sale_promotion/models/sale_order.py b/sale_promotion/models/sale_order.py new file mode 100644 index 000000000..906c19f65 --- /dev/null +++ b/sale_promotion/models/sale_order.py @@ -0,0 +1,181 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: Anusha P P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# 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 +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from dateutil import parser +from odoo import models, fields, api, _ +from odoo.exceptions import UserError + + +class SalePromotion(models.Model): + _inherit = 'sale.order' + + sale_promotion_id = fields.Many2one('sale.promotion', string="Sale Promotion") + + @api.multi + def button_dump_sale_promotion(self): + + for order in self: + if order.sale_promotion_id: + date_order = parser.parse(order.date_order).strftime('%Y-%m-%d') + product_list = [] + category_list = [] + for line in order.order_line: + count = 0 + categ_count = 0 + if line.is_promotion_line: + line.unlink() + else: + if product_list: + for data in product_list: + if data['product_id'] == line.product_id.id: + data['qty'] += line.product_uom_qty + count += 1 + if count == 0: + product_list.append({ + 'product_id': line.product_id.id, + 'qty': line.product_uom_qty, + }) + else: + product_list.append({ + 'product_id': line.product_id.id, + 'qty': line.product_uom_qty, + }) + if category_list: + for prod in category_list: + if prod['category'] == line.product_id.categ_id.id: + prod['qty'] += line.product_uom_qty + categ_count += 1 + + if categ_count == 0: + category_list.append({ + 'qty': line.product_uom_qty, + 'category': line.product_id.categ_id.id, + }) + else: + category_list.append({ + 'qty': line.product_uom_qty, + 'category': line.product_id.categ_id.id, + }) + category_rule_ids = [] + product_rule_ids = [] + for obj in self.sale_promotion_id: + for promo_lines in obj.item_ids: + if not promo_lines.date_start or promo_lines.date_start <= date_order: + if not promo_lines.date_end or promo_lines.date_end >= date_order: + if promo_lines.applied_on == 'product_category': + categ_val = {'rule': promo_lines, + 'category': promo_lines.categ_id.id, + 'qty': promo_lines.min_quantity, + } + category_rule_ids.append(categ_val) + elif promo_lines.applied_on == 'product': + + pr_val = {'rule': promo_lines, + 'product_id': promo_lines.product_tmpl_id.id, + 'qty': promo_lines.min_quantity, + } + product_rule_ids.append(pr_val) + sale_line_obj = self.env['sale.order.line'] + if product_rule_ids and product_list: + for data in product_list: + rules = [] + for i in product_rule_ids: + if i['product_id'] == data['product_id']: + if data['qty'] >= i['rule'].min_quantity: + rules.append(i) + if len(rules) > 1: + max_qty_rule = max(rules, key=lambda x: x['qty']) + for line in max_qty_rule['rule'].promotion_rule_lines: + sale_line_obj.create({ + 'name': line.product_id.name, + 'price_unit': 0, + 'product_uom_qty': line.quantity, + 'order_id': order.id, + 'discount': 0.0, + 'product_uom': line.product_id.uom_id.id, + 'product_id': line.product_id.id, + 'tax_id': [], + 'is_promotion_line': True, + }) + elif len(rules) == 1: + for r in rules: + for line in r['rule'].promotion_rule_lines: + sale_line_obj.create({ + 'name': line.product_id.name, + 'price_unit': 0, + 'product_uom_qty': line.quantity, + 'order_id': order.id, + 'discount': 0.0, + 'product_uom': line.product_id.uom_id.id, + 'product_id': line.product_id.id, + 'tax_id': [], + 'is_promotion_line': True, + }) + else: + pass + + if category_rule_ids and category_list: + for categ in category_list: + rules = [] + for r in category_rule_ids: + if categ['category'] == r['category']: + if categ['qty'] >= r['rule'].min_quantity: + rules.append(r) + if len(rules) > 1: + max_qty_rule = max(rules, key=lambda x: x['qty']) + for line in max_qty_rule['rule'].promotion_rule_lines: + sale_line_obj.create({ + 'name': line.product_id.name, + 'price_unit': 0, + 'product_uom_qty': line.quantity, + 'order_id': order.id, + 'discount': 0.0, + 'product_uom': line.product_id.uom_id.id, + 'product_id': line.product_id.id, + 'tax_id': [], + 'is_promotion_line': True, + }) + elif len(rules) == 1: + for r in rules: + for line in r['rule'].promotion_rule_lines: + sale_line_obj.create({ + 'name': line.product_id.name, + 'price_unit': 0, + 'product_uom_qty': line.quantity, + 'order_id': order.id, + 'discount': 0.0, + 'product_uom': line.product_id.uom_id.id, + 'product_id': line.product_id.id, + 'tax_id': [], + 'is_promotion_line': True, + }) + else: + pass + else: + raise UserError(_('Please Select an Promotion Rule.')) + + +class SaleOrderLine(models.Model): + _inherit = 'sale.order.line' + + is_promotion_line = fields.Boolean(string='Promotion Line') diff --git a/sale_promotion/models/sale_promotion_rule.py b/sale_promotion/models/sale_promotion_rule.py new file mode 100644 index 000000000..b4e5f032f --- /dev/null +++ b/sale_promotion/models/sale_promotion_rule.py @@ -0,0 +1,81 @@ +# -*- coding: utf-8 -*- +############################################################################# +# +# Cybrosys Technologies Pvt. Ltd. +# Copyright (C) 2018-TODAY Cybrosys Technologies(). +# Author: Anusha P P() +# you can modify it under the terms of the GNU LESSER +# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. +# +# It is forbidden to publish, distribute, sublicense, or sell copies +# of the Software or modified copies of the Software. +# +# 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 +# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. +# If not, see . +# +############################################################################# + +from odoo.exceptions import UserError +from odoo import models, fields, _, api + + +class SalePromotion(models.Model): + _name = 'sale.promotion' + + def _get_default_currency_id(self): + return self.env.user.company_id.currency_id.id + + name = fields.Char(string="Name", required=True) + item_ids = fields.One2many('sale.promotion.rule', 'promotion_id', string="Promotion Rules") + company_id = fields.Many2one('res.company') + currency_id = fields.Many2one('res.currency') + + +class SalePromotionRule(models.Model): + _name = 'sale.promotion.rule' + + name = fields.Char(string="Name", required=True) + promotion_id = fields.Many2one('sale.promotion', string="Promotion Rule") + applied_on = fields.Selection([('product_category', 'Product Category'), + ('product', 'Product')], string="Applied On", default='product', required=True) + min_quantity = fields.Integer(string="Minimum Quantity") + date_start = fields.Date(string="Date Start") + date_end = fields.Date(string="Date End") + categ_id = fields.Many2one('product.category', string="Product Category") + product_tmpl_id = fields.Many2one('product.product', string="Product") + + company_id = fields.Many2one('res.company', string='Company', readonly=True, related='promotion_id.company_id', store=True) + currency_id = fields.Many2one('res.currency', string='Currency', + readonly=True, related='promotion_id.currency_id', store=True) + promotion_rule_lines = fields.One2many('sale.promotion.rule.line', 'promotion_rule_id', string="Promotion Lines") + + @api.constrains('date_start', 'date_end') + def check_date(self): + if self.date_start and self.date_end: + if self.date_end < self.date_start: + raise UserError(_('Please check the Ending date.')) + + @api.constrains('promotion_rule_lines') + def check_promotion(self): + if not self.promotion_rule_lines: + raise UserError(_('Please Add some promotion products.')) + + +class SalePromotionLines(models.Model): + _name = 'sale.promotion.rule.line' + + product_id = fields.Many2one('product.product', string="Product") + quantity = fields.Integer(string="Quantity") + promotion_rule_id = fields.Many2one('sale.promotion.rule', string="Promotion Lines") + + + + + + diff --git a/sale_promotion/security/ir.model.access.csv b/sale_promotion/security/ir.model.access.csv new file mode 100644 index 000000000..52e7d9e33 --- /dev/null +++ b/sale_promotion/security/ir.model.access.csv @@ -0,0 +1,7 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_sale_promotion_manager,sale.promotion,model_sale_promotion,sales_team.group_sale_manager,1,1,1,1 +access_sale_promotion_salesman,sale.promotion,model_sale_promotion,sales_team.group_sale_salesman,1,1,1,1 +access_sale_promotion_rule_manager,sale.promotion.rule,model_sale_promotion_rule,sales_team.group_sale_manager,1,1,1,1 +access_sale_promotion_rule_salesman,sale.promotion.rule,model_sale_promotion_rule,sales_team.group_sale_salesman,1,1,1,1 +access_sale_promotion_rule_line_manager,sale.promotion.rule.line,model_sale_promotion_rule_line,sales_team.group_sale_manager,1,1,1,1 +access_sale_promotion_rule_line_salesman,sale.promotion.rule.line,model_sale_promotion_rule_line,sales_team.group_sale_salesman,1,1,1,1 \ No newline at end of file diff --git a/sale_promotion/static/description/banner.jpg b/sale_promotion/static/description/banner.jpg new file mode 100644 index 000000000..2625f7727 Binary files /dev/null and b/sale_promotion/static/description/banner.jpg differ diff --git a/sale_promotion/static/description/cybro_logo.png b/sale_promotion/static/description/cybro_logo.png new file mode 100644 index 000000000..bb309114c Binary files /dev/null and b/sale_promotion/static/description/cybro_logo.png differ diff --git a/sale_promotion/static/description/icon.png b/sale_promotion/static/description/icon.png new file mode 100644 index 000000000..5bb38d990 Binary files /dev/null and b/sale_promotion/static/description/icon.png differ diff --git a/sale_promotion/static/description/index.html b/sale_promotion/static/description/index.html new file mode 100644 index 000000000..d48543ffc --- /dev/null +++ b/sale_promotion/static/description/index.html @@ -0,0 +1,122 @@ +
+
+
+

Sale Promotion

+

Set sales promotion offers on product and product categories.

+

Cybrosys Technologies

+
+
+

Major Features:

+
    +
  •    Create sales promotion offers for products.
  • +
  •    Create sales promotion offers for product categories.
  • +
  •    Attract more customers.
  • +
+
+
+
+ +
+
+
+

Overview

+

+ The module, allows the user to set sales promotion offers on products and product category. + Create product and Product category based specific promotion rules and improve the sales. +

+
+
+
+ + +
+
+
+

+

Create Promotion Rules

+

+
+
+
+
+ Case 1.Buy One get One Free. +
+
+
+
+
+ +
+
+
+
+ +
+
+
+ +
+ +
+
+
+ Case 2.Buy 2 Get Another Free +
+
+
+
+
+ +
+
+
+
+ +
+
+
+
+ +
+
+
+

+

Select Offer In Sale Order

+

+
+
+
+ +
+ Select the promotion rule in the sale order and click on 'Apply Offer' +
+
+ +
+ +
+

Need Any Help?

+ +
+ diff --git a/sale_promotion/static/description/promo1.png b/sale_promotion/static/description/promo1.png new file mode 100644 index 000000000..4a54e3dfe Binary files /dev/null and b/sale_promotion/static/description/promo1.png differ diff --git a/sale_promotion/static/description/promo2.png b/sale_promotion/static/description/promo2.png new file mode 100644 index 000000000..887338a88 Binary files /dev/null and b/sale_promotion/static/description/promo2.png differ diff --git a/sale_promotion/static/description/promo3.png b/sale_promotion/static/description/promo3.png new file mode 100644 index 000000000..dc6a2e5ce Binary files /dev/null and b/sale_promotion/static/description/promo3.png differ diff --git a/sale_promotion/static/description/promo4.png b/sale_promotion/static/description/promo4.png new file mode 100644 index 000000000..8cced2075 Binary files /dev/null and b/sale_promotion/static/description/promo4.png differ diff --git a/sale_promotion/static/description/sale.png b/sale_promotion/static/description/sale.png new file mode 100644 index 000000000..0317d4634 Binary files /dev/null and b/sale_promotion/static/description/sale.png differ diff --git a/sale_promotion/views/promotion_product.xml b/sale_promotion/views/promotion_product.xml new file mode 100644 index 000000000..6b5a25802 --- /dev/null +++ b/sale_promotion/views/promotion_product.xml @@ -0,0 +1,18 @@ + + + + + product.template + product.template + + + + + + + + + + + + diff --git a/sale_promotion/views/sale_order.xml b/sale_promotion/views/sale_order.xml new file mode 100644 index 000000000..d83145bf6 --- /dev/null +++ b/sale_promotion/views/sale_order.xml @@ -0,0 +1,23 @@ + + + + + + sale.order.form + sale.order + + + +