diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index e826524eb57..a4bc99b8e7d 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -1,5 +1,6 @@ # -*- coding: utf-8 -*- +from ast import literal_eval from operator import itemgetter import time @@ -427,3 +428,11 @@ class ResPartner(models.Model): return super(ResPartner, self)._commercial_fields() + \ ['debit_limit', 'property_account_payable_id', 'property_account_receivable_id', 'property_account_position_id', 'property_payment_term_id', 'property_supplier_payment_term_id', 'last_time_entries_checked'] + + @api.multi + def action_view_partner_invoices(self): + self.ensure_one() + action = self.env.ref('account.action_invoice_refund_out_tree').read()[0] + action['domain'] = literal_eval(action['domain']) + action['domain'].append(('partner_id', 'child_of', self.id)) + return action diff --git a/addons/account/models/res_config.py b/addons/account/models/res_config.py index eedcbe2b7a7..97362c5f41c 100644 --- a/addons/account/models/res_config.py +++ b/addons/account/models/res_config.py @@ -135,6 +135,6 @@ class AccountConfigSettings(models.TransientModel): company = self.env['res.company'].browse(values.get('company_id')) if company.currency_id.id == values.get('currency_id'): values.pop('currency_id') - if company.accounts_code_digits == vals.get('code_digits'): - vals.pop('code_digits') + if company.accounts_code_digits == values.get('code_digits'): + values.pop('code_digits') return super(AccountConfigSettings, self).create(values) diff --git a/addons/account/views/partner_view.xml b/addons/account/views/partner_view.xml index 84607ad971d..2f028d6f1c2 100644 --- a/addons/account/views/partner_view.xml +++ b/addons/account/views/partner_view.xml @@ -95,6 +95,17 @@ +
+ +
diff --git a/addons/web_editor/static/src/js/base.js b/addons/web_editor/static/src/js/base.js index e4e2fcd6cad..4368f6de274 100644 --- a/addons/web_editor/static/src/js/base.js +++ b/addons/web_editor/static/src/js/base.js @@ -36,7 +36,7 @@ data.ready = function () { // TODO find a better way to find if we are in the backend or not // We don't load translation if we are in the backend since it's already done by the webclient if (!$(document).find('.o_web_client').length) { - load_translations(); + return load_translations(); } }); } else if(all_ready.state() === "resolved") { // can add async template diff --git a/doc/cla/corporate/avoinsystems.md b/doc/cla/corporate/avoinsystems.md index 13209ca53a3..4009dc466e5 100644 --- a/doc/cla/corporate/avoinsystems.md +++ b/doc/cla/corporate/avoinsystems.md @@ -17,3 +17,6 @@ Svante Suominen svante.suominen@avoin.systems https://github.com/svantesuominen Erno Iipponen erno.iipponen@avoin.systems https://github.com/iipponen Tuomo Aura tuomo.aura@avoin.systems https://github.com/tuomoaura Atte Isopuro atte.isopuro@avoin.systems https://github.com/aisopuro +Mikko Närjänen mikko.narjanen@avoin.systems https://github.com/mtnarjan +Toni Pesola toni.pesola@avoin.systems https://github.com/tonipes +Santeri Valjakka santeri.valjakka@avoin.systems https://github.com/hegenator diff --git a/doc/cla/individual/pashute.md b/doc/cla/individual/pashute.md new file mode 100644 index 00000000000..e0dde9fed9a --- /dev/null +++ b/doc/cla/individual/pashute.md @@ -0,0 +1,11 @@ +Israel, 2017-07-06 + +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, + +Moshe Flam pashute@gmail.com https://github.com/pashute diff --git a/doc/reference/orm.rst b/doc/reference/orm.rst index 876d74f65cc..00b284f9cb2 100644 --- a/doc/reference/orm.rst +++ b/doc/reference/orm.rst @@ -45,14 +45,20 @@ Field access ------------ Recordsets provide an "Active Record" interface: model fields can be read and -written directly from the record, but only on singletons (single-record -recordsets). Setting a field's value triggers an update to the database:: +written directly from the record as attributes, but only on singletons +(single-record recordsets). +Field values can also be accessed like dict items, which is more elegant and +safer than ``getattr()`` for dynamic field names. +Setting a field's value triggers an update to the database:: >>> record.name Example Name >>> record.company_id.name Company Name >>> record.name = "Bob" + >>> field = "name" + >>> record[field] + Bob Trying to read or write a field on multiple records will raise an error. diff --git a/odoo/tools/image.py b/odoo/tools/image.py index bacf6a9c999..0ff50967f0a 100644 --- a/odoo/tools/image.py +++ b/odoo/tools/image.py @@ -75,7 +75,7 @@ def image_resize_image(base64_source, size=(1024, 1024), encoding='base64', file if image.size != size: image = image_resize_and_sharpen(image, size) - if image.mode not in ["1", "L", "P", "RGB", "RGBA"]: + if image.mode not in ["1", "L", "P", "RGB", "RGBA"] or (filetype == 'JPEG' and image.mode == 'RGBA'): image = image.convert("RGB") background_stream = StringIO.StringIO() @@ -92,6 +92,7 @@ def image_resize_and_sharpen(image, size, preserve_aspect_ratio=False, factor=2. :param preserve_aspect_ratio: boolean (default: False) :param factor: Sharpen factor (default: 2.0) """ + origin_mode = image.mode if image.mode != 'RGBA': image = image.convert('RGBA') image.thumbnail(size, Image.ANTIALIAS) @@ -102,6 +103,8 @@ def image_resize_and_sharpen(image, size, preserve_aspect_ratio=False, factor=2. # create a transparent image for background and paste the image on it image = Image.new('RGBA', size, (255, 255, 255, 0)) image.paste(resized_image, ((size[0] - resized_image.size[0]) / 2, (size[1] - resized_image.size[1]) / 2)) + if image.mode != origin_mode: + image = image.convert(origin_mode) return image def image_save_for_web(image, fp=None, format=None): diff --git a/odoo/tools/misc.py b/odoo/tools/misc.py index 45ff5e28bbd..448ffdfed43 100644 --- a/odoo/tools/misc.py +++ b/odoo/tools/misc.py @@ -374,6 +374,28 @@ try: except ImportError: xlwt = None +try: + import xlsxwriter + + # add some sanitizations to respect the excel sheet name restrictions + # as the sheet name is often translatable, can not control the input + class PatchedXlsxWorkbook(xlsxwriter.Workbook): + + # TODO when xlsxwriter bump to 0.9.8, add worksheet_class=None parameter instead of kw + def add_worksheet(self, name=None, **kw): + # invalid Excel character: []:*?/\ + name = re.sub(r'[\[\]:*?/\\]', '', name) + + # maximum size is 31 characters + name = name[:31] + return super(PatchedXlsxWorkbook, self).add_worksheet(name, **kw) + + xlsxwriter.Workbook = PatchedXlsxWorkbook + +except ImportError: + xlsxwriter = None + + def to_xml(s): return s.replace('&','&').replace('<','<').replace('>','>')