diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index d75d0bbe546..f7b45c2d037 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -1156,7 +1156,10 @@ class AccountInvoiceLine(models.Model): doc = etree.XML(res['arch']) for node in doc.xpath("//field[@name='product_id']"): if self._context['type'] in ('in_invoice', 'in_refund'): - node.set('domain', "[('purchase_ok', '=', True)]") + # Hack to fix the stable version 8.0 -> saas-12 + # purchase_ok will be moved from purchase to product in master #13271 + if 'purchase_ok' in self.env['product.template']._fields: + node.set('domain', "[('purchase_ok', '=', True)]") else: node.set('domain', "[('sale_ok', '=', True)]") res['arch'] = etree.tostring(doc) diff --git a/addons/account_asset/views/account_asset_view.xml b/addons/account_asset/views/account_asset_view.xml index e2c5bf87360..1ae2ef9a0af 100644 --- a/addons/account_asset/views/account_asset_view.xml +++ b/addons/account_asset/views/account_asset_view.xml @@ -131,11 +131,7 @@ -
-
- + diff --git a/addons/account_budget/account_budget.py b/addons/account_budget/account_budget.py index 727c7c0db89..d67a73565e6 100644 --- a/addons/account_budget/account_budget.py +++ b/addons/account_budget/account_budget.py @@ -95,7 +95,6 @@ class crossovered_budget_lines(osv.osv): def _prac_amt(self, cr, uid, ids, context=None): res = {} - result = 0.0 if context is None: context = {} for line in self.browse(cr, uid, ids, context=context): @@ -109,8 +108,10 @@ class crossovered_budget_lines(osv.osv): "between to_date(%s,'yyyy-mm-dd') AND to_date(%s,'yyyy-mm-dd')) AND " "general_account_id=ANY(%s)", (line.analytic_account_id.id, date_from, date_to,acc_ids,)) result = cr.fetchone()[0] - if result is None: - result = 0.00 + if result is None: + result = 0.0 + else: + result = 0.0 res[line.id] = result return res diff --git a/addons/board/static/src/less/dashboard.less b/addons/board/static/src/less/dashboard.less index b0b41e65e34..36f0727c0ac 100644 --- a/addons/board/static/src/less/dashboard.less +++ b/addons/board/static/src/less/dashboard.less @@ -139,5 +139,4 @@ // Favorites menu in control panel .o_add_to_dashboard { display: none; // hidden by default - .o-search-options-dropdown-custom-li; } diff --git a/addons/hr_holidays/hr_holidays_view.xml b/addons/hr_holidays/hr_holidays_view.xml index bfe827774b2..3af60c1e3c7 100644 --- a/addons/hr_holidays/hr_holidays_view.xml +++ b/addons/hr_holidays/hr_holidays_view.xml @@ -250,7 +250,7 @@ form calendar {'search_default_year': 1, 'search_default_group_employee': 1} - [('holiday_type','=','employee'), ('type', '=', 'remove')] + [('holiday_type','=','employee'), ('type', '=', 'remove'), ('state', '!=', 'refuse')] diff --git a/addons/mail/static/src/js/chat_manager.js b/addons/mail/static/src/js/chat_manager.js index e3a11b19197..4e0df85c703 100644 --- a/addons/mail/static/src/js/chat_manager.js +++ b/addons/mail/static/src/js/chat_manager.js @@ -213,6 +213,22 @@ function make_message (data) { a.url = '/web/content/' + a.id + '?download=true'; }); + // format date to the local only once by message + // can not be done in preprocess, since it alter the original value + if (msg.tracking_value_ids && msg.tracking_value_ids.length) { + _.each(msg.tracking_value_ids, function(f) { + if (_.contains(['date', 'datetime'], f.field_type)) { + var format = (f.field_type === 'date') ? 'LL' : 'LLL'; + if (f.old_value) { + f.old_value = moment.utc(f.old_value).local().format(format); + } + if (f.new_value) { + f.new_value = moment.utc(f.new_value).local().format(format); + } + } + }); + } + return msg; } @@ -653,7 +669,7 @@ var chat_manager = { // post a message in a channel or execute a command return ChannelModel.call(data.command ? 'execute_command' : 'message_post', [options.channel_id], _.extend(msg, { message_type: 'comment', - content_subtype: 'html', + content_subtype: 'plaintext', subtype: 'mail.mt_comment', command: data.command, })); diff --git a/addons/mail/static/src/js/thread.js b/addons/mail/static/src/js/thread.js index 17b21c39a5b..ea593d21dfa 100644 --- a/addons/mail/static/src/js/thread.js +++ b/addons/mail/static/src/js/thread.js @@ -234,20 +234,6 @@ var Thread = Widget.extend({ msg.day = msg.date.format('LL'); } - if (message.tracking_value_ids && message.tracking_value_ids.length) { - _.each(message.tracking_value_ids, function(f) { - if (_.contains(['date', 'datetime'], f.field_type)) { - var format = (f.field_type === 'date') ? 'LL' : 'LLL'; - if (f.old_value) { - f.old_value = moment.utc(f.old_value).local().format(format); - } - if (f.new_value) { - f.new_value = moment.utc(f.new_value).local().format(format); - } - } - }); - } - if (_.contains(this.expanded_msg_ids, message.id)) { msg.expanded = true; } diff --git a/addons/mass_mailing/static/src/js/mass_mailing.js b/addons/mass_mailing/static/src/js/mass_mailing.js index 3785cf87070..e2bcb8a09ad 100644 --- a/addons/mass_mailing/static/src/js/mass_mailing.js +++ b/addons/mass_mailing/static/src/js/mass_mailing.js @@ -1,6 +1,7 @@ odoo.define('mass_mailing.mass_mailing', function (require) { var core = require('web.core'); +var FieldTextHtml = require('web_editor.backend').FieldTextHtml; var KanbanRecord = require('web_kanban.Record'); var KanbanView = require('web_kanban.KanbanView'); @@ -25,4 +26,21 @@ KanbanView.include({ }, }); +FieldTextHtml.include({ + get_datarecord: function() { + /* Avoid extremely long URIs by whitelisting fields in the datarecord + that get set as a get parameter */ + var datarecord = this._super(); + if (this.view.model === 'mail.mass_mailing') { + // these fields can potentially get very long, let's remove them + var blacklist = ['mailing_domain', 'contact_list_ids']; + for (var k in blacklist) { + delete datarecord[blacklist[k]]; + } + delete datarecord[this.name]; + } + return datarecord; + }, +}); + }); diff --git a/addons/mrp/report/bom_structure.py b/addons/mrp/report/bom_structure.py index 2618de3994b..f3ee9173ade 100644 --- a/addons/mrp/report/bom_structure.py +++ b/addons/mrp/report/bom_structure.py @@ -15,12 +15,12 @@ class bom_structure(report_sxw.rml_parse): def get_children(self, object, level=0): result = [] - def _get_rec(object, level): + def _get_rec(object, level, qty=1.0): for l in object: res = {} res['pname'] = l.product_id.name_get()[0][1] res['pcode'] = l.product_id.default_code - res['pqty'] = l.product_qty + res['pqty'] = l.product_qty * qty res['uname'] = l.product_uom.name res['level'] = level res['code'] = l.bom_id.code @@ -28,7 +28,7 @@ class bom_structure(report_sxw.rml_parse): if l.child_line_ids: if level<6: level += 1 - _get_rec(l.child_line_ids,level) + _get_rec(l.child_line_ids, level, qty=res['pqty']) if level>0 and level<6: level -= 1 return result diff --git a/addons/payment_paypal/models/__init__.py b/addons/payment_paypal/models/__init__.py index a1fe670f56f..e85dd257cf2 100644 --- a/addons/payment_paypal/models/__init__.py +++ b/addons/payment_paypal/models/__init__.py @@ -1,3 +1,4 @@ # -*- coding: utf-8 -*- +import account_invoice import paypal diff --git a/addons/payment_paypal/models/account_invoice.py b/addons/payment_paypal/models/account_invoice.py new file mode 100644 index 00000000000..7b7f3e377b7 --- /dev/null +++ b/addons/payment_paypal/models/account_invoice.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- + +from openerp import models, fields, api + +import werkzeug.urls + +class AccountInvoice(models.Model): + _inherit = 'account.invoice' + + paypal_url = fields.Char('Paypal Url', compute='_compute_paypal_url') + + @api.depends('type', 'number', 'company_id', 'currency_id') + def _compute_paypal_url(self): + for inv in self: + if inv.type == 'out_invoice' and inv.company_id.paypal_account: + params = { + "cmd": "_xclick", + "business": inv.company_id.paypal_account, + "item_name": "%s Invoice %s" % (inv.company_id.name, inv.number or ''), + "invoice": inv.number, + "amount": inv.residual, + "currency_code": inv.currency_id.name, + "button_subtype": "services", + "no_note": "1", + "bn": "Odoo_Invoice_PayNow_" + inv.currency_id.name, + } + inv.paypal_url = "https://www.paypal.com/cgi-bin/webscr?" + werkzeug.url_encode(params) diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 19288db4696..2c4a644b421 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -223,7 +223,7 @@ class PosOrder(models.Model): }) if data_type == 'product': - key = ('product', values['partner_id'], (values['product_id'], values['name']), values['analytic_account_id'], values['debit'] > 0) + key = ('product', values['partner_id'], (values['product_id'], tuple(values['tax_ids'][0][2]), values['name']), values['analytic_account_id'], values['debit'] > 0) elif data_type == 'tax': key = ('tax', values['partner_id'], values['tax_line_id'], values['debit'] > 0) elif data_type == 'counter_part': @@ -287,7 +287,7 @@ class PosOrder(models.Model): 'analytic_account_id': self._prepare_analytic_account(line), 'credit': ((amount > 0) and amount) or 0.0, 'debit': ((amount < 0) and -amount) or 0.0, - 'tax_ids': [(6, 0, line.tax_ids.ids)], + 'tax_ids': [(6, 0, line.tax_ids_after_fiscal_position.ids)], 'partner_id': partner_id }) diff --git a/addons/sale/sales_team.py b/addons/sale/sales_team.py index 309419eb97f..a234f867cf4 100644 --- a/addons/sale/sales_team.py +++ b/addons/sale/sales_team.py @@ -35,7 +35,8 @@ class crm_team(osv.Model): ('state', 'in', ['open', 'paid']), ('team_id', '=', team), ('date', '<=', date.today()), - ('date', '>=', date.today().replace(day=1)) + ('date', '>=', date.today().replace(day=1)), + ('type', 'in', ['out_invoice', 'out_refund']), ] invoices = obj_inv.search_read(cr, uid, domain, ['amount_untaxed_signed'], context=context) res[team] = sum([inv['amount_untaxed_signed'] for inv in invoices]) diff --git a/addons/stock/views/stock_pack_operation_views.xml b/addons/stock/views/stock_pack_operation_views.xml index 3a0aab844e2..5f7e46850e6 100644 --- a/addons/stock/views/stock_pack_operation_views.xml +++ b/addons/stock/views/stock_pack_operation_views.xml @@ -25,8 +25,8 @@ - - + + @@ -115,4 +115,4 @@ - \ No newline at end of file + diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml index 53f51c8f35d..cbfb63d4324 100644 --- a/addons/stock/views/stock_picking_views.xml +++ b/addons/stock/views/stock_picking_views.xml @@ -315,7 +315,7 @@ - + diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 798e8fdda5d..1b59afdc74b 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -309,7 +309,11 @@ class stock_quant(osv.osv): debit_value = currency_obj.round(cr, uid, move.company_id.currency_id, valuation_amount * qty) #check that all data is correct if move.company_id.currency_id.is_zero(debit_value): - raise UserError(_("The found valuation amount for product %s is zero. Which means there is probably a configuration error. Check the costing method and the standard price") % (move.product_id.name,)) + if move.product_id.cost_method == 'standard': + raise UserError(_("The found valuation amount for product %s is zero. Which means there is probably a configuration error. Check the costing method and the standard price") % (move.product_id.name,)) + else: + return [] + credit_value = debit_value if move.product_id.cost_method == 'average' and move.company_id.anglo_saxon_accounting: @@ -380,12 +384,13 @@ class stock_quant(osv.osv): move_obj = self.pool.get('account.move') for cost, qty in quant_cost_qty.items(): move_lines = self._prepare_account_move_line(cr, uid, move, qty, cost, credit_account_id, debit_account_id, context=context) - date = context.get('force_period_date', fields.date.context_today(self, cr, uid, context=context)) - new_move = move_obj.create(cr, uid, {'journal_id': journal_id, - 'line_ids': move_lines, - 'date': date, - 'ref': move.picking_id.name}, context=context) - move_obj.post(cr, uid, [new_move], context=context) + if move_lines: + date = context.get('force_period_date', fields.date.context_today(self, cr, uid, context=context)) + new_move = move_obj.create(cr, uid, {'journal_id': journal_id, + 'line_ids': move_lines, + 'date': date, + 'ref': move.picking_id.name}, context=context) + move_obj.post(cr, uid, [new_move], context=context) #def _reconcile_single_negative_quant(self, cr, uid, to_solve_quant, quant, quant_neg, qty, context=None): # move = self._get_latest_move(cr, uid, to_solve_quant, context=context) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index d9774cc5c55..902e7500650 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -34,7 +34,7 @@ from openerp.tools.translate import _ from openerp.tools import ustr from openerp.tools.misc import str2bool, xlwt from openerp import http -from openerp.http import request, serialize_exception as _serialize_exception +from openerp.http import request, serialize_exception as _serialize_exception, content_disposition from openerp.exceptions import AccessError _logger = logging.getLogger(__name__) @@ -413,10 +413,6 @@ def xml2json_from_elementtree(el, preserve_whitespaces=False): res["children"] = kids return res -def content_disposition(filename): - return request.registry['ir.http'].content_disposition(filename) - - def binary_content(xmlid=None, model='ir.attachment', id=None, field='datas', unique=False, filename=None, filename_field='datas_fname', download=False, mimetype=None, default_mimetype='application/octet-stream', env=None): return request.registry['ir.http'].binary_content( xmlid=xmlid, model=model, id=id, field=field, unique=unique, filename=filename, filename_field=filename_field, @@ -937,6 +933,11 @@ class Binary(http.Controller): addons_path = http.addons_manifest['web']['addons_path'] return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', image), 'rb').read() + def force_contenttype(self, headers, contenttype='image/png'): + dictheaders = dict(headers) + dictheaders['Content-Type'] = contenttype + return dictheaders.items() + @http.route(['/web/content', '/web/content/', '/web/content//', @@ -995,8 +996,15 @@ class Binary(http.Controller): if height > 500: height = 500 content = openerp.tools.image_resize_image(base64_source=content, size=(width or None, height or None), encoding='base64', filetype='PNG') + # resize force png as filetype + headers = self.force_contenttype(headers, contenttype='image/png') + + if content: + image_base64 = base64.b64decode(content) + else: + image_base64 = self.placeholder(image='placeholder.png') # could return (contenttype, content) in master + headers = self.force_contenttype(headers, contenttype='image/png') - image_base64 = content and base64.b64decode(content) or self.placeholder() headers.append(('Content-Length', len(image_base64))) response = request.make_response(image_base64, headers) response.status_code = status diff --git a/addons/web_editor/static/src/js/backend.js b/addons/web_editor/static/src/js/backend.js index 38f25e42103..468fab0c728 100644 --- a/addons/web_editor/static/src/js/backend.js +++ b/addons/web_editor/static/src/js/backend.js @@ -220,9 +220,12 @@ var FieldTextHtml = widget.extend({ this.$translate = $(); return def; }, + get_datarecord: function() { + return this.view.get_fields_values(); + }, get_url: function (_attr) { var src = this.options.editor_url || "/web_editor/field/html"; - var datarecord = this.view.get_fields_values(); + var datarecord = this.get_datarecord(); var attr = { 'model': this.view.model, @@ -269,7 +272,6 @@ var FieldTextHtml = widget.extend({ delete datarecord[this.name]; src += "&datarecord="+ encodeURIComponent(JSON.stringify(datarecord)); - return src; }, initialize_content: function () { @@ -384,4 +386,9 @@ core.form_widget_registry .add('html', FieldTextHtmlSimple) .add('html_frame', FieldTextHtml); +return { + FieldTextHtmlSimple: FieldTextHtmlSimple, + FieldTextHtml: FieldTextHtml, +}; + }); diff --git a/addons/web_kanban/static/src/js/kanban_widgets.js b/addons/web_kanban/static/src/js/kanban_widgets.js index 1c6e5532aa5..a29105ed9b5 100644 --- a/addons/web_kanban/static/src/js/kanban_widgets.js +++ b/addons/web_kanban/static/src/js/kanban_widgets.js @@ -139,7 +139,7 @@ var KanbanSelection = AbstractField.extend({ var current_state = _.find(this.states, function(state) { return state.name === self.get('value'); - }); + }) || {state_class: ''}; self.$el = $(QWeb.render("KanbanSelection", { current_state_class: current_state.state_class, diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index d73a0d9de9c..73008844b7c 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -910,9 +910,8 @@ Sitemap: sitemap.xml "widget": "contact", "fields": ["name", "address", "phone", "mobile", "fax", "email"]}'/> - - - + + Visualize on Google Maps