diff options
| author | Robot Odoo <xmo+roboto@odoo.com> | 2019-08-08 14:04:35 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-08-08 14:04:35 +0200 |
| commit | 3ddc2784bd7cc62609568bf2638e3538ddaa48aa (patch) | |
| tree | b02ba28c238ffb305ca79a4d5cb5793b9e2b48e4 | |
| parent | e3f327a61fd15a54afae0b20227696418d6d409c (diff) | |
| parent | 6173dd21415ab441caaf4404bf812ce2413e29f0 (diff) | |
[MERGE] sale: improvements in sales order UX
Back to Basics: Sales Order Drift
Various improvements in the UX of the Sales application, notably regarding
delivery and coupon. Other views/field labels/helper text changes as well.
Task 2033229
closes odoo/odoo#34963
Signed-off-by: Damien Bouvy (dbo) <dbo@odoo.com>
23 files changed, 166 insertions, 115 deletions
diff --git a/addons/delivery/models/sale_order.py b/addons/delivery/models/sale_order.py index 02c6bf605b3..fa6401f54c7 100644 --- a/addons/delivery/models/sale_order.py +++ b/addons/delivery/models/sale_order.py @@ -51,8 +51,14 @@ class SaleOrder(models.Model): def action_open_delivery_wizard(self): view_id = self.env.ref('delivery.choose_delivery_carrier_view_form').id + if self.env.context.get('carrier_recompute'): + name = _('Update shipping cost') + carrier = self.carrier_id + else: + name = _('Add a shipping method') + carrier = self.partner_id.property_delivery_carrier_id return { - 'name': _('Add a shipping method'), + 'name': name, 'type': 'ir.actions.act_window', 'view_mode': 'form', 'res_model': 'choose.delivery.carrier', @@ -61,27 +67,10 @@ class SaleOrder(models.Model): 'target': 'new', 'context': { 'default_order_id': self.id, - 'default_carrier_id': self.partner_id.property_delivery_carrier_id.id, + 'default_carrier_id': carrier.id, } } - def recompute_delivery_cost(self): - self.ensure_one() - delivery_line = self.order_line.filtered('is_delivery')[:1] - res = self.carrier_id.rate_shipment(self) - if res.get('success'): - self.delivery_message = res.get('warning_message', False) - else: - raise UserError(res['error_message']) - delivery_line.name = self.carrier_id.with_context(lang=self.partner_id.lang).name - if self.carrier_id.invoice_policy == 'real': - delivery_line.name += _(' (Estimated Cost: %s )') % self._format_currency_amount(res['price']) - else: - delivery_line.price_unit = res['price'] - if self.carrier_id.free_over and self._compute_amount_total_without_delivery() >= res['price']: - delivery_line.name += '\nFree Shipping' - self.recompute_delivery_price = False - def _create_delivery_line(self, carrier, price_unit, price_unit_in_description=False): SaleOrderLine = self.env['sale.order.line'] if self.partner_id: @@ -163,3 +152,17 @@ class SaleOrderLine(models.Model): def _is_delivery(self): self.ensure_one() return self.is_delivery + + # override to allow deletion of delivery line in a confirmed order + def _check_line_unlink(self): + """ + Extend the allowed deletion policy of SO lines. + + Lines that are delivery lines can be deleted from a confirmed order. + + :rtype: recordset sale.order.line + :returns: set of lines that cannot be deleted + """ + + undeletable_lines = super()._check_line_unlink() + return undeletable_lines.filtered(lambda line: not line.is_delivery) diff --git a/addons/delivery/views/delivery_view.xml b/addons/delivery/views/delivery_view.xml index 5a451302f91..ac0ef7071a7 100644 --- a/addons/delivery/views/delivery_view.xml +++ b/addons/delivery/views/delivery_view.xml @@ -323,23 +323,25 @@ <field name="recompute_delivery_price" invisible="1"/> </xpath> <xpath expr="//group[@name='note_group']" position="before"> - <div class="oe_left" style="margin-left: 10px"> + <div class="oe_right"> <button - string="Add a shipping method" + string="Add shipping" name="action_open_delivery_wizard" type="object" attrs="{'invisible': ['|', '|',('is_all_service', '=', True), ('order_line', '=', []), ('delivery_set', '=', True)]}" /> <button - string="Recompute shipping cost" - name="recompute_delivery_cost" + string="Update shipping cost" + name="action_open_delivery_wizard" + context="{'carrier_recompute':True}" type="object" class="text-warning btn-secondary" attrs="{'invisible': ['|', '|',('is_all_service', '=', True), ('recompute_delivery_price', '=', False), ('delivery_set', '=', False)]}" /> <button - string="Recompute shipping cost" - name="recompute_delivery_cost" + string="Update shipping cost" + name="action_open_delivery_wizard" + context="{'carrier_recompute':True}" type="object" attrs="{'invisible': ['|', '|',('is_all_service', '=', True), ('recompute_delivery_price', '=', True), ('delivery_set', '=', False)]}" /> diff --git a/addons/delivery/wizard/choose_delivery_carrier.py b/addons/delivery/wizard/choose_delivery_carrier.py index c7bac396c61..c02fa5d30ca 100644 --- a/addons/delivery/wizard/choose_delivery_carrier.py +++ b/addons/delivery/wizard/choose_delivery_carrier.py @@ -19,7 +19,7 @@ class ChooseDeliveryCarrier(models.TransientModel): ) delivery_type = fields.Selection(related='carrier_id.delivery_type') delivery_price = fields.Float() - display_price = fields.Float(string='Cost', readonly=True) + display_price = fields.Float(string='Cost') currency_id = fields.Many2one('res.currency', related='order_id.currency_id') available_carrier_ids = fields.Many2many("delivery.carrier", compute='_compute_available_carrier', string="Available Carriers") invoicing_message = fields.Text(compute='_compute_invoicing_message') @@ -29,18 +29,25 @@ class ChooseDeliveryCarrier(models.TransientModel): def _onchange_carrier_id(self): self.delivery_message = False if self.delivery_type in ('fixed', 'base_on_rule'): - vals = self.carrier_id.rate_shipment(self.order_id) - if vals.get('success'): - if vals.get('warning_message'): - self.delivery_message = vals['warning_message'] - self.delivery_price = vals['price'] - self.display_price = vals['carrier_price'] - else: + vals = self._get_shipment_rate() + if vals.get('error_message'): return {'error': vals['error_message']} else: self.display_price = 0 self.delivery_price = 0 + @api.onchange('order_id') + def _onchange_order_id(self): + # fixed and base_on_rule delivery price will computed on each carrier change so no need to recompute here + if self.carrier_id and self.order_id.delivery_set and self.delivery_type not in ('fixed', 'base_on_rule'): + vals = self._get_shipment_rate() + if vals.get('error_message'): + warning = { + 'title': '%s Error' % self.carrier_id.name, + 'message': vals.get('error_message') + } + return {'warning': warning} + @api.depends('carrier_id') def _compute_invoicing_message(self): self.ensure_one() @@ -51,19 +58,24 @@ class ChooseDeliveryCarrier(models.TransientModel): @api.depends('partner_id') def _compute_available_carrier(self): - carriers = self.env['delivery.carrier'].search([]) for rec in self: + carriers = self.env['delivery.carrier'].search(['|', ('company_id', '=', False), ('company_id', '=', rec.order_id.company_id.id)]) rec.available_carrier_ids = carriers.available_carriers(rec.order_id.partner_shipping_id) if rec.partner_id else carriers - def update_price(self): + def _get_shipment_rate(self): vals = self.carrier_id.rate_shipment(self.order_id) if vals.get('success'): - if vals['warning_message']: + if vals.get('warning_message'): self.delivery_message = vals['warning_message'] self.delivery_price = vals['price'] self.display_price = vals['carrier_price'] - else: - raise UserError(vals['error_message']) + return {} + return {'error_message': vals['error_message']} + + def update_price(self): + vals = self._get_shipment_rate() + if vals.get('error_message'): + raise UserError(vals.get('error_message')) return { 'name': _('Add a shipping method'), 'type': 'ir.actions.act_window', @@ -74,6 +86,9 @@ class ChooseDeliveryCarrier(models.TransientModel): } def button_confirm(self): - self.order_id.carrier_id = self.carrier_id - self.order_id.delivery_message = self.delivery_message - self.order_id.set_delivery_line(self.carrier_id, self.delivery_price) + self.order_id.set_delivery_line(self.carrier_id, self.display_price) + self.order_id.write({ + 'recompute_delivery_price': False, + 'delivery_message': self.delivery_message, + 'carrier_id': self.carrier_id.id + }) diff --git a/addons/delivery/wizard/choose_delivery_carrier_views.xml b/addons/delivery/wizard/choose_delivery_carrier_views.xml index e34849eccbe..c0876733a1a 100644 --- a/addons/delivery/wizard/choose_delivery_carrier_views.xml +++ b/addons/delivery/wizard/choose_delivery_carrier_views.xml @@ -7,17 +7,21 @@ <form> <field name='available_carrier_ids' invisible="1"/> <group> - <field name="carrier_id" domain="[('id', 'in', available_carrier_ids)]"/> - <field name="delivery_type" invisible="1"/> - <field name="currency_id" invisible="1"/> - <field name="order_id" invisible="1"/> - <field name='delivery_price' invisible="1"/> - <field name='display_price' string="Cost" widget="monetary" options="{'currency_field': 'currency_id'}" attrs="{'invisible': [('carrier_id','=', False)]}"/> + <group> + <field name="carrier_id" domain="[('id', 'in', available_carrier_ids)]" widget='radio'/> + <field name="delivery_type" invisible="1"/> + <field name="currency_id" invisible="1"/> + <field name="order_id" invisible="1"/> + <field name='delivery_price' invisible="1"/> + <label for="display_price"/> + <div class="o_row"> + <field name='display_price' widget="monetary" options="{'currency_field': 'currency_id'}" attrs="{'invisible': [('carrier_id','=', False)]}"/> + <button name="update_price" type="object" attrs="{'invisible': [('delivery_type','in', ('fixed', 'base_on_rule'))]}"> + <i class="fa fa-arrow-right mr-1"/>Get rate + </button> + </div> + </group> </group> - <button name="update_price" type="object" attrs="{'invisible': [('delivery_type','in', ('fixed', 'base_on_rule'))]}"> - <i class="fa fa-arrow-right"/> - Get rate - </button> <div role="alert" class="alert alert-warning" attrs="{'invisible': [('invoicing_message', '=', '')]}"> <field name="invoicing_message" nolabel="1"/> </div> @@ -25,7 +29,8 @@ <field name="delivery_message" nolabel="1"/> </div> <footer> - <button name="button_confirm" type="object" string="Add" class="btn-primary"/> + <button name="button_confirm" invisible="not context.get('carrier_recompute')" type="object" string="Update" class="btn-primary"/> + <button name="button_confirm" invisible="context.get('carrier_recompute')" type="object" string="Add" class="btn-primary"/> <button string="Discard" special="cancel" class="btn-secondary"/> </footer> </form> diff --git a/addons/event_sale/views/sale_order_views.xml b/addons/event_sale/views/sale_order_views.xml index 2ec78e4747e..4af17bfbaa9 100644 --- a/addons/event_sale/views/sale_order_views.xml +++ b/addons/event_sale/views/sale_order_views.xml @@ -30,7 +30,7 @@ /> <field name="event_ok" invisible="1"/> </xpath> - <xpath expr="//field[@name='order_line']//tree//field[@name='product_id']" position="after"> + <xpath expr="//field[@name='order_line']//tree//field[@name='product_template_id']" position="after"> <field name="event_ok" invisible="1" /> <field name="event_id" optional="hide" /> <field name="event_ticket_id" optional="hide" /> diff --git a/addons/sale/data/ir_sequence_data.xml b/addons/sale/data/ir_sequence_data.xml index 8e4559a0cdf..42859382008 100644 --- a/addons/sale/data/ir_sequence_data.xml +++ b/addons/sale/data/ir_sequence_data.xml @@ -6,7 +6,7 @@ <record id="seq_sale_order" model="ir.sequence"> <field name="name">Sales Order</field> <field name="code">sale.order</field> - <field name="prefix">SO</field> + <field name="prefix">S</field> <field name="padding">3</field> <field name="company_id" eval="False"/> </record> diff --git a/addons/sale/models/res_config_settings.py b/addons/sale/models/res_config_settings.py index a9ce8c651a6..e3ab9915355 100644 --- a/addons/sale/models/res_config_settings.py +++ b/addons/sale/models/res_config_settings.py @@ -7,6 +7,7 @@ from odoo import api, fields, models class ResConfigSettings(models.TransientModel): _inherit = 'res.config.settings' + group_auto_done_setting = fields.Boolean("Lock Confirmed Sales", implied_group='sale.group_auto_done_setting') module_sale_margin = fields.Boolean("Margins") quotation_validity_days = fields.Integer(related='company_id.quotation_validity_days', string="Default Quotation Validity (Days)", readonly=False) use_quotation_validity_days = fields.Boolean("Default Quotation Validity", config_parameter='sale.use_quotation_validity_days') @@ -39,7 +40,6 @@ class ResConfigSettings(models.TransientModel): domain="[('type', '=', 'service')]", config_parameter='sale.default_deposit_product_id', help='Default product used for payment advances') - auto_done_setting = fields.Boolean("Lock Confirmed Sales", config_parameter='sale.auto_done_setting') module_website_sale_digital = fields.Boolean("Digital Content") auth_signup_uninvited = fields.Selection([ diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 5ef365d4140..60f4cfad5fc 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -478,12 +478,6 @@ class SaleOrder(models.Model): } return invoice_vals - def print_quotation(self): - self.filtered(lambda s: s.state == 'draft').write({'state': 'sent'}) - - return self.env.ref('sale.action_report_saleorder')\ - .with_context({'discard_logo_check': True}).report_action(self) - def action_view_invoice(self): invoices = self.mapped('invoice_ids') action = self.env.ref('account.action_move_out_invoice_type').read()[0] @@ -694,7 +688,7 @@ class SaleOrder(models.Model): 'confirmation_date': fields.Datetime.now() }) self._action_confirm() - if self.env['ir.config_parameter'].sudo().get_param('sale.auto_done_setting'): + if self.env.user.has_group('sale.group_auto_done_setting'): self.action_done() return True @@ -1456,8 +1450,19 @@ class SaleOrderLine(models.Model): ]) return super(SaleOrderLine, self)._name_search(name, args=args, operator=operator, limit=limit, name_get_uid=name_get_uid) + def _check_line_unlink(self): + """ + Check wether a line can be deleted or not. + + Lines cannot be deleted if the order is confirmed; downpayment + lines who have not yet been invoiced bypass that exception. + :rtype: recordset sale.order.line + :returns: set of lines that cannot be deleted + """ + return self.filtered(lambda line: line.state in ('sale', 'done') and (line.invoice_lines or not line.is_downpayment)) + def unlink(self): - if self.filtered(lambda line: line.state in ('sale', 'done') and (line.invoice_lines or not line.is_downpayment)): + if self._check_line_unlink(): raise UserError(_('You can not remove an order line once the sales order is confirmed.\nYou should rather set the quantity to 0.')) return super(SaleOrderLine, self).unlink() diff --git a/addons/sale/security/sale_security.xml b/addons/sale/security/sale_security.xml index cbcd4baf8c3..0ecefa2bda8 100644 --- a/addons/sale/security/sale_security.xml +++ b/addons/sale/security/sale_security.xml @@ -1,6 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> + <record id="group_auto_done_setting" model="res.groups"> + <field name="name">Lock Confirmed Sales</field> + <field name="category_id" ref="base.module_category_hidden"/> + </record> + <record id="group_delivery_invoice_address" model="res.groups"> <field name="name">Addresses in Sales Orders</field> <field name="category_id" ref="base.module_category_hidden"/> diff --git a/addons/sale/static/src/js/tours/sale.js b/addons/sale/static/src/js/tours/sale.js index 4c97755017e..b4b8c9c1a4e 100644 --- a/addons/sale/static/src/js/tours/sale.js +++ b/addons/sale/static/src/js/tours/sale.js @@ -69,13 +69,8 @@ tour.register('sale_tour', { position: "right", id: "form_button_save_clicked" }, { - trigger: ".o_sale_print", - extra_trigger: ".o_sale_order.o_form_readonly", - content: _t("Print this quotation to preview it."), - position: "bottom" -}, { trigger: ".breadcrumb-item:not(.active):last", - extra_trigger: ".o_sale_order [data-value='sent'].btn-primary", + extra_trigger: ".o_sale_order [data-value='draft'].btn-primary", content: _t("Use the breadcrumbs to <b>go back to preceeding screens</b>."), position: "bottom" }, { diff --git a/addons/sale/tests/test_sale_to_invoice.py b/addons/sale/tests/test_sale_to_invoice.py index 97386c83b66..5a2458caf1e 100644 --- a/addons/sale/tests/test_sale_to_invoice.py +++ b/addons/sale/tests/test_sale_to_invoice.py @@ -78,7 +78,7 @@ class TestSaleToInvoice(TestCommonSaleNoChart): # Let's do an invoice for a deposit of 100 payment = self.env['sale.advance.payment.inv'].with_context(self.context).create({ 'advance_payment_method': 'fixed', - 'amount': 100, + 'fixed_amount': 100, 'deposit_account_id': self.account_income.id }) payment.create_invoices() diff --git a/addons/sale/views/res_config_settings_views.xml b/addons/sale/views/res_config_settings_views.xml index 702e54f36fc..f48bf6a07a4 100644 --- a/addons/sale/views/res_config_settings_views.xml +++ b/addons/sale/views/res_config_settings_views.xml @@ -241,10 +241,10 @@ </div> <div class="col-12 col-lg-6 o_setting_box"> <div class="o_setting_left_pane"> - <field name="auto_done_setting"/> + <field name="group_auto_done_setting"/> </div> <div class="o_setting_right_pane"> - <label for="auto_done_setting"/> + <label for="group_auto_done_setting"/> <div class="text-muted"> No longer edit orders once confirmed </div> diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml index 7889d22a07f..262c7ea8698 100644 --- a/addons/sale/views/sale_views.xml +++ b/addons/sale/views/sale_views.xml @@ -255,7 +255,6 @@ attrs="{'invisible': [('authorized_transaction_ids', '=', [])]}"/> <button name="action_quotation_send" string="Send by Email" type="object" states="draft" class="btn-primary"/> <button name="action_quotation_send" type="object" string="Send PRO-FORMA Invoice" groups="sale.group_proforma_sales" class="btn-primary" attrs="{'invisible': ['|', ('state', '!=', 'draft'), ('invoice_count','>=',1)]}" context="{'proforma': True}"/> - <button name="print_quotation" string="Print" type="object" states="draft" class="btn-primary o_sale_print"/> <button name="action_confirm" id="action_confirm" string="Confirm" class="btn-primary" type="object" attrs="{'invisible': [('state', 'not in', ['sent'])]}"/> @@ -263,13 +262,9 @@ string="Confirm" type="object" attrs="{'invisible': [('state', 'not in', ['draft'])]}"/> <button name="action_quotation_send" type="object" string="Send PRO-FORMA Invoice" groups="sale.group_proforma_sales" attrs="{'invisible': ['|', ('state', '=', 'draft'), ('invoice_count','>=',1)]}" context="{'proforma': True}"/> - <button name="print_quotation" string="Print" type="object" states="sent,sale" class="o_sale_print"/> <button name="action_quotation_send" string="Send by Email" type="object" states="sent,sale"/> <button name="action_cancel" states="draft,sent,sale" type="object" string="Cancel"/> <button name="action_draft" states="cancel" type="object" string="Set to Quotation"/> - <button name="action_done" type="object" string="Lock" states="sale" - help="If the sale is locked, you can not modify it anymore. However, you will still be able to invoice or deliver."/> - <button name="action_unlock" type="object" string="Unlock" states="done" groups="sales_team.group_sale_manager"/> <field name="state" widget="statusbar" statusbar_visible="draft,sent,sale"/> </header> <sheet> @@ -307,7 +302,7 @@ <field name="confirmation_date" attrs="{'invisible': [('state', 'in', ['draft', 'sent', 'cancel'])], 'required': [('state', 'in', ['sale', 'done'])]}"/> <field name="pricelist_id" groups="product.group_sale_pricelist"/> <field name="currency_id" invisible="1"/> - <field name="payment_term_id" options="{'no_create': True}"/> + <field name="payment_term_id" options="{'no_open':True,'no_create': True}"/> </group> </group> <notebook> @@ -592,7 +587,10 @@ </templates> </kanban> </field> - <group name="note_group"> + <group name="note_group" col="6"> + <group colspan="4"> + <field name="note" nolabel="1" placeholder="Terms and conditions..."/> + </group> <group class="oe_subtotal_footer oe_right" colspan="2" name="sale_total"> <field name="amount_untaxed" widget='monetary' options="{'currency_field': 'currency_id'}"/> <field name="amount_tax" widget='monetary' options="{'currency_field': 'currency_id'}"/> @@ -601,32 +599,31 @@ </div> <field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator" widget='monetary' options="{'currency_field': 'currency_id'}"/> </group> - <field name="note" placeholder="Terms and conditions..."/> <div class="oe_clear"/> </group> </page> <page string="Other Info" name="other_information"> <group> - <group name="sales_person"> - <field name="date_order" attrs="{'invisible': [('state', 'in', ['sale', 'done', 'cancel'])]}" string="Quotation Date"/> + <group name="sales_person" string="Sales"> + <field name="date_order" attrs="{'invisible': [('state', 'in', ['sale', 'done', 'cancel'])]}" string="Quotation Date" groups="base.group_no_one"/> <field name="user_id"/> <field name="team_id" options="{'no_create': True}"/> <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> - <field name="require_signature"/> - <field name="require_payment"/> + <field name="require_signature" groups="base.group_no_one"/> + <field name="require_payment" groups="base.group_no_one"/> <field name="reference" readonly="1" attrs="{'invisible': [('reference', '=', False)]}"/> - <field name="analytic_account_id" context="{'default_partner_id':partner_invoice_id, 'default_name':name}" attrs="{'readonly': [('invoice_count','!=',0),('state','=','sale')]}" groups="analytic.group_analytic_accounting" force_save="1"/> - </group> - <group name="sale_info"> <field name="client_order_ref"/> + </group> + <group name="sale_info" string="Invoicing"> <field name="fiscal_position_id" options="{'no_create': True}"/> + <field name="analytic_account_id" context="{'default_partner_id':partner_invoice_id, 'default_name':name}" attrs="{'readonly': [('invoice_count','!=',0),('state','=','sale')]}" groups="analytic.group_analytic_accounting" force_save="1"/> <field name="invoice_status" states="sale,done" invisible="1"/> </group> </group> <group> - <group string="Shipping Information" name="sale_shipping"> - <field name="expected_date" groups="sale.group_sale_order_dates"/> - <field name="commitment_date" groups="sale.group_sale_order_dates"/> + <group name="sale_shipping" groups="sale.group_sale_order_dates"> + <field name="expected_date"/> + <field name="commitment_date"/> </group> <group string="Reporting" groups="base.group_no_one"> <group name="technical" colspan="2" class="mb-0"> @@ -659,6 +656,19 @@ </field> </record> + <record id="view_sales_order_auto_done_setting" model="ir.ui.view"> + <field name="name">sale.order.form</field> + <field name="model">sale.order</field> + <field name="inherit_id" ref="sale.view_order_form"/> + <field name="groups_id" eval="[(4, ref('sale.group_auto_done_setting'))]"/> + <field name="arch" type="xml"> + <xpath expr="//form//header//button[@name='action_draft']" position="after"> + <button name="action_done" type="object" string="Lock" states="sale" + help="If the sale is locked, you can not modify it anymore. However, you will still be able to invoice or deliver." groups="sales_team.group_sale_manager"/> + <button name="action_unlock" type="object" string="Unlock" states="done" groups="sales_team.group_sale_manager"/> + </xpath> + </field> + </record> <record id="view_sales_order_filter" model="ir.ui.view"> <field name="name">sale.order.list.select</field> <field name="model">sale.order</field> diff --git a/addons/sale/wizard/sale_make_invoice_advance.py b/addons/sale/wizard/sale_make_invoice_advance.py index 73d5e0b63d7..f7ad90721ed 100644 --- a/addons/sale/wizard/sale_make_invoice_advance.py +++ b/addons/sale/wizard/sale_make_invoice_advance.py @@ -38,6 +38,12 @@ class SaleAdvancePaymentInv(models.TransientModel): return False + @api.model + def _default_currency_id(self): + if self._context.get('active_model') == 'sale.order' and self._context.get('active_id', False): + sale_order = self.env['sale.order'].browse(self._context.get('active_id')) + return sale_order.currency_id + advance_payment_method = fields.Selection([ ('delivered', 'Regular invoice'), ('percentage', 'Down payment (percentage)'), @@ -50,7 +56,9 @@ class SaleAdvancePaymentInv(models.TransientModel): product_id = fields.Many2one('product.product', string='Down Payment Product', domain=[('type', '=', 'service')], default=_default_product_id) count = fields.Integer(default=_count, string='Order Count') - amount = fields.Float('Down Payment Amount', digits='Account', help="The amount to be invoiced in advance, taxes excluded.") + amount = fields.Float('Down Payment Amount', digits='Account', help="The percentage of amount to be invoiced in advance, taxes excluded.") + currency_id = fields.Many2one('res.currency', string='Currency', default=_default_currency_id) + fixed_amount = fields.Monetary('Down Payment Amount(Fixed)', digits='Account', help="The fixed amount to be invoiced in advance, taxes excluded.") deposit_account_id = fields.Many2one("account.account", string="Income Account", domain=[('deprecated', '=', False)], help="Account used for deposits", default=_default_deposit_account_id) deposit_taxes_id = fields.Many2many("account.tax", string="Customer Taxes", help="Taxes used for deposits", default=_default_deposit_taxes_id) @@ -62,13 +70,13 @@ class SaleAdvancePaymentInv(models.TransientModel): return {} def _create_invoice(self, order, so_line, amount): - if self.amount <= 0.00: + if (self.advance_payment_method == 'percentage' and self.amount <= 0.00) or (self.advance_payment_method == 'fixed' and self.fixed_amount <= 0.00): raise UserError(_('The value of the down payment amount must be positive.')) if self.advance_payment_method == 'percentage': amount = order.amount_untaxed * self.amount / 100 name = _("Down payment of %s%%") % (self.amount,) else: - amount = self.amount + amount = self.fixed_amount name = _('Down Payment') invoice_vals = { @@ -121,7 +129,7 @@ class SaleAdvancePaymentInv(models.TransientModel): if self.advance_payment_method == 'percentage': amount = order.amount_untaxed * self.amount / 100 else: - amount = self.amount + amount = self.fixed_amount if self.product_id.invoice_policy != 'order': raise UserError(_('The product used to invoice a down payment should have an invoice policy set to "Ordered quantities". Please update your deposit product to be able to create a deposit invoice.')) if self.product_id.type != 'service': diff --git a/addons/sale/wizard/sale_make_invoice_advance_views.xml b/addons/sale/wizard/sale_make_invoice_advance_views.xml index c2b6a1e2445..03e7ebbff4d 100644 --- a/addons/sale/wizard/sale_make_invoice_advance_views.xml +++ b/addons/sale/wizard/sale_make_invoice_advance_views.xml @@ -24,8 +24,11 @@ invisible="1"/> <label for="amount" attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}"/> <div attrs="{'invisible': [('advance_payment_method', 'not in', ('fixed','percentage'))]}"> + <field name="currency_id" invisible="1"/> + <field name="fixed_amount" + attrs="{'required': [('advance_payment_method', '=', 'fixed')], 'invisible': [('advance_payment_method', '!=','fixed')]}" class="oe_inline"/> <field name="amount" - attrs="{'required': [('advance_payment_method', 'in', ('fixed','percentage'))]}" class="oe_inline" widget="monetary"/> + attrs="{'required': [('advance_payment_method', '=', 'percentage')], 'invisible': [('advance_payment_method', '!=', 'percentage')]}" class="oe_inline"/> <span attrs="{'invisible': [('advance_payment_method', '!=', 'percentage')]}" class="oe_inline">%</span> </div> diff --git a/addons/sale_coupon/views/sale_order_views.xml b/addons/sale_coupon/views/sale_order_views.xml index 16c1f3fc511..04f893a55e6 100644 --- a/addons/sale_coupon/views/sale_order_views.xml +++ b/addons/sale_coupon/views/sale_order_views.xml @@ -4,12 +4,16 @@ <field name="name">sale.order.form.inherit.coupon</field> <field name="model">sale.order</field> <field name="inherit_id" ref="sale.view_order_form" /> + <field name="priority">10</field> <field name="arch" type="xml"> - <xpath expr="//header/button[@name='action_cancel']" position="before"> - <button name="%(sale_coupon.sale_coupon_apply_code_action)d" string="Apply Coupon" type="action" groups="base.group_user" states="draft"/> - </xpath> - <xpath expr="//header/button[@name='action_cancel']" position="before"> - <button name="recompute_coupon_lines" string="Update Promotions" type="object" attrs="{'invisible': [('state', 'in', ['sale', 'done', 'cancel'])]}"/> + <xpath expr="//group[@name='note_group']" position="before"> + <div class="oe_right"> + <button name="%(sale_coupon.sale_coupon_apply_code_action)d" class="btn btn-secondary" + string="Add Coupon" type="action" groups="base.group_user" states="draft"/> + <button name="recompute_coupon_lines" class="btn btn-secondary" string="Add Promotions" + help="When clicked, the content of the order will be checked to detect (and apply) possible promotion programs." + type="object" attrs="{'invisible': [('state', 'in', ['sale', 'done', 'cancel'])]}"/> + </div> </xpath> </field> </record> diff --git a/addons/sale_coupon/wizard/sale_coupon_apply_code.py b/addons/sale_coupon/wizard/sale_coupon_apply_code.py index a31ddab4280..fde5b1bdec6 100644 --- a/addons/sale_coupon/wizard/sale_coupon_apply_code.py +++ b/addons/sale_coupon/wizard/sale_coupon_apply_code.py @@ -9,7 +9,7 @@ class SaleCouponApplyCode(models.TransientModel): _rec_name = 'coupon_code' _description = 'Sales Coupon Apply Code' - coupon_code = fields.Char(string="Coupon", required=True) + coupon_code = fields.Char(string="Code", required=True) def process_coupon(self): """ diff --git a/addons/sale_coupon/wizard/sale_coupon_apply_code_views.xml b/addons/sale_coupon/wizard/sale_coupon_apply_code_views.xml index 41977019bd6..bc146e58ec1 100644 --- a/addons/sale_coupon/wizard/sale_coupon_apply_code_views.xml +++ b/addons/sale_coupon/wizard/sale_coupon_apply_code_views.xml @@ -6,10 +6,12 @@ <field name="arch" type="xml"> <form string="Apply coupon"> <group> - <field name="coupon_code"/> + <group> + <field name="coupon_code"/> + </group> </group> <footer> - <button name="process_coupon" string="Apply Coupon" type="object" class="oe_highlight"/> + <button name="process_coupon" string="Apply" type="object" class="oe_highlight"/> <button special="cancel" string="Cancel"/> </footer> </form> @@ -17,7 +19,7 @@ </record> <record id="sale_coupon_apply_code_action" model="ir.actions.act_window"> - <field name="name">Enter Coupon Code</field> + <field name="name">Enter Promotion or Coupon Code</field> <field name="res_model">sale.coupon.apply.code</field> <field name="view_mode">form</field> <field name="target">new</field> diff --git a/addons/sale_crm/views/sale_order_views.xml b/addons/sale_crm/views/sale_order_views.xml index 5a2a134a73a..749258edf3b 100644 --- a/addons/sale_crm/views/sale_order_views.xml +++ b/addons/sale_crm/views/sale_order_views.xml @@ -14,9 +14,6 @@ <field name="model">sale.order</field> <field name="inherit_id" ref="sale.view_order_form"/> <field name="arch" type="xml"> - <field name="client_order_ref" position="after"> - <field name="tag_ids" widget="many2many_tags"/> - </field> <xpath expr="//group[@name='utm_link']" position="replace"> <group name="utm_link" colspan="2" class="mt-0"> <field name="campaign_id"/> diff --git a/addons/sale_stock/views/sale_order_views.xml b/addons/sale_stock/views/sale_order_views.xml index 7b1d086a7cc..f328b550e3a 100644 --- a/addons/sale_stock/views/sale_order_views.xml +++ b/addons/sale_stock/views/sale_order_views.xml @@ -17,7 +17,8 @@ </button> </xpath> <xpath expr="//group[@name='sale_shipping']" position="attributes"> - <attribute name="groups"></attribute> <!-- Remove the res.group on the group and set it on the field directly--> + <attribute name="groups"></attribute><!-- Remove the res.group on the group and set it on the field directly--> + <attribute name="string">Delivery</attribute> </xpath> <xpath expr="//field[@name='expected_date']" position="before"> <field name="warehouse_id" options="{'no_create': True}" groups="stock.group_stock_multi_warehouses"/> diff --git a/addons/website_sale/static/tests/tours/website_sale_shop_mail.js b/addons/website_sale/static/tests/tours/website_sale_shop_mail.js index d5a998500ed..eb82dcd9840 100644 --- a/addons/website_sale/static/tests/tours/website_sale_shop_mail.js +++ b/addons/website_sale/static/tests/tours/website_sale_shop_mail.js @@ -94,7 +94,7 @@ tour.register('shop_mail', { }, { content: "click on the first email", - trigger: '.o_data_cell:contains("(Ref SO")', + trigger: '.o_data_cell:contains("(Ref S")', }, { content: "check it's the correct email, and the URL is correct too", diff --git a/addons/website_sale/views/sale_order_views.xml b/addons/website_sale/views/sale_order_views.xml index cb31ebe716b..f26ba06f2b4 100644 --- a/addons/website_sale/views/sale_order_views.xml +++ b/addons/website_sale/views/sale_order_views.xml @@ -266,7 +266,7 @@ <attribute name="context">{'display_website': True}</attribute> </field> <field name="team_id" position="after"> - <field name="website_id" groups="website.group_multi_website"/> + <field name="website_id" attrs="{'invisible': [('website_id', '=', False)]}" groups="website.group_multi_website"/> </field> </field> </record> diff --git a/odoo/addons/test_main_flows/static/tests/tours/main_flow.js b/odoo/addons/test_main_flows/static/tests/tours/main_flow.js index f13cfb64e44..519198da5fe 100644 --- a/odoo/addons/test_main_flows/static/tests/tours/main_flow.js +++ b/odoo/addons/test_main_flows/static/tests/tours/main_flow.js @@ -389,12 +389,8 @@ tour.register('main_flow_tour', { trigger: 'label:contains("Untaxed Amount")', // click somewhere else to exit cell focus }, { - trigger: ".o_statusbar_buttons > button.o_sale_print:enabled", - content: _t("<p><b>Print this quotation.</b></p>"), - position: "bottom" -}, { trigger: ".o_statusbar_buttons > button:enabled:contains('Send by Email')", - extra_trigger: ".o_statusbar_status .btn-primary:contains('Quotation Sent')", + extra_trigger: ".o_statusbar_status .btn-primary:contains('Quotation')", content: _t("Try to send it to email"), position: "bottom", }, { |
