[MERGE] forward port branch saas-14 up to 73822d4eaf
This commit is contained in:
@@ -273,6 +273,14 @@ class Lead(models.Model):
|
||||
# ORM override (CRUD, fields_view_get, ...)
|
||||
# ----------------------------------------
|
||||
|
||||
@api.model
|
||||
def name_create(self, name):
|
||||
res = super(Lead, self).name_create(name)
|
||||
|
||||
# update the probability of the lead if the stage is set to update it automatically
|
||||
self.browse(res[0])._onchange_stage_id()
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
# set up context used to find the lead's sales channel which is needed
|
||||
|
||||
@@ -340,7 +340,8 @@
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d')),
|
||||
('date_closed', '=', False)]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Salesperson" name="groupby_salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
@@ -583,10 +584,10 @@
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d')), ('date_closed', '=', False)]"
|
||||
help="Show all opportunities for which the next action date is before today"/>
|
||||
<filter string="Overdue Opportunities" name="overdue_opp"
|
||||
domain="[('date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
domain="[('date_deadline', '<', context_today().strftime('%Y-%m-%d')), ('date_closed', '=', False)]"
|
||||
help="Show all opportunities with a deadline for which the next action date is before today"/>
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
|
||||
@@ -87,9 +87,8 @@ class MailController(http.Controller):
|
||||
record_sudo.sudo(uid).check_access_rule('read')
|
||||
except AccessError:
|
||||
return cls._redirect_to_messaging()
|
||||
|
||||
if record_action['type'] == 'ir.actions.act_url':
|
||||
return werkzeug.utils.redirect(record_action['url'])
|
||||
if record_action['type'] == 'ir.actions.act_url':
|
||||
return werkzeug.utils.redirect(record_action['url'])
|
||||
|
||||
url_params = {
|
||||
'view_type': record_action['view_type'],
|
||||
|
||||
@@ -1475,7 +1475,7 @@ exports.Orderline = Backbone.Model.extend({
|
||||
var recompute_base = function(base_amount, fixed_amount, percent_amount){
|
||||
if(fixed_amount === 0.0 && percent_amount === 0.0)
|
||||
return base_amount;
|
||||
return round_pr((base_amount - fixed_amount) / (1.0 + percent_amount / 100.0), currency_rounding);
|
||||
return (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0);
|
||||
}
|
||||
|
||||
// 4) Iterate the taxes in the reversed sequence order to retrieve the initial base of the computation.
|
||||
@@ -1529,7 +1529,6 @@ exports.Orderline = Backbone.Model.extend({
|
||||
|
||||
taxes_vals.push(tax_vals);
|
||||
});
|
||||
|
||||
return {
|
||||
taxes: taxes_vals,
|
||||
total_excluded: round_pr(total_excluded, currency_rounding_bak),
|
||||
|
||||
@@ -868,7 +868,7 @@ class SaleOrderLine(models.Model):
|
||||
return price
|
||||
else:
|
||||
from_currency = self.order_id.company_id.currency_id
|
||||
return from_currency.compute(product[pricelist_item.base], self.order_id.pricelist_id.currency_id)
|
||||
return from_currency.compute(pricelist_item and product[pricelist_item.base] or product.lst_price, self.order_id.pricelist_id.currency_id)
|
||||
|
||||
@api.multi
|
||||
@api.onchange('product_id')
|
||||
|
||||
@@ -968,7 +968,7 @@ class StockMove(models.Model):
|
||||
self._propagate_split(new_move, qty)
|
||||
# returning the first element of list returned by action_confirm is ok because we checked it wouldn't be exploded (and
|
||||
# thus the result of action_confirm should always be a list of 1 element length)
|
||||
new_move.action_confirm()
|
||||
new_move = new_move.action_confirm()
|
||||
# TDE FIXME: due to action confirm change
|
||||
return new_move.id
|
||||
|
||||
|
||||
@@ -578,6 +578,28 @@ var RTE = Widget.extend({
|
||||
_.defer(function () {
|
||||
self.historyRecordUndo($target, 'activate', true);
|
||||
});
|
||||
|
||||
// To Fix Google Chrome Tripleclick Issue, which selects the ending
|
||||
// whitespace characters (so Tripleclicking then typing text will remove
|
||||
// the whole paragraph instead of its content).
|
||||
// http://stackoverflow.com/questions/38467334/why-does-google-chrome-always-add-space-after-selected-text
|
||||
if ($.browser.chrome === true && event.originalEvent.detail === 3) {
|
||||
var currentSelection = range.create();
|
||||
if (currentSelection.sc.parentNode === currentSelection.ec) {
|
||||
_selectSC(currentSelection);
|
||||
} else if (currentSelection.eo === 0) {
|
||||
var $hasNext = $(currentSelection.sc).parent();
|
||||
while (!$hasNext.next().length && !$hasNext.is('body')) {
|
||||
$hasNext = $hasNext.parent();
|
||||
}
|
||||
if ($hasNext.next()[0] === currentSelection.ec) {
|
||||
_selectSC(currentSelection);
|
||||
}
|
||||
}
|
||||
}
|
||||
function _selectSC(selection) {
|
||||
range.create(selection.sc, selection.so, selection.sc, selection.sc.length).select();
|
||||
}
|
||||
},
|
||||
|
||||
editable: function () {
|
||||
|
||||
@@ -23,7 +23,9 @@ html, body {
|
||||
/* ----- GENERIC LAYOUTING HELPERS ---- */
|
||||
/* table */
|
||||
#wrapwrap, .o_editable {
|
||||
table.table {
|
||||
// Only style editor-made tables (shop/portal/... tables are not supposed to
|
||||
// use table-bordered...)
|
||||
table.table.table-bordered {
|
||||
table-layout: fixed;
|
||||
td {
|
||||
min-width: 20px;
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
import sale_order
|
||||
import sale_quote
|
||||
import product_template
|
||||
import ir_model_fields
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
# !! Attention, this is a hack of a hack, do not try this at home !!
|
||||
# This is done because website_quote defines the website_description
|
||||
# field which is also defined by website_sale, but these two modules
|
||||
# are independent of each other, the part of the ORM that generates
|
||||
# xmlids does not support this case therefore it only creates a single
|
||||
# pair of xmlids for whichever module is installed first, therefore
|
||||
# when uninstalling any of the two modules the website_descripion field
|
||||
# will be deleted from the db and the other module won't be able to
|
||||
# use this field, resulting in a crash and data-loss.
|
||||
# See opw-776464 for more details.
|
||||
#
|
||||
# This is hotfixed by overriding the unlink method so as to not delete
|
||||
# the website_description field if website_quote is uninstalled, it's
|
||||
# an ugly hack but it works and big changes can't be done to the ORM
|
||||
# in stable versions, Odoo v12+ will properly fix this
|
||||
|
||||
|
||||
class IrModelFields(models.Model):
|
||||
_inherit = "ir.model.fields"
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
# Prevent the deletion of the field "website_description"
|
||||
self = self.filtered(
|
||||
lambda rec: not (
|
||||
rec.model in ('product.product', 'product.template') and
|
||||
rec.name == 'website_description'
|
||||
)
|
||||
)
|
||||
return super(IrModelFields, self).unlink()
|
||||
@@ -3,6 +3,7 @@
|
||||
from odoo import api, fields, models, tools, _
|
||||
import odoo.addons.decimal_precision as dp
|
||||
from odoo.tools.translate import html_translate
|
||||
from odoo.tools import float_is_zero
|
||||
|
||||
|
||||
class ProductStyle(models.Model):
|
||||
@@ -139,6 +140,7 @@ class ProductTemplate(models.Model):
|
||||
|
||||
website_price = fields.Float('Website price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_public_price = fields.Float('Website public price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_price_difference = fields.Boolean('Website price difference', compute='_website_price')
|
||||
|
||||
def _website_price(self):
|
||||
# First filter out the ones that have no variant:
|
||||
@@ -148,6 +150,7 @@ class ProductTemplate(models.Model):
|
||||
for template, product in zip(self, self.mapped('product_variant_id')):
|
||||
template.website_price = product.website_price
|
||||
template.website_public_price = product.website_public_price
|
||||
template.website_price_difference = product.website_price_difference
|
||||
|
||||
def _default_website_sequence(self):
|
||||
self._cr.execute("SELECT MIN(website_sequence) FROM %s" % self._table)
|
||||
@@ -188,6 +191,7 @@ class Product(models.Model):
|
||||
|
||||
website_price = fields.Float('Website price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_public_price = fields.Float('Website public price', compute='_website_price', digits=dp.get_precision('Product Price'))
|
||||
website_price_difference = fields.Boolean('Website price difference', compute='_website_price')
|
||||
|
||||
def _website_price(self):
|
||||
qty = self._context.get('quantity', 1.0)
|
||||
@@ -204,6 +208,8 @@ class Product(models.Model):
|
||||
for p, p2 in zip(self, self2):
|
||||
taxes = partner.property_account_position_id.map_tax(p.taxes_id.sudo().filtered(lambda x: x.company_id == company_id))
|
||||
p.website_price = taxes.compute_all(p2.price, pricelist.currency_id, quantity=qty, product=p2, partner=partner)[ret]
|
||||
price_without_pricelist = taxes.compute_all(p.list_price, pricelist.currency_id)[ret]
|
||||
p.website_price_difference = False if float_is_zero(price_without_pricelist - p.website_price, precision_rounding=pricelist.currency_id.rounding) else True
|
||||
p.website_public_price = taxes.compute_all(p2.lst_price, quantity=qty, product=p2, partner=partner)[ret]
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -6,10 +6,10 @@ var ajax = require('web.ajax');
|
||||
$(document).ready(function () {
|
||||
// If option is enable
|
||||
if ($("#checkbox_cgv").length) {
|
||||
var enabling_button = function() {
|
||||
$("#checkbox_cgv").change(function() {
|
||||
$("div.oe_sale_acquirer_button").find('input, button').prop("disabled", !this.checked);
|
||||
};
|
||||
$("#checkbox_cgv").click(enabling_button).each(enabling_button);
|
||||
});
|
||||
$('#checkbox_cgv').trigger('change');
|
||||
}
|
||||
|
||||
// When choosing an acquirer, display its Pay Now button
|
||||
|
||||
@@ -104,7 +104,7 @@
|
||||
</h5>
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price" t-if="product.product_variant_ids">
|
||||
<b>
|
||||
<t t-if="(compute_currency(product.lst_price) - product.website_price ) > 0.01 and website.get_current_pricelist().discount_policy=='without_discount'">
|
||||
<t t-if="product.website_price_difference and website.get_current_pricelist().discount_policy=='without_discount'">
|
||||
<del class="text-danger mr8" style="white-space: nowrap;" t-esc="compute_currency(product.website_public_price)" t-options="{'widget': 'monetary', 'display_currency': website.get_current_pricelist().currency_id, 'from_currency': website.currency_id}" />
|
||||
</t>
|
||||
|
||||
@@ -595,7 +595,7 @@
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price mt16">
|
||||
<h4 class="oe_price_h4 css_editable_mode_hidden">
|
||||
<span class="text-danger oe_default_price" style="text-decoration: line-through; white-space: nowrap;"
|
||||
t-esc="compute_currency(product.website_public_price)" t-options="{'widget': 'monetary', 'display_currency': website.get_current_pricelist().currency_id, 'from_currency': website.currency_id}" t-att-style="'text-decoration: line-through; white-space: nowrap; ' + '' if (compute_currency(product.lst_price) - product.website_price) > 0.01 and website.get_current_pricelist().discount_policy == 'without_discount' else 'display: none;'"
|
||||
t-esc="compute_currency(product.website_public_price)" t-options="{'widget': 'monetary', 'display_currency': website.get_current_pricelist().currency_id, 'from_currency': website.currency_id}" t-att-style="'text-decoration: line-through; white-space: nowrap; ' + '' if product.website_price_difference and website.get_current_pricelist().discount_policy == 'without_discount' else 'display: none;'"
|
||||
/>
|
||||
<b class="oe_price" style="white-space: nowrap;" t-esc="product.website_price" t-options="{'widget': 'monetary', 'display_currency': website.currency_id}"/>
|
||||
<span itemprop="price" style="display:none;" t-esc="product.website_price"/>
|
||||
@@ -1002,7 +1002,7 @@
|
||||
<div class="text-muted hidden-xs" t-field="product.description_sale" />
|
||||
</td>
|
||||
<td class='td-price'>
|
||||
<t t-if="(compute_currency(product.lst_price) - product.website_price) > 0.01 and website.get_current_pricelist().discount_policy=='without_discount'">
|
||||
<t t-if="product.website_price_difference and website.get_current_pricelist().discount_policy=='without_discount'">
|
||||
<del class="text-danger mr8" style="white-space: nowrap;" t-field="product.lst_price" t-options="{'widget': 'monetary','from_currency': product.currency_id, 'display_currency': website.currency_id}" />
|
||||
</t>
|
||||
<span t-field="product.website_price" style="white-space: nowrap;" t-options="{'widget': 'monetary','display_currency': website.currency_id}" />
|
||||
|
||||
@@ -324,7 +324,7 @@ class IrHttp(models.AbstractModel):
|
||||
|
||||
# cache
|
||||
etag = hasattr(request, 'httprequest') and request.httprequest.headers.get('If-None-Match')
|
||||
retag = '"%s"' % hashlib.md5(last_update).hexdigest()
|
||||
retag = '"%s"' % hashlib.md5(content).hexdigest()
|
||||
status = status or (304 if etag == retag else 200)
|
||||
headers.append(('ETag', retag))
|
||||
headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))
|
||||
|
||||
@@ -687,6 +687,30 @@ class TestFields(common.TransactionCase):
|
||||
self.assertEqual(len(discussion.important_messages), 2)
|
||||
self.assertEqual(len(discussion.very_important_messages), 2)
|
||||
|
||||
def test_70_x2many_write(self):
|
||||
discussion = self.env.ref('test_new_api.discussion_0')
|
||||
Message = self.env['test_new_api.message']
|
||||
# There must be 3 messages, 0 important
|
||||
self.assertEqual(len(discussion.messages), 3)
|
||||
self.assertEqual(len(discussion.important_messages), 0)
|
||||
self.assertEqual(len(discussion.very_important_messages), 0)
|
||||
discussion.important_messages = [(0, 0, {
|
||||
'body': 'What is the answer?',
|
||||
'important': True,
|
||||
})]
|
||||
# There must be 4 messages, 1 important
|
||||
self.assertEqual(len(discussion.messages), 4)
|
||||
self.assertEqual(len(discussion.important_messages), 1)
|
||||
self.assertEqual(len(discussion.very_important_messages), 1)
|
||||
discussion.very_important_messages |= Message.new({
|
||||
'body': '42',
|
||||
'important': True,
|
||||
})
|
||||
# There must be 5 messages, 2 important
|
||||
self.assertEqual(len(discussion.messages), 5)
|
||||
self.assertEqual(len(discussion.important_messages), 2)
|
||||
self.assertEqual(len(discussion.very_important_messages), 2)
|
||||
|
||||
|
||||
class TestHtmlField(common.TransactionCase):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user