summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/delivery/models/sale_order.py41
-rw-r--r--addons/delivery/views/delivery_view.xml14
-rw-r--r--addons/delivery/wizard/choose_delivery_carrier.py47
-rw-r--r--addons/delivery/wizard/choose_delivery_carrier_views.xml27
-rw-r--r--addons/event_sale/views/sale_order_views.xml2
-rw-r--r--addons/sale/data/ir_sequence_data.xml2
-rw-r--r--addons/sale/models/res_config_settings.py2
-rw-r--r--addons/sale/models/sale.py21
-rw-r--r--addons/sale/security/sale_security.xml5
-rw-r--r--addons/sale/static/src/js/tours/sale.js7
-rw-r--r--addons/sale/tests/test_sale_to_invoice.py2
-rw-r--r--addons/sale/views/res_config_settings_views.xml4
-rw-r--r--addons/sale/views/sale_views.xml46
-rw-r--r--addons/sale/wizard/sale_make_invoice_advance.py16
-rw-r--r--addons/sale/wizard/sale_make_invoice_advance_views.xml5
-rw-r--r--addons/sale_coupon/views/sale_order_views.xml14
-rw-r--r--addons/sale_coupon/wizard/sale_coupon_apply_code.py2
-rw-r--r--addons/sale_coupon/wizard/sale_coupon_apply_code_views.xml8
-rw-r--r--addons/sale_crm/views/sale_order_views.xml3
-rw-r--r--addons/sale_stock/views/sale_order_views.xml3
-rw-r--r--addons/website_sale/static/tests/tours/website_sale_shop_mail.js2
-rw-r--r--addons/website_sale/views/sale_order_views.xml2
-rw-r--r--odoo/addons/test_main_flows/static/tests/tours/main_flow.js6
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','&gt;=',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','&gt;=',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",
}, {