[MERGE] forward report branch 11.0 up to daf9763e6e

This commit is contained in:
Christophe Simonis
2019-05-17 10:00:11 +02:00
24 changed files with 193 additions and 56 deletions
+1 -1
View File
@@ -371,7 +371,7 @@ class AccountMoveLine(models.Model):
else:
date = partial_line.credit_move_id.date if partial_line.debit_move_id == line else partial_line.debit_move_id.date
rate = line.currency_id.with_context(date=date).rate
amount_residual_currency += sign_partial_line * line.currency_id.round(partial_line.amount * rate)
amount_residual_currency += sign_partial_line * partial_line.amount * rate
#computing the `reconciled` field.
reconciled = False
+17 -8
View File
@@ -888,14 +888,23 @@ class Escpos:
self._raw(PAPER_FULL_CUT)
def cashdraw(self, pin):
""" Send pulse to kick the cash drawer """
if pin == 2:
self._raw(CD_KICK_2)
elif pin == 5:
self._raw(CD_KICK_5)
else:
raise CashDrawerError()
def cashdraw(self, pin, tries=5):
""" Send pulse to kick the cash drawer
With some printers the drawer will not open after one pulse, for this reason we will check
the drawer status up to 'tries' times and send a new pulse if the drawer is still closed. If the
drawer status is open, we will stop sending pulses.
"""
for i in range(tries):
if pin == 2:
self._raw(CD_KICK_2)
elif pin == 5:
self._raw(CD_KICK_5)
else:
raise CashDrawerError()
if i != tries - 1 and not self.get_printer_status()['printer']['drawer_pin_high']:
break
def hw(self, hw):
@@ -11,6 +11,7 @@
'views/fleet_views.xml',
'views/res_config_settings_views.xml',
'views/hr_contract_views.xml',
'security/security.xml',
],
'auto_install': True,
}
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Note for later versions, the group Employee Manager should become contract manager -->
<record id="hr.group_hr_manager" model="res.groups">
<field name="implied_ids" eval="[(4, ref('fleet.fleet_group_manager'))]"/>
</record>
</odoo>
+11 -11
View File
@@ -479,25 +479,25 @@
<field name='name'>119.01 IVA pendiente de pago</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_119_02' model='account.account.tag'>
<field name='name'>119.02 IVA de importación pendiente de pago</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_119_03' model='account.account.tag'>
<field name='name'>119.03 IEPS pendiente de pago</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_119_04' model='account.account.tag'>
<field name='name'>119.04 IEPS pendiente de pago en importación</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_120_01' model='account.account.tag'>
<field name='name'>120.01 Anticipo a proveedores nacional</field>
@@ -917,7 +917,7 @@
<field name='name'>180.01 Crédito mercantil</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_181_01' model='account.account.tag'>
<field name='name'>181.01 Gastos de instalación</field>
@@ -2423,7 +2423,7 @@
<field name='name'>503 Devoluciones, descuentos o bonificaciones sobre compras</field>
<field name='color'>1</field>
<field name='applicability'>accounts</field>
<field name='nature'>D</field>
<field name='nature'>A</field>
</record>
<record id='account_tag_503_01' model='account.account.tag'>
<field name='name'>503.01 Devoluciones, descuentos o bonificaciones sobre compras</field>
@@ -2585,13 +2585,13 @@
<field name='name'>505.01 Costo por venta de activo fijo</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_505_02' model='account.account.tag'>
<field name='name'>505.02 Costo por baja de activo fijo</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_601_01' model='account.account.tag'>
<field name='name'>601.01 Sueldos y salarios</field>
@@ -4781,7 +4781,7 @@
<field name='name'>607.01 Participación de los trabajadores en las utilidades</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_608_01' model='account.account.tag'>
<field name='name'>608.01 Participación en resultados de subsidiarias</field>
@@ -4799,7 +4799,7 @@
<field name='name'>610.01 Participación de los trabajadores en las utilidades diferida</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_611_01' model='account.account.tag'>
<field name='name'>611.01 Impuesto Sobre la renta</field>
@@ -4817,7 +4817,7 @@
<field name='name'>612.01 Gastos no deducibles para CUFIN</field>
<field name='color'>4</field>
<field name='applicability'>accounts</field>
<field name='nature'>A</field>
<field name='nature'>D</field>
</record>
<record id='account_tag_613_01' model='account.account.tag'>
<field name='name'>613.01 Depreciación de edificios</field>
+1 -1
View File
@@ -103,7 +103,7 @@ class MailActivity(models.Model):
('today', 'Today'),
('planned', 'Planned')], 'State',
compute='_compute_state')
recommended_activity_type_id = fields.Many2one('mail.activity.type', string="Recommended Activity Type", readonly=True)
recommended_activity_type_id = fields.Many2one('mail.activity.type', string="Recommended Activity Type")
previous_activity_type_id = fields.Many2one('mail.activity.type', string='Previous Activity Type', readonly=True)
has_recommended_activities = fields.Boolean(
'Next activities available',
+22 -5
View File
@@ -5,6 +5,7 @@ import base64
import datetime
import logging
import psycopg2
import smtplib
import threading
from collections import defaultdict
@@ -150,6 +151,7 @@ class MailMail(models.Model):
if notif_emails:
notifications = self.env['mail.notification'].search([
('mail_message_id', 'in', notif_emails.mapped('mail_message_id').ids),
('res_partner_id', 'in', notif_emails.mapped('recipient_ids').ids),
('is_email', '=', True)])
if mail_sent:
notifications.write({
@@ -301,6 +303,20 @@ class MailMail(models.Model):
})
mail_sent = False
# Update notification in a transient exception state to avoid concurrent
# update in case an email bounces while sending all emails related to current
# mail record.
notifs = self.env['mail.notification'].search([
('is_email', '=', True),
('mail_message_id', 'in', mail.mapped('mail_message_id').ids),
('res_partner_id', 'in', mail.mapped('recipient_ids').ids),
('email_status', 'not in', ('sent', 'canceled'))
])
if notifs:
notifs.sudo().write({
'email_status': 'exception',
})
# build an RFC2822 email.message.Message object and send it without queuing
res = None
for email in email_list:
@@ -348,11 +364,12 @@ class MailMail(models.Model):
'MemoryError while processing mail with ID %r and Msg-Id %r. Consider raising the --limit-memory-hard startup option',
mail.id, mail.message_id)
raise
except psycopg2.Error:
# If an error with the database occurs, chances are that the cursor is unusable.
# This will lead to an `psycopg2.InternalError` being raised when trying to write
# `state`, shadowing the original exception and forbid a retry on concurrent
# update. Let's bubble it.
except (psycopg2.Error, smtplib.SMTPServerDisconnected):
# If an error with the database or SMTP session occurs, chances are that the cursor
# or SMTP session are unusable, causing further errors when trying to save the state.
_logger.exception(
'Exception while processing mail with ID %r and Msg-Id %r.',
mail.id, mail.message_id)
raise
except Exception as e:
failure_reason = tools.ustr(e)
+4 -1
View File
@@ -353,6 +353,7 @@ class MailComposer(models.TransientModel):
- normal mode: return rendered values
/!\ for x2many field, this onchange return command instead of ids
"""
attachment_ids = []
if template_id and composition_mode == 'mass_mail':
template = self.env['mail.template'].browse(template_id)
fields = ['subject', 'body_html', 'email_from', 'reply_to', 'mail_server_id']
@@ -378,7 +379,7 @@ class MailComposer(models.TransientModel):
'res_id': 0,
'type': 'binary', # override default_type from context, possibly meant for another model!
}
values.setdefault('attachment_ids', list()).append(Attachment.create(data_attach).id)
attachment_ids.append(Attachment.create(data_attach).id)
else:
default_values = self.with_context(default_composition_mode=composition_mode, default_model=model, default_res_id=res_id).default_get(['composition_mode', 'model', 'res_id', 'parent_id', 'partner_ids', 'subject', 'body', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'])
values = dict((key, default_values[key]) for key in ['subject', 'body', 'partner_ids', 'email_from', 'reply_to', 'attachment_ids', 'mail_server_id'] if key in default_values)
@@ -391,6 +392,8 @@ class MailComposer(models.TransientModel):
# this force the complete replacement of x2many field with
# command and is compatible with onchange api.v7
values = self._convert_to_write(values)
if attachment_ids:
values.update(attachment_ids=[(6, 0, attachment_ids)])
return {'value': values}
+4 -1
View File
@@ -458,7 +458,7 @@ var Gui = core.Class.extend({
}
} else if (input === '-') {
if (options.firstinput) {
newbuf = '-';
newbuf = '-0';
} else if ( newbuf[0] === '-' ) {
newbuf = newbuf.substring(1,newbuf.length);
} else {
@@ -473,6 +473,9 @@ var Gui = core.Class.extend({
newbuf += input;
}
}
if (newbuf === "-") {
newbuf = "";
}
// End of input buffer at 12 characters.
if (newbuf.length > buffer.length && newbuf.length > 12) {
+1 -1
View File
@@ -1040,7 +1040,7 @@ class SaleOrderLine(models.Model):
result.setdefault(so_line_id, 0.0)
uom = product_uom_map.get(item['product_uom_id'][0])
if so_line.product_uom.category_id == uom.category_id:
qty = uom._compute_quantity(item['unit_amount'], so_line.product_uom)
qty = uom._compute_quantity(item['unit_amount'], so_line.product_uom, rounding_method='HALF-UP')
else:
qty = item['unit_amount']
result[so_line_id] += qty
@@ -825,6 +825,10 @@ registry.gallerySlider = Animation.extend({
destroy: function () {
this._super.apply(this, arguments);
if (!this.$indicator) {
return;
}
this.$prev.prependTo(this.$indicator);
this.$next.appendTo(this.$indicator);
this.$carousel.off('.gallery_slider');
@@ -170,18 +170,12 @@
<template id="references_block" name="Partner References Block">
<t t-if="any([p.website_published for p in partner.implemented_partner_ids])">
<h3 id="references">References</h3>
<div t-foreach="partner.implemented_partner_ids" t-as="reference" class="media">
<t t-if="reference.website_published">
<a class="pull-left" t-attf-href="/customers/#{slug(reference)}">
<span t-field="reference.image_small" t-options='{"widget": "image", "class": "center-block"}'/>
</a>
<div t-foreach="partner.implemented_partner_ids" t-if="reference.website_published" t-as="reference" class="media">
<span t-field="reference.image_small" t-options='{"widget": "image", "class": "center-block pull-left"}'/>
<div class="media-body" style="min-height: 64px;">
<a class="media-heading" t-attf-href="/customers/#{slug(reference)}">
<span t-field="reference.self"/>
</a>
<span class="media-heading" t-field="reference.self"/>
<div t-field='reference.website_short_description'/>
</div>
</t>
</div>
</t>
</template>
@@ -239,4 +239,13 @@
</t>
</template>
<template id="references_block_href" inherit_id="website_crm_partner_assign.references_block" name="Partner References Block">
<xpath expr="//div/span" position="replace">
<a class="pull-left" t-attf-href="/customers/#{slug(reference)}">$0</a>
</xpath>
<xpath expr="//div[hasclass('media-body')]/span" position="replace">
<a class="media-heading" t-attf-href="/customers/#{slug(reference)}">$0</a>
</xpath>
</template>
</odoo>
+21
View File
@@ -0,0 +1,21 @@
Germany, 2019-05-14
ecoservice GbR 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,
Falk Neubert neubert@ecoservice.de https://github.com/FNeu75
List of contributors:
* Christian Engelhardt c.engelhardt@ecoservice.de https://github.com/c-engelhardt-ecoservice
* Christian Schöttke c.schoettke@ecoservice.de https://github.com/cschoettke
* Falk Neubert neubert@ecoservice.de https://github.com/FNeu75
* Gülhan Celik g.celik@ecoservice.de https://github.com/guelhancelik
* Jan Brodersen 4rmitxes@gmail.com https://github.com/Armitxes
* Kamal Prajapati k.prajapati@ecoservice.de https://github.com/kamalprajapati
* Marcel Much m.much@ecoservice.de https://github.com/mmuch
+15 -9
View File
@@ -12,17 +12,24 @@ João Alfredo Gama Batista joao.gama@savoirfairelinux.com https://github.com/joa
List of contributors:
Agathe Mollé agathe.molle@savoirfairelinux.com https://github.com/Ehtaga
Bruno Joliveau bruno.joliveau@savoirfairelinux.com https://github.com/bjoliveau
David Dufresne david.dufresne@savoirfairelinux.com https://github.com/dufresnedavid
Istvan SZALAÏ istvan.szalai@savoirfairelinux.com https://github.com/ventilooo
João Alfredo Gama Batista joao.gama@savoirfairelinux.com https://github.com/joaoalf
Julien Jezequel-Breard julien.jezequel-breard@savoirfairelinux.com https://github.com/jjbreard
Quentin Lavallée-Bourdeau quentin.lavallee@savoirfairelinux.com https://github.com/qtiplb
Rim Ben Dhaou rim.bendhaou@savoirfairelinux.com https://github.com/rimbendhaou
Yasmine El Mrini yasmine.elmrini@savoirfairelinux.com https://github.com/yasmineelmrini
Luis Garcia Ruiz luis.garcia@savoirfairelinux.com https://github.com/eilst
Dimitar Apostolov dimitar.apostolov@savoirfairelinux.com https://github.com/dapostolov-sfl
Pierre Gault pierre.gault@savoirfairelinux.com https://github.com/gaultp
Maroua Romdhane maroua.romdhane@savoirfairelinux.com https://github.com/mromdhane
Jean-François Bolduc jean-francois.bolduc@savoirfairelinux.com https://github.com/fjdrake
Blaise Frison blaise.frison@savoirfairelinux.com https://github.com/bfrison-sfl
Larbi Gharib larbi.gharib@savoirfairelinux.com https://github.com/lgharib
Jananjoy Rajkumar jananjoy.rajkumar@savoirfairelinux.com https://github.com/jananjoy
Yasmine El Mrini yasmine.elmrini@savoirfairelinux.com https://github.com/yasmineelmrini (up to 2019-05-13)
Agathe Mollé agathe.molle@savoirfairelinux.com https://github.com/Ehtaga (up to 2019-05-13)
Bruno Joliveau bruno.joliveau@savoirfairelinux.com https://github.com/bjoliveau (up to 2019-05-13)
David Dufresne david.dufresne@savoirfairelinux.com https://github.com/dufresnedavid (up to 2019-05-13)
Istvan SZALAÏ istvan.szalai@savoirfairelinux.com https://github.com/ventilooo (up to 2019-05-13)
João Alfredo Gama Batista joao.gama@savoirfairelinux.com https://github.com/joaoalf (up to 2019-05-13)
Julien Jezequel-Breard julien.jezequel-breard@savoirfairelinux.com https://github.com/jjbreard (up to 2019-05-13)
Quentin Lavallée-Bourdeau quentin.lavallee@savoirfairelinux.com https://github.com/qtiplb (up to 2019-05-13)
Jérome Boisvert-Chouinard jerome.boisvertchouinard@savoirfairelinux.com https://github.com/jbchouinard (up to 2017-10-17)
Julie Moussu julie.mousse@savoirfairelinux.com https://github.com/JulieSFL (up to 2017-10-17)
Pierre Lamarche pierre.lamarche@savoirfairelinux.com https://github.com/plamarche (up to 2017-10-17)
@@ -34,6 +41,5 @@ Jordi Riera jordi.riera@savoirfairelinux.com https://github.com/foutoucour (up t
Julien Roux julien.roux@savoirfairelinux.com https://github.com/jrouxsfl (up to 2017-05-12)
Loïc Faure-Lacroix loic.lacroix@savoirfairelinux.com https://github.com/llacroix (up to 2017-05-12)
Maxime Chambreuil maxime.chambreuil@savoirfairelinux.com https://github.com/max3903 (up to 2017-05-12)
Pierre Gault pierre.gault@savoirfairelinux.com https://github.com/gaultp (up to 2017-05-12)
Sandy Carter sandy.carter@savoirfairelinux.com https://github.com/bwrsandman (up to 2017-05-12)
Vincent Vinet vincent.vinet@savoirfairelinux.com https://github.com/veloutin (up to 2017-05-12)
+11
View File
@@ -0,0 +1,11 @@
Belgium, 2019-05-09
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,
Geert Janssens geert@kobaltwit.be https://github.com/gjanssens
+12 -2
View File
@@ -119,10 +119,18 @@ def encode_rfc2822_address_header(header_text):
# Header as a string, using an unlimited line length.", the old one
# was "A synonym for Header.encode()." so call encode() directly?
name = Header(pycompat.to_text(name)).encode()
return formataddr((name, email))
# if the from does not follow the (name <addr>),* convention, we might
# try to encode meaningless strings as address, as getaddresses is naive
# note it would also fail on real addresses with non-ascii characters
try:
return formataddr((name, email))
except UnicodeEncodeError:
_logger.warning(_('Failed to encode the address %s\n'
'from mail header:\n%s') % addr, header_text)
return ""
addresses = getaddresses([pycompat.to_native(ustr(header_text))])
return COMMASPACE.join(encode_addr(a) for a in addresses)
return COMMASPACE.join(a for a in (encode_addr(addr) for addr in addresses) if a)
class IrMailServer(models.Model):
@@ -481,6 +489,8 @@ class IrMailServer(models.Model):
# do not quit() a pre-established smtp_session
if not smtp_session:
smtp.quit()
except smtplib.SMTPServerDisconnected:
raise
except Exception as e:
params = (ustr(smtp_server), e.__class__.__name__, ustr(e))
msg = _("Mail delivery failed via SMTP server '%s'.\n%s: %s") % params
+4 -4
View File
@@ -874,9 +874,9 @@ class UsersView(models.Model):
group_multi_company = self.env.ref('base.group_multi_company', False)
if group_multi_company and 'company_ids' in values:
if len(user.company_ids) <= 1 and user.id in group_multi_company.users.ids:
group_multi_company.write({'users': [(3, user.id)]})
user.write({'groups_id': [(3, group_multi_company.id)]})
elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids:
group_multi_company.write({'users': [(4, user.id)]})
user.write({'groups_id': [(4, group_multi_company.id)]})
return user
@api.multi
@@ -887,9 +887,9 @@ class UsersView(models.Model):
if group_multi_company and 'company_ids' in values:
for user in self:
if len(user.company_ids) <= 1 and user.id in group_multi_company.users.ids:
group_multi_company.write({'users': [(3, user.id)]})
user.write({'groups_id': [(3, group_multi_company.id)]})
elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids:
group_multi_company.write({'users': [(4, user.id)]})
user.write({'groups_id': [(4, group_multi_company.id)]})
return res
def _remove_reified_groups(self, values):
+1
View File
@@ -0,0 +1 @@
from . import models
+1
View File
@@ -5,4 +5,5 @@
'name': 'test_convert',
'description': "Data for xml conversion tests",
'version': '0.0.1',
'data': ['ir.model.access.csv'],
}
@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_test_convert_test_model,access_test_convert_test_model,model_test_convert_test_model,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_test_convert_test_model access_test_convert_test_model model_test_convert_test_model 1 1 1 1
+16
View File
@@ -0,0 +1,16 @@
from odoo import api, models
class TestModel(models.Model):
_name = 'test_convert.test_model'
@api.model
def action_test_date(self, today_date):
return True
@api.model
def action_test_time(self, cur_time):
return True
@api.model
def action_test_timezone(self, timezone):
return True
@@ -4,8 +4,10 @@
import collections
import unittest
from lxml import etree as ET
from lxml.builder import E
import odoo
from odoo.tests import common
from odoo.tools.convert import _eval_xml
@@ -16,6 +18,25 @@ class TestEvalXML(common.TransactionCase):
def eval_xml(self, node, obj=None):
return _eval_xml(obj, node, self.env)
def test_function_eval(self):
def id_get(): pass
Obj = collections.namedtuple('Obj', ['module', 'idref', 'id_get'])
obj = Obj('test_convert', {}, id_get)
try:
test_datetime = ET.XML("<function name='action_test_date' model='test_convert.test_model' eval='[datetime.now().strftime(\"%Y-%m-%d %H:%M:%S\")]'/>")
self.eval_xml(node=test_datetime, obj=obj)
test_time = ET.XML("<function name='action_test_time' model='test_convert.test_model' eval='[time.strftime(\"%Y-%m-%d %H:%M:%S\")]'/>")
self.eval_xml(node=test_time, obj=obj)
test_timedelta = ET.XML("<function name='action_test_date' model='test_convert.test_model' eval='[(datetime.today()-timedelta(days=365)).strftime(\"%Y-%m-%d %H:%M:%S\")]'/>")
self.eval_xml(node=test_timedelta, obj=obj)
test_relativedelta = ET.XML("<function name='action_test_date' model='test_convert.test_model' eval='[(datetime.today()+relativedelta(months=3)).strftime(\"%Y-%m-%d %H:%M:%S\")]'/>")
self.eval_xml(node=test_relativedelta, obj=obj)
test_timezone = ET.XML("<function name='action_test_timezone' model='test_convert.test_model' eval='[pytz.timezone(\"Asia/Calcutta\")]'/>")
self.eval_xml(node=test_timezone, obj=obj)
except ValueError as e:
self.fail(e.message)
def test_char(self):
self.assertEqual(
self.eval_xml(Field("foo")),
+4 -3
View File
@@ -179,15 +179,16 @@ def _eval_xml(self, node, env):
elif node.tag == "function":
args = []
a_eval = node.get('eval')
model_str = node.get('model')
# FIXME: should probably be exclusive
if a_eval:
self.idref['ref'] = self.id_get
args = safe_eval(a_eval, self.idref)
idref2 = _get_idref(self, env, model_str, self.idref)
args = safe_eval(a_eval, idref2)
for n in node:
return_val = _eval_xml(self, n, env)
if return_val is not None:
args.append(return_val)
model = env[node.get('model')]
model = env[model_str]
method = node.get('name')
# this one still depends on the old API
return odoo.api.call_kw(model, method, args, {})