diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 5b45f8dd02abe53b266875317cf539b6fd911b93..cc4dada2317229b75fbf055b2e5508b9c0969ca1 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -177,6 +177,8 @@ class account_invoice(models.Model): def _compute_payments(self): partial_lines = lines = self.env['account.move.line'] for line in self.move_id.line_id: + if line.account_id != self.account_id: + continue if line.reconcile_id: lines |= line.reconcile_id.line_id elif line.reconcile_partial_id: diff --git a/addons/account/edi/invoice_action_data.xml b/addons/account/edi/invoice_action_data.xml index 417169fa8cb6e2169ab308e047406a10486b6f13..766db9e1a99fc8279a83ae64b38cdc167e5901f9 100644 --- a/addons/account/edi/invoice_action_data.xml +++ b/addons/account/edi/invoice_action_data.xml @@ -23,7 +23,7 @@ <record id="email_template_edi_invoice" model="email.template"> <field name="name">Invoice - Send by Email</field> <field name="email_from">${(object.user_id.email or object.company_id.email or 'noreply@localhost')|safe}</field> - <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a'})</field> + <field name="subject">${object.company_id.name|safe} Invoice (Ref ${object.number or 'n/a'})</field> <field name="partner_to">${object.partner_id.id}</field> <field name="model_id" ref="account.model_account_invoice"/> <field name="auto_delete" eval="True"/> diff --git a/addons/account/report/account_invoice_report_view.xml b/addons/account/report/account_invoice_report_view.xml index 3fcae5d711b4839cc57ac0a07ad3c540254b0ff8..1983d7ec9fcd1d871fa2b39ec90ba3d5c27c8283 100644 --- a/addons/account/report/account_invoice_report_view.xml +++ b/addons/account/report/account_invoice_report_view.xml @@ -69,7 +69,7 @@ <separator/> <filter string="Invoice" domain="['|', ('type','=','out_invoice'),('type','=','in_invoice')]"/> <filter string="Refund" domain="['|', ('type','=','out_refund'),('type','=','in_refund')]"/> - <field name="partner_id"/> + <field name="partner_id" operator="child_of"/> <field name="user_id" /> <field name="categ_id" filter_domain="[('categ_id', 'child_of', self)]"/> <group expand="1" string="Group By"> diff --git a/addons/account/wizard/account_fiscalyear_close.py b/addons/account/wizard/account_fiscalyear_close.py index 9ad646df32cc16d623576e344ac358de9a47bfc7..e044e6032072a7ee8ca8dd1f8c15dcbef513d239 100644 --- a/addons/account/wizard/account_fiscalyear_close.py +++ b/addons/account/wizard/account_fiscalyear_close.py @@ -133,7 +133,7 @@ class account_fiscalyear_close(osv.osv_memory): FROM account_account a LEFT JOIN account_account_type t ON (a.user_type = t.id) WHERE a.active - AND a.type != 'view' + AND a.type not in ('view', 'consolidation') AND a.company_id = %s AND t.close_method = %s''', (company_id, 'unreconciled', )) account_ids = map(lambda x: x[0], cr.fetchall()) @@ -184,7 +184,7 @@ class account_fiscalyear_close(osv.osv_memory): FROM account_account a LEFT JOIN account_account_type t ON (a.user_type = t.id) WHERE a.active - AND a.type != 'view' + AND a.type not in ('view', 'consolidation') AND a.company_id = %s AND t.close_method = %s''', (company_id, 'detail', )) account_ids = map(lambda x: x[0], cr.fetchall()) @@ -213,7 +213,7 @@ class account_fiscalyear_close(osv.osv_memory): FROM account_account a LEFT JOIN account_account_type t ON (a.user_type = t.id) WHERE a.active - AND a.type != 'view' + AND a.type not in ('view', 'consolidation') AND a.company_id = %s AND t.close_method = %s''', (company_id, 'balance', )) account_ids = map(lambda x: x[0], cr.fetchall()) diff --git a/addons/account_analytic_plans/account_analytic_plans.py b/addons/account_analytic_plans/account_analytic_plans.py index 8121f7890eb49b3d58c6c56f4aaeaf4c2a2d110f..32e4d6cf66e7cc477d5987aaa2c3121dd258b4ac 100644 --- a/addons/account_analytic_plans/account_analytic_plans.py +++ b/addons/account_analytic_plans/account_analytic_plans.py @@ -210,7 +210,7 @@ class account_analytic_plan_instance(osv.osv): ana_plan_instance_obj = self.pool.get('account.analytic.plan.instance') acct_anal_acct = self.pool.get('account.analytic.account') acct_anal_plan_line_obj = self.pool.get('account.analytic.plan.line') - if context and 'journal_id' in context: + if context and context.get('journal_id'): journal = journal_obj.browse(cr, uid, context['journal_id'], context=context) pids = ana_plan_instance_obj.search(cr, uid, [('name','=',vals['name']), ('code','=',vals['code']), ('plan_id','<>',False)], context=context) diff --git a/addons/account_anglo_saxon/invoice.py b/addons/account_anglo_saxon/invoice.py index 8ef17fa886c7bba3a3665a17ae636bada3427e4d..a4d9f48e359eb7969462ded24907f4bd0fcc66c7 100644 --- a/addons/account_anglo_saxon/invoice.py +++ b/addons/account_anglo_saxon/invoice.py @@ -22,6 +22,7 @@ ############################################################################## from openerp.osv import osv, fields +from openerp.tools.float_utils import float_round as round class account_invoice_line(osv.osv): _inherit = "account.invoice.line" @@ -36,11 +37,12 @@ class account_invoice_line(osv.osv): company_currency = inv.company_id.currency_id.id def get_price(cr, uid, inv, company_currency, i_line, price_unit): cur_obj = self.pool.get('res.currency') + decimal_precision = self.pool.get('decimal.precision') if inv.currency_id.id != company_currency: price = cur_obj.compute(cr, uid, company_currency, inv.currency_id.id, price_unit * i_line.quantity, context={'date': inv.date_invoice}) else: price = price_unit * i_line.quantity - return price + return round(price, decimal_precision.precision_get(cr, uid, 'Account')) if inv.type in ('out_invoice','out_refund'): for i_line in inv.invoice_line: @@ -118,6 +120,8 @@ class account_invoice_line(osv.osv): fpos = i_line.invoice_id.fiscal_position or False a = self.pool.get('account.fiscal.position').map_account(cr, uid, fpos, oa) diff_res = [] + decimal_precision = self.pool.get('decimal.precision') + account_prec = decimal_precision.precision_get(cr, uid, 'Account') # calculate and write down the possible price difference between invoice price and product price for line in res: if a == line['account_id'] and i_line.product_id.id == line['product_id']: @@ -132,14 +136,14 @@ class account_invoice_line(osv.osv): if valuation_stock_move: valuation_price_unit = stock_move_obj.browse(cr, uid, valuation_stock_move[0], context=context).price_unit if valuation_price_unit != i_line.price_unit and line['price_unit'] == i_line.price_unit and acc: - price_diff = i_line.price_unit - valuation_price_unit - line.update({'price': valuation_price_unit * line['quantity']}) + price_diff = round(i_line.price_unit - valuation_price_unit, account_prec) + line.update({'price': round(valuation_price_unit * line['quantity'], account_prec)}) diff_res.append({ 'type': 'src', 'name': i_line.name[:64], 'price_unit': price_diff, 'quantity': line['quantity'], - 'price': price_diff * line['quantity'], + 'price': round(price_diff * line['quantity'], account_prec), 'account_id': acc, 'product_id': line['product_id'], 'uos_id': line['uos_id'], diff --git a/addons/account_voucher/account_voucher.py b/addons/account_voucher/account_voucher.py index 6341c8b1e106591120a34ea568ca2f692a2b66c1..a02768079f7250036596baeb16deaba1f9be5321 100644 --- a/addons/account_voucher/account_voucher.py +++ b/addons/account_voucher/account_voucher.py @@ -1044,7 +1044,8 @@ class account_voucher(osv.osv): 'period_id': voucher.period_id.id, 'partner_id': voucher.partner_id.id, 'currency_id': company_currency <> current_currency and current_currency or False, - 'amount_currency': company_currency <> current_currency and sign * voucher.amount or 0.0, + 'amount_currency': (sign * abs(voucher.amount) # amount < 0 for refunds + if company_currency != current_currency else 0.0), 'date': voucher.date, 'date_maturity': voucher.date_due } @@ -1210,7 +1211,7 @@ class account_voucher(osv.osv): if line.amount == line.amount_unreconciled: if not line.move_line_id: raise osv.except_osv(_('Wrong voucher line'),_("The invoice you are willing to pay is not valid anymore.")) - sign = voucher.type in ('payment', 'purchase') and -1 or 1 + sign = line.type =='dr' and -1 or 1 currency_rate_difference = sign * (line.move_line_id.amount_residual - amount) else: currency_rate_difference = 0.0 @@ -1268,8 +1269,7 @@ class account_voucher(osv.osv): # otherwise we use the rates of the system amount_currency = currency_obj.compute(cr, uid, company_currency, line.move_line_id.currency_id.id, move_line['debit']-move_line['credit'], context=ctx) if line.amount == line.amount_unreconciled: - sign = voucher.type in ('payment', 'purchase') and -1 or 1 - foreign_currency_diff = sign * line.move_line_id.amount_residual_currency + amount_currency + foreign_currency_diff = line.move_line_id.amount_residual_currency - abs(amount_currency) move_line['amount_currency'] = amount_currency voucher_line = move_line_obj.create(cr, uid, move_line) diff --git a/addons/analytic/analytic.py b/addons/analytic/analytic.py index e31efa0eb2a93c2ecbd7e29561abbd9fa3c554cd..36db9c0da857fe2387f09a6caf3a1179cfbde1be 100644 --- a/addons/analytic/analytic.py +++ b/addons/analytic/analytic.py @@ -307,7 +307,7 @@ class account_analytic_account(osv.osv): dom = [] for name2 in name.split('/'): name = name2.strip() - account_ids = self.search(cr, uid, dom + [('name', 'ilike', name)] + args, limit=limit, context=context) + account_ids = self.search(cr, uid, dom + [('name', operator, name)] + args, limit=limit, context=context) if not account_ids: break dom = [('parent_id','in',account_ids)] else: diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index fd55ca114a5b8f670073de3a538d6c52133efa67..5ef3d15da9143800449a509b30651026457120de 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -901,6 +901,15 @@ class calendar_event(osv.Model): 'partner_ids': fields.many2many('res.partner', 'calendar_event_res_partner_rel', string='Attendees', states={'done': [('readonly', True)]}), 'alarm_ids': fields.many2many('calendar.alarm', 'calendar_alarm_calendar_event_rel', string='Reminders', ondelete="restrict", copy=False), } + + def _get_default_partners(self, cr, uid, ctx=None): + ret = [self.pool['res.users'].browse(cr, uid, uid, context=ctx).partner_id.id] + active_id = ctx.get('active_id') + if ctx.get('active_model') == 'res.partner' and active_id: + if active_id not in ret: + ret.append(active_id) + return ret + _defaults = { 'end_type': 'count', 'count': 1, @@ -913,7 +922,7 @@ class calendar_event(osv.Model): 'interval': 1, 'active': 1, 'user_id': lambda self, cr, uid, ctx: uid, - 'partner_ids': lambda self, cr, uid, ctx: [self.pool['res.users'].browse(cr, uid, [uid], context=ctx)[0].partner_id.id] + 'partner_ids': _get_default_partners, } def _check_closing_date(self, cr, uid, ids, context=None): diff --git a/addons/calendar/calendar_view.xml b/addons/calendar/calendar_view.xml index 0c5f67ab2f986af695bbcc7677bb776018cfb0ef..29cc3e90e9c3f88e98eb8e519a06d9a6666b456d 100644 --- a/addons/calendar/calendar_view.xml +++ b/addons/calendar/calendar_view.xml @@ -41,7 +41,7 @@ <h1> <field name="name"/> </h1> - <label for="partner_ids" class="oe_edit_only"/> + <label for="partner_ids" string="Attendees" class="oe_edit_only"/> <h2> <field name="partner_ids" widget="many2manyattendee" context="{'force_email':True}" diff --git a/addons/crm/base_partner_merge.py b/addons/crm/base_partner_merge.py index 9749e7ae884acb28b6f9df7163c1538d9178b4b2..34b88008178866dfe01d78d98a5f3688a8752279 100644 --- a/addons/crm/base_partner_merge.py +++ b/addons/crm/base_partner_merge.py @@ -6,6 +6,7 @@ import htmlentitydefs import itertools import logging import operator +import psycopg2 import re from ast import literal_eval from openerp.tools import mute_logger @@ -186,28 +187,29 @@ class MergePartnerAutomatic(osv.TransientModel): for partner_id in partner_ids: cr.execute(query, (dst_partner.id, partner_id, dst_partner.id)) else: - cr.execute("SAVEPOINT recursive_partner_savepoint") try: - query = 'UPDATE "%(table)s" SET %(column)s = %%s WHERE %(column)s IN %%s' % query_dic - cr.execute(query, (dst_partner.id, partner_ids,)) - - if column == proxy._parent_name and table == 'res_partner': - query = """ - WITH RECURSIVE cycle(id, parent_id) AS ( - SELECT id, parent_id FROM res_partner - UNION - SELECT cycle.id, res_partner.parent_id - FROM res_partner, cycle - WHERE res_partner.id = cycle.parent_id AND - cycle.id != cycle.parent_id - ) - SELECT id FROM cycle WHERE id = parent_id AND id = %s - """ - cr.execute(query, (dst_partner.id,)) - if cr.fetchall(): - cr.execute("ROLLBACK TO SAVEPOINT recursive_partner_savepoint") - finally: - cr.execute("RELEASE SAVEPOINT recursive_partner_savepoint") + with mute_logger('openerp.sql_db'), cr.savepoint(): + query = 'UPDATE "%(table)s" SET %(column)s = %%s WHERE %(column)s IN %%s' % query_dic + cr.execute(query, (dst_partner.id, partner_ids,)) + + if column == proxy._parent_name and table == 'res_partner': + query = """ + WITH RECURSIVE cycle(id, parent_id) AS ( + SELECT id, parent_id FROM res_partner + UNION + SELECT cycle.id, res_partner.parent_id + FROM res_partner, cycle + WHERE res_partner.id = cycle.parent_id AND + cycle.id != cycle.parent_id + ) + SELECT id FROM cycle WHERE id = parent_id AND id = %s + """ + cr.execute(query, (dst_partner.id,)) + except psycopg2.Error: + # updating fails, most likely due to a violated unique constraint + # keeping record with nonexistent partner_id is useless, better delete it + query = 'DELETE FROM %(table)s WHERE %(column)s = %%s' % query_dic + cr.execute(query, (partner_id,)) def _update_reference_fields(self, cr, uid, src_partners, dst_partner, context=None): _logger.debug('_update_reference_fields for dst_partner: %s for src_partners: %r', dst_partner.id, list(map(operator.attrgetter('id'), src_partners))) diff --git a/addons/crm_claim/crm_claim_view.xml b/addons/crm_claim/crm_claim_view.xml index 6180b85660d31934813ada4dcab1a7eaf7c8bd11..8d36dc2047bc65803f56b955707080f233f1df4d 100644 --- a/addons/crm_claim/crm_claim_view.xml +++ b/addons/crm_claim/crm_claim_view.xml @@ -125,7 +125,7 @@ <group colspan="2" col="2" groups="base.group_user"> <separator colspan="2" string="Responsibilities"/> <field name="user_fault"/> - <field name="categ_id" widget="selection" + <field name="categ_id" options="{'no_create': True, 'no_open': True}" domain="[('object_id.model', '=', 'crm.claim')]"/> <field name="ref"/> </group> diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 8f59f148c2f42a4165550d19c2d10424773f7887..89d9212478cf0103b0bc7d64e0cc9964feda2ac6 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -34,9 +34,34 @@ from openerp import tools, api from openerp.tools.translate import _ from urllib import urlencode, quote as quote - _logger = logging.getLogger(__name__) + +def format_tz(pool, cr, uid, dt, tz=False, format=False, context=None): + context = dict(context or {}) + if tz: + context['tz'] = tz or pool.get('res.users').read(cr, SUPERUSER_ID, uid, ['tz'])['tz'] or "UTC" + timestamp = datetime.datetime.strptime(dt, tools.DEFAULT_SERVER_DATETIME_FORMAT) + + ts = fields.datetime.context_timestamp(cr, uid, timestamp, context) + + if format: + return ts.strftime(format) + else: + lang = context.get("lang") + lang_params = {} + if lang: + res_lang = pool.get('res.lang') + ids = res_lang.search(cr, uid, [("code", "=", lang)]) + if ids: + lang_params = res_lang.read(cr, uid, ids[0], ["date_format", "time_format"]) + format_date = lang_params.get("date_format", '%B-%d-%Y') + format_time = lang_params.get("time_format", '%I-%M %p') + + fdate = ts.strftime(format_date) + ftime = ts.strftime(format_time) + return "%s %s (%s)" % (fdate, ftime, tz) + try: # We use a jinja2 sandboxed environment to render mako templates. # Note that the rendering does not cover all the mako syntax, in particular @@ -165,6 +190,7 @@ class email_template(osv.osv): user = self.pool.get('res.users').browse(cr, uid, uid, context=context) records = self.pool[model].browse(cr, uid, res_ids, context=context) or [None] variables = { + 'format_tz': lambda dt, tz=False, format=False: format_tz(self.pool, cr, uid, dt, tz, format, context), 'user': user, 'ctx': context, # context kw would clash with mako internals } diff --git a/addons/l10n_be/account_financial_report.xml b/addons/l10n_be/account_financial_report.xml index ccc118d30aefc9c35893b43784443c94e3e0f632..2abfce472f7be502101e2585cb92af4903fe88c2 100644 --- a/addons/l10n_be/account_financial_report.xml +++ b/addons/l10n_be/account_financial_report.xml @@ -640,7 +640,7 @@ <field name="style_overwrite" eval="2"/> </record> <record id="account_financial_report_bnficepertedelexcerciceavantimpts1" model="account.financial.report"> - <field name="name">Bénéfice (Perte) de l'excercice avant impôts</field> + <field name="name">Bénéfice (Perte) de l'exercice avant impôts</field> <field eval="8" name="sequence"/> <field name="parent_id" ref="account_financial_report_belgiumpl0"/> <field name="display_detail">no_detail</field> @@ -674,7 +674,7 @@ <field name="style_overwrite" eval="2"/> </record> <record id="account_financial_report_bnficepertedelexcercice1" model="account.financial.report"> - <field name="name">Bénéfice (Perte) de l'excercice</field> + <field name="name">Bénéfice (Perte) de l'exercice</field> <field eval="12" name="sequence"/> <field name="parent_id" ref="account_financial_report_belgiumpl0"/> <field name="display_detail">no_detail</field> diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index e8dd37e0ad982cad3ca5a811dcc4554526601043..c57be5a1913aaf4f0adabe82dceca1b1e10cca20 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -67,6 +67,7 @@ class mail_followers(osv.Model): self.invalidate_cache(cr, uid, context=context) return res + _sql_constraints = [('mail_followers_res_partner_res_model_id_uniq','unique(res_model,res_id,partner_id)','Error, a partner cannot follow twice the same object.')] class mail_notification(osv.Model): """ Class holding notifications pushed to partners. Followers and partners diff --git a/addons/mail/static/src/xml/mail.xml b/addons/mail/static/src/xml/mail.xml index 14818313d36b54c503b5980a273b1a9e3273cdd5..bf5430c52de2f4ef34972e6e68d7b8ea4c879884 100644 --- a/addons/mail/static/src/xml/mail.xml +++ b/addons/mail/static/src/xml/mail.xml @@ -50,10 +50,11 @@ <t t-if="!widget.options.view_mailbox"> <div class="field_text oe_compact oe_compact_record"> <a class="oe_compose_post" t-if="widget.options.compose_placeholder"><t t-raw="widget.options.compose_placeholder"/></a> - <a class="oe_compose_post" t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox">Send a message</a> + <a class="oe_compose_post" t-if="!widget.options.compose_placeholder and !widget.options.view_mailbox" + title="Send a message to all followers of the document">Send a message</a> <t t-if="widget.options.display_log_button"> <span class="oe_grey oe_sep_word">or</span> - <a class="oe_compose_log">Log an internal note</a> + <a class="oe_compose_log" title="Log a note for this document. No notification will be sent">Log an internal note</a> </t> </div> </t> diff --git a/addons/payment_paypal/controllers/main.py b/addons/payment_paypal/controllers/main.py index 967059e59aeb5b6b6a9d83d7c610df8108e5b992..e07fa3d738f3809b06456ac2ef78135f4f6b3eb9 100644 --- a/addons/payment_paypal/controllers/main.py +++ b/addons/payment_paypal/controllers/main.py @@ -40,13 +40,21 @@ class PaypalController(http.Controller): Once data is validated, process it. """ res = False new_post = dict(post, cmd='_notify-validate') - urequest = urllib2.Request("https://www.sandbox.paypal.com/cgi-bin/webscr", werkzeug.url_encode(new_post)) + cr, uid, context = request.cr, request.uid, request.context + reference = post.get('item_number') + tx = None + if reference: + tx_ids = request.registry['payment.transaction'].search(cr, uid, [('reference', '=', reference)], context=context) + if tx_ids: + tx = request.registry['payment.transaction'].browse(cr, uid, tx_ids[0], context=context) + paypal_urls = request.registry['payment.acquirer']._get_paypal_urls(cr, uid, tx and tx.acquirer_id and tx.acquirer_id.env or 'prod', context=context) + validate_url = paypal_urls['paypal_form_url'] + urequest = urllib2.Request(validate_url, werkzeug.url_encode(new_post)) uopen = urllib2.urlopen(urequest) resp = uopen.read() if resp == 'VERIFIED': _logger.info('Paypal: validated data') - cr, uid, context = request.cr, SUPERUSER_ID, request.context - res = request.registry['payment.transaction'].form_feedback(cr, uid, post, 'paypal', context=context) + res = request.registry['payment.transaction'].form_feedback(cr, SUPERUSER_ID, post, 'paypal', context=context) elif resp == 'INVALID': _logger.warning('Paypal: answered INVALID on data verification') else: diff --git a/addons/point_of_sale/point_of_sale_view.xml b/addons/point_of_sale/point_of_sale_view.xml index 520c22db9c773d44547966080c8758802c2a9716..24951faba2d56152ab781d5aee74b1f9f1e620b2 100644 --- a/addons/point_of_sale/point_of_sale_view.xml +++ b/addons/point_of_sale/point_of_sale_view.xml @@ -60,6 +60,7 @@ </div> <field name="amount_total" nolabel="1" class="oe_subtotal_footer_separator"/> </group> + <div class="oe_clear"/> </page> <page string="Payments"> <field name="statement_ids" colspan="4" nolabel="1"> diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 5946133086c2ef8b366010367ce7e1497a7f3ac6..816b2a25741430d4816d83dff34bd7a781e9ee85 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -900,7 +900,7 @@ <br /> <t t-esc="widget.pos.company.name"/><br /> Phone: <t t-esc="widget.pos.company.phone || ''"/><br /> - User: <t t-esc="widget.pos.user.name"/><br /> + User: <t t-esc="widget.pos.cashier.name"/><br /> Shop: <t t-esc="widget.pos.shop.name"/><br /> <br /> <t t-if="widget.pos.config.receipt_header"> diff --git a/addons/portal_claim/__init__.py b/addons/portal_claim/__init__.py index 26c654db9ddf8488223753d7b7f9678ef81f464b..dbd07a05428e366fbe4d42a6173bd67df6a1368a 100644 --- a/addons/portal_claim/__init__.py +++ b/addons/portal_claim/__init__.py @@ -19,3 +19,4 @@ # ############################################################################## +import portal_claim \ No newline at end of file diff --git a/addons/portal_claim/portal_claim.py b/addons/portal_claim/portal_claim.py new file mode 100644 index 0000000000000000000000000000000000000000..0a3aa40eb56fa9c02692d65a08fa372238289362 --- /dev/null +++ b/addons/portal_claim/portal_claim.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +############################################################################## + +from openerp import SUPERUSER_ID +from openerp.osv import osv + + +class crm_claim(osv.osv): + _inherit = "crm.claim" + + def _get_default_partner_id(self, cr, uid, context=None): + """ Gives default partner_id """ + if context is None: + context = {} + if context.get('portal'): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + # Special case for portal users, as they are not allowed to call name_get on res.partner + # We save this call for the web client by returning it in default get + return self.pool['res.partner'].name_get(cr, SUPERUSER_ID, [user.partner_id.id], context=context)[0] + return False + + _defaults = { + 'partner_id': lambda s, cr, uid, c: s._get_default_partner_id(cr, uid, c), + } + +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/portal_sale/portal_sale_data.xml b/addons/portal_sale/portal_sale_data.xml index 6f8f3b3fd13e3d066fef717e1b93de5473bb2f0d..9deab37d07b1e8f33892df91e1b577a9b4204b2d 100644 --- a/addons/portal_sale/portal_sale_data.xml +++ b/addons/portal_sale/portal_sale_data.xml @@ -7,7 +7,7 @@ <record id="email_template_edi_sale" model="email.template"> <field name="name">Sales Order - Send by Email (Portal)</field> <field name="email_from">${(object.user_id.email or '')|safe}</field> - <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> + <field name="subject">${object.company_id.name|safe} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> <field name="partner_to">${object.partner_invoice_id.id}</field> <field name="model_id" ref="sale.model_sale_order"/> <field name="auto_delete" eval="True"/> @@ -98,7 +98,7 @@ <record id="email_template_edi_invoice" model="email.template"> <field name="name">Invoice - Send by Email (Portal)</field> <field name="email_from">${(object.user_id.email or object.company_id.email or 'noreply@localhost')|safe}</field> - <field name="subject">${object.company_id.name} Invoice (Ref ${object.number or 'n/a' })</field> + <field name="subject">${object.company_id.name|safe} Invoice (Ref ${object.number or 'n/a' })</field> <field name="partner_to">${object.partner_id.id}</field> <field name="model_id" ref="account.model_account_invoice"/> <field name="auto_delete" eval="True"/> diff --git a/addons/purchase/edi/purchase_order_action_data.xml b/addons/purchase/edi/purchase_order_action_data.xml index 391c61a8e948bc57f13fcd46b12036d82c1e7cc9..537f6580ecaf468cebb28dacdcfa199fe4889e48 100644 --- a/addons/purchase/edi/purchase_order_action_data.xml +++ b/addons/purchase/edi/purchase_order_action_data.xml @@ -19,8 +19,8 @@ <!--Email template --> <record id="email_template_edi_purchase" model="email.template"> <field name="name">RFQ - Send by Email</field> - <field name="email_from">${object.validator.email or ''}</field> - <field name="subject">${object.company_id.name} Order (Ref ${object.name or 'n/a' })</field> + <field name="email_from">${(object.validator.email or '')|safe}</field> + <field name="subject">${object.company_id.name|safe} Order (Ref ${object.name or 'n/a' })</field> <field name="partner_to">${object.partner_id.id}</field> <field name="model_id" ref="purchase.model_purchase_order"/> <field name="auto_delete" eval="True"/> diff --git a/addons/sale/edi/sale_order_action_data.xml b/addons/sale/edi/sale_order_action_data.xml index 4ad657d7f82044dce757d2547880035458817ceb..d17443a7577d776489c95f7d9ec46a48f3bb8cb3 100644 --- a/addons/sale/edi/sale_order_action_data.xml +++ b/addons/sale/edi/sale_order_action_data.xml @@ -21,7 +21,7 @@ <record id="email_template_edi_sale" model="email.template"> <field name="name">Sales Order - Send by Email</field> <field name="email_from">${(object.user_id.email or '')|safe}</field> - <field name="subject">${object.company_id.name} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> + <field name="subject">${object.company_id.name|safe} ${object.state in ('draft', 'sent') and 'Quotation' or 'Order'} (Ref ${object.name or 'n/a' })</field> <field name="partner_to">${object.partner_invoice_id.id}</field> <field name="model_id" ref="sale.model_sale_order"/> <field name="auto_delete" eval="True"/> diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 818b3ebe1afe1bc4952c978751e87eb8dde0d2d9..7a2320298fed111eab1c4a263b9b9f94b3e2a253 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1950,13 +1950,12 @@ class stock_move(osv.osv): product = self.pool.get('product.product').browse(cr, uid, [prod_id], context=ctx)[0] uos_id = product.uos_id and product.uos_id.id or False result = { + 'name': product.partner_ref, 'product_uom': product.uom_id.id, 'product_uos': uos_id, 'product_uom_qty': 1.00, 'product_uos_qty': self.pool.get('stock.move').onchange_quantity(cr, uid, ids, prod_id, 1.00, product.uom_id.id, uos_id)['value']['product_uos_qty'], } - if not ids: - result['name'] = product.partner_ref if loc_id: result['location_id'] = loc_id if loc_dest_id: diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index cf90e649f960745d330cb14cc66fd79b105132b3..62b65b037012dab7c12cb1bd92390d60ed46e926 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -8,7 +8,6 @@ font-weight: normal; font-style: normal; } - @font-face { font-family: "EntypoRegular"; src: url("/web/static/src/font/entypo-webfont.eot") format("eot"); @@ -1264,6 +1263,9 @@ .openerp .oe_view_manager_inline > .oe_view_manager_header, .openerp .oe_view_manager_inlineview > .oe_view_manager_header { display: none; } +.openerp .oe_popup_form { + display: table; +} .openerp .oe_popup_form .oe_formview .oe_form_pager { display: none !important; } @@ -2994,7 +2996,6 @@ top: 0px; } } - .kitten-mode-activated { background-size: cover; background-attachment: fixed; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 7834bf88ede555eb72bee9e4c9f3c3d24f5ae09b..efc51a7b7f2630df73ff91aef6ca8eca8e34bae9 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -1071,6 +1071,7 @@ $sheet-padding: 16px // }}} // FormPopup {{{ .oe_popup_form + display: table .oe_formview .oe_form_pager display: none !important // Customize label weight for popup wizard appear from another wizard according bootstrap3 diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 41bc0447756d8d0d00d9e26d05db42688337ab12..a90797335f3716c0bc362f571180abfcbc08ed90 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -5714,6 +5714,20 @@ instance.web.form.FieldBinaryImage = instance.web.form.FieldBinary.extend({ this._super.apply(this, arguments); this.render_value(); this.set_filename(''); + }, + set_value: function(value_){ + var changed = value_ !== this.get_value(); + this._super.apply(this, arguments); + // By default, on binary images read, the server returns the binary size + // This is possible that two images have the exact same size + // Therefore we trigger the change in case the image value hasn't changed + // So the image is re-rendered correctly + if (!changed){ + this.trigger("change:value", this, { + oldValue: value_, + newValue: value_ + }); + } } }); diff --git a/addons/web_graph/static/src/js/pivot_table.js b/addons/web_graph/static/src/js/pivot_table.js index 48ae9ec3830343ab72f92fb82b0bf6a15ea5c8d8..086e72059901e3ff3ddcac06dae2a21b2cbc52b4 100644 --- a/addons/web_graph/static/src/js/pivot_table.js +++ b/addons/web_graph/static/src/js/pivot_table.js @@ -28,7 +28,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ // ---------------------------------------------------------------------- // this.measures: list of measure [measure], measure = {field: _, string: _, type: _} // this.rows.groupby, this.cols.groupby : list of groupbys used for describing rows (...), - // a groupby is also {field:_, string:_, type:_} + // a groupby is also {field:_, string:_, type:_} // If its type is date/datetime, field can have the corresponding interval in its description, // for example 'create_date:week'. set_measures: function (measures) { @@ -65,7 +65,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ } var row_gb_changed = !_.isEqual(row_groupby, this.rows.groupby), col_gb_changed = !_.isEqual(col_groupby, this.cols.groupby); - + this.domain = domain; this.rows.groupby = row_groupby; this.cols.groupby = col_groupby; @@ -80,7 +80,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ // Cells manipulation methods // ---------------------------------------------------------------------- // cells are objects {x:_, y:_, values:_} where x < y and values is an array - // of values (one for each measure). The condition x < y might look + // of values (one for each measure). The condition x < y might look // unnecessary, but it makes the rest of the code simpler: headers // don't krow if they are rows or cols, they just know their id, so // it is useful that a call get_values(id1, id2) is the same as get_values(id2, id1) @@ -103,9 +103,9 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ // ---------------------------------------------------------------------- // Headers/Rows/Cols manipulation methods // ---------------------------------------------------------------------- - // this.rows.headers, this.cols.headers = [header] describe the tree structure + // this.rows.headers, this.cols.headers = [header] describe the tree structure // of rows/cols. Headers are objects - // { + // { // id:_, (unique id obviously) // path: [...], (array of all parents title, with its own title at the end) // title:_, (name of the row/col) @@ -150,12 +150,12 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ get_rows_leaves: function () { return _.where(this.rows.headers, {expanded:false}); }, - + // return all non expanded cols get_cols_leaves: function () { return _.where(this.cols.headers, {expanded:false}); }, - + get_ancestors: function (header) { var self = this; if (!header.children) return []; @@ -181,7 +181,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ }, main_row: function () { return this.rows.headers[0]; }, - + main_col: function () { return this.cols.headers[0]; }, // ---------------------------------------------------------------------- @@ -255,7 +255,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ // Data updating methods // ---------------------------------------------------------------------- // update_data will try to preserve the expand/not expanded status of each - // column/row. If you want to expand all, then set this.cols.headers/this.rows.headers + // column/row. If you want to expand all, then set this.cols.headers/this.rows.headers // to null before calling update_data. update_data: function () { var self = this; @@ -289,7 +289,7 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ data_pts.forEach(function (data_pt) { var row_value = (prefix || []).concat(data_pt.attributes.value.slice(0,index)); var col_value = data_pt.attributes.value.slice(index); - + if (expand && !_.find(col_headers, function (hdr) {return self.isEqual(col_value, hdr.path);})) { return; } @@ -427,6 +427,10 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ return _t('Undefined'); } else if (attrs.value[i] instanceof Array) { return attrs.value[i][1]; + }else if (grouped_on && self.fields[grouped_on].type === 'selection'){ + var selection = self.fields[grouped_on].selection; + var value_lookup = _.where(selection, {0:attrs.value[i]}); + return value_lookup ? value_lookup[0][1] : _t('Undefined'); } return attrs.value[i]; }); @@ -445,8 +449,8 @@ openerp.web_graph.PivotTable = openerp.web.Class.extend({ isEqual: function (path1, path2) { if (path1.length !== path2.length) { return false; } for (var i = 0; i < path1.length; i++) { - if (path1[i] !== path2[i]) { - return false; + if (path1[i] !== path2[i]) { + return false; } } return true; diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index d67fe5272609dd94d81e674ffadc6f32a52e6487..4c859c4badc341d01c9117584518c4ba0e044e6a 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -308,6 +308,9 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ self.do_clear_groups(); self.dataset.read_slice(self.fields_keys.concat(['__last_update']), { 'limit': self.limit }).done(function(records) { var kgroup = new instance.web_kanban.KanbanGroup(self, records, null, self.dataset); + if (!_.isEmpty(self.dataset.ids) && (self.dataset.index === null || self.dataset.index >= self.dataset.ids.length)) { + self.dataset.index = 0; + } self.do_add_groups([kgroup]).done(function() { if (_.isEmpty(records)) { self.no_result(); @@ -429,8 +432,8 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ stop: function(event, ui) { var stop_index = ui.item.index(); if (start_index !== stop_index) { - var $start_column = $('.oe_kanban_groups_records .oe_kanban_column').eq(start_index); - var $stop_column = $('.oe_kanban_groups_records .oe_kanban_column').eq(stop_index); + var $start_column = self.$('.oe_kanban_groups_records .oe_kanban_column').eq(start_index); + var $stop_column = self.$('.oe_kanban_groups_records .oe_kanban_column').eq(stop_index); var method = (start_index > stop_index) ? 'insertBefore' : 'insertAfter'; $start_column[method]($stop_column); var tmp_group = self.groups.splice(start_index, 1)[0]; @@ -1152,7 +1155,7 @@ instance.web_kanban.KanbanRecord = instance.web.Widget.extend({ */ instance.web_kanban.QuickCreate = instance.web.Widget.extend({ template: 'KanbanView.quick_create', - + /** * close_btn: If true, the widget will display a "Close" button able to trigger * a "close" event. diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index 99fd4fd379ef40b5bbf72cb100ccd7c8a743d341..f8cb81b50351ea42434c39812c3d3b3fe27c2d66 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -359,14 +359,12 @@ class Website(openerp.addons.web.controllers.main.Home): @http.route(['/website/seo_suggest/<keywords>'], type='http', auth="public", website=True) def seo_suggest(self, keywords): url = "http://google.com/complete/search" - param = { - 'ie': 'utf8', - 'oe': 'utf8', - 'output': 'toolbar', - 'q': keywords - } - req = urllib2.Request("%s?%s" % (url, werkzeug.url_encode(param))) - request = urllib2.urlopen(req) + try: + req = urllib2.Request("%s?%s" % (url, werkzeug.url_encode({ + 'ie': 'utf8', 'oe': 'utf8', 'output': 'toolbar', 'q': keywords}))) + request = urllib2.urlopen(req) + except (urllib2.HTTPError, urllib2.URLError): + return [] xmlroot = ET.fromstring(request.read()) return json.dumps([sugg[0].attrib['data'] for sugg in xmlroot if len(sugg) and sugg[0].attrib['data']]) @@ -399,10 +397,15 @@ class Website(openerp.addons.web.controllers.main.Home): The requested field is assumed to be base64-encoded image data in all cases. """ - response = werkzeug.wrappers.Response() - return request.registry['website']._image( - request.cr, request.uid, model, id, field, response, max_width, max_height) - + try: + response = werkzeug.wrappers.Response() + return request.registry['website']._image( + request.cr, request.uid, model, id, field, response, max_width, max_height) + except Exception: + logger.exception("Cannot render image field %r of record %s[%s] at size(%s,%s)", + field, model, id, max_width, max_height) + response = werkzeug.wrappers.Response() + return self.placeholder(response) #------------------------------------------------------ # Server actions diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 681d6560c7e73882ce6c1cd879138c30f3e0b537..45a6577e4c0cf2726c754755d88f4cf8aa524b35 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -546,10 +546,8 @@ class website(osv.osv): response.mimetype = Image.MIME[image.format] w, h = image.size - try: - max_w, max_h = int(max_width), int(max_height) - except: - max_w, max_h = (maxint, maxint) + max_w = int(max_width) if max_width else maxint + max_h = int(max_height) if max_height else maxint if w < max_w and h < max_h: response.data = data diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index afc2b59524f1c8843cc10d1554e60ebbd07df062..590b3e6f42e12d162668a5cbd5ef80b802846afe 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -99,10 +99,10 @@ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) - })(window,document,'script','//www.google-analytics.com/analytics.js','_gaw'); + })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - _gaw('create',_.str.trim('<t t-esc="website.google_analytics_key"/>')); - _gaw('send','pageview'); + ga('create',_.str.trim('<t t-esc="website.google_analytics_key"/>')); + ga('send','pageview'); </script> </t> </head> @@ -651,7 +651,6 @@ <div class="oe_structure"> <h1 class="container mt32"><t t-esc="status_code"/>: <t t-esc="status_message"/></h1> </div> - <div class="container" t-if="views"> <div class="alert alert-danger" t-if="qweb_exception and editable"> <h4>Template fallback</h4> diff --git a/addons/website_quote/models/order.py b/addons/website_quote/models/order.py index bb3f89a5c8d1da6c470540f129b576e81895a790..c9d72db04c1e7d0ca222d68692680e587c1bfb51 100644 --- a/addons/website_quote/models/order.py +++ b/addons/website_quote/models/order.py @@ -64,11 +64,14 @@ class sale_quote_line(osv.osv): def on_change_product_id(self, cr, uid, ids, product, context=None): vals = {} product_obj = self.pool.get('product.product').browse(cr, uid, product, context=context) + name = product_obj.name + if product_obj.description_sale: + name += '\n' + product_obj.description_sale vals.update({ 'price_unit': product_obj.list_price, 'product_uom_id': product_obj.uom_id.id, 'website_description': product_obj.website_description, - 'name': product_obj.name, + 'name': name, }) return {'value': vals} diff --git a/openerp/addons/base/ir/ir_cron.py b/openerp/addons/base/ir/ir_cron.py index 60790c8fdc617a84a020a23494354e1d666b9386..055c0d8bf5ab9cd1a5fbec55373fb7c6a441930e 100644 --- a/openerp/addons/base/ir/ir_cron.py +++ b/openerp/addons/base/ir/ir_cron.py @@ -24,9 +24,14 @@ import time import psycopg2 from datetime import datetime from dateutil.relativedelta import relativedelta +import pytz import openerp +<<<<<<< HEAD from openerp import SUPERUSER_ID, netsvc, api +======= +from openerp import netsvc, SUPERUSER_ID +>>>>>>> 0739bc4edabab7e74571087c01a2da68ccadb10e from openerp.osv import fields, osv from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT from openerp.tools.safe_eval import safe_eval as eval @@ -159,8 +164,8 @@ class ir_cron(osv.osv): """ try: with api.Environment.manage(): - now = datetime.now() - nextcall = datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT) + now = fields.datetime.context_timestamp(job_cr, SUPERUSER_ID, datetime.now()) + nextcall = fields.datetime.context_timestamp(job_cr, SUPERUSER_ID, datetime.strptime(job['nextcall'], DEFAULT_SERVER_DATETIME_FORMAT)) numbercall = job['numbercall'] ok = False @@ -176,7 +181,7 @@ class ir_cron(osv.osv): if not numbercall: addsql = ', active=False' cron_cr.execute("UPDATE ir_cron SET nextcall=%s, numbercall=%s"+addsql+" WHERE id=%s", - (nextcall.strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id'])) + (nextcall.astimezone(pytz.UTC).strftime(DEFAULT_SERVER_DATETIME_FORMAT), numbercall, job['id'])) self.invalidate_cache(cr, SUPERUSER_ID) finally: diff --git a/openerp/report/report_sxw.py b/openerp/report/report_sxw.py index e3917d8fa96b24a5f66882c2fc43c0a61b640742..03446dbeabdca2056ec405184bdb9861f047e1c7 100644 --- a/openerp/report/report_sxw.py +++ b/openerp/report/report_sxw.py @@ -192,7 +192,9 @@ class rml_parse(object): elif (hasattr(obj, '_field') and\ isinstance(obj._field, (float_field, function_field)) and\ obj._field.digits): - d = obj._field.digits[1] or DEFAULT_DIGITS + d = obj._field.digits[1] + if not d and d is not 0: + d = DEFAULT_DIGITS return d def formatLang(self, value, digits=None, date=False, date_time=False, grouping=True, monetary=False, dp=False, currency_obj=False): diff --git a/openerp/tools/yaml_import.py b/openerp/tools/yaml_import.py index 2a56ffeb0bc48216b3da45d62d16602ff9f235dd..eaca88952a3d77a6817499470f8ffbd6ed4019c8 100644 --- a/openerp/tools/yaml_import.py +++ b/openerp/tools/yaml_import.py @@ -461,16 +461,15 @@ class YamlInterpreter(object): result = getattr(model, match.group(1))(self.cr, SUPERUSER_ID, [], *args) for key, val in (result or {}).get('value', {}).items(): - assert key in fg, ( - "The field %r returned from the onchange call %r " - "does not exist in the source view %r (of object " - "%r). This field will be ignored (and thus not " - "populated) when clients saves the new record" % ( - key, match.group(1), view_info.get('name', '?'), model._name - )) - if key not in fields: - # do not shadow values explicitly set in yaml. - record_dict[key] = process_val(key, val) + if key in fg: + if key not in fields: + # do not shadow values explicitly set in yaml. + record_dict[key] = process_val(key, val) + else: + _logger.debug("The returning field '%s' from your on_change call '%s'" + " does not exist either on the object '%s', either in" + " the view '%s'", + key, match.group(1), model._name, view_info['name']) else: nodes = list(el) + nodes else: