From 27d86cf151ceae199bb35c3718af1e758bf9460e Mon Sep 17 00:00:00 2001 From: Alexis de Lattre Date: Wed, 6 Jul 2022 12:14:51 +0200 Subject: [PATCH] sale_usability: add a wizard to write discount on all lines The wizard is available under the "Action" menu. It has an option to apply only on product or service lines. Add a button "Send ack by email" on confirmed sale.order --- sale_usability/__init__.py | 1 + sale_usability/__manifest__.py | 2 + sale_usability/security/ir.model.access.csv | 3 + sale_usability/views/sale_order.xml | 3 + sale_usability/wizards/__init__.py | 1 + .../sale_invoice_discount_all_lines.py | 95 +++++++++++++++++++ .../sale_invoice_discount_all_lines_view.xml | 52 ++++++++++ 7 files changed, 157 insertions(+) create mode 100644 sale_usability/security/ir.model.access.csv create mode 100644 sale_usability/wizards/__init__.py create mode 100644 sale_usability/wizards/sale_invoice_discount_all_lines.py create mode 100644 sale_usability/wizards/sale_invoice_discount_all_lines_view.xml diff --git a/sale_usability/__init__.py b/sale_usability/__init__.py index 0650744..aee8895 100644 --- a/sale_usability/__init__.py +++ b/sale_usability/__init__.py @@ -1 +1,2 @@ from . import models +from . import wizards diff --git a/sale_usability/__manifest__.py b/sale_usability/__manifest__.py index c56a82e..e03f307 100644 --- a/sale_usability/__manifest__.py +++ b/sale_usability/__manifest__.py @@ -23,6 +23,8 @@ 'views/account_move.xml', 'views/res_company.xml', "views/res_partner.xml", + 'wizards/sale_invoice_discount_all_lines_view.xml', + 'security/ir.model.access.csv', ], 'installable': True, } diff --git a/sale_usability/security/ir.model.access.csv b/sale_usability/security/ir.model.access.csv new file mode 100644 index 0000000..0fede3e --- /dev/null +++ b/sale_usability/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_invoice_discount_all_lines_sale,Full access on sale.invoice.discount.all.lines to Sale user,model_sale_invoice_discount_all_lines,sales_team.group_sale_salesman,1,1,1,1 +access_sale_invoice_discount_all_lines_invoice,Full access on sale.invoice.discount.all.lines to Invoice user,model_sale_invoice_discount_all_lines,account.group_account_invoice,1,1,1,1 diff --git a/sale_usability/views/sale_order.xml b/sale_usability/views/sale_order.xml index 3e1129b..057ea5a 100644 --- a/sale_usability/views/sale_order.xml +++ b/sale_usability/views/sale_order.xml @@ -32,6 +32,9 @@ + diff --git a/sale_usability/wizards/__init__.py b/sale_usability/wizards/__init__.py new file mode 100644 index 0000000..a51d7d4 --- /dev/null +++ b/sale_usability/wizards/__init__.py @@ -0,0 +1 @@ +from . import sale_invoice_discount_all_lines diff --git a/sale_usability/wizards/sale_invoice_discount_all_lines.py b/sale_usability/wizards/sale_invoice_discount_all_lines.py new file mode 100644 index 0000000..d3a20a9 --- /dev/null +++ b/sale_usability/wizards/sale_invoice_discount_all_lines.py @@ -0,0 +1,95 @@ +# Copyright 2022 Akretion France (http://www.akretion.com/) +# @author: Alexis de Lattre +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models, fields, _ +from odoo.tools import float_compare +from odoo.exceptions import UserError + + +class SaleInvoiceDiscountAllLines(models.TransientModel): + _name = 'sale.invoice.discount.all.lines' + _description = 'Apply discount on all lines of a sale order or invoice' + + @api.model + def default_get(self, fields_list): + res = super().default_get(fields_list) + if self._context.get('active_id'): + if self._context.get('active_model') == 'sale.order': + res['sale_id'] = self._context['active_id'] + elif self._context.get('active_model') == 'account.move': + res['move_id'] = self._context['active_id'] + move = self.env['account.move'].browse(res['move_id']) + if move.state != 'draft': + raise UserError( + _("Invoice '%s' is not in draft state.") + % self.move_id.display_name) + else: + # I don't translate this because it should never happen. + raise UserError( + "This wizard can only work on a sale order or an invoice.") + else: + # I don't translate this because it should never happen. + raise UserError("Missing active_id in context. It should never happen.") + return res + + sale_id = fields.Many2one( + 'sale.order', string='Order', readonly=True) + move_id = fields.Many2one( + 'account.move', string='Invoice', readonly=True) + discount = fields.Float( + string='Discount', digits='Discount', required=True) + line_type = fields.Selection([ + ('all', 'All Lines'), + ('products', 'All Product Lines'), + ('services', 'All Service Lines'), + ], default='all', required=True, string='Apply on') + + def run(self): + prec = self.env['decimal.precision'].precision_get('Discount') + if float_compare(self.discount, 0, precision_digits=prec) < 0: + raise UserError(_("Discount cannot be negative.")) + if self.sale_id: + record = self.sale_id + line_obj = self.env['sale.order.line'] + domain = [('order_id', '=', self.sale_id.id)] + elif self.move_id: + record = self.move_id + if self.move_id.state != 'draft': + raise UserError(_( + "Invoice '%s' is not in draft state.") % self.move_id.display_name) + line_obj = self.env['account.move.line'] + domain = [ + ('move_id', '=', self.move_id.id), + ('exclude_from_invoice_tab', '=', False), + ] + else: + # I don't translate this because it should never happen. + raise UserError( + "The wizard is not linked to a sale order nor an invoice. " + "This should never happen.") + domain += [('display_type', '=', False)] + if self.line_type == 'products': + domain += [ + ('product_id', '!=', False), + ('product_id.type', '!=', 'service'), + ] + elif self.line_type == 'services': + domain += [ + ('product_id', '!=', False), + ('product_id.type', '=', 'service'), + ] + lines = line_obj.search(domain) + if not lines: + raise UserError(_("There are no lines to apply the discount on.")) + lines.with_context(check_move_validity=False).write({'discount': self.discount}) + if self.move_id: + self.move_id.with_context( + check_move_validity=False)._recompute_dynamic_lines( + recompute_all_taxes=True) + self.move_id._check_balanced() + record.message_post(body=_( + "Applied a {discount}% discount on {line_type}.").format( + discount=self.discount, + line_type=self._fields['line_type'].convert_to_export( + self.line_type, self))) diff --git a/sale_usability/wizards/sale_invoice_discount_all_lines_view.xml b/sale_usability/wizards/sale_invoice_discount_all_lines_view.xml new file mode 100644 index 0000000..83b6408 --- /dev/null +++ b/sale_usability/wizards/sale_invoice_discount_all_lines_view.xml @@ -0,0 +1,52 @@ + + + + + + + sale.invoice.discount.all.lines + +
+ + + + +
+
+
+
+
+ + + Discount on all lines + sale.invoice.discount.all.lines + form + new + + + form + + + + Discount on all lines + sale.invoice.discount.all.lines + form + new + + + form + + +