diff --git a/addons/account/account.py b/addons/account/account.py index c2c57c6c28e..5930377caa9 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -30,7 +30,7 @@ from openerp import SUPERUSER_ID from openerp import tools from openerp.osv import fields, osv, expression from openerp.tools.translate import _ -from openerp.tools.float_utils import float_round +from openerp.tools.float_utils import float_round as round import openerp.addons.decimal_precision as dp @@ -1938,15 +1938,15 @@ class account_tax(osv.osv): # 'base_code_id': fields.many2one('account.tax.code', 'Account Base Code', help="Use this code for the tax declaration."), 'tax_code_id': fields.many2one('account.tax.code', 'Account Tax Code', help="Use this code for the tax declaration."), - 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), - 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), + 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), + 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), # Same fields for refund invoices 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the tax declaration."), 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the tax declaration."), - 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), - 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), + 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), + 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), 'include_base_amount': fields.boolean('Included in base amount', help="Indicates if the amount of tax must be included in the base amount for the computation of the next taxes"), 'company_id': fields.many2one('res.company', 'Company', required=True), 'description': fields.char('Tax Code'), @@ -2144,7 +2144,7 @@ class account_tax(osv.osv): tax_compute_precision = precision if taxes and taxes[0].company_id.tax_calculation_rounding_method == 'round_globally': tax_compute_precision += 5 - totalin = totalex = float_round(price_unit * quantity, precision) + totalin = totalex = round(price_unit * quantity, precision) tin = [] tex = [] for tax in taxes: diff --git a/addons/account_check_writing/account_voucher.py b/addons/account_check_writing/account_voucher.py index e4933708f7a..c3abe28eef0 100644 --- a/addons/account_check_writing/account_voucher.py +++ b/addons/account_check_writing/account_voucher.py @@ -41,6 +41,23 @@ class account_voucher(osv.osv): 'number': fields.char('Number', size=32), } + def _amount_to_text(self, cr, uid, amount, currency_id, context=None): + # Currency complete name is not available in res.currency model + # Exceptions done here (EUR, USD, BRL) cover 75% of cases + # For other currencies, display the currency code + currency = self.pool['res.currency'].browse(cr, uid, currency_id, context=context) + if currency.name.upper() == 'EUR': + currency_name = 'Euro' + elif currency.name.upper() == 'USD': + currency_name = 'Dollars' + elif currency.name.upper() == 'BRL': + currency_name = 'reais' + else: + currency_name = currency.name + #TODO : generic amount_to_text is not ready yet, otherwise language (and country) and currency can be passed + #amount_in_word = amount_to_text(amount, context=context) + return amount_to_text(amount, currency=currency_name) + def onchange_amount(self, cr, uid, ids, amount, rate, partner_id, journal_id, currency_id, ttype, date, payment_rate_currency_id, company_id, context=None): """ Inherited - add amount_in_word and allow_check_writting in returned value dictionary """ if not context: @@ -48,22 +65,7 @@ class account_voucher(osv.osv): default = super(account_voucher, self).onchange_amount(cr, uid, ids, amount, rate, partner_id, journal_id, currency_id, ttype, date, payment_rate_currency_id, company_id, context=context) if 'value' in default: amount = 'amount' in default['value'] and default['value']['amount'] or amount - - # Currency complete name is not available in res.currency model - # Exceptions done here (EUR, USD, BRL) cover 75% of cases - # For other currencies, display the currency code - currency = self.pool['res.currency'].browse(cr, uid, currency_id, context=context) - if currency.name.upper() == 'EUR': - currency_name = 'Euro' - elif currency.name.upper() == 'USD': - currency_name = 'Dollars' - elif currency.name.upper() == 'BRL': - currency_name = 'reais' - else: - currency_name = currency.name - #TODO : generic amount_to_text is not ready yet, otherwise language (and country) and currency can be passed - #amount_in_word = amount_to_text(amount, context=context) - amount_in_word = amount_to_text(amount, currency=currency_name) + amount_in_word = self._amount_to_text(cr, uid, amount, currency_id, context=context) default['value'].update({'amount_in_word':amount_in_word}) if journal_id: allow_check_writing = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context).allow_check_writing @@ -92,6 +94,19 @@ class account_voucher(osv.osv): }, 'nodestroy': True } + def create(self, cr, uid, vals, context=None): + if vals.get('amount') and vals.get('journal_id') and 'amount_in_word' not in vals: + vals['amount_in_word'] = self._amount_to_text(cr, uid, vals['amount'], vals.get('currency_id') or \ + self.pool['account.journal'].browse(cr, uid, vals['journal_id'], context=context).currency.id or \ + self.pool['res.company'].browse(cr, uid, vals['company_id']).currency_id.id, context=context) + return super(account_voucher, self).create(cr, uid, vals, context=context) + + def write(self, cr, uid, ids, vals, context=None): + if vals.get('amount') and vals.get('journal_id') and 'amount_in_word' not in vals: + vals['amount_in_word'] = self._amount_to_text(cr, uid, vals['amount'], vals.get('currency_id') or \ + self.pool['account.journal'].browse(cr, uid, vals['journal_id'], context=context).currency.id or \ + self.pool['res.company'].browse(cr, uid, vals['company_id']).currency_id.id, context=context) + return super(account_voucher, self).write(cr, uid, ids, vals, context=context) def fields_view_get(self, cr, uid, view_id=None, view_type=False, context=None, toolbar=False, submenu=False): """ diff --git a/addons/audittrail/audittrail.py b/addons/audittrail/audittrail.py index ca4a15bca6e..71008e0855b 100644 --- a/addons/audittrail/audittrail.py +++ b/addons/audittrail/audittrail.py @@ -463,14 +463,12 @@ def process_data(cr, uid, pool, res_ids, model, method, old_values=None, new_val # if at least one modification has been found for model_id, resource_id in lines: line_model = pool.get('ir.model').browse(cr, SUPERUSER_ID, model_id).model - name = pool.get(line_model).name_get(cr, uid, [resource_id])[0][1] vals = { 'method': method, 'object_id': model_id, 'user_id': uid, 'res_id': resource_id, - 'name': name, } if (model_id, resource_id) not in old_values and method not in ('copy', 'read'): # the resource was not existing so we are forcing the method to 'create' @@ -481,7 +479,11 @@ def process_data(cr, uid, pool, res_ids, model, method, old_values=None, new_val # the resource is not existing anymore so we are forcing the method to 'unlink' # (because it could also come with the value 'write' if we are deleting the # record through a one2many field) + name = old_values[(model_id, resource_id)]['value'].get('name',False) vals.update({'method': 'unlink'}) + else : + name = pool[line_model].name_get(cr, uid, [resource_id])[0][1] + vals.update({'name': name}) # create the audittrail log in super admin mode, only if a change has been detected if lines[(model_id, resource_id)]: log_id = pool.get('audittrail.log').create(cr, SUPERUSER_ID, vals) diff --git a/addons/base_calendar/base_calendar.py b/addons/base_calendar/base_calendar.py index 25a3a1e211b..1ed59d9779a 100644 --- a/addons/base_calendar/base_calendar.py +++ b/addons/base_calendar/base_calendar.py @@ -1012,15 +1012,20 @@ class calendar_event(osv.osv): result[event] = "" return result + # hook method to fix the wrong signature + def _set_rulestring(self, cr, uid, ids, field_name, field_value, args, context=None): + return self._rrule_write(self, cr, uid, ids, field_name, field_value, args, context=context) + def _rrule_write(self, obj, cr, uid, ids, field_name, field_value, args, context=None): + if not isinstance(ids, list): + ids = [ids] data = self._get_empty_rrule_data() if field_value: data['recurrency'] = True for event in self.browse(cr, uid, ids, context=context): - rdate = rule_date or event.date - update_data = self._parse_rrule(field_value, dict(data), rdate) + update_data = self._parse_rrule(field_value, dict(data), event.date) data.update(update_data) - super(calendar_event, obj).write(cr, uid, ids, data, context=context) + super(calendar_event, self).write(cr, uid, ids, data, context=context) return True _columns = { @@ -1048,7 +1053,7 @@ defines the list of date/time exceptions for a recurring calendar component."), 'exrule': fields.char('Exception Rule', size=352, help="Defines a \ rule or repeating pattern of time to exclude from the recurring rule."), 'rrule': fields.function(_get_rulestring, type='char', size=124, \ - fnct_inv=_rrule_write, store=True, string='Recurrent Rule'), + fnct_inv=_set_rulestring, store=True, string='Recurrent Rule'), 'rrule_type': fields.selection([ ('daily', 'Day(s)'), ('weekly', 'Week(s)'), @@ -1372,7 +1377,7 @@ rule or repeating pattern of time to exclude from the recurring rule."), #repeat monthly by nweekday ((weekday, weeknumber), ) if r._bynweekday: data['week_list'] = day_list[r._bynweekday[0][0]].upper() - data['byday'] = r._bynweekday[0][1] + data['byday'] = str(r._bynweekday[0][1]) data['select1'] = 'day' data['rrule_type'] = 'monthly' @@ -1499,7 +1504,7 @@ rule or repeating pattern of time to exclude from the recurring rule."), # set end_date for calendar searching if vals.get('recurrency', True) and vals.get('end_type', 'count') in ('count', unicode('count')) and \ (vals.get('rrule_type') or vals.get('count') or vals.get('date') or vals.get('date_deadline')): - for data in self.read(cr, uid, ids, ['date', 'date_deadline', 'recurrency', 'rrule_type', 'count', 'end_type'], context=context): + for data in self.read(cr, uid, ids, ['end_date', 'date_deadline', 'recurrency', 'rrule_type', 'count', 'end_type'], context=context): end_date = self._set_recurrency_end_date(data, context=context) super(calendar_event, self).write(cr, uid, [data['id']], {'end_date': end_date}, context=context) @@ -1622,21 +1627,23 @@ rule or repeating pattern of time to exclude from the recurring rule."), return res def _set_recurrency_end_date(self, data, context=None): + if not data.get('recurrency'): + return False + + end_type = data.get('end_type') end_date = data.get('end_date') - rel_date = False - if data.get('recurrency') and data.get('end_type') in ('count', unicode('count')): - data_date_deadline = datetime.strptime(data.get('date_deadline'), '%Y-%m-%d %H:%M:%S') - if data.get('rrule_type') in ('daily', unicode('count')): - rel_date = relativedelta(days=data.get('count')+1) - elif data.get('rrule_type') in ('weekly', unicode('weekly')): - rel_date = relativedelta(days=(data.get('count')+1)*7) - elif data.get('rrule_type') in ('monthly', unicode('monthly')): - rel_date = relativedelta(months=data.get('count')+1) - elif data.get('rrule_type') in ('yearly', unicode('yearly')): - rel_date = relativedelta(years=data.get('count')+1) - end_date = data_date_deadline - if rel_date: - end_date += rel_date + + if end_type == 'count' and all(data.get(key) for key in ['count', 'rrule_type', 'date_deadline']): + count = data['count'] + 1 + delay, mult = { + 'daily': ('days', 1), + 'weekly': ('days', 7), + 'monthly': ('months', 1), + 'yearly': ('years', 1), + }[data['rrule_type']] + + deadline = datetime.strptime(data['date_deadline'], tools.DEFAULT_SERVER_DATETIME_FORMAT) + return deadline + relativedelta(**{delay: count * mult}) return end_date def create(self, cr, uid, vals, context=None): @@ -1646,9 +1653,12 @@ rule or repeating pattern of time to exclude from the recurring rule."), if vals.get('vtimezone', '') and vals.get('vtimezone', '').startswith('/freeassociation.sourceforge.net/tzfile/'): vals['vtimezone'] = vals['vtimezone'][40:] - vals['end_date'] = self._set_recurrency_end_date(vals, context=context) res = super(calendar_event, self).create(cr, uid, vals, context) + data = self.read(cr, uid, [res], ['end_date', 'date_deadline', 'recurrency', 'rrule_type', 'count', 'end_type'], context=context)[0] + end_date = self._set_recurrency_end_date(data, context=context) + self.write(cr, uid, [res], {'end_date': end_date}, context=context) + alarm_obj = self.pool.get('res.alarm') alarm_obj.do_alarm_create(cr, uid, [res], self._name, 'date', context=context) self.create_attendees(cr, uid, [res], context) diff --git a/addons/base_calendar/test/base_calendar_test.yml b/addons/base_calendar/test/base_calendar_test.yml index 7afd2e49551..c280bfe298a 100644 --- a/addons/base_calendar/test/base_calendar_test.yml +++ b/addons/base_calendar/test/base_calendar_test.yml @@ -52,3 +52,20 @@ - !python {model: calendar.event}: | self.write(cr, uid, [ref("calendar_event_alldaytestevent0")], {'alarm_id': ref("res_alarm_daybeforeeventstarts0")}) +- + I create a recuring rule for my event +- + !record {model: crm.meeting, id: crm_meeting_sprintreview1}: + name: Begin of month meeting + date: !eval time.strftime('%Y-%m-%d 12:00:00') + recurrency: true + rrule: FREQ=MONTHLY;INTERVAL=1;COUNT=12;BYDAY=1MO +- + I check that the attributes are set correctly +- + !assert {model: crm.meeting, id: crm_meeting_sprintreview1}: + - rrule_type == 'monthly' + - count == 12 + - select1 == 'day' + - byday == '1' + - week_list == 'MO' diff --git a/addons/base_import/models.py b/addons/base_import/models.py index 78e8ba3b17e..7ee92e5f4cc 100644 --- a/addons/base_import/models.py +++ b/addons/base_import/models.py @@ -11,6 +11,7 @@ except ImportError: import psycopg2 from openerp.osv import orm, fields +from openerp.osv.orm import BaseModel from openerp.tools.translate import _ FIELDS_RECURSION_LIMIT = 2 @@ -318,8 +319,12 @@ class ir_import(orm.TransientModel): }] _logger.info('importing %d rows...', len(data)) - import_result = self.pool[record.res_model].load( - cr, uid, import_fields, data, context=context) + # DO NOT FORWARD PORT, already fixed in trunk + # hack to avoid to call the load method from ir_translation (name clash) + if record.res_model == 'ir.translation': + import_result = BaseModel.load(self.pool['ir.translation'], cr, uid, import_fields, data, context=context) + else: + import_result = self.pool[record.res_model].load(cr, uid, import_fields, data, context=context) _logger.info('done') # If transaction aborted, RELEASE SAVEPOINT is going to raise diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 2ef6e7d6ef1..2dfd84f05af 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -312,7 +312,10 @@ class crm_lead(format_address, osv.osv): stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context=context) if not stage.on_change: return {'value': {}} - return {'value': {'probability': stage.probability}} + vals = {'probability': stage.probability} + if stage.probability >= 100 or (stage.probability == 0 and stage.sequence > 1): + vals['date_closed'] = fields.datetime.now() + return {'value': vals} def on_change_partner_id(self, cr, uid, ids, partner_id, context=None): values = {} @@ -406,7 +409,7 @@ class crm_lead(format_address, osv.osv): 'probability = 0 %, select "Change Probability Automatically".\n' 'Create a specific stage or edit an existing one by editing columns of your opportunity pipe.')) for stage_id, lead_ids in stages_leads.items(): - self.write(cr, uid, lead_ids, {'stage_id': stage_id, 'date_closed': fields.datetime.now()}, context=context) + self.write(cr, uid, lead_ids, {'stage_id': stage_id}, context=context) return True def case_mark_won(self, cr, uid, ids, context=None): @@ -427,7 +430,7 @@ class crm_lead(format_address, osv.osv): 'probability = 100 % and select "Change Probability Automatically".\n' 'Create a specific stage or edit an existing one by editing columns of your opportunity pipe.')) for stage_id, lead_ids in stages_leads.items(): - self.write(cr, uid, lead_ids, {'stage_id': stage_id, 'date_closed': fields.datetime.now()}, context=context) + self.write(cr, uid, lead_ids, {'stage_id': stage_id}, context=context) return True def case_escalate(self, cr, uid, ids, context=None): diff --git a/addons/l10n_fr/fr_fiscal_templates.xml b/addons/l10n_fr/fr_fiscal_templates.xml index bf49d15e08c..cca96422c22 100644 --- a/addons/l10n_fr/fr_fiscal_templates.xml +++ b/addons/l10n_fr/fr_fiscal_templates.xml @@ -40,11 +40,6 @@ - - - - - @@ -57,22 +52,12 @@ - - - - - - - - - - @@ -91,17 +76,6 @@ - - - - - - - - - - - @@ -124,17 +98,6 @@ - - - - - - - - - - - @@ -146,17 +109,6 @@ - - - - - - - - - - - @@ -177,11 +129,6 @@ - - - - - @@ -194,22 +141,12 @@ - - - - - - - - - - @@ -224,11 +161,6 @@ - - - - - @@ -241,23 +173,12 @@ - - - - - - - - - - - diff --git a/addons/l10n_fr/fr_tax.xml b/addons/l10n_fr/fr_tax.xml index 8633cca7141..22e1e12f03c 100644 --- a/addons/l10n_fr/fr_tax.xml +++ b/addons/l10n_fr/fr_tax.xml @@ -29,28 +29,6 @@ sale - - - - TVA collectée (vente) 19,6% - 19.6 - - percent - - - - - - - - - - - - - - sale - @@ -96,51 +74,7 @@ sale - - - TVA collectée (vente) 7,0% - 7.0 - - percent - - - - - - - - - - - - - - sale - - - - TVA collectée (vente) 5,0% - 5.0 - - percent - - - - - - - - - - - - - - sale - - - TVA collectée (vente) 5,5% 5.5 @@ -208,28 +142,6 @@ purchase - - - TVA déductible (achat) 19,6% - ACH-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA déductible (achat) 8,5% @@ -274,51 +186,7 @@ purchase - - - TVA déductible (achat) 7,0% - ACH-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA déductible (achat) 5,0% - ACH-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA déductible (achat) 5,5% ACH-5.5 @@ -387,29 +255,6 @@ purchase - - - TVA déductible (achat) 19,6% TTC - ACH-19.6-TTC - - - percent - - - - - - - - - - - - - - purchase - - TVA déductible (achat) 8,5% TTC @@ -456,53 +301,7 @@ purchase - - - TVA déductible (achat) 7,0% TTC - ACH-7.0-TTC - - - percent - - - - - - - - - - - - - - purchase - - - - TVA déductible (achat) 5,0% TTC - ACH-5.0-TTC - - - percent - - - - - - - - - - - - - - purchase - - - TVA déductible (achat) 5,5% TTC ACH-5.5-TTC @@ -573,28 +372,6 @@ purchase - - - TVA déd./immobilisation (achat) 19,6% - IMMO-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA déd./immobilisation (achat) 8,5% @@ -639,51 +416,7 @@ purchase - - - TVA déd./immobilisation (achat) 7,0% - IMMO-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA déd./immobilisation (achat) 5,0% - IMMO-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA déd./immobilisation (achat) 5,5% IMMO-5.5 @@ -751,28 +484,6 @@ purchase - - - TVA due s/ acq. intracommunautaire (achat) 19,6% - ACH_UE_due-19.6 - - percent - - - - - - - - - - - - - - purchase - - TVA due s/ acq. intracommunautaire (achat) 8,5% @@ -817,51 +528,7 @@ purchase - - - TVA due s/ acq. intracommunautaire (achat) 7,0% - ACH_UE_due-7.0 - - percent - - - - - - - - - - - - - - purchase - - - - TVA due s/ acq. intracommunautaire (achat) 5,0% - ACH_UE_due-5.0 - - percent - - - - - - - - - - - - - - purchase - - - TVA due s/ acq. intracommunautaire (achat) 5,5% ACH_UE_due-5.5 @@ -925,24 +592,6 @@ purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 19,6% - ACH_UE_ded.-19.6 - - percent - - - - - - - - - - purchase - - TVA déd. s/ acq. intracommunautaire (achat) 8,5% @@ -979,43 +628,7 @@ purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 7,0% - ACH_UE_ded.-7.0 - - percent - - - - - - - - - - purchase - - - - TVA déd. s/ acq. intracommunautaire (achat) 5,0% - ACH_UE_ded.-5.0 - - percent - - - - - - - - - - purchase - - - TVA déd. s/ acq. intracommunautaire (achat) 5,5% ACH_UE_ded.-5.5 diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 4c4a60bf99c..6b587f466b5 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -156,6 +156,7 @@ class mail_thread(osv.AbstractModel): if res[id]['message_unread_count']: title = res[id]['message_unread_count'] > 1 and _("You have %d unread messages") % res[id]['message_unread_count'] or _("You have one unread message") res[id]['message_summary'] = "9 %d %s" % (title, res[id].pop('message_unread_count'), _("New")) + res[id].pop('message_unread_count', None) return res def read_followers_data(self, cr, uid, follower_ids, context=None): diff --git a/addons/pad/pad.py b/addons/pad/pad.py index 474321edb1d..ab6f3d6b18c 100644 --- a/addons/pad/pad.py +++ b/addons/pad/pad.py @@ -14,6 +14,10 @@ _logger = logging.getLogger(__name__) class pad_common(osv.osv_memory): _name = 'pad.common' + def pad_is_configured(self, cr, uid, context=None): + user = self.pool.get('res.users').browse(cr, uid, uid, context=context) + return bool(user.company_id.pad_server) + def pad_generate_url(self, cr, uid, context=None): company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id; diff --git a/addons/pad/static/src/css/etherpad.css b/addons/pad/static/src/css/etherpad.css index facf15b28de..d2d8b748ab5 100644 --- a/addons/pad/static/src/css/etherpad.css +++ b/addons/pad/static/src/css/etherpad.css @@ -71,6 +71,7 @@ .oe_pad_loading{ text-align: center; opacity: 0.75; + font-style: italic; } .etherpad_readonly ul, .etherpad_readonly ol { diff --git a/addons/pad/static/src/js/pad.js b/addons/pad/static/src/js/pad.js index 97f87196cb9..e04184bc8f7 100644 --- a/addons/pad/static/src/js/pad.js +++ b/addons/pad/static/src/js/pad.js @@ -1,65 +1,81 @@ openerp.pad = function(instance) { + var _t = instance.web._t; instance.web.form.FieldPad = instance.web.form.AbstractField.extend(instance.web.form.ReinitializeWidgetMixin, { template: 'FieldPad', content: "", init: function() { + var self = this; this._super.apply(this, arguments); - this.set("configured", true); - this.on("change:configured", this, this.switch_configured); + this._configured_deferred = this.view.dataset.call('pad_is_configured').done(function(data) { + self.set("configured", !!data); + }).fail(function(data, event) { + event.preventDefault(); + self.set("configured", true); + }); }, initialize_content: function() { var self = this; - this.switch_configured(); this.$('.oe_pad_switch').click(function() { self.$el.toggleClass('oe_pad_fullscreen'); self.view.$el.find('.oe_chatter').toggle(); }); + this._configured_deferred.always(function() { + var configured = self.get('configured'); + self.$(".oe_unconfigured").toggle(!configured); + self.$(".oe_configured").toggle(configured); + }); this.render_value(); }, - switch_configured: function() { - this.$(".oe_unconfigured").toggle(! this.get("configured")); - this.$(".oe_configured").toggle(this.get("configured")); - }, render_value: function() { - var self = this; - if (this.get("configured") && ! this.get("value")) { - self.view.dataset.call('pad_generate_url', { - context: { - model: self.view.model, - field_name: self.name, - object_id: self.view.datarecord.id - }, - }).done(function(data) { - if (! data.url) { - self.set("configured", false); + var self = this; + this._configured_deferred.always(function() { + if (! self.get('configured')) { + return; + }; + var value = self.get('value'); + if (self.get('effective_readonly')) { + if (_.str.startsWith(value, 'http')) { + this.pad_loading_request = self.view.dataset.call('pad_get_content', {url: value}).done(function(data) { + self.$('.oe_pad_content').removeClass('oe_pad_loading').html('
'); + self.$('.oe_pad_readonly').html(data); + }).fail(function() { + self.$('.oe_pad_content').text(_t('Unable to load pad')); + }); } else { - self.set("value", data.url); + self.$('.oe_pad_content').addClass('oe_pad_loading').show().text(_t("This pad will be initialized on first edit")); } - }); - } - this.$('.oe_pad_content').html(""); - var value = this.get('value'); - if (this.pad_loading_request) { - this.pad_loading_request.abort(); - } - if (_.str.startsWith(value, 'http')) { - if (! this.get('effective_readonly')) { - var content = ''; - this.$('.oe_pad_content').html(content); - this._dirty_flag = true; - } else { - this.content = '
... Loading pad ...
'; - this.pad_loading_request = $.get(value + '/export/html').done(function(data) { - groups = /\<\s*body\s*\>(.*?)\<\s*\/body\s*\>/.exec(data); - data = (groups || []).length >= 2 ? groups[1] : ''; - self.$('.oe_pad_content').html('
'); - self.$('.oe_pad_readonly').html(data); - }).fail(function() { - self.$('.oe_pad_content').text('Unable to load pad'); + } + else { + var def = $.when(); + if (! value || !_.str.startsWith(value, 'http')) { + def = self.view.dataset.call('pad_generate_url', { + context: { + model: self.view.model, + field_name: self.name, + object_id: self.view.datarecord.id + }, + }).done(function(data) { + if (! data.url) { + self.set("configured", false); + } else { + self.set("value", data.url); + } + }); + } + def.then(function() { + value = self.get('value'); + if (_.str.startsWith(value, 'http')) { + var content = ''; + self.$('.oe_pad_content').html(content); + self._dirty_flag = true; + } + else { + self.$('.oe_pad_content').text(value); + } }); } - } + }); }, }); diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index ea43de0152e..9a20047a04d 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -497,10 +497,14 @@ class pos_order(osv.osv): _description = "Point of Sale" _order = "id desc" - def create_from_ui(self, cr, uid, orders, context=None): - #_logger.info("orders: %r", orders) + def create_from_ui(self, cr, uid, orders, context=None): + # Keep only new orders + submitted_references = [o['data']['name'] for o in orders] + existing_orders = self.search_read(cr, uid, domain=[('pos_reference', 'in', submitted_references)], fields=['pos_reference'], context=context) + existing_references = set([o['pos_reference'] for o in existing_orders]) + orders_to_save = [o for o in orders if o['data']['name'] not in existing_references] order_ids = [] - for tmp_order in orders: + for tmp_order in orders_to_save: to_invoice = tmp_order['to_invoice'] order = tmp_order['data'] diff --git a/addons/point_of_sale/security/point_of_sale_security.xml b/addons/point_of_sale/security/point_of_sale_security.xml index 54572ee3eb9..5e98bcba49e 100644 --- a/addons/point_of_sale/security/point_of_sale_security.xml +++ b/addons/point_of_sale/security/point_of_sale_security.xml @@ -19,5 +19,11 @@ [('company_id', '=', user.company_id.id)] + + Point Of Sale Config + + + [('warehouse_id.company_id','child_of',[user.company_id.id])] + diff --git a/addons/portal/mail_message.py b/addons/portal/mail_message.py index 4429df3be8b..36b06484e9e 100644 --- a/addons/portal/mail_message.py +++ b/addons/portal/mail_message.py @@ -35,14 +35,14 @@ class mail_message(osv.Model): """ if uid == SUPERUSER_ID: return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order, - context=context, count=False, access_rights_uid=access_rights_uid) + context=context, count=count, access_rights_uid=access_rights_uid) group_ids = self.pool.get('res.users').browse(cr, uid, uid, context=context).groups_id group_user_id = self.pool.get("ir.model.data").get_object_reference(cr, uid, 'base', 'group_user')[1] if group_user_id not in [group.id for group in group_ids]: args = [('subtype_id', '!=', False)] + list(args) return super(mail_message, self)._search(cr, uid, args, offset=offset, limit=limit, order=order, - context=context, count=False, access_rights_uid=access_rights_uid) + context=context, count=count, access_rights_uid=access_rights_uid) def check_access_rule(self, cr, uid, ids, operation, context=None): """ Add Access rules of mail.message for non-employee user: diff --git a/addons/project/project.py b/addons/project/project.py index 273fb4fcf3e..d5f4f891c61 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -729,9 +729,10 @@ class task(osv.osv): context = {} if default is None: default = {} - stage = self._get_default_stage_id(cr, uid, context=context) - if stage: - default['stage_id'] = stage + if not context.get('copy', False): + stage = self._get_default_stage_id(cr, uid, context=context) + if stage: + default['stage_id'] = stage return super(task, self).copy(cr, uid, id, default, context) def _is_template(self, cr, uid, ids, field_name, arg, context=None): diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index f53111bf47a..fa2a415f443 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -634,10 +634,9 @@ class purchase_order(osv.osv): 'name': self.pool.get('ir.sequence').get(cr, uid, 'stock.picking.in'), 'origin': order.name + ((order.origin and (':' + order.origin)) or ''), 'date': self.date_to_datetime(cr, uid, order.date_order, context), - 'partner_id': order.dest_address_id.id or order.partner_id.id, + 'partner_id': order.partner_id.id, 'invoice_state': '2binvoiced' if order.invoice_method == 'picking' else 'none', 'type': 'in', - 'partner_id': order.dest_address_id.id or order.partner_id.id, 'purchase_id': order.id, 'company_id': order.company_id.id, 'move_lines' : [], diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index 3f31837e38e..e2caa76f911 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -108,7 +108,7 @@ class purchase_requisition(osv.osv): seller_delay = product_supplier.delay seller_qty = product_supplier.qty supplier_pricelist = supplier.property_product_pricelist_purchase or False - seller_price = pricelist.price_get(cr, uid, [supplier_pricelist.id], product.id, qty, False, {'uom': default_uom_po_id})[supplier_pricelist.id] + seller_price = pricelist.price_get(cr, uid, [supplier_pricelist.id], product.id, qty, supplier.id, {'uom': default_uom_po_id})[supplier_pricelist.id] if seller_qty: qty = max(qty,seller_qty) date_planned = self._planned_date(requisition_line.requisition_id, seller_delay) diff --git a/addons/report_webkit/default_header.html b/addons/report_webkit/default_header.html index 0798209a5b7..37fd4135b62 100644 --- a/addons/report_webkit/default_header.html +++ b/addons/report_webkit/default_header.html @@ -1,3 +1,4 @@ + diff --git a/addons/report_webkit/webkit_report.py b/addons/report_webkit/webkit_report.py index 4f56d7efea7..60fb536ca96 100644 --- a/addons/report_webkit/webkit_report.py +++ b/addons/report_webkit/webkit_report.py @@ -112,6 +112,7 @@ def webkit_report_extender(report_name): return fct return fct1 + class WebKitParser(report_sxw): """Custom class that use webkit to render HTML reports Code partially taken from report openoffice. Thanks guys :) @@ -173,7 +174,7 @@ class WebKitParser(report_sxw): ), 'w' ) - head_file.write(header.encode('utf-8')) + head_file.write(self._sanitize_html(header.encode('utf-8'))) head_file.close() file_to_del.append(head_file.name) command.extend(['--header-html', head_file.name]) @@ -184,7 +185,7 @@ class WebKitParser(report_sxw): ), 'w' ) - foot_file.write(footer.encode('utf-8')) + foot_file.write(self._sanitize_html(footer.encode('utf-8'))) foot_file.close() file_to_del.append(foot_file.name) command.extend(['--footer-html', foot_file.name]) @@ -205,7 +206,7 @@ class WebKitParser(report_sxw): for html in html_list : html_file = file(os.path.join(tmp_dir, str(time.time()) + str(count) +'.body.html'), 'w') count += 1 - html_file.write(html.encode('utf-8')) + html_file.write(self._sanitize_html(html.encode('utf-8'))) html_file.close() file_to_del.append(html_file.name) command.append(html_file.name) @@ -366,7 +367,6 @@ class WebKitParser(report_sxw): pdf = self.generate_pdf(bin, report_xml, head, foot, htmls) return (pdf, 'pdf') - def create(self, cursor, uid, ids, data, context=None): """We override the create function in order to handle generator Code taken from report openoffice. Thanks guys :) """ @@ -387,11 +387,18 @@ class WebKitParser(report_sxw): report_xml.report_sxw = None else: return super(WebKitParser, self).create(cursor, uid, ids, data, context) - if report_xml.report_type != 'webkit' : + if report_xml.report_type != 'webkit': return super(WebKitParser, self).create(cursor, uid, ids, data, context) result = self.create_source_pdf(cursor, uid, ids, data, report_xml, context) if not result: return (False,False) return result + def _sanitize_html(self, html): + """wkhtmltopdf expects the html page to declare a doctype. + """ + if html and html[:9].upper() != "\n" + html + return html + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/sale.py b/addons/sale/sale.py index 3512d44f7c7..f1a7c704b02 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -180,8 +180,8 @@ class sale_order(osv.osv): 'date_confirm': fields.date('Confirmation Date', readonly=True, select=True, help="Date on which sales order is confirmed."), 'user_id': fields.many2one('res.users', 'Salesperson', states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, select=True, track_visibility='onchange'), 'partner_id': fields.many2one('res.partner', 'Customer', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, required=True, change_default=True, select=True, track_visibility='always'), - 'partner_invoice_id': fields.many2one('res.partner', 'Invoice Address', domain="[('parent_id','=',partner_id)]", readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order."), - 'partner_shipping_id': fields.many2one('res.partner', 'Delivery Address', domain="[('parent_id','=',partner_id)]", readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Delivery address for current sales order."), + 'partner_invoice_id': fields.many2one('res.partner', 'Invoice Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Invoice address for current sales order."), + 'partner_shipping_id': fields.many2one('res.partner', 'Delivery Address', readonly=True, required=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, help="Delivery address for current sales order."), 'order_policy': fields.selection([ ('manual', 'On Demand'), ], 'Create Invoice', required=True, readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]}, @@ -501,7 +501,7 @@ class sale_order(osv.osv): lines.append(line.id) created_lines = obj_sale_order_line.invoice_line_create(cr, uid, lines) if created_lines: - invoices.setdefault(o.partner_id.id, []).append((o, created_lines)) + invoices.setdefault(o.partner_invoice_id.id or o.partner_id.id, []).append((o, created_lines)) if not invoices: for o in self.browse(cr, uid, ids, context=context): for i in o.invoice_ids: diff --git a/addons/sale/wizard/sale_make_invoice.py b/addons/sale/wizard/sale_make_invoice.py index 3712feda20b..170259824f7 100644 --- a/addons/sale/wizard/sale_make_invoice.py +++ b/addons/sale/wizard/sale_make_invoice.py @@ -55,11 +55,12 @@ class sale_make_invoice(osv.osv_memory): raise osv.except_osv(_('Warning!'), _("You shouldn't manually invoice the following sale order %s") % (sale_order.name)) order_obj.action_invoice_create(cr, uid, context.get(('active_ids'), []), data['grouped'], date_invoice=data['invoice_date']) - - for o in order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context): + orders = order_obj.browse(cr, uid, context.get(('active_ids'), []), context=context) + for o in orders: for i in o.invoice_ids: newinv.append(i.id) - + # Dummy call to workflow, will not create another invoice but bind the new invoice to the subflow + order_obj.signal_manual_invoice(cr, uid, [o.id for o in orders if o.order_policy == 'manual']) result = mod_obj.get_object_reference(cr, uid, 'account', 'action_invoice_tree1') id = result and result[1] or False result = act_obj.read(cr, uid, [id], context=context)[0] diff --git a/addons/share/wizard/share_wizard.py b/addons/share/wizard/share_wizard.py index f6ee6379ef1..0a21d228ffd 100644 --- a/addons/share/wizard/share_wizard.py +++ b/addons/share/wizard/share_wizard.py @@ -925,7 +925,7 @@ class share_result_line(osv.osv_memory): 'login': fields.related('user_id', 'login', string='Login', type='char', size=64, required=True, readonly=True), 'password': fields.char('Password', size=64, readonly=True), 'share_url': fields.function(_share_url, string='Share URL', type='char', size=512), - 'share_wizard_id': fields.many2one('share.wizard', 'Share Wizard', required=True), + 'share_wizard_id': fields.many2one('share.wizard', 'Share Wizard', required=True, ondelete='cascade'), 'newly_created': fields.boolean('Newly created', readonly=True), } _defaults = { diff --git a/addons/stock/stock.py b/addons/stock/stock.py index d48ff80b1c1..7e73d7fcd53 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2232,14 +2232,14 @@ class stock_move(osv.osv): if move.picking_id: pickings.add(move.picking_id.id) if move.move_dest_id and move.move_dest_id.state == 'waiting': - self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}) + self.write(cr, uid, [move.move_dest_id.id], {'state': 'confirmed'}, context=context) if context.get('call_unlink',False) and move.move_dest_id.picking_id: workflow.trg_write(uid, 'stock.picking', move.move_dest_id.picking_id.id, cr) - self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}) + self.write(cr, uid, ids, {'state': 'cancel', 'move_dest_id': False}, context=context) if not context.get('call_unlink',False): for pick in self.pool.get('stock.picking').browse(cr, uid, list(pickings), context=context): if all(move.state == 'cancel' for move in pick.move_lines): - self.pool.get('stock.picking').write(cr, uid, [pick.id], {'state': 'cancel'}) + self.pool.get('stock.picking').write(cr, uid, [pick.id], {'state': 'cancel'}, context=context) for id in ids: workflow.trg_trigger(uid, 'stock.move', id, cr) diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 0443b888ef7..18eddb7a6aa 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -786,7 +786,7 @@ - + @@ -920,7 +920,7 @@ - +
@@ -1047,7 +1047,7 @@ - +
diff --git a/addons/stock/wizard/stock_change_product_qty.py b/addons/stock/wizard/stock_change_product_qty.py index 01ed6f2409a..cf4099ac3ba 100644 --- a/addons/stock/wizard/stock_change_product_qty.py +++ b/addons/stock/wizard/stock_change_product_qty.py @@ -33,6 +33,10 @@ class stock_change_product_qty(osv.osv_memory): 'prodlot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"), 'location_id': fields.many2one('stock.location', 'Location', required=True, domain="[('usage', '=', 'internal')]"), } + _defaults = { + 'new_quantity': 1, + 'product_id': lambda self, cr, uid, ctx: ctx and ctx.get('active_id', False) or False + } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): if context is None: context = {} @@ -54,20 +58,22 @@ class stock_change_product_qty(osv.osv_memory): @param context: A standard dictionary @return: A dictionary which of fields with values. """ - product_id = context and context.get('active_id', False) or False + res = super(stock_change_product_qty, self).default_get(cr, uid, fields, context=context) - if 'new_quantity' in fields: - res.update({'new_quantity': 1}) - if 'product_id' in fields: - res.update({'product_id': product_id}) if 'location_id' in fields: - try: - model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') - self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) - except (orm.except_orm, ValueError): - location_id = False - res.update({'location_id': location_id}) + location_id = res.get('location_id', False) + if not location_id: + try: + model, location_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'stock', 'stock_location_stock') + except (orm.except_orm, ValueError): + pass + if location_id: + try: + self.pool.get('stock.location').check_access_rule(cr, uid, [location_id], 'read', context=context) + except (orm.except_orm, ValueError): + pass + res['location_id'] = location_id return res def change_product_qty(self, cr, uid, ids, context=None): diff --git a/addons/stock/wizard/stock_move_view.xml b/addons/stock/wizard/stock_move_view.xml index 5dc0d0d824c..f2bc40d9a3f 100644 --- a/addons/stock/wizard/stock_move_view.xml +++ b/addons/stock/wizard/stock_move_view.xml @@ -67,6 +67,7 @@ form form new + {'form_view_ref': False} @@ -124,6 +125,7 @@ form form new + {'form_view_ref': False} diff --git a/addons/subscription/__openerp__.py b/addons/subscription/__openerp__.py index b301525eb42..5f8a1d1338b 100644 --- a/addons/subscription/__openerp__.py +++ b/addons/subscription/__openerp__.py @@ -37,7 +37,7 @@ e.g. To have an invoice generated automatically periodically: above. Specify the interval information and partner to be invoice. """, 'author': 'OpenERP SA', - 'depends': [], + 'depends': ['base'], 'data': ['security/subcription_security.xml', 'security/ir.model.access.csv', 'subscription_view.xml'], 'demo': ['subscription_demo.xml',], 'installable': True, diff --git a/addons/survey/wizard/survey_send_invitation.py b/addons/survey/wizard/survey_send_invitation.py index 44d3bec7f6e..e16d0d6ce77 100644 --- a/addons/survey/wizard/survey_send_invitation.py +++ b/addons/survey/wizard/survey_send_invitation.py @@ -128,23 +128,14 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we for use in exist_user: new_user.append(use.id) for id in survey_ref.browse(cr, uid, survey_ids): - report = self.create_report(cr, uid, [id.id], 'report.survey.form', id.title) - file = open(get_module_resource('survey', 'report') + id.title +".pdf") - file_data = "" - while 1: - line = file.readline() - file_data += line - if not line: - break - file.close() - attachments[id.title +".pdf"] = file_data - os.remove(get_module_resource('survey', 'report') + id.title +".pdf") + result, format = openerp.report.render_report(cr, uid, [id.id], 'survey.form', {}, {}) + attachments[id.title +".pdf"] = result for partner in self.pool.get('res.partner').browse(cr, uid, partner_ids): if not partner.email: skipped+= 1 continue - user = user_ref.search(cr, uid, [('login', "=", partner.email)]) + user = user_ref.search(cr, uid, [('partner_id', "=", partner.id)]) if user: if user[0] not in new_user: new_user.append(user[0]) @@ -185,6 +176,8 @@ Thanks,''') % (name, self.pool.get('ir.config_parameter').get_param(cr, uid, 'we if ans: res_data = {'name': partner.name or _('Unknown'), 'login': partner.email, + 'email': partner.email, + 'partner_id': partner.id, 'password': passwd, 'address_id': partner.id, 'groups_id': [[6, 0, [group_id]]],