[MERGE] forward port branch saas-11 up to a0aca47fef
This commit is contained in:
@@ -1145,7 +1145,8 @@ class AccountInvoiceLine(models.Model):
|
||||
|
||||
@api.one
|
||||
@api.depends('price_unit', 'discount', 'invoice_line_tax_ids', 'quantity',
|
||||
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id')
|
||||
'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id',
|
||||
'invoice_id.date_invoice')
|
||||
def _compute_price(self):
|
||||
currency = self.invoice_id and self.invoice_id.currency_id or None
|
||||
price = self.price_unit * (1 - (self.discount or 0.0) / 100.0)
|
||||
@@ -1154,7 +1155,7 @@ class AccountInvoiceLine(models.Model):
|
||||
taxes = self.invoice_line_tax_ids.compute_all(price, currency, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
|
||||
self.price_subtotal = price_subtotal_signed = taxes['total_excluded'] if taxes else self.quantity * price
|
||||
if self.invoice_id.currency_id and self.invoice_id.company_id and self.invoice_id.currency_id != self.invoice_id.company_id.currency_id:
|
||||
price_subtotal_signed = self.invoice_id.currency_id.compute(price_subtotal_signed, self.invoice_id.company_id.currency_id)
|
||||
price_subtotal_signed = self.invoice_id.currency_id.with_context(date=self.invoice_id.date_invoice).compute(price_subtotal_signed, self.invoice_id.company_id.currency_id)
|
||||
sign = self.invoice_id.type in ['in_refund', 'out_refund'] and -1 or 1
|
||||
self.price_subtotal_signed = price_subtotal_signed * sign
|
||||
|
||||
|
||||
@@ -380,7 +380,7 @@ class AccountMoveLine(models.Model):
|
||||
statement_id = fields.Many2one('account.bank.statement', string='Statement',
|
||||
help="The bank statement used for bank reconciliation", index=True, copy=False)
|
||||
reconciled = fields.Boolean(compute='_amount_residual', store=True)
|
||||
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Matching Number")
|
||||
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Matching Number", copy=False)
|
||||
matched_debit_ids = fields.One2many('account.partial.reconcile', 'credit_move_id', String='Matched Debits',
|
||||
help='Debit journal items that are matched with this journal item.')
|
||||
matched_credit_ids = fields.One2many('account.partial.reconcile', 'debit_move_id', String='Matched Credits',
|
||||
@@ -1006,7 +1006,7 @@ class AccountMoveLine(models.Model):
|
||||
rec_move_ids = self.env['account.partial.reconcile']
|
||||
for account_move_line in self:
|
||||
for invoice in account_move_line.payment_id.invoice_ids:
|
||||
if account_move_line in invoice.payment_move_line_ids:
|
||||
if invoice.id == self.env.context.get('invoice_id') and account_move_line in invoice.payment_move_line_ids:
|
||||
account_move_line.payment_id.write({'invoice_ids': [(3, invoice.id, None)]})
|
||||
rec_move_ids += account_move_line.matched_debit_ids
|
||||
rec_move_ids += account_move_line.matched_credit_ids
|
||||
@@ -1325,7 +1325,7 @@ class AccountPartialReconcile(models.Model):
|
||||
company_currency_id = fields.Many2one('res.currency', related='company_id.currency_id', readonly=True,
|
||||
help='Utility field to express amount currency')
|
||||
company_id = fields.Many2one('res.company', related='debit_move_id.company_id', store=True, string='Currency')
|
||||
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Full Reconcile")
|
||||
full_reconcile_id = fields.Many2one('account.full.reconcile', string="Full Reconcile", copy=False)
|
||||
|
||||
def create_exchange_rate_entry(self, aml_to_fix, amount_diff, diff_in_currency, currency, move_date):
|
||||
""" Automatically create a journal entry to book the exchange rate difference.
|
||||
|
||||
@@ -59,7 +59,7 @@ var ShowPaymentLineWidget = form_common.AbstractField.extend({
|
||||
var payment_id = parseInt($(this).attr('payment-id'))
|
||||
if (payment_id !== undefined && payment_id !== NaN){
|
||||
new Model("account.move.line")
|
||||
.call("remove_move_reconcile", [payment_id])
|
||||
.call("remove_move_reconcile", [payment_id, {'invoice_id': self.view.datarecord.id}])
|
||||
.then(function (result) {
|
||||
self.view.reload();
|
||||
});
|
||||
|
||||
@@ -34,24 +34,30 @@ class ResPartner(models.Model):
|
||||
:param name : the partner name to search
|
||||
:param limit : the limit of result to return
|
||||
"""
|
||||
name = '%' + name + '%'
|
||||
excluded_partner_ids = [self.env.user.partner_id.id]
|
||||
self.env.cr.execute("""
|
||||
SELECT
|
||||
U.id as user_id,
|
||||
P.id as id,
|
||||
P.name as name,
|
||||
CASE WHEN B.last_poll IS NULL THEN 'offline'
|
||||
WHEN age(now() AT TIME ZONE 'UTC', B.last_poll) > interval %s THEN 'offline'
|
||||
WHEN age(now() AT TIME ZONE 'UTC', B.last_presence) > interval %s THEN 'away'
|
||||
ELSE 'online'
|
||||
END as im_status
|
||||
FROM res_users U
|
||||
JOIN res_partner P ON P.id = U.partner_id
|
||||
LEFT JOIN bus_presence B ON B.user_id = U.id
|
||||
WHERE P.name ILIKE %s
|
||||
AND P.id NOT IN %s
|
||||
AND U.active = 't'
|
||||
LIMIT %s
|
||||
""", ("%s seconds" % DISCONNECTION_TIMER, "%s seconds" % AWAY_TIMER, name, tuple(excluded_partner_ids), limit))
|
||||
return self.env.cr.dictfetchall()
|
||||
# This method is supposed to be used only in the context of channel creation or
|
||||
# extension via an invite. As both of these actions require the 'create' access
|
||||
# right, we check this specific ACL.
|
||||
if self.env['mail.channel'].check_access_rights('create', raise_exception=False):
|
||||
name = '%' + name + '%'
|
||||
excluded_partner_ids = [self.env.user.partner_id.id]
|
||||
self.env.cr.execute("""
|
||||
SELECT
|
||||
U.id as user_id,
|
||||
P.id as id,
|
||||
P.name as name,
|
||||
CASE WHEN B.last_poll IS NULL THEN 'offline'
|
||||
WHEN age(now() AT TIME ZONE 'UTC', B.last_poll) > interval %s THEN 'offline'
|
||||
WHEN age(now() AT TIME ZONE 'UTC', B.last_presence) > interval %s THEN 'away'
|
||||
ELSE 'online'
|
||||
END as im_status
|
||||
FROM res_users U
|
||||
JOIN res_partner P ON P.id = U.partner_id
|
||||
LEFT JOIN bus_presence B ON B.user_id = U.id
|
||||
WHERE P.name ILIKE %s
|
||||
AND P.id NOT IN %s
|
||||
AND U.active = 't'
|
||||
LIMIT %s
|
||||
""", ("%s seconds" % DISCONNECTION_TIMER, "%s seconds" % AWAY_TIMER, name, tuple(excluded_partner_ids), limit))
|
||||
return self.env.cr.dictfetchall()
|
||||
else:
|
||||
return {}
|
||||
|
||||
@@ -169,6 +169,7 @@ class Attendee(models.Model):
|
||||
'datas_fname': 'invitation.ics',
|
||||
'datas': str(ics_file).encode('base64')})]
|
||||
vals['model'] = None # We don't want to have the mail in the tchatter while in queue!
|
||||
vals['res_id'] = False
|
||||
current_mail = self.env['mail.mail'].browse(mail_id)
|
||||
current_mail.mail_message_id.write(vals)
|
||||
mails_to_send |= current_mail
|
||||
@@ -1357,11 +1358,11 @@ class Meeting(models.Model):
|
||||
super(Meeting, real_meetings).write(values)
|
||||
|
||||
# set end_date for calendar searching
|
||||
if values.get('recurrency') and values.get('end_type', 'count') in ('count', unicode('count')) and \
|
||||
(values.get('rrule_type') or values.get('count') or values.get('start') or values.get('stop')):
|
||||
if any(field in values for field in ['recurrency', 'end_type', 'count', 'rrule_type', 'start', 'stop']):
|
||||
for real_meeting in real_meetings:
|
||||
final_date = real_meeting._get_recurrency_end_date()
|
||||
super(Meeting, real_meeting).write({'final_date': final_date})
|
||||
if real_meeting.recurrency and real_meeting.end_type in ('count', unicode('count')):
|
||||
final_date = real_meeting._get_recurrency_end_date()
|
||||
super(Meeting, real_meeting).write({'final_date': final_date})
|
||||
|
||||
attendees_create = False
|
||||
if values.get('partner_ids', False):
|
||||
@@ -1461,7 +1462,8 @@ class Meeting(models.Model):
|
||||
for r in result:
|
||||
if r['user_id']:
|
||||
user_id = type(r['user_id']) in (tuple, list) and r['user_id'][0] or r['user_id']
|
||||
if user_id == self.env.user.id:
|
||||
partner_id = self.env.user.partner_id.id
|
||||
if user_id == self.env.user.id or partner_id in r.get("partner_ids", []):
|
||||
continue
|
||||
if r['privacy'] == 'private':
|
||||
for f in r.keys():
|
||||
|
||||
@@ -49,8 +49,11 @@ class StockPicking(models.Model):
|
||||
_inherit = 'stock.picking'
|
||||
|
||||
def _default_uom(self):
|
||||
uom_categ_id = self.env.ref('product.product_uom_categ_kgm').id
|
||||
return self.env['product.uom'].search([('category_id', '=', uom_categ_id), ('factor', '=', 1)], limit=1)
|
||||
weight_uom_id = self.env.ref('product.product_uom_kgm', raise_if_not_found=False)
|
||||
if not weight_uom_id:
|
||||
uom_categ_id = self.env.ref('product.product_uom_categ_kgm').id
|
||||
weight_uom_id = self.env['product.uom'].search([('category_id', '=', uom_categ_id), ('factor', '=', 1)], limit=1)
|
||||
return weight_uom_id
|
||||
|
||||
@api.one
|
||||
@api.depends('pack_operation_ids')
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, models
|
||||
from openerp.exceptions import AccessError
|
||||
|
||||
|
||||
class Partner(models.Model):
|
||||
@@ -13,12 +14,10 @@ class Partner(models.Model):
|
||||
""" Extend the mail's static mention suggestions by adding the employees. """
|
||||
suggestions = super(Partner, self).get_static_mention_suggestions()
|
||||
|
||||
employee_group_id = self.env['ir.model.data'].xmlid_to_res_id('base.group_user')
|
||||
self._cr.execute("""
|
||||
SELECT P.id, P.name, P.email
|
||||
FROM res_users U
|
||||
INNER JOIN res_groups_users_rel R ON U.id = R.uid
|
||||
INNER JOIN res_partner P ON P.id = U.partner_id
|
||||
WHERE R.gid = %s AND U.active = 't'""", (employee_group_id,))
|
||||
suggestions.append(self._cr.dictfetchall())
|
||||
return suggestions
|
||||
try:
|
||||
employee_group = self.env.ref('base.group_user')
|
||||
for user in employee_group.users:
|
||||
suggestions.append((user.partner_id.id, user.name, user.email))
|
||||
return suggestions
|
||||
except AccessError:
|
||||
return suggestions
|
||||
|
||||
@@ -113,23 +113,31 @@ class Followers(models.Model):
|
||||
# Modifying followers change access rights to individual documents. As the
|
||||
# cache may contain accessible/inaccessible data, one has to refresh it.
|
||||
#
|
||||
@api.multi
|
||||
def _invalidate_documents(self):
|
||||
""" Invalidate the cache of the documents followed by ``self``. """
|
||||
for record in self:
|
||||
if record.res_id:
|
||||
self.env[record.res_model].invalidate_cache(ids=[record.res_id])
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
res = super(Followers, self).create(vals)
|
||||
self.invalidate_cache()
|
||||
res._invalidate_documents()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
if 'res_model' in vals or 'res_id' in vals:
|
||||
self._invalidate_documents()
|
||||
res = super(Followers, self).write(vals)
|
||||
self.invalidate_cache()
|
||||
self._invalidate_documents()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
res = super(Followers, self).unlink()
|
||||
self.invalidate_cache()
|
||||
return res
|
||||
self._invalidate_documents()
|
||||
return super(Followers, self).unlink()
|
||||
|
||||
_sql_constraints = [
|
||||
('mail_followers_res_partner_res_model_id_uniq', 'unique(res_model,res_id,partner_id)', 'Error, a partner cannot follow twice the same object.'),
|
||||
|
||||
@@ -701,6 +701,13 @@ class Message(models.Model):
|
||||
message_id = tools.generate_tracking_message_id('private')
|
||||
return message_id
|
||||
|
||||
@api.multi
|
||||
def _invalidate_documents(self):
|
||||
""" Invalidate the cache of the documents followed by ``self``. """
|
||||
for record in self:
|
||||
if record.res_id:
|
||||
self.env[record.model].invalidate_cache(ids=[record.res_id])
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
# coming from mail.js that does not have pid in its values
|
||||
@@ -717,6 +724,7 @@ class Message(models.Model):
|
||||
values['record_name'] = self._get_record_name(values)
|
||||
|
||||
message = super(Message, self).create(values)
|
||||
message._invalidate_documents()
|
||||
|
||||
if not self.env.context.get('message_create_from_mail_mail'):
|
||||
message._notify(force_send=self.env.context.get('mail_notify_force_send', True),
|
||||
@@ -730,6 +738,14 @@ class Message(models.Model):
|
||||
self.check_access_rule('read')
|
||||
return super(Message, self).read(fields=fields, load=load)
|
||||
|
||||
@api.multi
|
||||
def write(self, vals):
|
||||
if 'model' in vals or 'res_id' in vals:
|
||||
self._invalidate_documents()
|
||||
res = super(Message, self).write(vals)
|
||||
self._invalidate_documents()
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
# cascade-delete attachments that are directly attached to the message (should only happen
|
||||
@@ -738,6 +754,7 @@ class Message(models.Model):
|
||||
self.mapped('attachment_ids').filtered(
|
||||
lambda attach: attach.res_model == self._name and (attach.res_id in self.ids or attach.res_id == 0)
|
||||
).unlink()
|
||||
self._invalidate_documents()
|
||||
return super(Message, self).unlink()
|
||||
|
||||
#------------------------------------------------------
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import babel
|
||||
import base64
|
||||
import copy
|
||||
import datetime
|
||||
@@ -18,6 +19,20 @@ from odoo.exceptions import UserError
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def format_date(env, date, pattern=False):
|
||||
date = datetime.datetime.strptime(date[:10], tools.DEFAULT_SERVER_DATE_FORMAT)
|
||||
lang_code = env.context.get('lang') or 'en_US'
|
||||
if not pattern:
|
||||
lang = env['res.lang']._lang_get(lang_code)
|
||||
pattern = lang.date_format
|
||||
try:
|
||||
locale = babel.Locale.parse(lang_code)
|
||||
pattern = tools.posix_to_ldml(pattern, locale=locale)
|
||||
return babel.dates.format_date(date, format=pattern, locale=locale)
|
||||
except babel.core.UnknownLocaleError:
|
||||
return date.strftime(pattern)
|
||||
|
||||
|
||||
def format_tz(env, dt, tz=False, format=False):
|
||||
record_user_timestamp = env.user.sudo().with_context(tz=tz or env.user.sudo().tz or 'UTC')
|
||||
timestamp = datetime.datetime.strptime(dt, tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
@@ -350,6 +365,7 @@ class MailTemplate(models.Model):
|
||||
for record in records:
|
||||
res_to_rec[record.id] = record
|
||||
variables = {
|
||||
'format_date': lambda date, format=False, context=self._context: format_dae(self.env, date, format),
|
||||
'format_tz': lambda dt, tz=False, format=False, context=self._context: format_tz(self.env, dt, tz, format),
|
||||
'user': self.env.user,
|
||||
'ctx': self._context, # context kw would clash with mako internals
|
||||
|
||||
@@ -12,6 +12,17 @@ class TestMailFeatures(TestMail):
|
||||
alias = self.env['mail.alias'].with_context(alias_model_name='mail.channel').create({'alias_name': 'b4r+_#_R3wl$$'})
|
||||
self.assertEqual(alias.alias_name, 'b4r+_-_r3wl-', 'Disallowed chars should be replaced by hyphens')
|
||||
|
||||
def test_10_cache_invalidation(self):
|
||||
""" Test that creating a mail-thread record does not invalidate the whole cache. """
|
||||
# make a new record in cache
|
||||
record = self.env['res.partner'].new({'name': 'Brave New Partner'})
|
||||
self.assertTrue(record.name)
|
||||
|
||||
# creating a mail-thread record should not invalidate the whole cache
|
||||
self.env['res.partner'].create({'name': 'Actual Partner'})
|
||||
self.assertTrue(record.name)
|
||||
|
||||
|
||||
@mute_logger('odoo.addons.mail.models.mail_mail')
|
||||
def test_needaction(self):
|
||||
na_emp1_base = self.env['mail.message'].sudo(self.user_employee)._needaction_count(domain=[])
|
||||
|
||||
@@ -268,3 +268,8 @@ class TestMailMessage(TestMail):
|
||||
msg.toggle_message_starred()
|
||||
self.assertFalse(msg.starred)
|
||||
self.assertTrue(msg_emp.starred)
|
||||
|
||||
def test_60_cache_invalidation(self):
|
||||
msg_cnt = len(self.group_pigs.message_ids)
|
||||
self.group_pigs.message_post(body='Hi!', subject='test')
|
||||
self.assertEqual(len(self.group_pigs.message_ids), msg_cnt + 1)
|
||||
|
||||
@@ -394,7 +394,6 @@ class Task(models.Model):
|
||||
self.partner_id = self.project_id.partner_id
|
||||
self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
|
||||
else:
|
||||
self.partner_id = False
|
||||
self.stage_id = False
|
||||
|
||||
@api.onchange('user_id')
|
||||
|
||||
@@ -25,7 +25,12 @@ class PurchaseOrder(models.Model):
|
||||
amount_untaxed = amount_tax = 0.0
|
||||
for line in order.order_line:
|
||||
amount_untaxed += line.price_subtotal
|
||||
amount_tax += line.price_tax
|
||||
# FORWARDPORT UP TO 10.0
|
||||
if order.company_id.tax_calculation_rounding_method == 'round_globally':
|
||||
taxes = line.taxes_id.compute_all(line.price_unit, line.order_id.currency_id, line.product_qty, product=line.product_id, partner=line.order_id.partner_id)
|
||||
amount_tax += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
|
||||
else:
|
||||
amount_tax += line.price_tax
|
||||
order.update({
|
||||
'amount_untaxed': order.currency_id.round(amount_untaxed),
|
||||
'amount_tax': order.currency_id.round(amount_tax),
|
||||
|
||||
@@ -27,7 +27,13 @@ class SaleOrder(models.Model):
|
||||
amount_untaxed = amount_tax = 0.0
|
||||
for line in order.order_line:
|
||||
amount_untaxed += line.price_subtotal
|
||||
amount_tax += line.price_tax
|
||||
# FORWARDPORT UP TO 10.0
|
||||
if order.company_id.tax_calculation_rounding_method == 'round_globally':
|
||||
price = line.price_unit * (1 - (line.discount or 0.0) / 100.0)
|
||||
taxes = line.tax_id.compute_all(price, line.order_id.currency_id, line.product_uom_qty, product=line.product_id, partner=line.order_id.partner_id)
|
||||
amount_tax += sum(t.get('amount', 0.0) for t in taxes.get('taxes', []))
|
||||
else:
|
||||
amount_tax += line.price_tax
|
||||
order.update({
|
||||
'amount_untaxed': order.pricelist_id.currency_id.round(amount_untaxed),
|
||||
'amount_tax': order.pricelist_id.currency_id.round(amount_tax),
|
||||
|
||||
@@ -262,7 +262,7 @@
|
||||
|
||||
<div class="form-group field-password">
|
||||
<label for="password" class="control-label">Password</label>
|
||||
<input type="password" name="password" id="password" class="form-control" required="required" t-att-autofocus="'autofocus' if login else None"/>
|
||||
<input type="password" name="password" id="password" class="form-control" required="required" t-att-autofocus="'autofocus' if login else None" maxlength="4096"/>
|
||||
</div>
|
||||
|
||||
<p class="alert alert-danger" t-if="error">
|
||||
|
||||
@@ -134,7 +134,7 @@ dom.hasProgrammaticStyle = function (node) {
|
||||
};
|
||||
dom.mergeFilter = function (prev, cur, parent) {
|
||||
// merge text nodes
|
||||
if (prev && (dom.isText(prev) || ("H1 H2 H3 H4 H5 H6 LI P".indexOf(prev.tagName) !== -1 && prev !== cur.parentNode)) && dom.isText(cur)) {
|
||||
if (prev && (dom.isText(prev) || (['H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'LI', 'P'].indexOf(prev.tagName) !== -1 && prev !== cur.parentNode)) && dom.isText(cur)) {
|
||||
return true;
|
||||
}
|
||||
if (prev && prev.tagName === "P" && dom.isText(cur)) {
|
||||
|
||||
@@ -112,7 +112,6 @@ table.table_desc tr td {
|
||||
height: 100%;
|
||||
.item {
|
||||
height: 100%;
|
||||
background-size: cover;
|
||||
}
|
||||
.carousel-control {
|
||||
cursor: pointer;
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<t t-call="web.layout">
|
||||
<t t-set="html_data" t-value="{
|
||||
'lang': lang and lang.replace('_', '-'),
|
||||
'data-website-id': website.id if (editable or translatable) and website else None,
|
||||
'data-website-id': website.id if website else None,
|
||||
'data-editable': '1' if editable else None,
|
||||
'data-translatable': '1' if translatable else None,
|
||||
'data-edit_translations': '1' if edit_translations else None,
|
||||
|
||||
@@ -302,6 +302,27 @@ class Website(models.Model):
|
||||
DEFAULT_PAYMENT_TERM = 'account.account_payment_term_immediate'
|
||||
return self.env.ref(DEFAULT_PAYMENT_TERM, False).id or partner.property_payment_term_id.id
|
||||
|
||||
@api.multi
|
||||
def _prepare_sale_order_values(self, partner, pricelist):
|
||||
self.ensure_one()
|
||||
affiliate_id = request.session.get('affiliate_id')
|
||||
salesperson_id = affiliate_id if self.env['res.users'].sudo().browse(affiliate_id).exists() else request.website.salesperson_id.id
|
||||
addr = partner.address_get(['delivery', 'invoice'])
|
||||
values = {
|
||||
'partner_id': partner.id,
|
||||
'pricelist_id': pricelist.id,
|
||||
'payment_term_id': self.sale_get_payment_term(partner),
|
||||
'team_id': self.salesteam_id.id,
|
||||
'partner_invoice_id': addr['invoice'],
|
||||
'partner_shipping_id': addr['delivery'],
|
||||
'user_id': salesperson_id or self.salesperson_id.id,
|
||||
}
|
||||
company = self.company_id or pricelist.company_id
|
||||
if company:
|
||||
values['company_id'] = company.id
|
||||
|
||||
return values
|
||||
|
||||
@api.multi
|
||||
def sale_get_order(self, force_create=False, code=None, update_pricelist=False, force_pricelist=False):
|
||||
""" Return the current sale order after mofications specified by params.
|
||||
@@ -337,25 +358,8 @@ class Website(models.Model):
|
||||
# create so if needed
|
||||
if not sale_order and (force_create or code):
|
||||
# TODO cache partner_id session
|
||||
affiliate_id = request.session.get('affiliate_id')
|
||||
if self.env['res.users'].sudo().browse(affiliate_id).exists():
|
||||
salesperson_id = affiliate_id
|
||||
else:
|
||||
salesperson_id = request.website.salesperson_id.id
|
||||
addr = partner.address_get(['delivery', 'invoice'])
|
||||
so_data = {
|
||||
'partner_id': partner.id,
|
||||
'pricelist_id': pricelist_id,
|
||||
'payment_term_id': self.sale_get_payment_term(partner),
|
||||
'team_id': self.salesteam_id.id,
|
||||
'partner_invoice_id': addr['invoice'],
|
||||
'partner_shipping_id': addr['delivery'],
|
||||
'user_id': salesperson_id or self.salesperson_id.id,
|
||||
}
|
||||
company = self.company_id or self.env['product.pricelist'].browse(pricelist_id).sudo().company_id
|
||||
if company:
|
||||
so_data['company_id'] = company.id
|
||||
|
||||
pricelist = self.env['product.pricelist'].browse(pricelist_id).sudo()
|
||||
so_data = self._prepare_sale_order_values(partner, pricelist)
|
||||
sale_order = self.env['sale.order'].sudo().create(so_data)
|
||||
|
||||
# set fiscal position
|
||||
|
||||
@@ -584,7 +584,8 @@
|
||||
|
||||
<template id="product_price">
|
||||
<div itemprop="offers" itemscope="itemscope" itemtype="http://schema.org/Offer" class="product_price mt16">
|
||||
<h4 class="oe_price_h4 css_editable_mode_hidden">
|
||||
<h4 class="oe_price_h4 css_editable_mode_hidden decimal_precision"
|
||||
t-att-data-precision="user_id.partner_id.property_product_pricelist.currency_id.rounding">
|
||||
<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;'"
|
||||
/>
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
import models
|
||||
@@ -0,0 +1 @@
|
||||
import website
|
||||
@@ -0,0 +1,15 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, models
|
||||
|
||||
class Website(models.Model):
|
||||
_inherit = 'website'
|
||||
|
||||
@api.multi
|
||||
def _prepare_sale_order_values(self, partner, pricelist):
|
||||
self.ensure_one()
|
||||
values = super(Website, self)._prepare_sale_order_values(partner, pricelist)
|
||||
if values['company_id']:
|
||||
warehouses = self.env['stock.warehouse'].sudo().search([('company_id', '=', values['company_id'])], limit=1)
|
||||
if warehouses:
|
||||
values['warehouse_id'] = warehouses.id
|
||||
return values
|
||||
@@ -476,7 +476,7 @@ class Slide(models.Model):
|
||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
for slide in self.filtered(lambda slide: slide.website_published and slide.channel_id.publish_template_id):
|
||||
publish_template = slide.channel_id.publish_template_id
|
||||
html_body = publish_template.with_context({'base_url': base_url}).render_template(publish_template.body_html, 'slide.slide', slide.id)
|
||||
html_body = publish_template.with_context(base_url=base_url).render_template(publish_template.body_html, 'slide.slide', slide.id)
|
||||
subject = publish_template.render_template(publish_template.subject, 'slide.slide', slide.id)
|
||||
slide.channel_id.message_post(
|
||||
subject=subject,
|
||||
@@ -487,7 +487,7 @@ class Slide(models.Model):
|
||||
@api.one
|
||||
def send_share_email(self, email):
|
||||
base_url = self.env['ir.config_parameter'].get_param('web.base.url')
|
||||
return self.channel_id.share_template_id.with_context({'email': email, 'base_url': base_url}).send_mail(self.id)
|
||||
return self.channel_id.share_template_id.with_context(email=email, base_url=base_url).send_mail(self.id)
|
||||
|
||||
# --------------------------------------------------
|
||||
# Parsing methods
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
Honduras, 2017-01-12
|
||||
|
||||
Business Analytics Consulting Group S.A. de C.V. agrees to the terms of the Odoo Corporate Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
Leandro Gabriel Di Pasquale dipasquale@bacgroup.net https://github.com/leodipasquale
|
||||
|
||||
List of contributors:
|
||||
|
||||
Allan Daniel Aguilar Archaga aguilar@bacgroup.net https://github.com/allanaguilar
|
||||
Cesar Alejandro Rodriguez Castillo rodriguez@bacgroup.net https://github.com/hondurasopen
|
||||
Dewin Javier Garcia Armijo garcia@bacgroup.net https://github.com/dewingarcia
|
||||
Jose Reynaldo Cabrera Ayala cabrera@bacgroup.net https://github.com/jcabrera1990
|
||||
Leandro Gabriel Di Pasquale dipasquale@bacgroup.net https://github.com/leodipasquale
|
||||
Edwin Omar McClellan Salinas mcclellan@bacgroup.net https://github.com/prometeomcclellan
|
||||
Salvatore Josue Trimarchi Pinto trimarchi@bacgroup.net https://github.com/salvatoretrimarchi
|
||||
@@ -0,0 +1,11 @@
|
||||
canada, 2017/1/3
|
||||
|
||||
I hereby agree to the terms of the Odoo Individual Contributor License
|
||||
Agreement v1.0.
|
||||
|
||||
I declare that I am authorized and able to make this agreement and sign this
|
||||
declaration.
|
||||
|
||||
Signed,
|
||||
|
||||
kevin hua boogerlad@gmail.com https://github.com/boogerlad
|
||||
@@ -265,13 +265,13 @@ class Property(models.Model):
|
||||
elif value <= 0 and operator == '>=':
|
||||
operator = '<'
|
||||
include_zero = True
|
||||
elif value <= 0 and operator == '>':
|
||||
elif value < 0 and operator == '>':
|
||||
operator = '<='
|
||||
include_zero = True
|
||||
elif value >= 0 and operator == '<=':
|
||||
operator = '>'
|
||||
include_zero = True
|
||||
elif value >= 0 and operator == '<':
|
||||
elif value > 0 and operator == '<':
|
||||
operator = '>='
|
||||
include_zero = True
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import json
|
||||
import locale
|
||||
import logging
|
||||
import re
|
||||
@@ -69,7 +70,7 @@ class Lang(models.Model):
|
||||
'Provided as the thousand separator in each case.')
|
||||
for lang in self:
|
||||
try:
|
||||
if not all(isinstance(x, int) for x in safe_eval(lang.grouping)):
|
||||
if not all(isinstance(x, int) for x in json.loads(lang.grouping)):
|
||||
raise ValidationError(warning)
|
||||
except Exception:
|
||||
raise ValidationError(warning)
|
||||
|
||||
+1
-1
@@ -820,7 +820,7 @@ more details.
|
||||
passing the `csrf=False` parameter to the `route` decorator.
|
||||
""", request.httprequest.path)
|
||||
|
||||
raise werkzeug.exceptions.BadRequest('Invalid CSRF Token')
|
||||
raise werkzeug.exceptions.BadRequest('Session expired (invalid CSRF token)')
|
||||
|
||||
r = self._call_function(**self.params)
|
||||
if not r:
|
||||
|
||||
Reference in New Issue
Block a user