From fe79fe27e6decb0e83b50c129ceab379759718ee Mon Sep 17 00:00:00 2001 From: Haresh Chavda Date: Thu, 11 Jan 2018 10:53:48 +0530 Subject: [PATCH] [ADD] sales_discount Module --- addons/sales_discount/__init__.py | 4 + addons/sales_discount/__manifest__.py | 27 ++ .../data/sale_discount_data.xml | 22 ++ addons/sales_discount/demo/sale_order.xml | 36 +++ addons/sales_discount/i18n/sales_discount.pot | 247 ++++++++++++++++++ addons/sales_discount/models/__init__.py | 5 + .../models/res_config_settings.py | 61 +++++ addons/sales_discount/models/sale.py | 154 +++++++++++ .../models/sale_discount_config.py | 49 ++++ .../report/sale_order_report_view.xml | 43 +++ .../security/ir.model.access.csv | 3 + .../static/description/icon.png | Bin 0 -> 9516 bytes addons/sales_discount/tests/__init__.py | 5 + .../sales_discount/tests/discount_common.py | 30 +++ .../tests/test_01_sale_order_discount.py | 81 ++++++ .../tests/test_02_so_discount_invoice.py | 17 ++ .../views/res_config_settings_views.xml | 45 ++++ .../views/sale_discount_config_view.xml | 26 ++ addons/sales_discount/views/sale_view.xml | 44 ++++ 19 files changed, 899 insertions(+) create mode 100644 addons/sales_discount/__init__.py create mode 100644 addons/sales_discount/__manifest__.py create mode 100644 addons/sales_discount/data/sale_discount_data.xml create mode 100644 addons/sales_discount/demo/sale_order.xml create mode 100644 addons/sales_discount/i18n/sales_discount.pot create mode 100644 addons/sales_discount/models/__init__.py create mode 100644 addons/sales_discount/models/res_config_settings.py create mode 100644 addons/sales_discount/models/sale.py create mode 100644 addons/sales_discount/models/sale_discount_config.py create mode 100644 addons/sales_discount/report/sale_order_report_view.xml create mode 100644 addons/sales_discount/security/ir.model.access.csv create mode 100644 addons/sales_discount/static/description/icon.png create mode 100644 addons/sales_discount/tests/__init__.py create mode 100644 addons/sales_discount/tests/discount_common.py create mode 100644 addons/sales_discount/tests/test_01_sale_order_discount.py create mode 100644 addons/sales_discount/tests/test_02_so_discount_invoice.py create mode 100644 addons/sales_discount/views/res_config_settings_views.xml create mode 100644 addons/sales_discount/views/sale_discount_config_view.xml create mode 100644 addons/sales_discount/views/sale_view.xml diff --git a/addons/sales_discount/__init__.py b/addons/sales_discount/__init__.py new file mode 100644 index 00000000..14d55858 --- /dev/null +++ b/addons/sales_discount/__init__.py @@ -0,0 +1,4 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from . import models +from . import report diff --git a/addons/sales_discount/__manifest__.py b/addons/sales_discount/__manifest__.py new file mode 100644 index 00000000..2720a13c --- /dev/null +++ b/addons/sales_discount/__manifest__.py @@ -0,0 +1,27 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +{ + 'name': "Sale Discount", + 'category': 'Sale', + 'description': """ + Customized module for amending discounts + """, + 'summary': 'Global Discount on Sale Order', + 'author': 'FlectraHQ', + 'website': 'https://flectrahq.com', + 'version': '1.0', + 'depends': ['sale_management', 'sale_stock', 'account_discount'], + 'data': [ + 'security/ir.model.access.csv', + 'data/sale_discount_data.xml', + 'views/res_config_settings_views.xml', + 'views/sale_discount_config_view.xml', + 'views/sale_view.xml', + 'report/sale_order_report_view.xml', + ], + 'demo': [ + 'demo/sale_order.xml', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/addons/sales_discount/data/sale_discount_data.xml b/addons/sales_discount/data/sale_discount_data.xml new file mode 100644 index 00000000..b7b15e7b --- /dev/null +++ b/addons/sales_discount/data/sale_discount_data.xml @@ -0,0 +1,22 @@ + + + + + + 1000 + 25 + + + + + 500 + 15 + + + + + 100 + 10 + + + diff --git a/addons/sales_discount/demo/sale_order.xml b/addons/sales_discount/demo/sale_order.xml new file mode 100644 index 00000000..e32a097f --- /dev/null +++ b/addons/sales_discount/demo/sale_order.xml @@ -0,0 +1,36 @@ + + + + + + per + 10 + + + + draft + + + + + + + Headset USB + + 10 + + 65 + 10 + + + + + Laptop E5023 + + 20 + + 2950.00 + 10 + + + diff --git a/addons/sales_discount/i18n/sales_discount.pot b/addons/sales_discount/i18n/sales_discount.pot new file mode 100644 index 00000000..dd5c766e --- /dev/null +++ b/addons/sales_discount/i18n/sales_discount.pot @@ -0,0 +1,247 @@ +# Translation of Flectra Server. +# This file contains the translation of the following modules: +# * sales_discount +# +msgid "" +msgstr "" +"Project-Id-Version: Flectra Server 1.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-01-04 11:42+0000\n" +"PO-Revision-Date: 2018-01-04 11:42+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.inherit_view_order_form +msgid "(Update Discount)" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.inherit_view_order_form +msgid "(update)" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.custom_report_sale_layout +msgid "Discount" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.custom_report_sale_layout +msgid "Gross Amount" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.custom_report_sale_layout +msgid "Taxes" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.custom_report_sale_layout +msgid "Total" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.custom_report_sale_layout +msgid "Untaxed Amount" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.res_config_settings_view_discount_form +msgid "Allow global discount on the sale order" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale_discount_config.py:21 +#, python-format +msgid "Assigned group already exist!" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_create_uid +msgid "Created by" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_create_date +msgid "Created on" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_order_discount +#: model:ir.ui.view,arch_db:sales_discount.inherit_view_order_form +msgid "Discount" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_order_discount_per +msgid "Discount (%)" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale.py:121 +#, python-format +msgid "Discount (%s) should be less than Gross Amount (%s)." +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_order_discount_amount +msgid "Discount Amount" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_order_discount_method +msgid "Discount Method" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale.py:118 +#, python-format +msgid "Discount should be less than Gross Amount" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_display_name +msgid "Display Name" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_res_config_settings_global_discount_apply +msgid "Do you want to set global discount limit?" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_res_config_settings_global_discount_fix_amount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_fix_amount +msgid "Fix Amount" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale_discount_config.py:30 +#, python-format +msgid "Fix amount (%s) is greater than configuration Amount (%s)!" +msgstr "" + +#. module: sales_discount +#: selection:sale.order,discount_method:0 +msgid "Fixed" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_res_config_settings_global_discount_per_so_invoice_line +msgid "Global Discounts" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_order_gross_amount +msgid "Gross Amount" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_group_id +msgid "Groups" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_id +msgid "ID" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.inherit_view_order_form +msgid "Invoices" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config___last_update +msgid "Last Modified on" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_write_uid +msgid "Last Updated by" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_write_date +msgid "Last Updated on" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_sale_discount_config_percentage +#: selection:sale.order,discount_method:0 +msgid "Percentage" +msgstr "" + +#. module: sales_discount +#: model:ir.model.fields,field_description:sales_discount.field_res_config_settings_global_discount_percentage +msgid "Percentage (%)" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale_discount_config.py:44 +#, python-format +msgid "Percentage (%s) is greater than configuration Percentage (%s)!" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale.py:94 +#, python-format +msgid "Percentage should be between 0% to 100%" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale_discount_config.py:38 +#, python-format +msgid "Percentage should be between 0% to 100%!" +msgstr "" + +#. module: sales_discount +#: model:ir.model,name:sales_discount.model_sale_order +msgid "Quotation" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.sale_discount_config_view_form +msgid "Sale Discount Configuration" +msgstr "" + +#. module: sales_discount +#: model:ir.ui.view,arch_db:sales_discount.res_config_settings_view_discount_form +msgid "Sale Order Discount" +msgstr "" + +#. module: sales_discount +#: model:ir.actions.act_window,name:sales_discount.action_sale_discount_config +#: model:ir.ui.menu,name:sales_discount.menu_sale_order +msgid "Sales Discounts" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale.py:97 +#: code:addons/sales_discount/models/sale.py:106 +#, python-format +msgid "You are not allowed to apply Discount Percentage (%s) more than configured Discount Percentage (%s) in configuration setting!" +msgstr "" + +#. module: sales_discount +#: code:addons/sales_discount/models/sale.py:127 +#: code:addons/sales_discount/models/sale.py:136 +#, python-format +msgid "You're not allowed to apply Discount Amount (%s) more than configured amount (%s) in configuration setting!" +msgstr "" + +#. module: sales_discount +#: model:ir.model,name:sales_discount.model_res_config_settings +msgid "res.config.settings" +msgstr "" + +#. module: sales_discount +#: model:ir.model,name:sales_discount.model_sale_discount_config +msgid "sale.discount.config" +msgstr "" + diff --git a/addons/sales_discount/models/__init__.py b/addons/sales_discount/models/__init__.py new file mode 100644 index 00000000..02acd6a9 --- /dev/null +++ b/addons/sales_discount/models/__init__.py @@ -0,0 +1,5 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from . import res_config_settings +from . import sale_discount_config +from . import sale diff --git a/addons/sales_discount/models/res_config_settings.py b/addons/sales_discount/models/res_config_settings.py new file mode 100644 index 00000000..a84481cd --- /dev/null +++ b/addons/sales_discount/models/res_config_settings.py @@ -0,0 +1,61 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import api, fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + global_discount_per_so_invoice_line = fields.Boolean( + "Global Discounts", + implied_group='sales_discount.global_discount_per_so_invoice_line') + global_discount_apply = fields.Boolean( + "Do you want to set global discount limit?", + implied_group='sales_discount.global_discount_apply') + global_discount_fix_amount = fields.Integer( + 'Fix Amount', + implied_group='sales_discount.global_discount_apply') + global_discount_percentage = fields.Integer( + 'Percentage (%)', + implied_group='sales_discount.global_discount_percentage') + + @api.onchange('global_discount_per_so_invoice_line') + def onchange_global_discount_per_so_invoice_line(self): + if not self.global_discount_per_so_invoice_line: + self.global_discount_apply = False + + @api.onchange('global_discount_apply') + def onchange_global_discount_apply(self): + if not self.global_discount_apply: + self.global_discount_fix_amount = False + + @api.model + def get_values(self): + res = super(ResConfigSettings, self).get_values() + res.update( + global_discount_per_so_invoice_line=self.env[ + 'ir.config_parameter'].sudo().get_param( + 'global_discount_per_so_invoice_line'), + global_discount_apply=self.env[ + 'ir.config_parameter'].sudo().get_param( + 'global_discount_apply'), + global_discount_fix_amount=int(self.env[ + 'ir.config_parameter'].sudo().get_param( + 'global_discount_fix_amount')), + global_discount_percentage=int(self.env[ + 'ir.config_parameter'].sudo().get_param( + 'global_discount_percentage')), + ) + return res + + def set_values(self): + super(ResConfigSettings, self).set_values() + params = self.env['ir.config_parameter'].sudo() + params.set_param('global_discount_per_so_invoice_line', + self.global_discount_per_so_invoice_line) + params.set_param('global_discount_apply', + self.global_discount_apply) + params.set_param('global_discount_fix_amount', + self.global_discount_fix_amount) + params.set_param('global_discount_percentage', + self.global_discount_percentage) diff --git a/addons/sales_discount/models/sale.py b/addons/sales_discount/models/sale.py new file mode 100644 index 00000000..7a61ad7b --- /dev/null +++ b/addons/sales_discount/models/sale.py @@ -0,0 +1,154 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import api, fields, models, _ +from flectra.tools.misc import formatLang +from flectra.exceptions import Warning + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + @api.multi + @api.depends('discount_amount', 'discount_per', 'amount_untaxed') + def _get_discount(self): + total_discount = 0.0 + for record in self: + for so_line_id in record.order_line: + total_price = \ + (so_line_id.product_uom_qty * so_line_id.price_unit) + total_discount += (total_price * so_line_id.discount) / 100 + record.discount = record.pricelist_id.currency_id.round(total_discount) + + @api.multi + @api.depends('order_line', 'discount_per', 'discount_amount') + def _get_total_amount(self): + for order_id in self: + order_id.gross_amount = sum( + [line_id.product_uom_qty * + line_id.price_unit for line_id in order_id.order_line]) + + discount_method = fields.Selection( + [('fixed', 'Fixed'), ('per', 'Percentage')], string="Discount Method") + discount_amount = fields.Float(string="Discount Amount") + discount_per = fields.Float(string="Discount (%)") + discount = fields.Monetary( + string='Discount', store=True, readonly=True, compute='_get_discount', + track_visibility='always') + gross_amount = fields.Float(string="Gross Amount", + compute='_get_total_amount', store=True) + + @api.multi + def calculate_discount(self): + self._check_constrains() + for line in self.order_line: + line.write({'discount': 0.0}) + amount_untaxed = self.amount_untaxed + if self.discount_method == 'per': + for line in self.order_line: + line.write({'discount': line.discount + self.discount_per}) + else: + for line in self.order_line: + discount_value_ratio = \ + (self.discount_amount * + line.price_subtotal) / amount_untaxed + discount_per_ratio = \ + (discount_value_ratio * 100) / line.price_subtotal + line.write({'discount': line.discount + discount_per_ratio}) + + @api.onchange('discount_method') + def onchange_discount_method(self): + self.discount_amount = 0.0 + self.discount_per = 0.0 + if self.discount_method and not self.order_line: + raise Warning('No Sale Order Line(s) were found!') + + @api.constrains('discount_per', 'discount_amount', 'order_line') + def _check_constrains(self): + self.onchange_discount_per() + self.onchange_discount_amount() + + @api.multi + def get_maximum_per_amount(self): + sale_dis_config_obj = self.env['sale.discount.config'] + max_percentage = 0 + max_amount = 0 + check_group = False + for groups_id in self.env.user.groups_id: + sale_dis_config_id = \ + sale_dis_config_obj.search([('group_id', '=', groups_id.id)]) + if sale_dis_config_id: + check_group = True + if sale_dis_config_id.percentage > max_percentage: + max_percentage = sale_dis_config_id.percentage + if sale_dis_config_id.fix_amount > max_amount: + max_amount = sale_dis_config_id.fix_amount + return {'max_percentage': max_percentage, + 'max_amount': max_amount, 'check_group': check_group} + + @api.onchange('discount_per') + def onchange_discount_per(self): + values = self.get_maximum_per_amount() + if self.discount_method == 'per' and ( + self.discount_per > 100 or self.discount_per < 0) and \ + values.get('check_group', False): + raise Warning(_("Percentage should be between 0% to 100%")) + if self.discount_per > values.get('max_percentage', False) and \ + values.get('check_group', False): + raise Warning(_("You are not allowed to apply Discount Percentage" + " (%s) more than configured Discount Percentage " + "(%s) in configuration setting!") % ( + formatLang(self.env, self.discount_per, digits=2), + formatLang(self.env, values['max_percentage'], digits=2))) + config_id = self.env['res.config.settings'].search( + [], order='id desc', limit=1) + if config_id and config_id.global_discount_apply: + if config_id.global_discount_percentage < self.discount_per: + raise Warning(_("You are not allowed to apply Discount " + "Percentage (%s) more than configured " + "Discount Percentage (%s) in configuration " + "setting!") % ( + formatLang(self.env, self.discount_per, digits=2), + formatLang(self.env, config_id.global_discount_percentage, + digits=2))) + + @api.onchange('discount_amount') + def onchange_discount_amount(self): + values = self.get_maximum_per_amount() + if self.discount < 0: + raise Warning(_("Discount should be less than Gross Amount")) + discount = self.discount or self.discount_amount + if discount > self.gross_amount: + raise Warning(_("Discount (%s) should be less than " + "Gross Amount (%s).") % ( + formatLang(self.env, discount, digits=2), + formatLang(self.env, self.gross_amount, digits=2))) + if self.discount_amount > values.get('max_amount', False) \ + and values.get('check_group', False): + raise Warning(_("You're not allowed to apply Discount Amount " + "(%s) more than configured amount (%s) in " + "configuration setting!") % ( + formatLang(self.env, self.discount_amount, digits=2), + formatLang(self.env, values['max_amount'], digits=2))) + config_id = self.env['res.config.settings'].search( + [], order='id desc', limit=1) + if config_id and config_id.global_discount_apply: + if config_id.global_discount_fix_amount < self.discount_amount: + raise Warning(_("You're not allowed to apply Discount " + "Amount (%s) more than configured amount " + "(%s) in configuration setting!") % ( + formatLang(self.env, self.discount_amount, digits=2), + formatLang(self.env, config_id.global_discount_fix_amount, + digits=2))) + + @api.multi + def _prepare_invoice(self): + sale_order = self.env['sale.order'].browse( + self._context.get('active_ids', [])) + invoice_vals = super(SaleOrder, self)._prepare_invoice() + invoice_vals.update({ + 'discount_method': sale_order.discount_method, + 'discount_amount': sale_order.discount_amount, + 'discount_per': sale_order.discount_per, + 'discount': sale_order.discount, + }) + return invoice_vals diff --git a/addons/sales_discount/models/sale_discount_config.py b/addons/sales_discount/models/sale_discount_config.py new file mode 100644 index 00000000..55507a16 --- /dev/null +++ b/addons/sales_discount/models/sale_discount_config.py @@ -0,0 +1,49 @@ +# Part of Flectra See LICENSE file for full copyright and licensing details. + +from flectra import api, fields, models, _ +from flectra.tools.misc import formatLang + + +class SaleDiscountConfig(models.Model): + _name = 'sale.discount.config' + + group_id = fields.Many2one('res.groups', 'Groups', required=True) + fix_amount = fields.Float('Fix Amount', required=True) + percentage = fields.Float('Percentage', required=True) + + @api.constrains('group_id') + def _check_already_exist(self): + check_name = self.search( + [('id', '!=', self.id), + ('group_id.name', '=', self.group_id.name)]) + if check_name: + raise ValueError( + _("Assigned group already exist!")) + + @api.constrains('fix_amount') + def _check_fix_amount_value(self): + config_id = self.env['res.config.settings'].search( + [], order='id desc', limit=1) + if config_id and config_id.global_discount_apply \ + and config_id.global_discount_fix_amount < self.fix_amount: + raise ValueError( + _("Fix amount (%s) is greater than configuration Amount (%s)!" + ) % (formatLang( + self.env, self.fix_amount, digits=2), formatLang( + self.env, config_id.global_discount_fix_amount, digits=2))) + + @api.constrains('percentage') + def _check_percentage(self): + if self.percentage < 0 or self.percentage > 100: + raise ValueError(_("Percentage should be between 0% to 100%!")) + config_id = self.env[ + 'res.config.settings'].search([], order='id desc', limit=1) + if config_id and config_id.global_discount_apply \ + and config_id.global_discount_percentage < self.percentage: + raise ValueError( + _("Percentage (%s) is greater than configuration Percentage " + "(%s)!") % (formatLang( + self.env, self.percentage, digits=2), + formatLang(self.env, + config_id.global_discount_percentage, + digits=2))) diff --git a/addons/sales_discount/report/sale_order_report_view.xml b/addons/sales_discount/report/sale_order_report_view.xml new file mode 100644 index 00000000..82062614 --- /dev/null +++ b/addons/sales_discount/report/sale_order_report_view.xml @@ -0,0 +1,43 @@ + + + + \ No newline at end of file diff --git a/addons/sales_discount/security/ir.model.access.csv b/addons/sales_discount/security/ir.model.access.csv new file mode 100644 index 00000000..bb67f931 --- /dev/null +++ b/addons/sales_discount/security/ir.model.access.csv @@ -0,0 +1,3 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_sale_discount_config,access_sale_discount_config,model_sale_discount_config,base.group_system,1,1,1,1 +access_sale_discount_config_all,access_sale_discount_config_all,model_sale_discount_config,,1,0,0,0 diff --git a/addons/sales_discount/static/description/icon.png b/addons/sales_discount/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c386810fe3a5e9edc78da3677a1ebba87b4763f4 GIT binary patch literal 9516 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 + + + res.config.settings.view.form.inherit.discount.sale + res.config.settings + + + + +

Sale Order Discount

+
+
+
+ +
+
+
+
+
+
+
+
+
diff --git a/addons/sales_discount/views/sale_discount_config_view.xml b/addons/sales_discount/views/sale_discount_config_view.xml new file mode 100644 index 00000000..e1607d9b --- /dev/null +++ b/addons/sales_discount/views/sale_discount_config_view.xml @@ -0,0 +1,26 @@ + + + + sale.discount.config + sale.discount.config + + + + + + + + + + + + Sales Discounts + sale.discount.config + form + tree + + + + diff --git a/addons/sales_discount/views/sale_view.xml b/addons/sales_discount/views/sale_view.xml new file mode 100644 index 00000000..2b7fbf52 --- /dev/null +++ b/addons/sales_discount/views/sale_view.xml @@ -0,0 +1,44 @@ + + + + + sale.order.inherit.form + sale.order + + + + + + + +
+ + + + + +
+
+
+
+