[IMP] website_quote: UOM onchange, respect pricelists on SO, layout fixes
* add on_change_uom behaviour to quote templates lines * fixes frontend layout issues * use action_button_confirm instead of signal_workflow when confirming the quote in the frontend to trigger all events properly * adapt prices using pricelist on onchange_template_id on sale orders
This commit is contained in:
committed by
Olivier Dony
parent
8b5a1f1035
commit
5047f4ecad
@@ -72,7 +72,7 @@ class sale_quote(http.Controller):
|
||||
if token != order.access_token:
|
||||
return request.website.render('website.404')
|
||||
attachments=sign and [('signature.png', sign.decode('base64'))] or []
|
||||
order_obj.signal_workflow(request.cr, SUPERUSER_ID, [order_id], 'order_confirm', context=request.context)
|
||||
order_obj.action_button_confirm(request.cr, SUPERUSER_ID, [order_id], context=request.context)
|
||||
message = _('Order signed by %s') % (signer,)
|
||||
self.__message_post(message, order_id, type='comment', subtype='mt_comment', attachments=attachments)
|
||||
return True
|
||||
|
||||
@@ -64,8 +64,8 @@ class sale_quote_line(osv.osv):
|
||||
'discount': 0.0,
|
||||
'sequence': 10,
|
||||
}
|
||||
def on_change_product_id(self, cr, uid, ids, product, context=None):
|
||||
vals = {}
|
||||
def on_change_product_id(self, cr, uid, ids, product, uom_id=None, context=None):
|
||||
vals, domain = {}, []
|
||||
product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
|
||||
name = product_obj.name
|
||||
if product_obj.description_sale:
|
||||
@@ -75,8 +75,22 @@ class sale_quote_line(osv.osv):
|
||||
'product_uom_id': product_obj.uom_id.id,
|
||||
'website_description': product_obj and (product_obj.quote_description or product_obj.website_description) or '',
|
||||
'name': name,
|
||||
'product_uom_id': uom_id or product_obj.uom_id.id,
|
||||
})
|
||||
return {'value': vals}
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
if vals['product_uom_id'] != product_obj.uom_id.id:
|
||||
selected_uom = uom_obj.browse(cr, uid, vals['product_uom_id'], context=context)
|
||||
new_price = uom_obj._compute_price(cr, uid, product_obj.uom_id.id, vals['price_unit'], vals['product_uom_id'])
|
||||
vals['price_unit'] = new_price
|
||||
if not uom_id:
|
||||
domain = {'product_uom_id': [('category_id', '=', product_obj.uom_id.category_id.id)]}
|
||||
return {'value': vals, 'domain': domain}
|
||||
|
||||
def product_uom_change(self, cr, uid, ids, product, uom_id, context=None):
|
||||
context = context or {}
|
||||
if not uom_id:
|
||||
return {'value': {'price_unit': 0.0, 'uom_id': False}}
|
||||
return self.on_change_product_id(cr, uid, ids, product, uom_id=uom_id, context=context)
|
||||
|
||||
def _inject_quote_description(self, cr, uid, values, context=None):
|
||||
values = dict(values or {})
|
||||
@@ -169,7 +183,7 @@ class sale_order(osv.osv):
|
||||
'url': '/quote/%s' % (quote.id)
|
||||
}
|
||||
|
||||
def onchange_template_id(self, cr, uid, ids, template_id, partner=False, fiscal_position=False, context=None):
|
||||
def onchange_template_id(self, cr, uid, ids, template_id, partner=False, fiscal_position=False, pricelist_id=False, context=None):
|
||||
if not template_id:
|
||||
return True
|
||||
|
||||
@@ -177,6 +191,8 @@ class sale_order(osv.osv):
|
||||
context = dict(context or {})
|
||||
context['lang'] = self.pool['res.partner'].browse(cr, uid, partner, context).lang
|
||||
|
||||
pricelist_obj = self.pool['product.pricelist']
|
||||
|
||||
lines = [(5,)]
|
||||
quote_template = self.pool.get('sale.quote.template').browse(cr, uid, template_id, context=context)
|
||||
for line in quote_template.quote_line:
|
||||
@@ -185,11 +201,16 @@ class sale_order(osv.osv):
|
||||
line.product_uom_id.id, line.name, partner, False, True, time.strftime('%Y-%m-%d'),
|
||||
False, fiscal_position, True, context)
|
||||
data = res.get('value', {})
|
||||
if pricelist_id:
|
||||
price = pricelist_obj.price_get(cr, uid, [pricelist_id], line.product_id.id, 1, context=context)[pricelist_id]
|
||||
else:
|
||||
price = line.price_unit
|
||||
|
||||
if 'tax_id' in data:
|
||||
data['tax_id'] = [(6, 0, data['tax_id'])]
|
||||
data.update({
|
||||
'name': line.name,
|
||||
'price_unit': line.price_unit,
|
||||
'price_unit': price,
|
||||
'discount': line.discount,
|
||||
'product_uom_qty': line.product_uom_qty,
|
||||
'product_id': line.product_id.id,
|
||||
@@ -270,18 +291,34 @@ class sale_quote_option(osv.osv):
|
||||
_defaults = {
|
||||
'quantity': 1,
|
||||
}
|
||||
def on_change_product_id(self, cr, uid, ids, product, context=None):
|
||||
vals = {}
|
||||
|
||||
def on_change_product_id(self, cr, uid, ids, product, uom_id=None, context=None):
|
||||
vals, domain = {}, []
|
||||
product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
|
||||
name = product_obj.name
|
||||
if product_obj.description_sale:
|
||||
name += '\n' + product_obj.description_sale
|
||||
vals.update({
|
||||
'price_unit': product_obj.list_price,
|
||||
'website_description': product_obj.product_tmpl_id.quote_description,
|
||||
'name': product_obj.name,
|
||||
'uom_id': product_obj.product_tmpl_id.uom_id.id,
|
||||
'name': name,
|
||||
'uom_id': uom_id or product_obj.uom_id.id,
|
||||
})
|
||||
if product_obj.description_sale:
|
||||
vals['name'] += '\n'+product_obj.description_sale
|
||||
return {'value': vals}
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
if vals['uom_id'] != product_obj.uom_id.id:
|
||||
selected_uom = uom_obj.browse(cr, uid, vals['uom_id'], context=context)
|
||||
new_price = uom_obj._compute_price(cr, uid, product_obj.uom_id.id,
|
||||
vals['price_unit'], vals['uom_id'])
|
||||
vals['price_unit'] = new_price
|
||||
if not uom_id:
|
||||
domain = {'uom_id': [('category_id', '=', product_obj.uom_id.category_id.id)]}
|
||||
return {'value': vals, 'domain': domain}
|
||||
|
||||
def product_uom_change(self, cr, uid, ids, product, uom_id, context=None):
|
||||
if not uom_id:
|
||||
return {'value': {'price_unit': 0.0, 'uom_id': False}}
|
||||
return self.on_change_product_id(cr, uid, ids, product, uom_id=uom_id, context=context)
|
||||
|
||||
|
||||
class sale_order_option(osv.osv):
|
||||
_name = "sale.order.option"
|
||||
@@ -302,20 +339,34 @@ class sale_order_option(osv.osv):
|
||||
_defaults = {
|
||||
'quantity': 1,
|
||||
}
|
||||
def on_change_product_id(self, cr, uid, ids, product, context=None):
|
||||
vals = {}
|
||||
def on_change_product_id(self, cr, uid, ids, product, uom_id=None, context=None):
|
||||
vals, domain = {}, []
|
||||
if not product:
|
||||
return vals
|
||||
product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context)
|
||||
name = product_obj.name
|
||||
if product_obj.description_sale:
|
||||
name += '\n'+product_obj.description_sale
|
||||
vals.update({
|
||||
'price_unit': product_obj.list_price,
|
||||
'website_description': product_obj and (product_obj.quote_description or product_obj.website_description),
|
||||
'name': product_obj.name,
|
||||
'uom_id': product_obj.product_tmpl_id.uom_id.id,
|
||||
'name': name,
|
||||
'uom_id': uom_id or product_obj.uom_id.id,
|
||||
})
|
||||
if product_obj.description_sale:
|
||||
vals['name'] += '\n'+product_obj.description_sale
|
||||
return {'value': vals}
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
if vals['uom_id'] != product_obj.uom_id.id:
|
||||
selected_uom = uom_obj.browse(cr, uid, vals['uom_id'], context=context)
|
||||
new_price = uom_obj._compute_price(cr, uid, product_obj.uom_id.id, vals['price_unit'], vals['uom_id'])
|
||||
vals['price_unit'] = new_price
|
||||
if not uom_id:
|
||||
domain = {'uom_id': [('category_id', '=', product_obj.uom_id.category_id.id)]}
|
||||
return {'value': vals, 'domain': domain}
|
||||
|
||||
def product_uom_change(self, cr, uid, ids, product, uom_id, context=None):
|
||||
context = context or {}
|
||||
if not uom_id:
|
||||
return {'value': {'price_unit': 0.0, 'uom_id': False}}
|
||||
return self.on_change_product_id(cr, uid, ids, product, uom_id=uom_id, context=context)
|
||||
|
||||
class product_template(osv.Model):
|
||||
_inherit = "product.template"
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
}
|
||||
|
||||
/* mobile preview: the left menu overlaps the quote */
|
||||
@media (max-width: 768px) {
|
||||
@media (max-width: 992px) {
|
||||
.o_website_quote .bs-sidebar {
|
||||
position: relative;
|
||||
}
|
||||
@@ -42,11 +42,16 @@
|
||||
padding-left: 30px;
|
||||
font-size: 90%;
|
||||
}
|
||||
@media (max-width: 991px) {
|
||||
.o_website_quote .bs-sidebar {
|
||||
position: static;
|
||||
}
|
||||
|
||||
/* Table Styling */
|
||||
.wq-table {
|
||||
table-layout: auto !important;
|
||||
}
|
||||
|
||||
.wq-table th, .wq-table td {
|
||||
border-top: none !important;
|
||||
}
|
||||
|
||||
@media (min-width: 992px) {
|
||||
.o_website_quote .bs-sidebar .nav > .active > ul {
|
||||
display: block;
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<template id="pricing" name="Price">
|
||||
<section id="quote">
|
||||
<h1 class="page-header">Pricing</h1>
|
||||
<table class="table">
|
||||
<table class="table wq-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Products</th>
|
||||
@@ -47,24 +47,22 @@
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
<strong class="text-right">
|
||||
<div t-field="line.price_unit"
|
||||
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'
|
||||
t-att-style="line.discount and 'text-decoration: line-through' or ''"
|
||||
t-att-class="line.discount and 'text-danger' or ''"/>
|
||||
<!-- TODO: apply monetary widget formating -->
|
||||
<div t-if="line.discount">
|
||||
<t t-esc="'%.2f' % ((1-line.discount / 100.0) * line.price_unit)"/>
|
||||
</div>
|
||||
</strong>
|
||||
<div t-field="line.price_unit"
|
||||
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'
|
||||
t-att-style="line.discount and 'text-decoration: line-through' or ''"
|
||||
t-att-class="(line.discount and 'text-danger' or '') + 'text-right'"/>
|
||||
<!-- TODO: apply monetary widget formating -->
|
||||
<div t-if="line.discount">
|
||||
<t t-esc="'%.2f' % ((1-line.discount / 100.0) * line.price_unit)"/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="text-right"
|
||||
t-field="line.price_subtotal"
|
||||
t-field-options='{"widget": "monetary", "display_currency": "quotation.pricelist_id.currency_id"}'/>
|
||||
</td>
|
||||
<td>
|
||||
<a t-attf-href="./update_line/#{ line.id }/?order_id=#{ quotation.id }&unlink=True&token=#{ quotation.access_token }" class="mb8 js_update_line_json pull-right hidden-print" t-if="line.option_line_id">
|
||||
<td class="text-center">
|
||||
<a t-attf-href="./update_line/#{ line.id }/?order_id=#{ quotation.id }&unlink=True&token=#{ quotation.access_token }" class="mb8 js_update_line_json hidden-print" t-if="line.option_line_id">
|
||||
<span class="fa fa-trash-o"></span>
|
||||
</a>
|
||||
</td>
|
||||
@@ -449,7 +447,9 @@
|
||||
<template id="optional_products">
|
||||
<section t-if="quotation.options" id="options">
|
||||
<h1 class="page-header">Options</h1>
|
||||
<table class="table table-hover">
|
||||
</section>
|
||||
<section id="options">
|
||||
<table class="table table-hover wq-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Products</th>
|
||||
@@ -463,7 +463,7 @@
|
||||
<tr t-foreach="quotation.options" t-as="option">
|
||||
<t t-if="not option.line_id">
|
||||
<td>
|
||||
<div t-field="option.product_id.name"/>
|
||||
<div t-field="option.product_id.name_template"/>
|
||||
</td>
|
||||
<td>
|
||||
<div t-field="option.name"/>
|
||||
@@ -484,9 +484,9 @@
|
||||
</div>
|
||||
</strong>
|
||||
</td>
|
||||
<td>
|
||||
<a t-if="quotation.state in ['draft', 'sent']" t-attf-href="/quote/add_line/#{ option.id }/#{ quotation.id }/#{ quotation.access_token }" class="mb8 hidden-print">
|
||||
<span class="fa fa-shopping-cart" title="Buy the product"/>
|
||||
<td class="text-center" t-if="quotation.state in ['draft', 'sent']">
|
||||
<a t-attf-href="/quote/add_line/#{ option.id }/#{ quotation.id }/#{ quotation.access_token }" class="mb8 hidden-print">
|
||||
<span class="fa fa-shopping-cart"/>
|
||||
</a>
|
||||
</td>
|
||||
</t>
|
||||
|
||||
@@ -31,10 +31,10 @@
|
||||
<field name="quote_viewed" invisible="1"/>
|
||||
<field name="options">
|
||||
<tree string="Sales Quotation Template Lines" editable="bottom">
|
||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||
<field name="product_id" on_change="on_change_product_id(product_id, uom_id)"/>
|
||||
<field name="name"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom_id" groups="product.group_uom"/>
|
||||
<field name="uom_id" groups="product.group_uom" on_change="product_uom_change(product_id, uom_id)"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
<field name="website_description" invisible="1"/>
|
||||
@@ -42,8 +42,8 @@
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='payment_term']" position="after">
|
||||
<field name="template_id" on_change="onchange_template_id(template_id, partner_id, fiscal_position)"/>
|
||||
<xpath expr="//field[@name='project_id']" position="before">
|
||||
<field name="template_id" on_change="onchange_template_id(template_id, partner_id, fiscal_position, pricelist_id)"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='client_order_ref']" position="after">
|
||||
<field name="website_description" invisible="1"/>
|
||||
@@ -79,7 +79,7 @@
|
||||
<form string="Sales Quotation Template Lines">
|
||||
<group>
|
||||
<group>
|
||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||
<field name="product_id" on_change="on_change_product_id(product_id, product_uom_id)"/>
|
||||
<label for="product_uom_qty"/>
|
||||
<div>
|
||||
<field
|
||||
@@ -103,10 +103,10 @@
|
||||
</form>
|
||||
<tree string="Sales Quotation Template Lines" editable="bottom">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||
<field name="product_id" on_change="on_change_product_id(product_id, product_uom_id)"/>
|
||||
<field name="name"/>
|
||||
<field name="product_uom_qty"/>
|
||||
<field name="product_uom_id" groups="product.group_uom"/>
|
||||
<field name="product_uom_id" groups="product.group_uom" on_change="product_uom_change(product_id, product_uom_id"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="website_description" invisible="1"/>
|
||||
@@ -116,10 +116,10 @@
|
||||
<page string="Suggested Products">
|
||||
<field name="options">
|
||||
<tree string="Sales Quotation Template Lines" editable="bottom">
|
||||
<field name="product_id" on_change="on_change_product_id(product_id)"/>
|
||||
<field name="product_id" on_change="on_change_product_id(product_id, uom_id)"/>
|
||||
<field name="name"/>
|
||||
<field name="quantity"/>
|
||||
<field name="uom_id" groups="product.group_uom"/>
|
||||
<field name="uom_id" groups="product.group_uom" on_change="product_uom_change(product_id, uom_id)"/>
|
||||
<field name="price_unit"/>
|
||||
<field name="discount" groups="sale.group_discount_per_so_line"/>
|
||||
<field name="website_description" invisible="1"/>
|
||||
|
||||
Reference in New Issue
Block a user