[MERGE] forward port branch saas-11.3 up to 37bca6ae50

This commit is contained in:
Christophe Simonis
2019-05-17 12:19:55 +02:00
24 changed files with 170 additions and 49 deletions
+1 -1
View File
@@ -531,7 +531,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
@@ -106,7 +106,8 @@
<filter string="Payment Method" name="paymentmethod" domain="[]" context="{'group_by': 'payment_method_id'}"/>
<filter string="State" name="state" domain="[]" context="{'group_by': 'state'}"/>
<filter string="Currency" name="currency" domain="[]" context="{'group_by': 'currency_id'}" groups="base.group_multi_currency"/>
<filter string="Company" name="company" domain="[]" context="{'group_by': 'company_id'}" groups="base.group_multi_company"/>
<!-- company_id is not stored (cdca5d4751a979f6b694f02d88160843f81a77f5). But we can't break structure-->
<filter string="Company" name="company" domain="[]" context="{'group_by': 'company_id'}" groups="base.group_multi_company" invisible="1" />
</search>
</field>
</record>
+17 -8
View File
@@ -894,14 +894,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
@@ -146,7 +146,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',
+4 -1
View File
@@ -376,6 +376,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']
@@ -401,7 +402,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)
@@ -414,6 +415,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
@@ -1284,7 +1284,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
@@ -930,6 +930,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');
@@ -164,13 +164,9 @@
<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-if="reference.website_published" t-as="reference" class="media mt-3">
<a t-attf-href="/customers/#{slug(reference)}">
<span t-field="reference.image_medium" t-options='{"widget": "image", "class": "d-block mr-3"}'/>
</a>
<span t-field="reference.image_medium" t-options='{"widget": "image", "class": "d-block mr-3"}'/>
<div class="media-body" style="min-height: 64px;">
<a t-attf-href="/customers/#{slug(reference)}">
<span t-field="reference.self"/>
</a>
<span t-field="reference.self"/>
<div t-field='reference.website_short_description'/>
</div>
</div>
@@ -234,4 +234,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 t-attf-href="/customers/#{slug(reference)}">$0</a>
</xpath>
<xpath expr="//div[hasclass('media-body')]/span" position="replace">
<a 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
+10 -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):
+4 -4
View File
@@ -1113,9 +1113,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
@@ -1126,9 +1126,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, {})