[MERGE] forward port branch saas-15 up to d44cd77884

This commit is contained in:
Christophe Simonis
2018-06-12 16:31:54 +02:00
28 changed files with 147 additions and 27 deletions
+9 -2
View File
@@ -1507,6 +1507,13 @@ class AccountInvoiceLine(models.Model):
return accounts['income']
return accounts['expense']
def _set_currency(self):
company = self.invoice_id.company_id
currency = self.invoice_id.currency_id
if company and currency:
if company.currency_id != currency:
self.price_unit = self.price_unit * currency.with_context(dict(self._context or {}, date=self.invoice_id.date_invoice)).rate
def _set_taxes(self):
""" Used in on_change to set taxes and price."""
if self.invoice_id.type in ('out_invoice', 'out_refund'):
@@ -1525,8 +1532,10 @@ class AccountInvoiceLine(models.Model):
prec = self.env['decimal.precision'].precision_get('Product Price')
if not self.price_unit or float_compare(self.price_unit, self.product_id.standard_price, precision_digits=prec) == 0:
self.price_unit = fix_price(self.product_id.standard_price, taxes, fp_taxes)
self._set_currency()
else:
self.price_unit = fix_price(self.product_id.lst_price, taxes, fp_taxes)
self._set_currency()
@api.onchange('product_id')
def _onchange_product_id(self):
@@ -1575,8 +1584,6 @@ class AccountInvoiceLine(models.Model):
domain['uom_id'] = [('category_id', '=', product.uom_id.category_id.id)]
if company and currency:
if company.currency_id != currency:
self.price_unit = self.price_unit * currency.with_context(dict(self._context or {}, date=self.invoice_id.date_invoice)).rate
if self.uom_id and self.uom_id.id != product.uom_id.id:
self.price_unit = product.uom_id._compute_price(self.price_unit, self.uom_id)
+1 -1
View File
@@ -14,7 +14,7 @@ class ResConfigSettings(models.TransientModel):
'account.journal',
related='company_id.currency_exchange_journal_id',
string="Exchange Gain or Loss Journal",
domain=[('type', '=', 'general')],
domain="[('company_id', '=', company_id), ('type', '=', 'general')]",
help='The accounting journal where automatic exchange differences will be registered')
has_chart_of_accounts = fields.Boolean(compute='_compute_has_chart_of_accounts', string='Company has a chart of accounts')
chart_template_id = fields.Many2one('account.chart.template', string='Template',
@@ -248,14 +248,16 @@
<span><t t-esc="dashboard.account_balance"/></span>
</div>
</div>
<div class="row" name="latest_statement" t-if="dashboard.last_balance != dashboard.account_balance">
<div class="col-xs-6">
<span title="Latest Statement">Latest Statement</span>
<t t-if="dashboard.last_balance != dashboard.account_balance">
<div class="row" name="latest_statement">
<div class="col-xs-6">
<span title="Latest Statement">Latest Statement</span>
</div>
<div class="col-xs-6 text-right">
<span><t t-esc="dashboard.last_balance"/></span>
</div>
</div>
<div class="col-xs-6 text-right">
<span><t t-esc="dashboard.last_balance"/></span>
</div>
</div>
</t>
</div>
</t>
<t t-name="JournalBodySalePurchase" id="account.JournalBodySalePurchase">
@@ -50,7 +50,11 @@ class AccountBankStatementImport(models.TransientModel):
# Create the bank statements
statement_ids, notifications = self._create_bank_statements(stmts_vals)
# Now that the import worked out, set it as the bank_statements_source of the journal
journal.bank_statements_source = 'file_import'
if journal.bank_statements_source != 'file_import':
# Use sudo() because only 'account.group_account_manager'
# has write access on 'account.journal', but 'account.group_account_user'
# must be able to import bank statement files
journal.sudo().bank_statements_source = 'file_import'
# Finally dispatch to reconciliation interface
action = self.env.ref('account.action_bank_reconcile_bank_statements')
return {
+1 -1
View File
@@ -10,7 +10,7 @@ class ResPartner(models.Model):
def _compute_event_count(self):
for partner in self:
partner.event_count = self.env['event.event'].search_count([('registration_ids.partner_id', 'child_of', partner.ids)])
partner.event_count = self.env['event.event'].sudo().search_count([('registration_ids.partner_id', 'child_of', partner.ids)])
@api.multi
def action_event_view(self):
+2
View File
@@ -80,6 +80,8 @@ class MrpBom(models.Model):
def onchange_product_tmpl_id(self):
if self.product_tmpl_id:
self.product_uom_id = self.product_tmpl_id.uom_id.id
if self.product_id.product_tmpl_id != self.product_tmpl_id:
self.product_id = False
@api.onchange('routing_id')
def onchange_routing_id(self):
+1
View File
@@ -358,6 +358,7 @@ class MrpWorkorder(models.Model):
move_line = production_move.move_line_ids.filtered(lambda x: x.lot_id.id == self.final_lot_id.id)
if move_line:
move_line.product_uom_qty += self.qty_producing
move_line.qty_done += self.qty_producing
else:
move_line.create({'move_id': production_move.id,
'product_id': production_move.product_id.id,
+1 -1
View File
@@ -190,7 +190,7 @@
<label for="duration"/>
<div>
<button style="pointer-events: none;" class="oe_inline label label-default">
<field name="duration" widget="mrp_time_counter"/>
<field name="duration" widget="mrp_time_counter" help="Time the currently logged user spent on this workorder."/>
</button>
</div>
</group>
+2 -2
View File
@@ -38,8 +38,8 @@ class PosSession(models.Model):
paid=order.amount_paid,
))
order.action_pos_order_done()
orders = session.order_ids.filtered(lambda order: order.state in ['invoiced', 'done'])
orders.sudo()._reconcile_payments()
orders_to_reconcile = session.order_ids.filtered(lambda order: order.state in ['invoiced', 'done'] and order.partner_id)
orders_to_reconcile.sudo()._reconcile_payments()
config_id = fields.Many2one(
'pos.config', string='Point of Sale',
+1 -1
View File
@@ -140,7 +140,7 @@ class PortalWizardUser(models.TransientModel):
if wizard_user.partner_id.company_id:
company_id = wizard_user.partner_id.company_id.id
else:
company_id = self.env['res.company']._company_default_get('res.users')
company_id = self.env['res.company']._company_default_get('res.users').id
user_portal = wizard_user.sudo().with_context(company_id=company_id)._create_user()
else:
user_portal = user
+2
View File
@@ -222,6 +222,7 @@ class ProductProduct(models.Model):
for supplier_info in self.seller_ids:
if supplier_info.name.id == self._context.get('partner_id'):
self.code = supplier_info.product_code or self.default_code
break
else:
self.code = self.default_code
@@ -230,6 +231,7 @@ class ProductProduct(models.Model):
for supplier_info in self.seller_ids:
if supplier_info.name.id == self._context.get('partner_id'):
product_name = supplier_info.product_name or self.default_code
break
self.partner_ref = '%s%s' % (self.code and '[%s] ' % self.code or '', product_name)
else:
self.partner_ref = self.name_get()[0][1]
+1 -1
View File
@@ -1,4 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import test_variants, test_uom, test_pricelist, test_product_pricelist
from . import test_seller, test_variants, test_uom, test_pricelist, test_product_pricelist
+28
View File
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.tests.common import TransactionCase
class TestSeller(TransactionCase):
def setUp(self):
super(TestSeller, self).setUp()
self.product_service = self.env.ref('product.product_product_2')
self.product_service.default_code = 'DEFCODE'
self.asustec = self.env.ref('base.res_partner_1')
self.camptocamp = self.env.ref('base.res_partner_12')
def test_10_sellers(self):
self.product_service.write({'seller_ids': [
(0, 0, {'name': self.asustec.id, 'product_code': 'ASUCODE'}),
(0, 0, {'name': self.camptocamp.id, 'product_code': 'C2CCODE'}),
]})
default_code = self.product_service.code
self.assertEqual("DEFCODE", default_code, "Default code not used in product name")
context_code = self.product_service\
.with_context(partner_id=self.camptocamp.id)\
.code
self.assertEqual('C2CCODE', context_code, "Partner's code not used in product name with context set")
+1 -1
View File
@@ -753,7 +753,7 @@ class Task(models.Model):
groups = [new_group] + groups
for group_name, group_method, group_data in groups:
if group_name in ['customer', 'portal']:
if group_name == 'customer':
continue
group_data['has_button_access'] = True
@@ -59,12 +59,16 @@ var EventRegistrationForm = Widget.extend({
$button.attr('disabled', true);
return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) {
var $modal = $(modal);
$modal.modal({backdrop: 'static', keyboard: false});
$modal.find('.modal-body > div').removeClass('container'); // retrocompatibility - REMOVE ME in master / saas-19
$modal.insertAfter($form).modal();
$modal.on('click', '.js_goto_event', function () {
$modal.modal('hide');
$button.prop('disabled', false);
});
$modal.on('click', '.close', function () {
$button.prop('disabled', false);
});
});
}
},
+5
View File
@@ -422,6 +422,11 @@ class Post(models.Model):
raise KarmaError('User karma not sufficient to post an image or link.')
return content
@api.constrains('parent_id')
def _check_parent_id(self):
if not self._check_recursion():
raise ValidationError(_('You cannot create recursive forum posts.'))
@api.model
def create(self, vals):
if 'content' in vals and vals.get('forum_id'):
+7 -3
View File
@@ -20,13 +20,17 @@ class WebsiteLivechat(http.Controller):
@http.route('/livechat/channel/<model("im_livechat.channel"):channel>', type='http', auth='public', website=True)
def channel_rating(self, channel, **kw):
# get the last 100 ratings and the repartition per grade
ratings = request.env['rating.rating'].search([('res_model', '=', 'mail.channel'), ('res_id', 'in', channel.sudo().channel_ids.ids)], order='create_date desc', limit=100)
repartition = channel.sudo().channel_ids.rating_get_grades()
domain = [
('res_model', '=', 'mail.channel'), ('res_id', 'in', channel.sudo().channel_ids.ids),
('consumed', '=', True), ('rating', '>=', 1),
]
ratings = request.env['rating.rating'].search(domain, order='create_date desc', limit=100)
repartition = channel.sudo().channel_ids.rating_get_grades(domain=domain)
# compute percentage
percentage = dict.fromkeys(['great', 'okay', 'bad'], 0)
for grade in repartition:
percentage[grade] = repartition[grade] * 100 / sum(repartition.values()) if sum(repartition.values()) else 0
percentage[grade] = round(repartition[grade] * 100.0 / sum(repartition.values()), 1) if sum(repartition.values()) else 0
# the value dict to render the template
values = {
+17
View File
@@ -0,0 +1,17 @@
China, 2018-04-28
Shine IT 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,
Tony Gu tony@openerp.cn https://github.com/digitalsatori
List of contributors:
Tony Gu tony@openerp.cn https://github.com/digitalsatori
Joshua Jan joshua@openerp.cn https://github.com/joshuajan
Xiao Guo xerxes@openerp.cn https://github.com/xerxesnoPT
+11
View File
@@ -0,0 +1,11 @@
France, 2018-06-07
I here by 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,
Flavien CHÊNE me@flavienchene.fr https://github.com/Zelwak
+11
View File
@@ -0,0 +1,11 @@
France, 2018-05-24
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,
Martin Duvergey martin.duvergey@gpsante.fr https://github.com/mduvergey
+11
View File
@@ -0,0 +1,11 @@
Belgium, 2018-05-17
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,
Rod Schouteden rod@schout-it.be https://github.com/schout-it
+1 -1
View File
@@ -46,7 +46,7 @@ On Linux, using an installer
.. code-block:: console
$ python3 /usr/bin/odoo.py -d <database_name> -i web_enterprise --stop-after-init
$ python3 /usr/bin/odoo-bin -d <database_name> -i web_enterprise --stop-after-init
* You should be able to connect to your Odoo Enterprise instance using your usual mean of identification.
You can then link your database with your Odoo Enterprise Subscription by entering the code you received
+4 -2
View File
@@ -22,6 +22,8 @@ from odoo.tools import ustr, pycompat
_logger = logging.getLogger(__name__)
_test_logger = logging.getLogger('odoo.tests')
SMTP_TIMEOUT = 60
class MailDeliveryException(except_orm):
"""Specific exception subclass for mail delivery errors"""
@@ -225,9 +227,9 @@ class IrMailServer(models.Model):
"You could use STARTTLS instead. "
"If SSL is needed, an upgrade to Python 2.6 on the server-side "
"should do the trick."))
connection = smtplib.SMTP_SSL(smtp_server, smtp_port)
connection = smtplib.SMTP_SSL(smtp_server, smtp_port, timeout=SMTP_TIMEOUT)
else:
connection = smtplib.SMTP(smtp_server, smtp_port)
connection = smtplib.SMTP(smtp_server, smtp_port, timeout=SMTP_TIMEOUT)
connection.set_debuglevel(smtp_debug)
if smtp_encryption == 'starttls':
# starttls() will perform ehlo() if needed first
+5
View File
@@ -790,6 +790,11 @@ class GroupsView(models.Model):
xml = E.field(E.group(*(xml1), col="2"), E.group(*(xml2), col="4"), name="groups_id", position="replace")
xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS"))
xml_content = etree.tostring(xml, pretty_print=True, encoding="unicode")
if not view.check_access_rights('write', raise_exception=False):
# erp manager has the rights to update groups/users but not
# to modify ir.ui.view
if self.env.user.has_group('base.group_erp_manager'):
view = view.sudo()
new_context = dict(view._context)
new_context.pop('install_mode_data', None) # don't set arch_fs for this computed view
+1 -1
View File
@@ -152,7 +152,7 @@
<field name="name">res.partner.rule.private.employee</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="domain_force">
[('type', '!=', 'private')]
['|', ('type', '!=', 'private'), ('type', '=', False)]
</field>
<field name="groups" eval="[
(4, ref('base.group_user')),
+1 -1
View File
@@ -3940,7 +3940,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
vals = self.copy_data(default)[0]
# To avoid to create a translation in the lang of the user, copy_translation will do it
new = self.with_context(lang=None).create(vals)
self.copy_translations(new)
self.with_context(from_copy_translation=True).copy_translations(new)
return new
@api.multi
+4 -1
View File
@@ -360,7 +360,10 @@ class GeventServer(CommonServer):
def start(self):
import gevent
from gevent.wsgi import WSGIServer
try:
from gevent.pywsgi import WSGIServer
except ImportError:
from gevent.wsgi import WSGIServer
if os.name == 'posix':
+1
View File
@@ -70,6 +70,7 @@ def py2exe_options():
'dist_dir': 'dist',
'packages': [
'asynchat', 'asyncore',
'BeautifulSoup',
'commands',
'dateutil',
'decimal',