From 63ee6a74a91a630f5b5049f10ae67291df85f760 Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Mon, 9 Jul 2012 17:09:54 +0530 Subject: [PATCH 001/171] [IMP] project : column project_task_history_cumulative.project_id does not exist. lp bug: https://launchpad.net/bugs/1022509 fixed bzr revid: mdi@tinyerp.com-20120709113954-l17wlurxovz6z05i --- addons/project/report/project_cumulative.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/addons/project/report/project_cumulative.xml b/addons/project/report/project_cumulative.xml index 3f7f3b26d96..535fbaf5f87 100644 --- a/addons/project/report/project_cumulative.xml +++ b/addons/project/report/project_cumulative.xml @@ -9,7 +9,6 @@ - @@ -63,9 +62,7 @@ - - From e2ba0eb95c38a16839836deeaa9a73b58221e9aa Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Mon, 9 Jul 2012 18:42:59 +0530 Subject: [PATCH 002/171] [IMP] project : Revert the unneccessary changes. bzr revid: mdi@tinyerp.com-20120709131259-c9fl7g3hgwhxutgb --- addons/project/report/project_cumulative.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/project/report/project_cumulative.xml b/addons/project/report/project_cumulative.xml index 535fbaf5f87..177e945bc93 100644 --- a/addons/project/report/project_cumulative.xml +++ b/addons/project/report/project_cumulative.xml @@ -9,6 +9,7 @@ + From 9805c665c89784bdaa5234668ed79ca2280cb124 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 19 Sep 2012 13:40:47 +0200 Subject: [PATCH 003/171] [ADD] big bit on new import: pretty much everything but o2m bzr revid: xmo@openerp.com-20120919114047-w4paoim95oxr91zb --- openerp/addons/base/ir/__init__.py | 1 + openerp/addons/base/ir/ir_fields.py | 193 ++++ openerp/osv/fields.py | 42 +- openerp/osv/orm.py | 226 +++- .../tests/addons/test_impex/tests/__init__.py | 3 +- .../addons/test_impex/tests/test_load.py | 987 ++++++++++++++++++ openerp/tests/test_misc.py | 38 +- openerp/tools/misc.py | 34 + setup.py | 1 + 9 files changed, 1507 insertions(+), 18 deletions(-) create mode 100644 openerp/addons/base/ir/ir_fields.py create mode 100644 openerp/tests/addons/test_impex/tests/test_load.py diff --git a/openerp/addons/base/ir/__init__.py b/openerp/addons/base/ir/__init__.py index bc4f3eecfbf..ba8b785f39d 100644 --- a/openerp/addons/base/ir/__init__.py +++ b/openerp/addons/base/ir/__init__.py @@ -40,6 +40,7 @@ import wizard import ir_config_parameter import osv_memory_autovacuum import ir_mail_server +import ir_fields # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py new file mode 100644 index 00000000000..f73067c9f6c --- /dev/null +++ b/openerp/addons/base/ir/ir_fields.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +import functools +import operator +import warnings +from openerp.osv import orm, fields +from openerp.tools.translate import _ + +class ConversionNotFound(ValueError): pass + +class ir_fields_converter(orm.Model): + _name = 'ir.fields.converter' + + def to_field(self, cr, uid, model, column, fromtype=str, context=None): + """ Fetches a converter for the provided column object, from the + specified type. + + A converter is simply a callable taking a value of type ``fromtype`` + (or a composite of ``fromtype``, e.g. list or dict) and returning a + value acceptable for a write() on the column ``column``. + + By default, tries to get a method on itself with a name matching the + pattern ``_$fromtype_$column._type`` and returns it. + + :param cr: openerp cursor + :param uid: ID of user calling the converter + :param column: column object to generate a value for + :type column: :class:`fields._column` + :param type fromtype: type to convert to something fitting for ``column`` + :param context: openerp request context + :return: a function (fromtype -> column.write_type), if a converter is found + :rtype: Callable | None + """ + # FIXME: return None + converter = getattr( + self, '_%s_to_%s' % (fromtype.__name__, column._type)) + if not converter: return None + + return functools.partial( + converter, cr, uid, model, column, context=context) + + def _str_to_boolean(self, cr, uid, model, column, value, context=None): + return value.lower() not in ('', '0', 'false', 'off') + + def _str_to_integer(self, cr, uid, model, column, value, context=None): + if not value: return False + return int(value) + + def _str_to_float(self, cr, uid, model, column, value, context=None): + if not value: return False + return float(value) + + def _str_to_char(self, cr, uid, model, column, value, context=None): + return value or False + + def _str_to_text(self, cr, uid, model, column, value, context=None): + return value or False + + def _get_translations(self, cr, uid, types, src, context): + Translations = self.pool['ir.translation'] + tnx_ids = Translations.search( + cr, uid, [('type', 'in', types), ('src', '=', src)], context=context) + tnx = Translations.read(cr, uid, tnx_ids, ['value'], context=context) + return map(operator.itemgetter('value'), tnx) + def _str_to_selection(self, cr, uid, model, column, value, context=None): + + selection = column.selection + if not isinstance(selection, (tuple, list)): + # FIXME: Don't pass context to avoid translations? + # Or just copy context & remove lang? + selection = selection(model, cr, uid) + for item, label in selection: + labels = self._get_translations( + cr, uid, ('selection', 'model'), label, context=context) + labels.append(label) + if value == unicode(item) or value in labels: + return item + raise ValueError( + _(u"Value '%s' not found in selection field '%%(field)s'") % ( + value)) + + + def db_id_for(self, cr, uid, model, column, subfield, value, context=None): + """ Finds a database id for the reference ``value`` in the referencing + subfield ``subfield`` of the provided column of the provided model. + + :param cr: OpenERP cursor + :param uid: OpenERP user id + :param model: model to which the column belongs + :param column: relational column for which references are provided + :param subfield: a relational subfield allowing building of refs to + existing records: ``None`` for a name_get/name_search, + ``id`` for an external id and ``.id`` for a database + id + :param value: value of the reference to match to an actual record + :param context: OpenERP request context + :return: a pair of the matched database identifier (if any) and the + translated user-readable name for the field + :rtype: (ID|None, unicode) + """ + id = None + RelatedModel = self.pool[column._obj] + if subfield == '.id': + field_type = _(u"database id") + try: tentative_id = int(value) + except ValueError: tentative_id = value + if RelatedModel.search(cr, uid, [('id', '=', tentative_id)], + context=context): + id = tentative_id + elif subfield == 'id': + field_type = _(u"external id") + if '.' in value: + module, xid = value.split('.', 1) + else: + module, xid = '', value + ModelData = self.pool['ir.model.data'] + try: + md_id = ModelData._get_id(cr, uid, module, xid) + model_data = ModelData.read(cr, uid, [md_id], ['res_id'], + context=context) + if model_data: + id = model_data[0]['res_id'] + except ValueError: pass # leave id is None + elif subfield is None: + field_type = _(u"name") + ids = RelatedModel.name_search( + cr, uid, name=value, operator='=', context=context) + if ids: + if len(ids) > 1: + warnings.warn( + _(u"Found multiple matches for field '%%(field)s' (%d matches)") + % (len(ids)), orm.ImportWarning) + id, _name = ids[0] + else: + raise Exception(u"Unknown sub-field '%s'" % subfield) + return id, field_type + + def _referencing_subfield(self, record): + """ Checks the record for the subfields allowing referencing (an + existing record in an other table), errors out if it finds potential + conflicts (multiple referencing subfields) or non-referencing subfields + returns the name of the correct subfield. + + :param record: + :return: the record subfield to use for referencing + :rtype: str + """ + # Can import by name_get, external id or database id + allowed_fields = set([None, 'id', '.id']) + fieldset = set(record.iterkeys()) + if fieldset - allowed_fields: + raise ValueError( + _(u"Can not create Many-To-One records indirectly, import the field separately")) + if len(fieldset) > 1: + raise ValueError( + _(u"Ambiguous specification for field '%(field)s', only provide one of name, external id or database id")) + + # only one field left possible, unpack + [subfield] = fieldset + return subfield + + def _str_to_many2one(self, cr, uid, model, column, values, context=None): + # Should only be one record, unpack + [record] = values + + subfield = self._referencing_subfield(record) + + reference = record[subfield] + id, subfield_type = self.db_id_for( + cr, uid, model, column, subfield, reference, context=context) + + if id is None: + raise ValueError( + _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") + % {'field_type': subfield_type, 'value': reference}) + return id + def _str_to_many2many(self, cr, uid, model, column, value, context=None): + [record] = value + + subfield = self._referencing_subfield(record) + + ids = [] + for reference in record[subfield].split(','): + id, subfield_type = self.db_id_for( + cr, uid, model, column, subfield, reference, context=context) + if id is None: + raise ValueError( + _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") + % {'field_type': subfield_type, 'value': reference}) + ids.append(id) + + return [(6, 0, ids)] + def _str_to_one2many(self, cr, uid, model, column, value, context=None): + return value diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 037d2a90986..36c6600a7e5 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -1588,19 +1588,32 @@ def field_to_dict(model, cr, user, field, context=None): class column_info(object): - """Struct containing details about an osv column, either one local to - its model, or one inherited via _inherits. + """ Struct containing details about an osv column, either one local to + its model, or one inherited via _inherits. - :attr name: name of the column - :attr column: column instance, subclass of osv.fields._column - :attr parent_model: if the column is inherited, name of the model - that contains it, None for local columns. - :attr parent_column: the name of the column containing the m2o - relationship to the parent model that contains - this column, None for local columns. - :attr original_parent: if the column is inherited, name of the original - parent model that contains it i.e in case of multilevel - inheritence, None for local columns. + .. attribute:: name + + name of the column + + .. attribute:: column + + column instance, subclass of :class:`_column` + + .. attribute:: parent_model + + if the column is inherited, name of the model that contains it, + ``None`` for local columns. + + .. attribute:: parent_column + + the name of the column containing the m2o relationship to the + parent model that contains this column, ``None`` for local columns. + + .. attribute:: original_parent + + if the column is inherited, name of the original parent model that + contains it i.e in case of multilevel inheritance, ``None`` for + local columns. """ def __init__(self, name, column, parent_model=None, parent_column=None, original_parent=None): self.name = name @@ -1609,5 +1622,10 @@ class column_info(object): self.parent_column = parent_column self.original_parent = original_parent + def __str__(self): + return '%s(%s, %s, %s, %s, %s)' % ( + self.__name__, self.name, self.column, + self.parent_model, self.parent_column, self.original_parent) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 9d3b92a3d31..57cc5823dd9 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -52,13 +52,17 @@ import re import simplejson import time import types + +import psycopg2 from lxml import etree +import warnings import fields import openerp import openerp.netsvc as netsvc import openerp.tools as tools from openerp.tools.config import config +from openerp.tools.misc import CountingStream from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ from openerp import SUPERUSER_ID @@ -1242,7 +1246,7 @@ class BaseModel(object): * The last item is currently unused, with no specific semantics :param fields: list of fields to import - :param data: data to import + :param datas: data to import :param mode: 'init' or 'update' for record creation :param current_module: module name :param noupdate: flag for record creation @@ -1438,6 +1442,199 @@ class BaseModel(object): self._parent_store_compute(cr) return position, 0, 0, 0 + def load(self, cr, uid, fields, data, context=None): + """ + + :param cr: cursor for the request + :param int uid: ID of the user attempting the data import + :param fields: list of fields to import, at the same index as the corresponding data + :type fields: list(str) + :param data: row-major matrix of data to import + :type data: list(list(str)) + :param dict context: + :returns: + """ + cr.execute('SAVEPOINT model_load') + messages = [] + + fields = map(fix_import_export_id_paths, fields) + ModelData = self.pool['ir.model.data'] + + mode = 'init' + current_module = '' + noupdate = False + + ids = [] + for id, xid, record, info in self._convert_records(cr, uid, + self._extract_records(cr, uid, fields, data, + context=context, log=messages.append), + context=context, log=messages.append): + cr.execute('SAVEPOINT model_load_save') + try: + ids.append(ModelData._update(cr, uid, self._name, + current_module, record, mode=mode, xml_id=xid, + noupdate=noupdate, res_id=id, context=context)) + cr.execute('RELEASE SAVEPOINT model_load_save') + except psycopg2.Error, e: + # Failed to write, log to messages, rollback savepoint (to + # avoid broken transaction) and keep going + cr.execute('ROLLBACK TO SAVEPOINT model_load_save') + messages.append(dict(info, type="error", message=str(e))) + if any(message['type'] == 'error' for message in messages): + cr.execute('ROLLBACK TO SAVEPOINT model_load') + return False, messages + return ids, messages + def _extract_records(self, cr, uid, fields_, data, + context=None, log=lambda a: None): + """ Generates record dicts from the data iterable. + + The result is a generator of dicts mapping field names to raw + (unconverted, unvalidated) values. + + For relational fields, if sub-fields were provided the value will be + a list of sub-records + + The following sub-fields may be set on the record (by key): + * None is the name_get for the record (to use with name_create/name_search) + * "id" is the External ID for the record + * ".id" is the Database ID for the record + + :param ImportLogger logger: + """ + columns = dict((k, v.column) for k, v in self._all_columns.iteritems()) + # Fake columns to avoid special cases in extractor + columns[None] = fields.char('rec_name') + columns['id'] = fields.char('External ID') + columns['.id'] = fields.integer('Database ID') + + # m2o fields can't be on multiple lines so exclude them from the + # is_relational field rows filter, but special-case it later on to + # be handled with relational fields (as it can have subfields) + is_relational = lambda field: columns[field]._type in ('one2many', 'many2many', 'many2one') + get_o2m_values = itemgetter_tuple( + [index for index, field in enumerate(fields_) + if columns[field[0]]._type == 'one2many']) + get_nono2m_values = itemgetter_tuple( + [index for index, field in enumerate(fields_) + if columns[field[0]]._type != 'one2many']) + # Checks if the provided row has any non-empty non-relational field + def only_o2m_values(row, f=get_nono2m_values, g=get_o2m_values): + return any(g(row)) and not any(f(row)) + + rows = CountingStream(data) + while True: + row = next(rows, None) + if row is None: return + record_row_index = rows.index + + # copy non-relational fields to record dict + record = dict((field[0], value) + for field, value in itertools.izip(fields_, row) + if not is_relational(field[0])) + + # Get all following rows which have relational values attached to + # the current record (no non-relational values) + # WARNING: replaces existing ``rows`` + record_span, _rows = span(only_o2m_values, rows) + # stitch record row back on for relational fields + record_span = itertools.chain([row], record_span) + for relfield in set( + field[0] for field in fields_ + if is_relational(field[0])): + column = columns[relfield] + # FIXME: how to not use _obj without relying on fields_get? + Model = self.pool[column._obj] + + # copy stream to reuse for next relational field + fieldrows, record_span = itertools.tee(record_span) + # get only cells for this sub-field, should be strictly + # non-empty, field path [None] is for name_get column + indices, subfields = zip(*((index, field[1:] or [None]) + for index, field in enumerate(fields_) + if field[0] == relfield)) + + # return all rows which have at least one value for the + # subfields of relfield + relfield_data = filter(any, map(itemgetter_tuple(indices), fieldrows)) + record[relfield] = [subrecord + for subrecord, _subinfo in Model._extract_records( + cr, uid, subfields, relfield_data, + context=context, log=log)] + # Ensure full consumption of the span (and therefore advancement of + # ``rows``) even if there are no relational fields. Needs two as + # the code above stiched the row back on (so first call may only + # get the stiched row without advancing the underlying operator row + # itself) + next(record_span, None) + next(record_span, None) + + # old rows consumption (by iterating the span) should be done here, + # at this point the old ``rows`` is 1 past `span` (either on the + # next record row or past ``StopIteration``, so wrap new ``rows`` + # (``_rows``) in a counting stream indexed 1-before the old + # ``rows`` + rows = CountingStream(_rows, rows.index - 1) + yield record, {'rows': {'from': record_row_index,'to': rows.index}} + def _convert_records(self, cr, uid, records, + context=None, log=lambda a: None): + """ Converts records from the source iterable (recursive dicts of + strings) into forms which can be written to the database (via + self.create or (ir.model.data)._update) + + :param ImportLogger parent_logger: + :returns: a list of triplets of (id, xid, record) + :rtype: list((int|None, str|None, dict)) + """ + Converter = self.pool['ir.fields.converter'] + columns = dict((k, v.column) for k, v in self._all_columns.iteritems()) + converters = dict( + (k, Converter.to_field(cr, uid, self, column, context=context)) + for k, column in columns.iteritems()) + + stream = CountingStream(records) + for record, extras in stream: + dbid = False + xid = False + converted = {} + # name_get/name_create + if None in record: pass + # xid + if 'id' in record: + xid = record['id'] + # dbid + if '.id' in record: + try: + dbid = int(record['.id']) + except ValueError: + # in case of overridden id column + dbid = record['.id'] + if not self.search(cr, uid, [('id', '=', dbid)], context=context): + log(dict(extras, + type='error', + record=stream.index, + field='.id', + message=_(u"Unknown database identifier '%s'") % dbid)) + dbid = False + + for field, strvalue in record.iteritems(): + if field in (None, 'id', '.id'): continue + + message_base = dict(extras, record=stream.index, field=field) + with warnings.catch_warnings(record=True) as w: + try: + converted[field] = converters[field](strvalue) + + for warning in w: + log(dict(message_base, type='warning', + message=unicode(warning.message) % message_base)) + except ValueError, e: + log(dict(message_base, + type='error', + message=unicode(e) % message_base + )) + + yield dbid, xid, converted, dict(extras, record=stream.index) + def get_invalid_fields(self, cr, uid): return list(self._invalids) @@ -5108,5 +5305,32 @@ class AbstractModel(BaseModel): _auto = False # don't create any database backend for AbstractModels _register = False # not visible in ORM registry, meant to be python-inherited only +def span(predicate, iterable): + """ Splits the iterable between the longest prefix of ``iterable`` whose + elements satisfy ``predicate`` and the rest. + If called with a list, equivalent to:: + + takewhile(predicate, lst), dropwhile(predicate, lst) + + :param callable predicate: + :param iterable: + :rtype: (iterable, iterable) + """ + it1, it2 = itertools.tee(iterable) + return (itertools.takewhile(predicate, it1), + itertools.dropwhile(predicate, it2)) +def itemgetter_tuple(items): + """ Fixes itemgetter inconsistency (useful in some cases) of not returning + a tuple if len(items) == 1: always returns an n-tuple where n = len(items) + """ + if len(items) == 0: + return lambda a: () + if len(items) == 1: + return lambda gettable: (gettable[items[0]],) + return operator.itemgetter(*items) +class ImportWarning(Warning): + """ Used to send warnings upwards the stack during the import process + """ + pass # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/addons/test_impex/tests/__init__.py b/openerp/tests/addons/test_impex/tests/__init__.py index d6af53cba1b..8cab56fcc9a 100644 --- a/openerp/tests/addons/test_impex/tests/__init__.py +++ b/openerp/tests/addons/test_impex/tests/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -from . import test_export, test_import +from . import test_export, test_import, test_load fast_suite = [ ] @@ -8,6 +8,7 @@ fast_suite = [ checks = [ test_export, test_import, + test_load, ] # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py new file mode 100644 index 00000000000..80e5cfe9c8f --- /dev/null +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -0,0 +1,987 @@ +# -*- coding: utf-8 -*- +import openerp.modules.registry +import openerp + +from openerp.tests import common +from openerp.tools.misc import mute_logger + +def message(msg, type='error', from_=0, to_=0, record=0, field='value'): + return { + 'type': type, + 'rows': {'from': from_, 'to': to_}, + 'record': record, + 'field': field, + 'message': msg + } + +def error(row, message, record=None, **kwargs): + """ Failed import of the record ``record`` at line ``row``, with the error + message ``message`` + + :param str message: + :param dict record: + """ + return ( + -1, dict(record or {}, **kwargs), + "Line %d : %s" % (row, message), + '') + +def values(seq, field='value'): + return [item[field] for item in seq] + +class ImporterCase(common.TransactionCase): + model_name = False + + def __init__(self, *args, **kwargs): + super(ImporterCase, self).__init__(*args, **kwargs) + self.model = None + + def setUp(self): + super(ImporterCase, self).setUp() + self.model = self.registry(self.model_name) + self.registry('ir.model.data').clear_caches() + + def import_(self, fields, rows, context=None): + return self.model.load( + self.cr, openerp.SUPERUSER_ID, fields, rows, context=context) + def read(self, fields=('value',), domain=(), context=None): + return self.model.read( + self.cr, openerp.SUPERUSER_ID, + self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context), + fields=fields, context=context) + def browse(self, domain=(), context=None): + return self.model.browse( + self.cr, openerp.SUPERUSER_ID, + self.model.search(self.cr, openerp.SUPERUSER_ID, domain, context=context), + context=context) + + def xid(self, record): + ModelData = self.registry('ir.model.data') + + ids = ModelData.search( + self.cr, openerp.SUPERUSER_ID, + [('model', '=', record._table_name), ('res_id', '=', record.id)]) + if ids: + d = ModelData.read( + self.cr, openerp.SUPERUSER_ID, ids, ['name', 'module'])[0] + if d['module']: + return '%s.%s' % (d['module'], d['name']) + return d['name'] + + name = dict(record.name_get())[record.id] + # fix dotted name_get results, otherwise xid lookups blow up + name = name.replace('.', '-') + ModelData.create(self.cr, openerp.SUPERUSER_ID, { + 'name': name, + 'model': record._table_name, + 'res_id': record.id, + 'module': '__test__' + }) + return '__test__.' + name + +class test_ids_stuff(ImporterCase): + model_name = 'export.integer' + + def test_create_with_id(self): + ids, messages = self.import_(['.id', 'value'], [['42', '36']]) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 0, 'to': 0}, + 'record': 0, + 'field': '.id', + 'message': u"Unknown database identifier '42'", + }]) + def test_create_with_xid(self): + ids, messages = self.import_(['id', 'value'], [['somexmlid', '42']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + self.assertEqual( + 'somexmlid', + self.xid(self.browse()[0])) + + def test_update_with_id(self): + id = self.model.create(self.cr, openerp.SUPERUSER_ID, {'value': 36}) + self.assertEqual( + 36, + self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value) + + ids, messages = self.import_(['.id', 'value'], [[str(id), '42']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + self.assertEqual( + [42], # updated value to imported + values(self.read())) + + def test_update_with_xid(self): + self.import_(['id', 'value'], [['somexmlid', '36']]) + self.assertEqual([36], values(self.read())) + + self.import_(['id', 'value'], [['somexmlid', '1234567']]) + self.assertEqual([1234567], values(self.read())) + +class test_boolean_field(ImporterCase): + model_name = 'export.boolean' + + def test_empty(self): + self.assertEqual( + self.import_(['value'], []), + ([], [])) + + def test_exported(self): + ids, messages = self.import_(['value'], [['False'], ['True'], ]) + self.assertEqual(len(ids), 2) + self.assertFalse(messages) + records = self.read() + self.assertEqual([ + False, + True, + ], values(records)) + + def test_falses(self): + ids, messages = self.import_( + ['value'], + [[u'0'], [u'off'], + [u'false'], [u'FALSE'], + [u'OFF'], [u''], + ]) + self.assertEqual(len(ids), 6) + self.assertFalse(messages) + self.assertEqual([ + False, + False, + False, + False, + False, + False, + ], + values(self.read())) + + def test_trues(self): + ids, messages = self.import_( + ['value'], + [['no'], + ['None'], + ['nil'], + ['()'], + ['f'], + ['#f'], + # Problem: OpenOffice (and probably excel) output localized booleans + ['VRAI'], + ]) + self.assertEqual(len(ids), 7) + # FIXME: should warn for values which are not "true", "yes" or "1" + self.assertFalse(messages) + self.assertEqual( + [True] * 7, + values(self.read())) + +class test_integer_field(ImporterCase): + model_name = 'export.integer' + + def test_none(self): + self.assertEqual( + self.import_(['value'], []), + ([], [])) + + def test_empty(self): + ids, messages = self.import_(['value'], [['']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + self.assertEqual( + [False], + values(self.read())) + + def test_zero(self): + ids, messages = self.import_(['value'], [['0']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + + ids, messages = self.import_(['value'], [['-0']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + + self.assertEqual([False, False], values(self.read())) + + def test_positives(self): + ids, messages = self.import_(['value'], [ + ['1'], + ['42'], + [str(2**31-1)], + ['12345678'] + ]) + self.assertEqual(len(ids), 4) + self.assertFalse(messages) + + self.assertEqual([ + 1, 42, 2**31-1, 12345678 + ], values(self.read())) + + def test_negatives(self): + ids, messages = self.import_(['value'], [ + ['-1'], + ['-42'], + [str(-(2**31 - 1))], + [str(-(2**31))], + ['-12345678'] + ]) + self.assertEqual(len(ids), 5) + self.assertFalse(messages) + self.assertEqual([ + -1, -42, -(2**31 - 1), -(2**31), -12345678 + ], values(self.read())) + + @mute_logger('openerp.sql_db') + def test_out_of_range(self): + ids, messages = self.import_(['value'], [[str(2**31)]]) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 0, 'to': 0}, + 'record': 0, + 'message': "integer out of range\n" + }]) + + ids, messages = self.import_(['value'], [[str(-2**32)]]) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 0, 'to': 0}, + 'record': 0, + 'message': "integer out of range\n" + }]) + + def test_nonsense(self): + ids, messages = self.import_(['value'], [['zorglub']]) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 0, 'to': 0}, + 'record': 0, + 'field': 'value', + 'message': u"invalid literal for int() with base 10: 'zorglub'", + }]) + +class test_float_field(ImporterCase): + model_name = 'export.float' + def test_none(self): + self.assertEqual( + self.import_(['value'], []), + ([], [])) + + def test_empty(self): + ids, messages = self.import_(['value'], [['']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + self.assertEqual( + [False], + values(self.read())) + + def test_zero(self): + ids, messages = self.import_(['value'], [['0']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + + ids, messages = self.import_(['value'], [['-0']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + + self.assertEqual([False, False], values(self.read())) + + def test_positives(self): + ids, messages = self.import_(['value'], [ + ['1'], + ['42'], + [str(2**31-1)], + ['12345678'], + [str(2**33)], + ['0.000001'], + ]) + self.assertEqual(len(ids), 6) + self.assertFalse(messages) + + self.assertEqual([ + 1, 42, 2**31-1, 12345678, 2.0**33, .000001 + ], values(self.read())) + + def test_negatives(self): + ids, messages = self.import_(['value'], [ + ['-1'], + ['-42'], + [str(-2**31 + 1)], + [str(-2**31)], + ['-12345678'], + [str(-2**33)], + ['-0.000001'], + ]) + self.assertEqual(len(ids), 7) + self.assertFalse(messages) + self.assertEqual([ + -1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001 + ], values(self.read())) + + def test_nonsense(self): + ids, messages = self.import_(['value'], [['foobar']]) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 0, 'to': 0}, + 'record': 0, + 'field': 'value', + 'message': u"invalid literal for float(): foobar", + }]) + +class test_string_field(ImporterCase): + model_name = 'export.string.bounded' + + def test_empty(self): + ids, messages = self.import_(['value'], [['']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + self.assertEqual([False], values(self.read())) + + def test_imported(self): + ids, messages = self.import_(['value'], [ + [u'foobar'], + [u'foobarbaz'], + [u'Með suð í eyrum við spilum endalaust'], + [u"People 'get' types. They use them all the time. Telling " + u"someone he can't pound a nail with a banana doesn't much " + u"surprise him."] + ]) + self.assertEqual(len(ids), 4) + self.assertFalse(messages) + self.assertEqual([ + u"foobar", + u"foobarbaz", + u"Með suð í eyrum ", + u"People 'get' typ", + ], values(self.read())) + +class test_unbound_string_field(ImporterCase): + model_name = 'export.string' + + def test_imported(self): + ids, messages = self.import_(['value'], [ + [u'í dag viðrar vel til loftárása'], + # ackbar.jpg + [u"If they ask you about fun, you tell them – fun is a filthy" + u" parasite"] + ]) + self.assertEqual(len(ids), 2) + self.assertFalse(messages) + self.assertEqual([ + u"í dag viðrar vel til loftárása", + u"If they ask you about fun, you tell them – fun is a filthy parasite" + ], values(self.read())) + +class test_text(ImporterCase): + model_name = 'export.text' + + def test_empty(self): + ids, messages = self.import_(['value'], [['']]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + self.assertEqual([False], values(self.read())) + + def test_imported(self): + s = (u"Breiðskífa er notað um útgefna hljómplötu sem inniheldur " + u"stúdíóupptökur frá einum flytjanda. Breiðskífur eru oftast " + u"milli 25-80 mínútur og er lengd þeirra oft miðuð við 33⅓ " + u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín " + u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá" + u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.") + ids, messages = self.import_(['value'], [[s]]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + self.assertEqual([s], values(self.read())) + +class test_selection(ImporterCase): + model_name = 'export.selection' + translations_fr = [ + ("Qux", "toto"), + ("Bar", "titi"), + ("Foo", "tete"), + ] + + def test_imported(self): + ids, messages = self.import_(['value'], [ + ['Qux'], + ['Bar'], + ['Foo'], + ['2'], + ]) + self.assertEqual(len(ids), 4) + self.assertFalse(messages) + self.assertEqual([3, 2, 1, 2], values(self.read())) + + def test_imported_translated(self): + self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { + 'name': u'Français', + 'code': 'fr_FR', + 'translatable': True, + 'date_format': '%d.%m.%Y', + 'decimal_point': ',', + 'thousand_sep': ' ', + }) + Translations = self.registry('ir.translation') + for source, value in self.translations_fr: + Translations.create(self.cr, openerp.SUPERUSER_ID, { + 'name': 'export.selection,value', + 'lang': 'fr_FR', + 'type': 'selection', + 'src': source, + 'value': value + }) + + ids, messages = self.import_(['value'], [ + ['toto'], + ['tete'], + ['titi'], + ], context={'lang': 'fr_FR'}) + self.assertEqual(len(ids), 3) + self.assertFalse(messages) + + self.assertEqual([3, 1, 2], values(self.read())) + + ids, messages = self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + + def test_invalid(self): + ids, messages = self.import_(['value'], [['Baz']]) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 0, 'to': 0}, + 'record': 0, + 'field': 'value', + 'message': "Value 'Baz' not found in selection field 'value'", + }]) + + ids, messages = self.import_(['value'], [[42]]) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 0, 'to': 0}, + 'record': 0, + 'field': 'value', + 'message': "Value '42' not found in selection field 'value'", + }]) + +class test_selection_function(ImporterCase): + model_name = 'export.selection.function' + translations_fr = [ + ("Corge", "toto"), + ("Grault", "titi"), + ("Whee", "tete"), + ("Moog", "tutu"), + ] + + def test_imported(self): + """ import uses fields_get, so translates import label (may or may not + be good news) *and* serializes the selection function to reverse it: + import does not actually know that the selection field uses a function + """ + # NOTE: conflict between a value and a label => ? + ids, messages = self.import_(['value'], [ + ['3'], + ["Grault"], + ]) + self.assertEqual(len(ids), 2) + self.assertFalse(messages) + self.assertEqual( + ['3', '1'], + values(self.read())) + + def test_translated(self): + """ Expects output of selection function returns translated labels + """ + self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { + 'name': u'Français', + 'code': 'fr_FR', + 'translatable': True, + 'date_format': '%d.%m.%Y', + 'decimal_point': ',', + 'thousand_sep': ' ', + }) + Translations = self.registry('ir.translation') + for source, value in self.translations_fr: + Translations.create(self.cr, openerp.SUPERUSER_ID, { + 'name': 'export.selection,value', + 'lang': 'fr_FR', + 'type': 'selection', + 'src': source, + 'value': value + }) + ids, messages = self.import_(['value'], [ + ['toto'], + ['tete'], + ], context={'lang': 'fr_FR'}) + self.assertIs(ids, False) + self.assertEqual(messages, [{ + 'type': 'error', + 'rows': {'from': 1, 'to': 1}, + 'record': 1, + 'field': 'value', + 'message': "Value 'tete' not found in selection field 'value'", + }]) + ids, messages = self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + +class test_m2o(ImporterCase): + model_name = 'export.many2one' + + def test_by_name(self): + # create integer objects + integer_id1 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + integer_id2 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 36}) + # get its name + name1 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1] + name2 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2] + + ids , messages = self.import_(['value'], [ + # import by name_get + [name1], + [name1], + [name2], + ]) + self.assertFalse(messages) + self.assertEqual(len(ids), 3) + # correct ids assigned to corresponding records + self.assertEqual([ + (integer_id1, name1), + (integer_id1, name1), + (integer_id2, name2),], + values(self.read())) + + def test_by_xid(self): + ExportInteger = self.registry('export.integer') + integer_id = ExportInteger.create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + xid = self.xid(ExportInteger.browse( + self.cr, openerp.SUPERUSER_ID, [integer_id])[0]) + + ids, messages = self.import_(['value/id'], [[xid]]) + self.assertFalse(messages) + self.assertEqual(len(ids), 1) + b = self.browse() + self.assertEqual(42, b[0].value.value) + + def test_by_id(self): + integer_id = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + ids, messages = self.import_(['value/.id'], [[integer_id]]) + self.assertFalse(messages) + self.assertEqual(len(ids), 1) + b = self.browse() + self.assertEqual(42, b[0].value.value) + + def test_by_names(self): + integer_id1 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + integer_id2 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + name1 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id1]))[integer_id1] + name2 = dict(self.registry('export.integer').name_get( + self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2] + # names should be the same + self.assertEqual(name1, name2) + + ids, messages = self.import_(['value'], [[name2]]) + self.assertEqual( + messages, + [message(u"Found multiple matches for field 'value' (2 matches)", + type='warning')]) + self.assertEqual(len(ids), 1) + self.assertEqual([ + (integer_id1, name1) + ], values(self.read())) + + def test_fail_by_implicit_id(self): + """ Can't implicitly import records by id + """ + # create integer objects + integer_id1 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 42}) + integer_id2 = self.registry('export.integer').create( + self.cr, openerp.SUPERUSER_ID, {'value': 36}) + + # Because name_search all the things. Fallback schmallback + ids, messages = self.import_(['value'], [ + # import by id, without specifying it + [integer_id1], + [integer_id2], + [integer_id1], + ]) + self.assertEqual(messages, [ + message(u"No matching record found for name '%s' in field 'value'" % id, + from_=index, to_=index, record=index) + for index, id in enumerate([integer_id1, integer_id2, integer_id1])]) + self.assertIs(ids, False) + + def test_sub_field(self): + """ Does not implicitly create the record, does not warn that you can't + import m2o subfields (at all)... + """ + ids, messages = self.import_(['value/value'], [['42']]) + self.assertEqual(messages, [ + message(u"Can not create Many-To-One records indirectly, import " + u"the field separately")]) + self.assertIs(ids, False) + + def test_fail_noids(self): + ids, messages = self.import_(['value'], [['nameisnoexist:3']]) + self.assertEqual(messages, [message( + u"No matching record found for name 'nameisnoexist:3' " + u"in field 'value'")]) + self.assertIs(ids, False) + + ids, messages = self.import_(['value/id'], [['noxidhere']]) + self.assertEqual(messages, [message( + u"No matching record found for external id 'noxidhere' " + u"in field 'value'")]) + self.assertIs(ids, False) + + ids, messages = self.import_(['value/.id'], [['66']]) + self.assertEqual(messages, [message( + u"No matching record found for database id '66' " + u"in field 'value'")]) + self.assertIs(ids, False) + + def test_fail_multiple(self): + ids, messages = self.import_( + ['value', 'value/id'], + [['somename', 'somexid']]) + self.assertEqual(messages, [message( + u"Ambiguous specification for field 'value', only provide one of " + u"name, external id or database id")]) + self.assertIs(ids, False) + +class test_m2m(ImporterCase): + model_name = 'export.many2many' + + # apparently, one and only thing which works is a + # csv_internal_sep-separated list of ids, xids, or names (depending if + # m2m/.id, m2m/id or m2m[/anythingelse] + def test_ids(self): + id1 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + id5 = self.registry('export.many2many.other').create( + self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'}) + + ids, messages = self.import_(['value/.id'], [ + ['%d,%d' % (id1, id2)], + ['%d,%d,%d' % (id1, id3, id4)], + ['%d,%d,%d' % (id1, id2, id3)], + ['%d' % id5] + ]) + self.assertFalse(messages) + self.assertEqual(len(ids), 4) + + ids = lambda records: [record.id for record in records] + + b = self.browse() + self.assertEqual(ids(b[0].value), [id1, id2]) + self.assertEqual(values(b[0].value), [3, 44]) + + self.assertEqual(ids(b[2].value), [id1, id2, id3]) + self.assertEqual(values(b[2].value), [3, 44, 84]) + + def test_noids(self): + ids, messages = self.import_(['value/.id'], [['42']]) + self.assertEqual(messages, [message( + u"No matching record found for database id '42' in field " + u"'value'")]) + self.assertIs(ids, False) + + def test_xids(self): + M2O_o = self.registry('export.many2many.other') + id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4]) + + ids, messages = self.import_(['value/id'], [ + ['%s,%s' % (self.xid(records[0]), self.xid(records[1]))], + ['%s' % self.xid(records[3])], + ['%s,%s' % (self.xid(records[2]), self.xid(records[1]))], + ]) + self.assertFalse(messages) + self.assertEqual(len(ids), 3) + + b = self.browse() + self.assertEqual(values(b[0].value), [3, 44]) + self.assertEqual(values(b[2].value), [44, 84]) + def test_noxids(self): + ids, messages = self.import_(['value/id'], [['noxidforthat']]) + self.assertEqual(messages, [message( + u"No matching record found for external id 'noxidforthat' " + u"in field 'value'")]) + self.assertIs(ids, False) + + def test_names(self): + M2O_o = self.registry('export.many2many.other') + id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4]) + + name = lambda record: dict(record.name_get())[record.id] + + ids, messages = self.import_(['value'], [ + ['%s,%s' % (name(records[1]), name(records[2]))], + ['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))], + ['%s,%s' % (name(records[0]), name(records[3]))], + ]) + self.assertFalse(messages) + self.assertEqual(len(ids), 3) + + b = self.browse() + self.assertEqual(values(b[1].value), [3, 44, 84]) + self.assertEqual(values(b[2].value), [3, 9]) + + def test_nonames(self): + ids, messages = self.import_(['value'], [['wherethem2mhavenonames']]) + self.assertEqual(messages, [message( + u"No matching record found for name 'wherethem2mhavenonames' in " + u"field 'value'")]) + self.assertIs(ids, False) + + def test_import_to_existing(self): + M2O_o = self.registry('export.many2many.other') + id1 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 3, 'str': 'record0'}) + id2 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 44, 'str': 'record1'}) + id3 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 84, 'str': 'record2'}) + id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) + + xid = 'myxid' + ids, messages = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]]) + self.assertFalse(messages) + self.assertEqual(len(ids), 1) + ids, messages = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]]) + self.assertFalse(messages) + self.assertEqual(len(ids), 1) + + b = self.browse() + self.assertEqual(len(b), 1) + # TODO: replacement of existing m2m values is correct? + self.assertEqual(values(b[0].value), [84, 9]) + +class test_o2m(ImporterCase): + model_name = 'export.one2many' + + def test_name_get(self): + # FIXME: bloody hell why can't this just name_create the record? + self.assertRaises( + IndexError, + self.import_, + ['const', 'value'], + [['5', u'Java is a DSL for taking large XML files' + u' and converting them to stack traces']]) + + def test_single(self): + ids, messages = self.import_(['const', 'value/value'], [ + ['5', '63'] + ]) + self.assertEqual(len(ids), 1) + self.assertFalse(messages) + + (b,) = self.browse() + self.assertEqual(b.const, 5) + self.assertEqual(values(b.value), [63]) + + def test_multicore(self): + ids, messages = self.import_(['const', 'value/value'], [ + ['5', '63'], + ['6', '64'], + ]) + self.assertEqual(len(ids), 2) + self.assertFalse(messages) + + b1, b2 = self.browse() + self.assertEqual(b1.const, 5) + self.assertEqual(values(b1.value), [63]) + self.assertEqual(b2.const, 6) + self.assertEqual(values(b2.value), [64]) + + def test_multisub(self): + ids, messages = self.import_(['const', 'value/value'], [ + ['5', '63'], + ['', '64'], + ['', '65'], + ['', '66'], + ]) + self.assertEqual(len(ids), 4) + self.assertFalse(messages) + + (b,) = self.browse() + self.assertEqual(values(b.value), [63, 64, 65, 66]) + + def test_multi_subfields(self): + ids, messages = self.import_(['value/str', 'const', 'value/value'], [ + ['this', '5', '63'], + ['is', '', '64'], + ['the', '', '65'], + ['rhythm', '', '66'], + ]) + self.assertEqual(len(ids), 4) + self.assertFalse(messages) + + (b,) = self.browse() + self.assertEqual(values(b.value), [63, 64, 65, 66]) + self.assertEqual( + values(b.value, 'str'), + 'this is the rhythm'.split()) + + def test_link_inline(self): + id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Bf', 'value': 109 + }) + id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Me', 'value': 262 + }) + + try: + self.import_(['const', 'value/.id'], [ + ['42', '%d,%d' % (id1, id2)] + ]) + self.fail("Should have raised a valueerror") + except ValueError, e: + # should be Exception(Database ID doesn't exist: export.one2many.child : $id1,$id2) + self.assertIs(type(e), ValueError) + self.assertEqual( + e.args[0], + "invalid literal for int() with base 10: '%d,%d'" % (id1, id2)) + + def test_link(self): + id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Bf', 'value': 109 + }) + id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Me', 'value': 262 + }) + + ids, messages = self.import_(['const', 'value/.id'], [ + ['42', str(id1)], + ['', str(id2)], + ]) + self.assertEqual(len(ids), 2) + self.assertFalse(messages) + + # No record values alongside id => o2m resolution skipped altogether, + # creates 2 records => remove/don't import columns sideshow columns, + # get completely different semantics + b, b1 = self.browse() + self.assertEqual(b.const, 42) + self.assertEqual(values(b.value), []) + self.assertEqual(b1.const, 4) + self.assertEqual(values(b1.value), []) + + def test_link_2(self): + O2M_c = self.registry('export.one2many.child') + id1 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Bf', 'value': 109 + }) + id2 = O2M_c.create(self.cr, openerp.SUPERUSER_ID, { + 'str': 'Me', 'value': 262 + }) + + ids, messages = self.import_(['const', 'value/.id', 'value/value'], [ + ['42', str(id1), '1'], + ['', str(id2), '2'], + ]) + self.assertEqual(len(ids), 2) + self.assertFalse(messages) + + (b,) = self.browse() + # if an id (db or xid) is provided, expectations that objects are + # *already* linked and emits UPDATE (1, id, {}). + # Noid => CREATE (0, ?, {}) + # TODO: xid ignored aside from getting corresponding db id? + self.assertEqual(b.const, 42) + self.assertEqual(values(b.value), []) + + # FIXME: updates somebody else's records? + self.assertEqual( + O2M_c.read(self.cr, openerp.SUPERUSER_ID, id1), + {'id': id1, 'str': 'Bf', 'value': 1, 'parent_id': False}) + self.assertEqual( + O2M_c.read(self.cr, openerp.SUPERUSER_ID, id2), + {'id': id2, 'str': 'Me', 'value': 2, 'parent_id': False}) + +class test_o2m_multiple(ImporterCase): + model_name = 'export.one2many.multiple' + + def test_multi_mixed(self): + ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ + ['5', '11', '21'], + ['', '12', '22'], + ['', '13', '23'], + ['', '14', ''], + ]) + self.assertEqual(len(ids), 4) + self.assertFalse(messages) + # Oh yeah, that's the stuff + (b, b1, b2) = self.browse() + self.assertEqual(values(b.child1), [11]) + self.assertEqual(values(b.child2), [21]) + + self.assertEqual(values(b1.child1), [12]) + self.assertEqual(values(b1.child2), [22]) + + self.assertEqual(values(b2.child1), [13, 14]) + self.assertEqual(values(b2.child2), [23]) + + def test_multi(self): + ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ + ['5', '11', '21'], + ['', '12', ''], + ['', '13', ''], + ['', '14', ''], + ['', '', '22'], + ['', '', '23'], + ]) + self.assertEqual(len(ids), 6) + self.assertFalse(messages) + # What the actual fuck? + (b, b1) = self.browse() + self.assertEqual(values(b.child1), [11, 12, 13, 14]) + self.assertEqual(values(b.child2), [21]) + self.assertEqual(values(b1.child2), [22, 23]) + + def test_multi_fullsplit(self): + ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ + ['5', '11', ''], + ['', '12', ''], + ['', '13', ''], + ['', '14', ''], + ['', '', '21'], + ['', '', '22'], + ['', '', '23'], + ]) + self.assertEqual(len(ids), 7) + self.assertFalse(messages) + # oh wow + (b, b1) = self.browse() + self.assertEqual(b.const, 5) + self.assertEqual(values(b.child1), [11, 12, 13, 14]) + self.assertEqual(b1.const, 36) + self.assertEqual(values(b1.child2), [21, 22, 23]) + +# function, related, reference: written to db as-is... +# => function uses @type for value coercion/conversion diff --git a/openerp/tests/test_misc.py b/openerp/tests/test_misc.py index 7661f253b17..54ae69899a6 100644 --- a/openerp/tests/test_misc.py +++ b/openerp/tests/test_misc.py @@ -2,12 +2,12 @@ # > PYTHONPATH=. python2 openerp/tests/test_misc.py import unittest2 +from ..tools import misc -class test_misc(unittest2.TestCase): +class append_content_to_html(unittest2.TestCase): """ Test some of our generic utility functions """ def test_append_to_html(self): - from openerp.tools import append_content_to_html test_samples = [ ('some content', '--\nYours truly', True, 'some content\n
--\nYours truly
\n'), @@ -15,7 +15,37 @@ class test_misc(unittest2.TestCase): 'some content\n\n\n

--

\n

Yours truly

\n\n\n'), ] for html, content, flag, expected in test_samples: - self.assertEqual(append_content_to_html(html,content,flag), expected, 'append_content_to_html is broken') + self.assertEqual(misc.append_content_to_html(html,content,flag), expected, 'append_content_to_html is broken') + +class test_countingstream(unittest2.TestCase): + def test_empty_stream(self): + s = misc.CountingStream(iter([])) + self.assertEqual(s.index, -1) + self.assertIsNone(next(s, None)) + self.assertEqual(s.index, 0) + + def test_single(self): + s = misc.CountingStream(xrange(1)) + self.assertEqual(s.index, -1) + self.assertEqual(next(s, None), 0) + self.assertIsNone(next(s, None)) + self.assertEqual(s.index, 1) + + def test_full(self): + s = misc.CountingStream(xrange(42)) + for _ in s: + pass + self.assertEqual(s.index, 42) + + def test_repeated(self): + """ Once the CountingStream has stopped iterating, the index should not + increase anymore (the internal state should not be allowed to change) + """ + s = misc.CountingStream(iter([])) + self.assertIsNone(next(s, None)) + self.assertEqual(s.index, 0) + self.assertIsNone(next(s, None)) + self.assertEqual(s.index, 0) if __name__ == '__main__': - unittest2.main() \ No newline at end of file + unittest2.main() diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index 9a7b3499425..1ee3caf98d9 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -1220,4 +1220,38 @@ class mute_logger(object): with self: return func(*args, **kwargs) return deco + +_ph = object() +class CountingStream(object): + """ Stream wrapper counting the number of element it has yielded. Similar + role to ``enumerate``, but for use when the iteration process of the stream + isn't fully under caller control (the stream can be iterated from multiple + points including within a library) + + ``start`` allows overriding the starting index (the index before the first + item is returned). + + On each iteration (call to :meth:`~.next`), increases its :attr:`~.index` + by one. + + .. attribute:: index + + ``int``, index of the last yielded element in the stream. If the stream + has ended, will give an index 1-past the stream + """ + def __init__(self, stream, start=-1): + self.stream = iter(stream) + self.index = start + self.stopped = False + def __iter__(self): + return self + def next(self): + if self.stopped: raise StopIteration() + self.index += 1 + val = next(self.stream, _ph) + if val is _ph: + self.stopped = True + raise StopIteration() + return val + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/setup.py b/setup.py index a26b74e255d..4451bc1b710 100755 --- a/setup.py +++ b/setup.py @@ -116,6 +116,7 @@ setuptools.setup( extras_require = { 'SSL' : ['pyopenssl'], }, + tests_require = ['unittest2'], **py2exe_options() ) From c9e0cfd64aa064df9a2544597bf77579ac4e08da Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 20 Sep 2012 12:25:45 +0200 Subject: [PATCH 004/171] [ADD] force linking to existing o2m being updated bzr revid: xmo@openerp.com-20120920102545-30tkodb4s1dng5hp --- openerp/addons/base/ir/ir_fields.py | 34 ++++++++++++++-- .../addons/test_impex/tests/test_load.py | 39 +++++++------------ 2 files changed, 45 insertions(+), 28 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index f73067c9f6c..6b3898f3fdd 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -5,6 +5,22 @@ import warnings from openerp.osv import orm, fields from openerp.tools.translate import _ +REFERENCING_FIELDS = set([None, 'id', '.id']) +def only_ref_fields(record): + return dict((k, v) for k, v in record.iteritems() + if k in REFERENCING_FIELDS) +def exclude_ref_fields(record): + return dict((k, v) for k, v in record.iteritems() + if k not in REFERENCING_FIELDS) + +CREATE = lambda values: (0, False, values) +UPDATE = lambda id, values: (1, id, values) +DELETE = lambda id: (2, id, False) +FORGET = lambda id: (3, id, False) +LINK_TO = lambda id: (4, id, False) +DELETE_ALL = lambda: (5, False, False) +REPLACE_WITH = lambda ids: (6, False, ids) + class ConversionNotFound(ValueError): pass class ir_fields_converter(orm.Model): @@ -145,9 +161,8 @@ class ir_fields_converter(orm.Model): :rtype: str """ # Can import by name_get, external id or database id - allowed_fields = set([None, 'id', '.id']) fieldset = set(record.iterkeys()) - if fieldset - allowed_fields: + if fieldset - REFERENCING_FIELDS: raise ValueError( _(u"Can not create Many-To-One records indirectly, import the field separately")) if len(fieldset) > 1: @@ -190,4 +205,17 @@ class ir_fields_converter(orm.Model): return [(6, 0, ids)] def _str_to_one2many(self, cr, uid, model, column, value, context=None): - return value + commands = [] + for subfield, record in zip((self._referencing_subfield( + only_ref_fields(record)) + for record in value), + value): + id, subfield_type = self.db_id_for( + cr, uid, model, column, subfield, record[subfield], context=context) + writable = exclude_ref_fields(record) + if id: + commands.append(LINK_TO(id)) + commands.append(UPDATE(id, writable)) + else: + commands.append(CREATE(writable)) + return commands diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 80e5cfe9c8f..44b6c1a56df 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -869,10 +869,13 @@ class test_o2m(ImporterCase): "invalid literal for int() with base 10: '%d,%d'" % (id1, id2)) def test_link(self): - id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + """ O2M relating to an existing record (update) force a LINK_TO as well + """ + O2M = self.registry('export.one2many.child') + id1 = O2M.create(self.cr, openerp.SUPERUSER_ID, { 'str': 'Bf', 'value': 109 }) - id2 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { + id2 = O2M.create(self.cr, openerp.SUPERUSER_ID, { 'str': 'Me', 'value': 262 }) @@ -880,17 +883,14 @@ class test_o2m(ImporterCase): ['42', str(id1)], ['', str(id2)], ]) - self.assertEqual(len(ids), 2) self.assertFalse(messages) + self.assertEqual(len(ids), 1) - # No record values alongside id => o2m resolution skipped altogether, - # creates 2 records => remove/don't import columns sideshow columns, - # get completely different semantics - b, b1 = self.browse() + [b] = self.browse() self.assertEqual(b.const, 42) - self.assertEqual(values(b.value), []) - self.assertEqual(b1.const, 4) - self.assertEqual(values(b1.value), []) + # automatically forces link between core record and o2ms + self.assertEqual(values(b.value), [109, 262]) + self.assertEqual(values(b.value, field='parent_id'), [b, b]) def test_link_2(self): O2M_c = self.registry('export.one2many.child') @@ -905,24 +905,13 @@ class test_o2m(ImporterCase): ['42', str(id1), '1'], ['', str(id2), '2'], ]) - self.assertEqual(len(ids), 2) self.assertFalse(messages) + self.assertEqual(len(ids), 1) - (b,) = self.browse() - # if an id (db or xid) is provided, expectations that objects are - # *already* linked and emits UPDATE (1, id, {}). - # Noid => CREATE (0, ?, {}) - # TODO: xid ignored aside from getting corresponding db id? + [b] = self.browse() self.assertEqual(b.const, 42) - self.assertEqual(values(b.value), []) - - # FIXME: updates somebody else's records? - self.assertEqual( - O2M_c.read(self.cr, openerp.SUPERUSER_ID, id1), - {'id': id1, 'str': 'Bf', 'value': 1, 'parent_id': False}) - self.assertEqual( - O2M_c.read(self.cr, openerp.SUPERUSER_ID, id2), - {'id': id2, 'str': 'Me', 'value': 2, 'parent_id': False}) + self.assertEqual(values(b.value), [1, 2]) + self.assertEqual(values(b.value, field='parent_id'), [b, b]) class test_o2m_multiple(ImporterCase): model_name = 'export.one2many.multiple' From 8e841cd8f7603fc59be78b7d73283f65e4247840 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 20 Sep 2012 12:56:12 +0200 Subject: [PATCH 005/171] [FIX] non-linking o2m tests, corresponding code bzr revid: xmo@openerp.com-20120920105612-03ifizt2iv08tdhz --- openerp/addons/base/ir/ir_fields.py | 22 +++++--- openerp/tests/addons/test_impex/models.py | 6 +++ .../addons/test_impex/tests/test_load.py | 53 ++++++++----------- 3 files changed, 45 insertions(+), 36 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 6b3898f3fdd..44351229c26 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -206,16 +206,26 @@ class ir_fields_converter(orm.Model): return [(6, 0, ids)] def _str_to_one2many(self, cr, uid, model, column, value, context=None): commands = [] - for subfield, record in zip((self._referencing_subfield( - only_ref_fields(record)) - for record in value), - value): - id, subfield_type = self.db_id_for( - cr, uid, model, column, subfield, record[subfield], context=context) + + for record in value: + id = None + refs = only_ref_fields(record) + # there are ref fields in the record + if refs: + subfield = self._referencing_subfield(refs) + reference = record[subfield] + id, subfield_type = self.db_id_for( + cr, uid, model, column, subfield, reference, context=context) + if id is None: + raise ValueError( + _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") + % {'field_type': subfield_type, 'value': reference}) + writable = exclude_ref_fields(record) if id: commands.append(LINK_TO(id)) commands.append(UPDATE(id, writable)) else: commands.append(CREATE(writable)) + return commands diff --git a/openerp/tests/addons/test_impex/models.py b/openerp/tests/addons/test_impex/models.py index 455ea6b22f4..37894ccb78c 100644 --- a/openerp/tests/addons/test_impex/models.py +++ b/openerp/tests/addons/test_impex/models.py @@ -67,6 +67,12 @@ class One2ManyChild(orm.Model): def name_get(self, cr, uid, ids, context=None): return [(record.id, "%s:%s" % (self._name, record.value)) for record in self.browse(cr, uid, ids, context=context)] + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): + return (self.name_get(cr, user, + self.search(cr, user, [['value', operator, int(name.split(':')[1])]]) + , context=context) + if isinstance(name, basestring) and name.split(':')[0] == self._name + else []) class One2ManyMultiple(orm.Model): _name = 'export.one2many.multiple' diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 44b6c1a56df..25159dcd2ba 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -786,20 +786,21 @@ class test_o2m(ImporterCase): model_name = 'export.one2many' def test_name_get(self): - # FIXME: bloody hell why can't this just name_create the record? - self.assertRaises( - IndexError, - self.import_, + s = u'Java is a DSL for taking large XML files and converting them ' \ + u'to stack traces' + ids, messages = self.import_( ['const', 'value'], - [['5', u'Java is a DSL for taking large XML files' - u' and converting them to stack traces']]) + [['5', s]]) + self.assertEqual(messages, [message( + u"No matching record found for name '%s' in field 'value'" % s)]) + self.assertIs(ids, False) def test_single(self): ids, messages = self.import_(['const', 'value/value'], [ ['5', '63'] ]) - self.assertEqual(len(ids), 1) self.assertFalse(messages) + self.assertEqual(len(ids), 1) (b,) = self.browse() self.assertEqual(b.const, 5) @@ -810,8 +811,8 @@ class test_o2m(ImporterCase): ['5', '63'], ['6', '64'], ]) - self.assertEqual(len(ids), 2) self.assertFalse(messages) + self.assertEqual(len(ids), 2) b1, b2 = self.browse() self.assertEqual(b1.const, 5) @@ -826,8 +827,8 @@ class test_o2m(ImporterCase): ['', '65'], ['', '66'], ]) - self.assertEqual(len(ids), 4) self.assertFalse(messages) + self.assertEqual(len(ids), 1) (b,) = self.browse() self.assertEqual(values(b.value), [63, 64, 65, 66]) @@ -839,8 +840,8 @@ class test_o2m(ImporterCase): ['the', '', '65'], ['rhythm', '', '66'], ]) - self.assertEqual(len(ids), 4) self.assertFalse(messages) + self.assertEqual(len(ids), 1) (b,) = self.browse() self.assertEqual(values(b.value), [63, 64, 65, 66]) @@ -923,18 +924,12 @@ class test_o2m_multiple(ImporterCase): ['', '13', '23'], ['', '14', ''], ]) - self.assertEqual(len(ids), 4) self.assertFalse(messages) + self.assertEqual(len(ids), 1) # Oh yeah, that's the stuff - (b, b1, b2) = self.browse() - self.assertEqual(values(b.child1), [11]) - self.assertEqual(values(b.child2), [21]) - - self.assertEqual(values(b1.child1), [12]) - self.assertEqual(values(b1.child2), [22]) - - self.assertEqual(values(b2.child1), [13, 14]) - self.assertEqual(values(b2.child2), [23]) + [b] = self.browse() + self.assertEqual(values(b.child1), [11, 12, 13, 14]) + self.assertEqual(values(b.child2), [21, 22, 23]) def test_multi(self): ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ @@ -945,13 +940,12 @@ class test_o2m_multiple(ImporterCase): ['', '', '22'], ['', '', '23'], ]) - self.assertEqual(len(ids), 6) self.assertFalse(messages) - # What the actual fuck? - (b, b1) = self.browse() + self.assertEqual(len(ids), 1) + + [b] = self.browse() self.assertEqual(values(b.child1), [11, 12, 13, 14]) - self.assertEqual(values(b.child2), [21]) - self.assertEqual(values(b1.child2), [22, 23]) + self.assertEqual(values(b.child2), [21, 22, 23]) def test_multi_fullsplit(self): ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ @@ -963,14 +957,13 @@ class test_o2m_multiple(ImporterCase): ['', '', '22'], ['', '', '23'], ]) - self.assertEqual(len(ids), 7) self.assertFalse(messages) - # oh wow - (b, b1) = self.browse() + self.assertEqual(len(ids), 1) + + [b] = self.browse() self.assertEqual(b.const, 5) self.assertEqual(values(b.child1), [11, 12, 13, 14]) - self.assertEqual(b1.const, 36) - self.assertEqual(values(b1.child2), [21, 22, 23]) + self.assertEqual(values(b.child2), [21, 22, 23]) # function, related, reference: written to db as-is... # => function uses @type for value coercion/conversion From fdba99aaeb3998b6f1e76e69fc77bf45b0c73ee2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 20 Sep 2012 13:09:14 +0200 Subject: [PATCH 006/171] [ADD] inline o2m LINK_TO (m2m-style) bzr revid: xmo@openerp.com-20120920110914-hy2rtivhn9cs5wuc --- openerp/addons/base/ir/ir_fields.py | 13 ++++++++-- .../addons/test_impex/tests/test_load.py | 24 ++++++++++--------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 44351229c26..ae0d33298ca 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -204,10 +204,19 @@ class ir_fields_converter(orm.Model): ids.append(id) return [(6, 0, ids)] - def _str_to_one2many(self, cr, uid, model, column, value, context=None): + def _str_to_one2many(self, cr, uid, model, column, records, context=None): commands = [] - for record in value: + if len(records) == 1 and exclude_ref_fields(records[0]) == {}: + # only one row with only ref field, field=ref1,ref2,ref3 as in + # m2o/m2m + record = records[0] + subfield = self._referencing_subfield(record) + # transform [{subfield:ref1,ref2,ref3}] into + # [{subfield:ref1},{subfield:ref2},{subfield:ref3}] + records = ({subfield:item} for item in record[subfield].split(',')) + + for record in records: id = None refs = only_ref_fields(record) # there are ref fields in the record diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 25159dcd2ba..9ce0d5628ed 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -850,6 +850,8 @@ class test_o2m(ImporterCase): 'this is the rhythm'.split()) def test_link_inline(self): + """ m2m-style specification for o2ms + """ id1 = self.registry('export.one2many.child').create(self.cr, openerp.SUPERUSER_ID, { 'str': 'Bf', 'value': 109 }) @@ -857,17 +859,17 @@ class test_o2m(ImporterCase): 'str': 'Me', 'value': 262 }) - try: - self.import_(['const', 'value/.id'], [ - ['42', '%d,%d' % (id1, id2)] - ]) - self.fail("Should have raised a valueerror") - except ValueError, e: - # should be Exception(Database ID doesn't exist: export.one2many.child : $id1,$id2) - self.assertIs(type(e), ValueError) - self.assertEqual( - e.args[0], - "invalid literal for int() with base 10: '%d,%d'" % (id1, id2)) + ids, messages = self.import_(['const', 'value/.id'], [ + ['42', '%d,%d' % (id1, id2)] + ]) + self.assertFalse(messages) + self.assertEqual(len(ids), 1) + + [b] = self.browse() + self.assertEqual(b.const, 42) + # automatically forces link between core record and o2ms + self.assertEqual(values(b.value), [109, 262]) + self.assertEqual(values(b.value, field='parent_id'), [b, b]) def test_link(self): """ O2M relating to an existing record (update) force a LINK_TO as well From a9bc82c46dc007fa3bf0d479d6afab40530bb648 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 20 Sep 2012 17:04:43 +0200 Subject: [PATCH 007/171] [IMP] translation tests, translated acceptable values for boolean fields bzr revid: xmo@openerp.com-20120920150443-l9lna4bnkta7n2o8 --- openerp/addons/base/ir/ir_fields.py | 27 +++- .../addons/test_impex/tests/test_load.py | 144 ++++++++---------- 2 files changed, 90 insertions(+), 81 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index ae0d33298ca..10b80511c73 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import functools import operator +import itertools import warnings from openerp.osv import orm, fields from openerp.tools.translate import _ @@ -55,7 +56,29 @@ class ir_fields_converter(orm.Model): converter, cr, uid, model, column, context=context) def _str_to_boolean(self, cr, uid, model, column, value, context=None): - return value.lower() not in ('', '0', 'false', 'off') + # all translatables used for booleans + true, yes, false, no = _(u"true"), _(u"yes"), _(u"false"), _(u"no") + # potentially broken casefolding? What about locales? + trues = set(word.lower() for word in itertools.chain( + [u'1', u"true", u"yes"], # don't use potentially translated values + self._get_translations(cr, uid, ['code'], u"true", context=context), + self._get_translations(cr, uid, ['code'], u"yes", context=context), + )) + if value.lower() in trues: return True + + # potentially broken casefolding? What about locales? + falses = set(word.lower() for word in itertools.chain( + [u'', u"0", u"false", u"no"], + self._get_translations(cr, uid, ['code'], u"false", context=context), + self._get_translations(cr, uid, ['code'], u"no", context=context), + )) + if value.lower() in falses: return False + + warnings.warn( + _(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'") + % (value, yes), + orm.ImportWarning) + return True def _str_to_integer(self, cr, uid, model, column, value, context=None): if not value: return False @@ -86,7 +109,7 @@ class ir_fields_converter(orm.Model): selection = selection(model, cr, uid) for item, label in selection: labels = self._get_translations( - cr, uid, ('selection', 'model'), label, context=context) + cr, uid, ('selection', 'model', 'code'), label, context=context) labels.append(label) if value == unicode(item) or value in labels: return item diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 9ce0d5628ed..28957c29555 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -79,6 +79,27 @@ class ImporterCase(common.TransactionCase): }) return '__test__.' + name + def add_translations(self, name, type, code, *tnx): + Lang = self.registry('res.lang') + if not Lang.search(self.cr, openerp.SUPERUSER_ID, [('code', '=', code)]): + Lang.create(self.cr, openerp.SUPERUSER_ID, { + 'name': code, + 'code': code, + 'translatable': True, + 'date_format': '%d.%m.%Y', + 'decimal_point': ',', + }) + Translations = self.registry('ir.translation') + for source, value in tnx: + Translations.create(self.cr, openerp.SUPERUSER_ID, { + 'name': name, + 'lang': code, + 'type': type, + 'src': source, + 'value': value, + 'state': 'translated', + }) + class test_ids_stuff(ImporterCase): model_name = 'export.integer' @@ -139,41 +160,39 @@ class test_boolean_field(ImporterCase): ], values(records)) def test_falses(self): - ids, messages = self.import_( - ['value'], - [[u'0'], [u'off'], - [u'false'], [u'FALSE'], - [u'OFF'], [u''], - ]) - self.assertEqual(len(ids), 6) + for lang, source, value in [('fr_FR', 'no', u'non'), + ('de_DE', 'no', u'nein'), + ('ru_RU', 'no', u'нет'), + ('nl_BE', 'false', u'vals'), + ('lt_LT', 'false', u'klaidingas')]: + self.add_translations('test_import.py', 'code', lang, (source, value)) + falses = [[u'0'], [u'no'], [u'false'], [u'FALSE'], [u''], + [u'non'], # no, fr + [u'nein'], # no, de + [u'нет'], # no, ru + [u'vals'], # false, nl + [u'klaidingas'], # false, lt, + ] + + ids, messages = self.import_(['value'], falses) self.assertFalse(messages) - self.assertEqual([ - False, - False, - False, - False, - False, - False, - ], - values(self.read())) + self.assertEqual(len(ids), len(falses)) + self.assertEqual([False] * len(falses), values(self.read())) def test_trues(self): - ids, messages = self.import_( - ['value'], - [['no'], - ['None'], - ['nil'], - ['()'], - ['f'], - ['#f'], - # Problem: OpenOffice (and probably excel) output localized booleans - ['VRAI'], + trues = [['None'], ['nil'], ['()'], ['f'], ['#f'], + # Problem: OpenOffice (and probably excel) output localized booleans + ['VRAI'], ['ok'], ['true'], ['yes'], ['1'], ] + ids, messages = self.import_(['value'], trues) + self.assertEqual(len(ids), 10) + self.assertEqual(messages, [ + message(u"Unknown value '%s' for boolean field 'value', assuming 'yes'" % v[0], + type='warning', from_=i, to_=i, record=i) + for i, v in enumerate(trues) + if v[0] != 'true' if v[0] != 'yes' if v[0] != '1' ]) - self.assertEqual(len(ids), 7) - # FIXME: should warn for values which are not "true", "yes" or "1" - self.assertFalse(messages) self.assertEqual( - [True] * 7, + [True] * 10, values(self.read())) class test_integer_field(ImporterCase): @@ -399,9 +418,9 @@ class test_text(ImporterCase): class test_selection(ImporterCase): model_name = 'export.selection' translations_fr = [ - ("Qux", "toto"), - ("Bar", "titi"), ("Foo", "tete"), + ("Bar", "titi"), + ("Qux", "toto"), ] def test_imported(self): @@ -416,23 +435,8 @@ class test_selection(ImporterCase): self.assertEqual([3, 2, 1, 2], values(self.read())) def test_imported_translated(self): - self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { - 'name': u'Français', - 'code': 'fr_FR', - 'translatable': True, - 'date_format': '%d.%m.%Y', - 'decimal_point': ',', - 'thousand_sep': ' ', - }) - Translations = self.registry('ir.translation') - for source, value in self.translations_fr: - Translations.create(self.cr, openerp.SUPERUSER_ID, { - 'name': 'export.selection,value', - 'lang': 'fr_FR', - 'type': 'selection', - 'src': source, - 'value': value - }) + self.add_translations( + 'export.selection,value', 'selection', 'fr_FR', *self.translations_fr) ids, messages = self.import_(['value'], [ ['toto'], @@ -474,7 +478,7 @@ class test_selection_function(ImporterCase): translations_fr = [ ("Corge", "toto"), ("Grault", "titi"), - ("Whee", "tete"), + ("Wheee", "tete"), ("Moog", "tutu"), ] @@ -483,7 +487,7 @@ class test_selection_function(ImporterCase): be good news) *and* serializes the selection function to reverse it: import does not actually know that the selection field uses a function """ - # NOTE: conflict between a value and a label => ? + # NOTE: conflict between a value and a label => pick first ids, messages = self.import_(['value'], [ ['3'], ["Grault"], @@ -497,38 +501,20 @@ class test_selection_function(ImporterCase): def test_translated(self): """ Expects output of selection function returns translated labels """ - self.registry('res.lang').create(self.cr, openerp.SUPERUSER_ID, { - 'name': u'Français', - 'code': 'fr_FR', - 'translatable': True, - 'date_format': '%d.%m.%Y', - 'decimal_point': ',', - 'thousand_sep': ' ', - }) - Translations = self.registry('ir.translation') - for source, value in self.translations_fr: - Translations.create(self.cr, openerp.SUPERUSER_ID, { - 'name': 'export.selection,value', - 'lang': 'fr_FR', - 'type': 'selection', - 'src': source, - 'value': value - }) + self.add_translations( + 'export.selection,value', 'selection', 'fr_FR', *self.translations_fr) + ids, messages = self.import_(['value'], [ - ['toto'], + ['titi'], ['tete'], ], context={'lang': 'fr_FR'}) - self.assertIs(ids, False) - self.assertEqual(messages, [{ - 'type': 'error', - 'rows': {'from': 1, 'to': 1}, - 'record': 1, - 'field': 'value', - 'message': "Value 'tete' not found in selection field 'value'", - }]) - ids, messages = self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}) - self.assertEqual(len(ids), 1) self.assertFalse(messages) + self.assertEqual(len(ids), 2) + self.assertEqual(values(self.read()), ['1', '2']) + + ids, messages = self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}) + self.assertFalse(messages) + self.assertEqual(len(ids), 1) class test_m2o(ImporterCase): model_name = 'export.many2one' From b4421c8fba5a7208a0cf38708cfaa235d9bfa497 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 24 Sep 2012 12:14:04 +0200 Subject: [PATCH 008/171] [ADD] cache dict on the cursor, for request-local repeated reads bzr revid: xmo@openerp.com-20120924101404-cel6oy1akabbo7id --- openerp/sql_db.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/openerp/sql_db.py b/openerp/sql_db.py index 7b6f4469b8d..a6ebcd0300f 100644 --- a/openerp/sql_db.py +++ b/openerp/sql_db.py @@ -138,6 +138,16 @@ class Cursor(object): sure you use psycopg2 v2.4.2 or newer if you use PostgreSQL 9.1 and the performance hit is a concern for you. + .. attribute:: cache + + Cache dictionary with a "request" (-ish) lifecycle, only lives as + long as the cursor itself does and proactively cleared when the + cursor is closed. + + This cache should *only* be used to store repeatable reads as it + ignores rollbacks and savepoints, it should not be used to store + *any* data which may be modified during the life of the cursor. + """ IN_MAX = 1000 # decent limit on size of IN queries - guideline = Oracle limit @@ -182,6 +192,8 @@ class Cursor(object): self._default_log_exceptions = True + self.cache = {} + def __del__(self): if not self.__closed and not self._cnx.closed: # Oops. 'self' has not been closed explicitly. @@ -279,6 +291,8 @@ class Cursor(object): if not self._obj: return + del self.cache + if self.sql_log: self.__closer = frame_codeinfo(currentframe(),3) self.print_log() From 449a86a51f62c2e3379fc649c55d89080c278fa4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 24 Sep 2012 12:32:57 +0200 Subject: [PATCH 009/171] [IMP] cache boolean and selection translations for the request (cursor) to avoid fetching them over and over again bzr revid: xmo@openerp.com-20120924103257-1jgc3qhddzzi5c17 --- openerp/addons/base/ir/ir_fields.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 10b80511c73..73e748015a3 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -95,11 +95,20 @@ class ir_fields_converter(orm.Model): return value or False def _get_translations(self, cr, uid, types, src, context): + types = tuple(types) + # Cache translations so they don't have to be reloaded from scratch on + # every row of the file + tnx_cache = cr.cache.setdefault(self._name, {}) + if tnx_cache.setdefault(types, {}) and src in tnx_cache[types]: + return tnx_cache[types][src] + Translations = self.pool['ir.translation'] tnx_ids = Translations.search( cr, uid, [('type', 'in', types), ('src', '=', src)], context=context) tnx = Translations.read(cr, uid, tnx_ids, ['value'], context=context) - return map(operator.itemgetter('value'), tnx) + result = tnx_cache[types][src] = map(operator.itemgetter('value'), tnx) + return result + def _str_to_selection(self, cr, uid, model, column, value, context=None): selection = column.selection From 877e21ffdebeba412b7cb4db5fdf352adfac2ff2 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 24 Sep 2012 12:52:30 +0200 Subject: [PATCH 010/171] [IMP] return fields_get-style translated field strings (if available) in user-readable warning and error messages from import, rather than logical field names bzr revid: xmo@openerp.com-20120924105230-1b7157xbruy2e5zr --- openerp/osv/orm.py | 19 +++++++++++--- .../addons/test_impex/tests/test_load.py | 26 +++++++++---------- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 57cc5823dd9..e58d6b76a76 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1585,8 +1585,15 @@ class BaseModel(object): :returns: a list of triplets of (id, xid, record) :rtype: list((int|None, str|None, dict)) """ + if context is None: context = {} Converter = self.pool['ir.fields.converter'] columns = dict((k, v.column) for k, v in self._all_columns.iteritems()) + Translation = self.pool['ir.translation'] + field_names = dict( + (f, (Translation._get_source(cr, uid, self._name + ',' + f, 'field', + context.get('lang', False) or 'en_US') + or column.string or f)) + for f, column in columns.iteritems()) converters = dict( (k, Converter.to_field(cr, uid, self, column, context=context)) for k, column in columns.iteritems()) @@ -1619,18 +1626,24 @@ class BaseModel(object): for field, strvalue in record.iteritems(): if field in (None, 'id', '.id'): continue - message_base = dict(extras, record=stream.index, field=field) + # In warnings and error messages, use translated string as + # field name + message_base = dict( + extras, record=stream.index, field=field_names[field]) with warnings.catch_warnings(record=True) as w: try: converted[field] = converters[field](strvalue) + # In warning and error returned, use logical field name + # as field so client can reverse for warning in w: - log(dict(message_base, type='warning', + log(dict(message_base, type='warning', field=field, message=unicode(warning.message) % message_base)) except ValueError, e: log(dict(message_base, type='error', - message=unicode(e) % message_base + field=field, + message=unicode(e) % message_base, )) yield dbid, xid, converted, dict(extras, record=stream.index) diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 28957c29555..cf95f5bb6c4 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -186,7 +186,7 @@ class test_boolean_field(ImporterCase): ids, messages = self.import_(['value'], trues) self.assertEqual(len(ids), 10) self.assertEqual(messages, [ - message(u"Unknown value '%s' for boolean field 'value', assuming 'yes'" % v[0], + message(u"Unknown value '%s' for boolean field 'unknown', assuming 'yes'" % v[0], type='warning', from_=i, to_=i, record=i) for i, v in enumerate(trues) if v[0] != 'true' if v[0] != 'yes' if v[0] != '1' @@ -460,7 +460,7 @@ class test_selection(ImporterCase): 'rows': {'from': 0, 'to': 0}, 'record': 0, 'field': 'value', - 'message': "Value 'Baz' not found in selection field 'value'", + 'message': "Value 'Baz' not found in selection field 'unknown'", }]) ids, messages = self.import_(['value'], [[42]]) @@ -470,7 +470,7 @@ class test_selection(ImporterCase): 'rows': {'from': 0, 'to': 0}, 'record': 0, 'field': 'value', - 'message': "Value '42' not found in selection field 'value'", + 'message': "Value '42' not found in selection field 'unknown'", }]) class test_selection_function(ImporterCase): @@ -583,7 +583,7 @@ class test_m2o(ImporterCase): ids, messages = self.import_(['value'], [[name2]]) self.assertEqual( messages, - [message(u"Found multiple matches for field 'value' (2 matches)", + [message(u"Found multiple matches for field 'unknown' (2 matches)", type='warning')]) self.assertEqual(len(ids), 1) self.assertEqual([ @@ -607,7 +607,7 @@ class test_m2o(ImporterCase): [integer_id1], ]) self.assertEqual(messages, [ - message(u"No matching record found for name '%s' in field 'value'" % id, + message(u"No matching record found for name '%s' in field 'unknown'" % id, from_=index, to_=index, record=index) for index, id in enumerate([integer_id1, integer_id2, integer_id1])]) self.assertIs(ids, False) @@ -626,19 +626,19 @@ class test_m2o(ImporterCase): ids, messages = self.import_(['value'], [['nameisnoexist:3']]) self.assertEqual(messages, [message( u"No matching record found for name 'nameisnoexist:3' " - u"in field 'value'")]) + u"in field 'unknown'")]) self.assertIs(ids, False) ids, messages = self.import_(['value/id'], [['noxidhere']]) self.assertEqual(messages, [message( u"No matching record found for external id 'noxidhere' " - u"in field 'value'")]) + u"in field 'unknown'")]) self.assertIs(ids, False) ids, messages = self.import_(['value/.id'], [['66']]) self.assertEqual(messages, [message( u"No matching record found for database id '66' " - u"in field 'value'")]) + u"in field 'unknown'")]) self.assertIs(ids, False) def test_fail_multiple(self): @@ -646,7 +646,7 @@ class test_m2o(ImporterCase): ['value', 'value/id'], [['somename', 'somexid']]) self.assertEqual(messages, [message( - u"Ambiguous specification for field 'value', only provide one of " + u"Ambiguous specification for field 'unknown', only provide one of " u"name, external id or database id")]) self.assertIs(ids, False) @@ -690,7 +690,7 @@ class test_m2m(ImporterCase): ids, messages = self.import_(['value/.id'], [['42']]) self.assertEqual(messages, [message( u"No matching record found for database id '42' in field " - u"'value'")]) + u"'unknown'")]) self.assertIs(ids, False) def test_xids(self): @@ -716,7 +716,7 @@ class test_m2m(ImporterCase): ids, messages = self.import_(['value/id'], [['noxidforthat']]) self.assertEqual(messages, [message( u"No matching record found for external id 'noxidforthat' " - u"in field 'value'")]) + u"in field 'unknown'")]) self.assertIs(ids, False) def test_names(self): @@ -745,7 +745,7 @@ class test_m2m(ImporterCase): ids, messages = self.import_(['value'], [['wherethem2mhavenonames']]) self.assertEqual(messages, [message( u"No matching record found for name 'wherethem2mhavenonames' in " - u"field 'value'")]) + u"field 'unknown'")]) self.assertIs(ids, False) def test_import_to_existing(self): @@ -778,7 +778,7 @@ class test_o2m(ImporterCase): ['const', 'value'], [['5', s]]) self.assertEqual(messages, [message( - u"No matching record found for name '%s' in field 'value'" % s)]) + u"No matching record found for name '%s' in field 'unknown'" % s)]) self.assertIs(ids, False) def test_single(self): From a9c2cfcdb91c79cc60de909a4e23bd9e9e5f9787 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 24 Sep 2012 16:57:50 +0200 Subject: [PATCH 011/171] [IMP] make help attribute available on all actions bzr revid: xmo@openerp.com-20120924145750-n0gj4bww1d83h3fy --- openerp/addons/base/ir/ir_actions.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/ir/ir_actions.py b/openerp/addons/base/ir/ir_actions.py index 383f78181e9..b14f4cd7e84 100644 --- a/openerp/addons/base/ir/ir_actions.py +++ b/openerp/addons/base/ir/ir_actions.py @@ -19,14 +19,11 @@ # ############################################################################## -import ast -import copy import logging import os import re import time import tools -from xml import dom import netsvc from osv import fields,osv @@ -47,6 +44,9 @@ class actions(osv.osv): 'name': fields.char('Name', size=64, required=True), 'type': fields.char('Action Type', required=True, size=32,readonly=True), 'usage': fields.char('Action Usage', size=32), + 'help': fields.text('Action description', + help='Optional help text for the users with a description of the target view, such as its usage and purpose.', + translate=True), } _defaults = { 'usage': lambda *a: False, @@ -107,6 +107,7 @@ class report_xml(osv.osv): r['report_xsl'] and opj('addons',r['report_xsl'])) _name = 'ir.actions.report.xml' + _inherit = 'ir.actions.actions' _table = 'ir_act_report_xml' _sequence = 'ir_actions_id_seq' _order = 'name' @@ -155,6 +156,7 @@ report_xml() class act_window(osv.osv): _name = 'ir.actions.act_window' _table = 'ir_act_window' + _inherit = 'ir.actions.actions' _sequence = 'ir_actions_id_seq' _order = 'name' @@ -245,9 +247,6 @@ class act_window(osv.osv): 'filter': fields.boolean('Filter'), 'auto_search':fields.boolean('Auto Search'), 'search_view' : fields.function(_search_view, type='text', string='Search View'), - 'help': fields.text('Action description', - help='Optional help text for the users with a description of the target view, such as its usage and purpose.', - translate=True), 'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view"), } @@ -331,6 +330,7 @@ act_wizard() class act_url(osv.osv): _name = 'ir.actions.url' _table = 'ir_act_url' + _inherit = 'ir.actions.actions' _sequence = 'ir_actions_id_seq' _order = 'name' _columns = { @@ -432,6 +432,7 @@ class actions_server(osv.osv): _name = 'ir.actions.server' _table = 'ir_act_server' + _inherit = 'ir.actions.actions' _sequence = 'ir_actions_id_seq' _order = 'sequence,name' _columns = { From f143902d1afa345c8fad935a80a6b404a9a3cfb7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 24 Sep 2012 17:04:17 +0200 Subject: [PATCH 012/171] [IMP] allow converters to add data to import messages, formalize message keys bzr revid: xmo@openerp.com-20120924150417-c2y7g7vdsfz66363 --- openerp/addons/base/ir/ir_fields.py | 33 +++++++-- openerp/osv/orm.py | 70 +++++++++++++++---- .../addons/test_impex/tests/test_load.py | 32 +++------ 3 files changed, 94 insertions(+), 41 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 73e748015a3..2256542533b 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -38,6 +38,26 @@ class ir_fields_converter(orm.Model): By default, tries to get a method on itself with a name matching the pattern ``_$fromtype_$column._type`` and returns it. + Converter callables can either return a value to their caller or raise + ``ValueError``, which will be interpreted as a validation & conversion + failure. + + ValueError can have either one or two parameters. The first parameter + is mandatory, **must** be a unicode string and will be used as the + user-visible message for the error (it should be translatable and + translated). It can contain a ``field`` named format placeholder so the + caller can inject the field's translated, user-facing name (@string). + + The second parameter is optional and, if provided, must be a mapping. + This mapping will be merged into the error dictionary returned to the + client. + + If a converter can perform its function but has to make assumptions + about the data, it can send a warning to the user through signalling + an :class:`~openerp.osv.orm.ImportWarning` (via ``warnings.warn``). The + handling of a warning at the upper levels is the same as + ``ValueError`` above. + :param cr: openerp cursor :param uid: ID of user calling the converter :param column: column object to generate a value for @@ -74,10 +94,9 @@ class ir_fields_converter(orm.Model): )) if value.lower() in falses: return False - warnings.warn( + warnings.warn(orm.ImportWarning( _(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'") - % (value, yes), - orm.ImportWarning) + % (value, yes))) return True def _str_to_integer(self, cr, uid, model, column, value, context=None): @@ -124,7 +143,9 @@ class ir_fields_converter(orm.Model): return item raise ValueError( _(u"Value '%s' not found in selection field '%%(field)s'") % ( - value)) + value), { + 'moreinfo': map(operator.itemgetter(1), selection) + }) def db_id_for(self, cr, uid, model, column, subfield, value, context=None): @@ -174,9 +195,9 @@ class ir_fields_converter(orm.Model): cr, uid, name=value, operator='=', context=context) if ids: if len(ids) > 1: - warnings.warn( + warnings.warn(orm.ImportWarning( _(u"Found multiple matches for field '%%(field)s' (%d matches)") - % (len(ids)), orm.ImportWarning) + % (len(ids)))) id, _name = ids[0] else: raise Exception(u"Unknown sub-field '%s'" % subfield) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index e58d6b76a76..98e24075311 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1444,6 +1444,40 @@ class BaseModel(object): def load(self, cr, uid, fields, data, context=None): """ + Attempts to load the data matrix, and returns a list of ids (or + ``False`` if there was an error and no id could be generated) and a + list of messages. + + Each message is a dictionary with the following keys: + + ``type`` + the type of message, either ``warning`` or ``error``. Any ``error`` + message indicates the import failed and was rolled back. + ``message`` + the message's actual text, which should be translated and can be + shown to the user directly + ``rows`` + a dict with 2 keys ``from`` and ``to``, indicates the range of rows + in ``data`` which generated the message + ``record`` + a single integer, for warnings the index of the record which + generated the message (can be obtained from a non-false ``ids`` + result) + ``field`` + the name of the (logical) OpenERP field for which the error or + warning was generated + ``moreinfo`` (optional) + A string, a list or a dict, leading to more information about the + warning. + + * If ``moreinfo`` is a string, it is a supplementary warnings + message which should be hidden by default + * If ``moreinfo`` is a list, it provides a number of possible or + alternative values for the string + * If ``moreinfo`` is a dict, it is an OpenERP action descriptor + which can be executed to get more information about the issues + with the field. If present, the ``help`` key serves as a label + for the action (e.g. the text of the link). :param cr: cursor for the request :param int uid: ID of the user attempting the data import @@ -1598,6 +1632,14 @@ class BaseModel(object): (k, Converter.to_field(cr, uid, self, column, context=context)) for k, column in columns.iteritems()) + def _log(base, field, exception): + type = 'warning' if isinstance(exception, Warning) else 'error' + record = dict(base, field=field, type=type, + message=unicode(exception.args[0]) % base) + if len(exception.args) > 1 and exception.args[1]: + record.update(exception.args[1]) + log(record) + stream = CountingStream(records) for record, extras in stream: dbid = False @@ -1630,21 +1672,23 @@ class BaseModel(object): # field name message_base = dict( extras, record=stream.index, field=field_names[field]) - with warnings.catch_warnings(record=True) as w: - try: + try: + with warnings.catch_warnings(record=True) as ws: converted[field] = converters[field](strvalue) - # In warning and error returned, use logical field name - # as field so client can reverse - for warning in w: - log(dict(message_base, type='warning', field=field, - message=unicode(warning.message) % message_base)) - except ValueError, e: - log(dict(message_base, - type='error', - field=field, - message=unicode(e) % message_base, - )) + for warning in ws: + # bubble non-import warnings upward + if warning.category != ImportWarning: + warnings.warn(warning.message, warning.category) + continue + w = warning.message + if isinstance(w, basestring): + # wrap warning string in an ImportWarning for + # uniform handling + w = ImportWarning(w) + _log(message_base, field, w) + except ValueError, e: + _log(message_base, field, e) yield dbid, xid, converted, dict(extras, record=stream.index) diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index cf95f5bb6c4..f97dafaaf39 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -5,14 +5,10 @@ import openerp from openerp.tests import common from openerp.tools.misc import mute_logger -def message(msg, type='error', from_=0, to_=0, record=0, field='value'): - return { - 'type': type, - 'rows': {'from': from_, 'to': to_}, - 'record': record, - 'field': field, - 'message': msg - } +def message(msg, type='error', from_=0, to_=0, record=0, field='value', **kwargs): + return dict(kwargs, + type=type, rows={'from': from_, 'to': to_}, record=record, + field=field, message=msg) def error(row, message, record=None, **kwargs): """ Failed import of the record ``record`` at line ``row``, with the error @@ -455,23 +451,15 @@ class test_selection(ImporterCase): def test_invalid(self): ids, messages = self.import_(['value'], [['Baz']]) self.assertIs(ids, False) - self.assertEqual(messages, [{ - 'type': 'error', - 'rows': {'from': 0, 'to': 0}, - 'record': 0, - 'field': 'value', - 'message': "Value 'Baz' not found in selection field 'unknown'", - }]) + self.assertEqual(messages, [message( + u"Value 'Baz' not found in selection field 'unknown'", + moreinfo="Foo Bar Qux".split())]) ids, messages = self.import_(['value'], [[42]]) self.assertIs(ids, False) - self.assertEqual(messages, [{ - 'type': 'error', - 'rows': {'from': 0, 'to': 0}, - 'record': 0, - 'field': 'value', - 'message': "Value '42' not found in selection field 'unknown'", - }]) + self.assertEqual(messages, [message( + u"Value '42' not found in selection field 'unknown'", + moreinfo="Foo Bar Qux".split())]) class test_selection_function(ImporterCase): model_name = 'export.selection.function' From 9f2e7ba7ef86808ea5f10af178e7aeb939426323 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 24 Sep 2012 17:15:02 +0200 Subject: [PATCH 013/171] [IMP] return a dict from Model.load for easier future extensibility (if needed) rather than a tuple. also easier/cleaner to unpack on the JS side bzr revid: xmo@openerp.com-20120924151502-4robe639ctpuvb94 --- openerp/osv/orm.py | 6 +- .../addons/test_impex/tests/test_load.py | 378 +++++++++--------- 2 files changed, 192 insertions(+), 192 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 98e24075311..b824be91b96 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1486,7 +1486,7 @@ class BaseModel(object): :param data: row-major matrix of data to import :type data: list(list(str)) :param dict context: - :returns: + :returns: {ids: list(int)|False, messages: [Message]} """ cr.execute('SAVEPOINT model_load') messages = [] @@ -1516,8 +1516,8 @@ class BaseModel(object): messages.append(dict(info, type="error", message=str(e))) if any(message['type'] == 'error' for message in messages): cr.execute('ROLLBACK TO SAVEPOINT model_load') - return False, messages - return ids, messages + ids = False + return {'ids': ids, 'messages': messages} def _extract_records(self, cr, uid, fields_, data, context=None, log=lambda a: None): """ Generates record dicts from the data iterable. diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index f97dafaaf39..060b32c48f5 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -100,9 +100,9 @@ class test_ids_stuff(ImporterCase): model_name = 'export.integer' def test_create_with_id(self): - ids, messages = self.import_(['.id', 'value'], [['42', '36']]) - self.assertIs(ids, False) - self.assertEqual(messages, [{ + result = self.import_(['.id', 'value'], [['42', '36']]) + self.assertIs(result['ids'], False) + self.assertEqual(result['messages'], [{ 'type': 'error', 'rows': {'from': 0, 'to': 0}, 'record': 0, @@ -110,9 +110,9 @@ class test_ids_stuff(ImporterCase): 'message': u"Unknown database identifier '42'", }]) def test_create_with_xid(self): - ids, messages = self.import_(['id', 'value'], [['somexmlid', '42']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['id', 'value'], [['somexmlid', '42']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual( 'somexmlid', self.xid(self.browse()[0])) @@ -123,9 +123,9 @@ class test_ids_stuff(ImporterCase): 36, self.model.browse(self.cr, openerp.SUPERUSER_ID, id).value) - ids, messages = self.import_(['.id', 'value'], [[str(id), '42']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['.id', 'value'], [[str(id), '42']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual( [42], # updated value to imported values(self.read())) @@ -143,12 +143,12 @@ class test_boolean_field(ImporterCase): def test_empty(self): self.assertEqual( self.import_(['value'], []), - ([], [])) + {'ids': [], 'messages': []}) def test_exported(self): - ids, messages = self.import_(['value'], [['False'], ['True'], ]) - self.assertEqual(len(ids), 2) - self.assertFalse(messages) + result = self.import_(['value'], [['False'], ['True'], ]) + self.assertEqual(len(result['ids']), 2) + self.assertFalse(result['messages']) records = self.read() self.assertEqual([ False, @@ -170,18 +170,18 @@ class test_boolean_field(ImporterCase): [u'klaidingas'], # false, lt, ] - ids, messages = self.import_(['value'], falses) - self.assertFalse(messages) - self.assertEqual(len(ids), len(falses)) + result = self.import_(['value'], falses) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), len(falses)) self.assertEqual([False] * len(falses), values(self.read())) def test_trues(self): trues = [['None'], ['nil'], ['()'], ['f'], ['#f'], # Problem: OpenOffice (and probably excel) output localized booleans ['VRAI'], ['ok'], ['true'], ['yes'], ['1'], ] - ids, messages = self.import_(['value'], trues) - self.assertEqual(len(ids), 10) - self.assertEqual(messages, [ + result = self.import_(['value'], trues) + self.assertEqual(len(result['ids']), 10) + self.assertEqual(result['messages'], [ message(u"Unknown value '%s' for boolean field 'unknown', assuming 'yes'" % v[0], type='warning', from_=i, to_=i, record=i) for i, v in enumerate(trues) @@ -197,69 +197,69 @@ class test_integer_field(ImporterCase): def test_none(self): self.assertEqual( self.import_(['value'], []), - ([], [])) + {'ids': [], 'messages': []}) def test_empty(self): - ids, messages = self.import_(['value'], [['']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual( [False], values(self.read())) def test_zero(self): - ids, messages = self.import_(['value'], [['0']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['0']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) - ids, messages = self.import_(['value'], [['-0']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['-0']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual([False, False], values(self.read())) def test_positives(self): - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['1'], ['42'], [str(2**31-1)], ['12345678'] ]) - self.assertEqual(len(ids), 4) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 4) + self.assertFalse(result['messages']) self.assertEqual([ 1, 42, 2**31-1, 12345678 ], values(self.read())) def test_negatives(self): - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['-1'], ['-42'], [str(-(2**31 - 1))], [str(-(2**31))], ['-12345678'] ]) - self.assertEqual(len(ids), 5) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 5) + self.assertFalse(result['messages']) self.assertEqual([ -1, -42, -(2**31 - 1), -(2**31), -12345678 ], values(self.read())) @mute_logger('openerp.sql_db') def test_out_of_range(self): - ids, messages = self.import_(['value'], [[str(2**31)]]) - self.assertIs(ids, False) - self.assertEqual(messages, [{ + result = self.import_(['value'], [[str(2**31)]]) + self.assertIs(result['ids'], False) + self.assertEqual(result['messages'], [{ 'type': 'error', 'rows': {'from': 0, 'to': 0}, 'record': 0, 'message': "integer out of range\n" }]) - ids, messages = self.import_(['value'], [[str(-2**32)]]) - self.assertIs(ids, False) - self.assertEqual(messages, [{ + result = self.import_(['value'], [[str(-2**32)]]) + self.assertIs(result['ids'], False) + self.assertEqual(result['messages'], [{ 'type': 'error', 'rows': {'from': 0, 'to': 0}, 'record': 0, @@ -267,9 +267,9 @@ class test_integer_field(ImporterCase): }]) def test_nonsense(self): - ids, messages = self.import_(['value'], [['zorglub']]) - self.assertIs(ids, False) - self.assertEqual(messages, [{ + result = self.import_(['value'], [['zorglub']]) + self.assertIs(result['ids'], False) + self.assertEqual(result['messages'], [{ 'type': 'error', 'rows': {'from': 0, 'to': 0}, 'record': 0, @@ -282,29 +282,29 @@ class test_float_field(ImporterCase): def test_none(self): self.assertEqual( self.import_(['value'], []), - ([], [])) + {'ids': [], 'messages': []}) def test_empty(self): - ids, messages = self.import_(['value'], [['']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual( [False], values(self.read())) def test_zero(self): - ids, messages = self.import_(['value'], [['0']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['0']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) - ids, messages = self.import_(['value'], [['-0']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['-0']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual([False, False], values(self.read())) def test_positives(self): - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['1'], ['42'], [str(2**31-1)], @@ -312,15 +312,15 @@ class test_float_field(ImporterCase): [str(2**33)], ['0.000001'], ]) - self.assertEqual(len(ids), 6) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 6) + self.assertFalse(result['messages']) self.assertEqual([ 1, 42, 2**31-1, 12345678, 2.0**33, .000001 ], values(self.read())) def test_negatives(self): - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['-1'], ['-42'], [str(-2**31 + 1)], @@ -329,16 +329,16 @@ class test_float_field(ImporterCase): [str(-2**33)], ['-0.000001'], ]) - self.assertEqual(len(ids), 7) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 7) + self.assertFalse(result['messages']) self.assertEqual([ -1, -42, -(2**31 - 1), -(2**31), -12345678, -2.0**33, -.000001 ], values(self.read())) def test_nonsense(self): - ids, messages = self.import_(['value'], [['foobar']]) - self.assertIs(ids, False) - self.assertEqual(messages, [{ + result = self.import_(['value'], [['foobar']]) + self.assertIs(result['ids'], False) + self.assertEqual(result['messages'], [{ 'type': 'error', 'rows': {'from': 0, 'to': 0}, 'record': 0, @@ -350,13 +350,13 @@ class test_string_field(ImporterCase): model_name = 'export.string.bounded' def test_empty(self): - ids, messages = self.import_(['value'], [['']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual([False], values(self.read())) def test_imported(self): - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ [u'foobar'], [u'foobarbaz'], [u'Með suð í eyrum við spilum endalaust'], @@ -364,8 +364,8 @@ class test_string_field(ImporterCase): u"someone he can't pound a nail with a banana doesn't much " u"surprise him."] ]) - self.assertEqual(len(ids), 4) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 4) + self.assertFalse(result['messages']) self.assertEqual([ u"foobar", u"foobarbaz", @@ -377,14 +377,14 @@ class test_unbound_string_field(ImporterCase): model_name = 'export.string' def test_imported(self): - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ [u'í dag viðrar vel til loftárása'], # ackbar.jpg [u"If they ask you about fun, you tell them – fun is a filthy" u" parasite"] ]) - self.assertEqual(len(ids), 2) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 2) + self.assertFalse(result['messages']) self.assertEqual([ u"í dag viðrar vel til loftárása", u"If they ask you about fun, you tell them – fun is a filthy parasite" @@ -394,9 +394,9 @@ class test_text(ImporterCase): model_name = 'export.text' def test_empty(self): - ids, messages = self.import_(['value'], [['']]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['']]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual([False], values(self.read())) def test_imported(self): @@ -406,9 +406,9 @@ class test_text(ImporterCase): u"snúninga 12 tommu vínylplötur (sem geta verið allt að 30 mín " u"hvor hlið).\n\nBreiðskífur eru stundum tvöfaldar og eru þær þá" u" gefnar út á tveimur geisladiskum eða tveimur vínylplötum.") - ids, messages = self.import_(['value'], [[s]]) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [[s]]) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) self.assertEqual([s], values(self.read())) class test_selection(ImporterCase): @@ -420,44 +420,44 @@ class test_selection(ImporterCase): ] def test_imported(self): - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['Qux'], ['Bar'], ['Foo'], ['2'], ]) - self.assertEqual(len(ids), 4) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 4) + self.assertFalse(result['messages']) self.assertEqual([3, 2, 1, 2], values(self.read())) def test_imported_translated(self): self.add_translations( 'export.selection,value', 'selection', 'fr_FR', *self.translations_fr) - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['toto'], ['tete'], ['titi'], ], context={'lang': 'fr_FR'}) - self.assertEqual(len(ids), 3) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 3) + self.assertFalse(result['messages']) self.assertEqual([3, 1, 2], values(self.read())) - ids, messages = self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}) - self.assertEqual(len(ids), 1) - self.assertFalse(messages) + result = self.import_(['value'], [['Foo']], context={'lang': 'fr_FR'}) + self.assertEqual(len(result['ids']), 1) + self.assertFalse(result['messages']) def test_invalid(self): - ids, messages = self.import_(['value'], [['Baz']]) - self.assertIs(ids, False) - self.assertEqual(messages, [message( + result = self.import_(['value'], [['Baz']]) + self.assertIs(result['ids'], False) + self.assertEqual(result['messages'], [message( u"Value 'Baz' not found in selection field 'unknown'", moreinfo="Foo Bar Qux".split())]) - ids, messages = self.import_(['value'], [[42]]) - self.assertIs(ids, False) - self.assertEqual(messages, [message( + result = self.import_(['value'], [[42]]) + self.assertIs(result['ids'], False) + self.assertEqual(result['messages'], [message( u"Value '42' not found in selection field 'unknown'", moreinfo="Foo Bar Qux".split())]) @@ -476,12 +476,12 @@ class test_selection_function(ImporterCase): import does not actually know that the selection field uses a function """ # NOTE: conflict between a value and a label => pick first - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['3'], ["Grault"], ]) - self.assertEqual(len(ids), 2) - self.assertFalse(messages) + self.assertEqual(len(result['ids']), 2) + self.assertFalse(result['messages']) self.assertEqual( ['3', '1'], values(self.read())) @@ -492,17 +492,17 @@ class test_selection_function(ImporterCase): self.add_translations( 'export.selection,value', 'selection', 'fr_FR', *self.translations_fr) - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['titi'], ['tete'], ], context={'lang': 'fr_FR'}) - self.assertFalse(messages) - self.assertEqual(len(ids), 2) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 2) self.assertEqual(values(self.read()), ['1', '2']) - ids, messages = self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + result = self.import_(['value'], [['Wheee']], context={'lang': 'fr_FR'}) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) class test_m2o(ImporterCase): model_name = 'export.many2one' @@ -519,14 +519,14 @@ class test_m2o(ImporterCase): name2 = dict(self.registry('export.integer').name_get( self.cr, openerp.SUPERUSER_ID,[integer_id2]))[integer_id2] - ids , messages = self.import_(['value'], [ + result = self.import_(['value'], [ # import by name_get [name1], [name1], [name2], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 3) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 3) # correct ids assigned to corresponding records self.assertEqual([ (integer_id1, name1), @@ -541,18 +541,18 @@ class test_m2o(ImporterCase): xid = self.xid(ExportInteger.browse( self.cr, openerp.SUPERUSER_ID, [integer_id])[0]) - ids, messages = self.import_(['value/id'], [[xid]]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + result = self.import_(['value/id'], [[xid]]) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) b = self.browse() self.assertEqual(42, b[0].value.value) def test_by_id(self): integer_id = self.registry('export.integer').create( self.cr, openerp.SUPERUSER_ID, {'value': 42}) - ids, messages = self.import_(['value/.id'], [[integer_id]]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + result = self.import_(['value/.id'], [[integer_id]]) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) b = self.browse() self.assertEqual(42, b[0].value.value) @@ -568,12 +568,12 @@ class test_m2o(ImporterCase): # names should be the same self.assertEqual(name1, name2) - ids, messages = self.import_(['value'], [[name2]]) + result = self.import_(['value'], [[name2]]) self.assertEqual( - messages, + result['messages'], [message(u"Found multiple matches for field 'unknown' (2 matches)", type='warning')]) - self.assertEqual(len(ids), 1) + self.assertEqual(len(result['ids']), 1) self.assertEqual([ (integer_id1, name1) ], values(self.read())) @@ -588,55 +588,55 @@ class test_m2o(ImporterCase): self.cr, openerp.SUPERUSER_ID, {'value': 36}) # Because name_search all the things. Fallback schmallback - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ # import by id, without specifying it [integer_id1], [integer_id2], [integer_id1], ]) - self.assertEqual(messages, [ + self.assertEqual(result['messages'], [ message(u"No matching record found for name '%s' in field 'unknown'" % id, from_=index, to_=index, record=index) for index, id in enumerate([integer_id1, integer_id2, integer_id1])]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) def test_sub_field(self): """ Does not implicitly create the record, does not warn that you can't import m2o subfields (at all)... """ - ids, messages = self.import_(['value/value'], [['42']]) - self.assertEqual(messages, [ + result = self.import_(['value/value'], [['42']]) + self.assertEqual(result['messages'], [ message(u"Can not create Many-To-One records indirectly, import " u"the field separately")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) def test_fail_noids(self): - ids, messages = self.import_(['value'], [['nameisnoexist:3']]) - self.assertEqual(messages, [message( + result = self.import_(['value'], [['nameisnoexist:3']]) + self.assertEqual(result['messages'], [message( u"No matching record found for name 'nameisnoexist:3' " u"in field 'unknown'")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) - ids, messages = self.import_(['value/id'], [['noxidhere']]) - self.assertEqual(messages, [message( + result = self.import_(['value/id'], [['noxidhere']]) + self.assertEqual(result['messages'], [message( u"No matching record found for external id 'noxidhere' " u"in field 'unknown'")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) - ids, messages = self.import_(['value/.id'], [['66']]) - self.assertEqual(messages, [message( + result = self.import_(['value/.id'], [['66']]) + self.assertEqual(result['messages'], [message( u"No matching record found for database id '66' " u"in field 'unknown'")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) def test_fail_multiple(self): - ids, messages = self.import_( + result = self.import_( ['value', 'value/id'], [['somename', 'somexid']]) - self.assertEqual(messages, [message( + self.assertEqual(result['messages'], [message( u"Ambiguous specification for field 'unknown', only provide one of " u"name, external id or database id")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) class test_m2m(ImporterCase): model_name = 'export.many2many' @@ -656,14 +656,14 @@ class test_m2m(ImporterCase): id5 = self.registry('export.many2many.other').create( self.cr, openerp.SUPERUSER_ID, {'value': 99, 'str': 'record4'}) - ids, messages = self.import_(['value/.id'], [ + result = self.import_(['value/.id'], [ ['%d,%d' % (id1, id2)], ['%d,%d,%d' % (id1, id3, id4)], ['%d,%d,%d' % (id1, id2, id3)], ['%d' % id5] ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 4) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 4) ids = lambda records: [record.id for record in records] @@ -675,11 +675,11 @@ class test_m2m(ImporterCase): self.assertEqual(values(b[2].value), [3, 44, 84]) def test_noids(self): - ids, messages = self.import_(['value/.id'], [['42']]) - self.assertEqual(messages, [message( + result = self.import_(['value/.id'], [['42']]) + self.assertEqual(result['messages'], [message( u"No matching record found for database id '42' in field " u"'unknown'")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) def test_xids(self): M2O_o = self.registry('export.many2many.other') @@ -689,23 +689,23 @@ class test_m2m(ImporterCase): id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) records = M2O_o.browse(self.cr, openerp.SUPERUSER_ID, [id1, id2, id3, id4]) - ids, messages = self.import_(['value/id'], [ + result = self.import_(['value/id'], [ ['%s,%s' % (self.xid(records[0]), self.xid(records[1]))], ['%s' % self.xid(records[3])], ['%s,%s' % (self.xid(records[2]), self.xid(records[1]))], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 3) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 3) b = self.browse() self.assertEqual(values(b[0].value), [3, 44]) self.assertEqual(values(b[2].value), [44, 84]) def test_noxids(self): - ids, messages = self.import_(['value/id'], [['noxidforthat']]) - self.assertEqual(messages, [message( + result = self.import_(['value/id'], [['noxidforthat']]) + self.assertEqual(result['messages'], [message( u"No matching record found for external id 'noxidforthat' " u"in field 'unknown'")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) def test_names(self): M2O_o = self.registry('export.many2many.other') @@ -717,24 +717,24 @@ class test_m2m(ImporterCase): name = lambda record: dict(record.name_get())[record.id] - ids, messages = self.import_(['value'], [ + result = self.import_(['value'], [ ['%s,%s' % (name(records[1]), name(records[2]))], ['%s,%s,%s' % (name(records[0]), name(records[1]), name(records[2]))], ['%s,%s' % (name(records[0]), name(records[3]))], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 3) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 3) b = self.browse() self.assertEqual(values(b[1].value), [3, 44, 84]) self.assertEqual(values(b[2].value), [3, 9]) def test_nonames(self): - ids, messages = self.import_(['value'], [['wherethem2mhavenonames']]) - self.assertEqual(messages, [message( + result = self.import_(['value'], [['wherethem2mhavenonames']]) + self.assertEqual(result['messages'], [message( u"No matching record found for name 'wherethem2mhavenonames' in " u"field 'unknown'")]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) def test_import_to_existing(self): M2O_o = self.registry('export.many2many.other') @@ -744,12 +744,12 @@ class test_m2m(ImporterCase): id4 = M2O_o.create(self.cr, openerp.SUPERUSER_ID, {'value': 9, 'str': 'record3'}) xid = 'myxid' - ids, messages = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) - ids, messages = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + result = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id1, id2)]]) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) + result = self.import_(['id', 'value/.id'], [[xid, '%d,%d' % (id3, id4)]]) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) b = self.browse() self.assertEqual(len(b), 1) @@ -762,31 +762,31 @@ class test_o2m(ImporterCase): def test_name_get(self): s = u'Java is a DSL for taking large XML files and converting them ' \ u'to stack traces' - ids, messages = self.import_( + result = self.import_( ['const', 'value'], [['5', s]]) - self.assertEqual(messages, [message( + self.assertEqual(result['messages'], [message( u"No matching record found for name '%s' in field 'unknown'" % s)]) - self.assertIs(ids, False) + self.assertIs(result['ids'], False) def test_single(self): - ids, messages = self.import_(['const', 'value/value'], [ + result = self.import_(['const', 'value/value'], [ ['5', '63'] ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) (b,) = self.browse() self.assertEqual(b.const, 5) self.assertEqual(values(b.value), [63]) def test_multicore(self): - ids, messages = self.import_(['const', 'value/value'], [ + result = self.import_(['const', 'value/value'], [ ['5', '63'], ['6', '64'], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 2) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 2) b1, b2 = self.browse() self.assertEqual(b1.const, 5) @@ -795,27 +795,27 @@ class test_o2m(ImporterCase): self.assertEqual(values(b2.value), [64]) def test_multisub(self): - ids, messages = self.import_(['const', 'value/value'], [ + result = self.import_(['const', 'value/value'], [ ['5', '63'], ['', '64'], ['', '65'], ['', '66'], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) (b,) = self.browse() self.assertEqual(values(b.value), [63, 64, 65, 66]) def test_multi_subfields(self): - ids, messages = self.import_(['value/str', 'const', 'value/value'], [ + result = self.import_(['value/str', 'const', 'value/value'], [ ['this', '5', '63'], ['is', '', '64'], ['the', '', '65'], ['rhythm', '', '66'], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) (b,) = self.browse() self.assertEqual(values(b.value), [63, 64, 65, 66]) @@ -833,11 +833,11 @@ class test_o2m(ImporterCase): 'str': 'Me', 'value': 262 }) - ids, messages = self.import_(['const', 'value/.id'], [ + result = self.import_(['const', 'value/.id'], [ ['42', '%d,%d' % (id1, id2)] ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) [b] = self.browse() self.assertEqual(b.const, 42) @@ -856,12 +856,12 @@ class test_o2m(ImporterCase): 'str': 'Me', 'value': 262 }) - ids, messages = self.import_(['const', 'value/.id'], [ + result = self.import_(['const', 'value/.id'], [ ['42', str(id1)], ['', str(id2)], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) [b] = self.browse() self.assertEqual(b.const, 42) @@ -878,12 +878,12 @@ class test_o2m(ImporterCase): 'str': 'Me', 'value': 262 }) - ids, messages = self.import_(['const', 'value/.id', 'value/value'], [ + result = self.import_(['const', 'value/.id', 'value/value'], [ ['42', str(id1), '1'], ['', str(id2), '2'], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) [b] = self.browse() self.assertEqual(b.const, 42) @@ -894,21 +894,21 @@ class test_o2m_multiple(ImporterCase): model_name = 'export.one2many.multiple' def test_multi_mixed(self): - ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ + result = self.import_(['const', 'child1/value', 'child2/value'], [ ['5', '11', '21'], ['', '12', '22'], ['', '13', '23'], ['', '14', ''], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) # Oh yeah, that's the stuff [b] = self.browse() self.assertEqual(values(b.child1), [11, 12, 13, 14]) self.assertEqual(values(b.child2), [21, 22, 23]) def test_multi(self): - ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ + result = self.import_(['const', 'child1/value', 'child2/value'], [ ['5', '11', '21'], ['', '12', ''], ['', '13', ''], @@ -916,15 +916,15 @@ class test_o2m_multiple(ImporterCase): ['', '', '22'], ['', '', '23'], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) [b] = self.browse() self.assertEqual(values(b.child1), [11, 12, 13, 14]) self.assertEqual(values(b.child2), [21, 22, 23]) def test_multi_fullsplit(self): - ids, messages = self.import_(['const', 'child1/value', 'child2/value'], [ + result = self.import_(['const', 'child1/value', 'child2/value'], [ ['5', '11', ''], ['', '12', ''], ['', '13', ''], @@ -933,8 +933,8 @@ class test_o2m_multiple(ImporterCase): ['', '', '22'], ['', '', '23'], ]) - self.assertFalse(messages) - self.assertEqual(len(ids), 1) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) [b] = self.browse() self.assertEqual(b.const, 5) From e6c8f1739a70ba8c0252ed8ace839c30f7e794dc Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 25 Sep 2012 09:42:56 +0200 Subject: [PATCH 014/171] [ADD] 'more info' action to m2o, o2m and m2m linking failures bzr revid: xmo@openerp.com-20120925074256-18puerjbfo3om265 --- openerp/addons/base/ir/ir_fields.py | 31 +++++++------- .../addons/test_impex/tests/test_load.py | 42 +++++++++---------- 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 2256542533b..cec74e8976e 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -201,6 +201,21 @@ class ir_fields_converter(orm.Model): id, _name = ids[0] else: raise Exception(u"Unknown sub-field '%s'" % subfield) + + if id is None: + raise ValueError( + _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") + % {'field_type': field_type, 'value': value}, { + 'moreinfo': { + 'type': 'ir.actions.act_window', + 'target': 'new', + 'res_model': column._obj, + 'view_mode': 'tree,form', + 'view_type': 'form', + 'views': [(False, 'tree', (False, 'form'))], + 'help': _(u"See all possible values") + } + }) return id, field_type def _referencing_subfield(self, record): @@ -235,12 +250,8 @@ class ir_fields_converter(orm.Model): reference = record[subfield] id, subfield_type = self.db_id_for( cr, uid, model, column, subfield, reference, context=context) - - if id is None: - raise ValueError( - _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") - % {'field_type': subfield_type, 'value': reference}) return id + def _str_to_many2many(self, cr, uid, model, column, value, context=None): [record] = value @@ -250,13 +261,9 @@ class ir_fields_converter(orm.Model): for reference in record[subfield].split(','): id, subfield_type = self.db_id_for( cr, uid, model, column, subfield, reference, context=context) - if id is None: - raise ValueError( - _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") - % {'field_type': subfield_type, 'value': reference}) ids.append(id) - return [(6, 0, ids)] + def _str_to_one2many(self, cr, uid, model, column, records, context=None): commands = [] @@ -278,10 +285,6 @@ class ir_fields_converter(orm.Model): reference = record[subfield] id, subfield_type = self.db_id_for( cr, uid, model, column, subfield, reference, context=context) - if id is None: - raise ValueError( - _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") - % {'field_type': subfield_type, 'value': reference}) writable = exclude_ref_fields(record) if id: diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 060b32c48f5..f625ecb7043 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -9,18 +9,16 @@ def message(msg, type='error', from_=0, to_=0, record=0, field='value', **kwargs return dict(kwargs, type=type, rows={'from': from_, 'to': to_}, record=record, field=field, message=msg) - -def error(row, message, record=None, **kwargs): - """ Failed import of the record ``record`` at line ``row``, with the error - message ``message`` - - :param str message: - :param dict record: - """ - return ( - -1, dict(record or {}, **kwargs), - "Line %d : %s" % (row, message), - '') +def moreaction(model): + return { + 'type': 'ir.actions.act_window', + 'target': 'new', + 'res_model': model, + 'view_mode': 'tree,form', + 'view_type': 'form', + 'views': [(False, 'tree', (False, 'form'))], + 'help': u"See all possible values" + } def values(seq, field='value'): return [item[field] for item in seq] @@ -596,7 +594,8 @@ class test_m2o(ImporterCase): ]) self.assertEqual(result['messages'], [ message(u"No matching record found for name '%s' in field 'unknown'" % id, - from_=index, to_=index, record=index) + from_=index, to_=index, record=index, + moreinfo=moreaction('export.integer')) for index, id in enumerate([integer_id1, integer_id2, integer_id1])]) self.assertIs(result['ids'], False) @@ -614,19 +613,19 @@ class test_m2o(ImporterCase): result = self.import_(['value'], [['nameisnoexist:3']]) self.assertEqual(result['messages'], [message( u"No matching record found for name 'nameisnoexist:3' " - u"in field 'unknown'")]) + u"in field 'unknown'", moreinfo=moreaction('export.integer'))]) self.assertIs(result['ids'], False) result = self.import_(['value/id'], [['noxidhere']]) self.assertEqual(result['messages'], [message( u"No matching record found for external id 'noxidhere' " - u"in field 'unknown'")]) + u"in field 'unknown'", moreinfo=moreaction('export.integer'))]) self.assertIs(result['ids'], False) result = self.import_(['value/.id'], [['66']]) self.assertEqual(result['messages'], [message( u"No matching record found for database id '66' " - u"in field 'unknown'")]) + u"in field 'unknown'", moreinfo=moreaction('export.integer'))]) self.assertIs(result['ids'], False) def test_fail_multiple(self): @@ -678,7 +677,7 @@ class test_m2m(ImporterCase): result = self.import_(['value/.id'], [['42']]) self.assertEqual(result['messages'], [message( u"No matching record found for database id '42' in field " - u"'unknown'")]) + u"'unknown'", moreinfo=moreaction('export.many2many.other'))]) self.assertIs(result['ids'], False) def test_xids(self): @@ -703,8 +702,8 @@ class test_m2m(ImporterCase): def test_noxids(self): result = self.import_(['value/id'], [['noxidforthat']]) self.assertEqual(result['messages'], [message( - u"No matching record found for external id 'noxidforthat' " - u"in field 'unknown'")]) + u"No matching record found for external id 'noxidforthat' in field" + u" 'unknown'", moreinfo=moreaction('export.many2many.other'))]) self.assertIs(result['ids'], False) def test_names(self): @@ -733,7 +732,7 @@ class test_m2m(ImporterCase): result = self.import_(['value'], [['wherethem2mhavenonames']]) self.assertEqual(result['messages'], [message( u"No matching record found for name 'wherethem2mhavenonames' in " - u"field 'unknown'")]) + u"field 'unknown'", moreinfo=moreaction('export.many2many.other'))]) self.assertIs(result['ids'], False) def test_import_to_existing(self): @@ -766,7 +765,8 @@ class test_o2m(ImporterCase): ['const', 'value'], [['5', s]]) self.assertEqual(result['messages'], [message( - u"No matching record found for name '%s' in field 'unknown'" % s)]) + u"No matching record found for name '%s' in field 'unknown'" % s, + moreinfo=moreaction('export.one2many.child'))]) self.assertIs(result['ids'], False) def test_single(self): From 359b4a44aa2d10cac6a0ce9bd623ac6b4e1a17d7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 25 Sep 2012 12:02:32 +0200 Subject: [PATCH 015/171] [ADD] ability to convert postgres error messages to human-readable ones also convert 'violates not-null constraint' to something about fields being required bzr revid: xmo@openerp.com-20120925100232-bfmxcxda65cki5kv --- openerp/osv/orm.py | 31 ++++++++++++++++++- openerp/tests/addons/test_impex/models.py | 1 + .../addons/test_impex/tests/test_load.py | 15 +++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index b824be91b96..25974760bdd 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -42,6 +42,7 @@ """ import calendar +import collections import copy import datetime import itertools @@ -1493,6 +1494,7 @@ class BaseModel(object): fields = map(fix_import_export_id_paths, fields) ModelData = self.pool['ir.model.data'] + fg = self.fields_get(cr, uid, context=context) mode = 'init' current_module = '' @@ -1509,11 +1511,16 @@ class BaseModel(object): current_module, record, mode=mode, xml_id=xid, noupdate=noupdate, res_id=id, context=context)) cr.execute('RELEASE SAVEPOINT model_load_save') + except psycopg2.Warning, e: + cr.execute('ROLLBACK TO SAVEPOINT model_load_save') + messages.append(dict(info, type='warning', message=str(e))) except psycopg2.Error, e: # Failed to write, log to messages, rollback savepoint (to # avoid broken transaction) and keep going cr.execute('ROLLBACK TO SAVEPOINT model_load_save') - messages.append(dict(info, type="error", message=str(e))) + messages.append(dict( + info, type="error", + **PGERROR_TO_OE[e.pgcode](self, fg, info, e))) if any(message['type'] == 'error' for message in messages): cr.execute('ROLLBACK TO SAVEPOINT model_load') ids = False @@ -5390,4 +5397,26 @@ class ImportWarning(Warning): """ Used to send warnings upwards the stack during the import process """ pass + + +def convert_pgerror_23502(model, fields, info, e): + m = re.match(r'^null value in column "(?P\w+)" violates ' + r'not-null constraint\n$', + str(e)) + if not m or m.group('field') not in fields: + return {'message': unicode(e)} + field = fields[m.group('field')] + return { + 'message': _(u"Missing required value for the field '%(field)s'") % { + 'field': field['string'] + }, + 'field': m.group('field'), + } + +PGERROR_TO_OE = collections.defaultdict( + # shape of mapped converters + lambda: (lambda model, fvg, info, pgerror: {'message': unicode(pgerror)}), { + # not_null_violation + '23502': convert_pgerror_23502, +}) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/tests/addons/test_impex/models.py b/openerp/tests/addons/test_impex/models.py index 37894ccb78c..c56ddc649e2 100644 --- a/openerp/tests/addons/test_impex/models.py +++ b/openerp/tests/addons/test_impex/models.py @@ -17,6 +17,7 @@ models = [ ('float', fields.float()), ('decimal', fields.float(digits=(16, 3))), ('string.bounded', fields.char('unknown', size=16)), + ('string.required', fields.char('unknown', size=None, required=True)), ('string', fields.char('unknown', size=None)), ('date', fields.date()), ('datetime', fields.datetime()), diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index f625ecb7043..eb777a57988 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -388,6 +388,21 @@ class test_unbound_string_field(ImporterCase): u"If they ask you about fun, you tell them – fun is a filthy parasite" ], values(self.read())) +class test_required_string_field(ImporterCase): + model_name = 'export.string.required' + + def test_empty(self): + result = self.import_(['value'], [[]]) + self.assertEqual(result['messages'], [message( + u"Missing required value for the field 'unknown'")]) + self.assertIs(result['ids'], False) + + def test_not_provided(self): + result = self.import_(['const'], [['12']]) + self.assertEqual(result['messages'], [message( + u"Missing required value for the field 'unknown'")]) + self.assertIs(result['ids'], False) + class test_text(ImporterCase): model_name = 'export.text' From d5c69fa87e9d17c92c00347af73fce53fb47dcd4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 25 Sep 2012 15:59:55 +0200 Subject: [PATCH 016/171] [FIX] use lists instead of iterators in BaseModel._extract_records * although it does use an explicit external index, it turns out the code is less complex * the rewrapping of (many) iterators on top of one another ended up blowing Python's stack during ``next`` calls, which Python does *not* like * added a 900-ish import test file to check for these things bzr revid: xmo@openerp.com-20120925135955-oielhopegnefyctm --- openerp/addons/base/ir/ir_fields.py | 5 +- openerp/osv/orm.py | 53 +++++-------------- .../addons/test_impex/tests/test_load.py | 13 +++++ 3 files changed, 30 insertions(+), 41 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index cec74e8976e..03d95fc67a6 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -69,7 +69,7 @@ class ir_fields_converter(orm.Model): """ # FIXME: return None converter = getattr( - self, '_%s_to_%s' % (fromtype.__name__, column._type)) + self, '_%s_to_%s' % (fromtype.__name__, column._type), None) if not converter: return None return functools.partial( @@ -113,6 +113,9 @@ class ir_fields_converter(orm.Model): def _str_to_text(self, cr, uid, model, column, value, context=None): return value or False + def _str_to_binary(self, cr, uid, model, column, value, context=None): + return value or False + def _get_translations(self, cr, uid, types, src, context): types = tuple(types) # Cache translations so they don't have to be reloaded from scratch on diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 25974760bdd..eb7745b2f6e 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1527,7 +1527,7 @@ class BaseModel(object): return {'ids': ids, 'messages': messages} def _extract_records(self, cr, uid, fields_, data, context=None, log=lambda a: None): - """ Generates record dicts from the data iterable. + """ Generates record dicts from the data sequence. The result is a generator of dicts mapping field names to raw (unconverted, unvalidated) values. @@ -1562,12 +1562,11 @@ class BaseModel(object): def only_o2m_values(row, f=get_nono2m_values, g=get_o2m_values): return any(g(row)) and not any(f(row)) - rows = CountingStream(data) + index = 0 while True: - row = next(rows, None) - if row is None: return - record_row_index = rows.index + if index >= len(data): return + row = data[index] # copy non-relational fields to record dict record = dict((field[0], value) for field, value in itertools.izip(fields_, row) @@ -1575,10 +1574,10 @@ class BaseModel(object): # Get all following rows which have relational values attached to # the current record (no non-relational values) - # WARNING: replaces existing ``rows`` - record_span, _rows = span(only_o2m_values, rows) + record_span = itertools.takewhile( + only_o2m_values, itertools.islice(data, index + 1, None)) # stitch record row back on for relational fields - record_span = itertools.chain([row], record_span) + record_span = list(itertools.chain([row], record_span)) for relfield in set( field[0] for field in fields_ if is_relational(field[0])): @@ -1586,8 +1585,6 @@ class BaseModel(object): # FIXME: how to not use _obj without relying on fields_get? Model = self.pool[column._obj] - # copy stream to reuse for next relational field - fieldrows, record_span = itertools.tee(record_span) # get only cells for this sub-field, should be strictly # non-empty, field path [None] is for name_get column indices, subfields = zip(*((index, field[1:] or [None]) @@ -1596,26 +1593,17 @@ class BaseModel(object): # return all rows which have at least one value for the # subfields of relfield - relfield_data = filter(any, map(itemgetter_tuple(indices), fieldrows)) + relfield_data = filter(any, map(itemgetter_tuple(indices), record_span)) record[relfield] = [subrecord for subrecord, _subinfo in Model._extract_records( cr, uid, subfields, relfield_data, context=context, log=log)] - # Ensure full consumption of the span (and therefore advancement of - # ``rows``) even if there are no relational fields. Needs two as - # the code above stiched the row back on (so first call may only - # get the stiched row without advancing the underlying operator row - # itself) - next(record_span, None) - next(record_span, None) - # old rows consumption (by iterating the span) should be done here, - # at this point the old ``rows`` is 1 past `span` (either on the - # next record row or past ``StopIteration``, so wrap new ``rows`` - # (``_rows``) in a counting stream indexed 1-before the old - # ``rows`` - rows = CountingStream(_rows, rows.index - 1) - yield record, {'rows': {'from': record_row_index,'to': rows.index}} + yield record, {'rows': { + 'from': index, + 'to': index + len(record_span) - 1 + }} + index += len(record_span) def _convert_records(self, cr, uid, records, context=None, log=lambda a: None): """ Converts records from the source iterable (recursive dicts of @@ -5369,21 +5357,6 @@ class AbstractModel(BaseModel): _auto = False # don't create any database backend for AbstractModels _register = False # not visible in ORM registry, meant to be python-inherited only -def span(predicate, iterable): - """ Splits the iterable between the longest prefix of ``iterable`` whose - elements satisfy ``predicate`` and the rest. - - If called with a list, equivalent to:: - - takewhile(predicate, lst), dropwhile(predicate, lst) - - :param callable predicate: - :param iterable: - :rtype: (iterable, iterable) - """ - it1, it2 = itertools.tee(iterable) - return (itertools.takewhile(predicate, it1), - itertools.dropwhile(predicate, it2)) def itemgetter_tuple(items): """ Fixes itemgetter inconsistency (useful in some cases) of not returning a tuple if len(items) == 1: always returns an n-tuple where n = len(items) diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index eb777a57988..72442963af5 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -1,4 +1,7 @@ # -*- coding: utf-8 -*- +import json +import pkgutil + import openerp.modules.registry import openerp @@ -956,5 +959,15 @@ class test_o2m_multiple(ImporterCase): self.assertEqual(values(b.child1), [11, 12, 13, 14]) self.assertEqual(values(b.child2), [21, 22, 23]) +class test_realworld(common.TransactionCase): + def test_bigfile(self): + data = json.loads(pkgutil.get_data(self.__module__, 'contacts_big.json')) + result = self.registry('res.partner').load( + self.cr, openerp.SUPERUSER_ID, + ['name', 'mobile', 'email', 'image'], + data) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), len(data)) + # function, related, reference: written to db as-is... # => function uses @type for value coercion/conversion From e61dc509341ed2cd1222869048a81b2db209e21d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 25 Sep 2012 17:59:15 +0200 Subject: [PATCH 017/171] [IMP] convert empty import fields to False values without going through converters simplifies the converter methods by avoiding redundant emptiness checks bzr revid: xmo@openerp.com-20120925155915-82p2s6stpww37p5n --- openerp/addons/base/ir/ir_fields.py | 8 +++----- openerp/osv/orm.py | 4 +++- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 03d95fc67a6..621dd93d469 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -100,21 +100,19 @@ class ir_fields_converter(orm.Model): return True def _str_to_integer(self, cr, uid, model, column, value, context=None): - if not value: return False return int(value) def _str_to_float(self, cr, uid, model, column, value, context=None): - if not value: return False return float(value) def _str_to_char(self, cr, uid, model, column, value, context=None): - return value or False + return value def _str_to_text(self, cr, uid, model, column, value, context=None): - return value or False + return value def _str_to_binary(self, cr, uid, model, column, value, context=None): - return value or False + return value def _get_translations(self, cr, uid, types, src, context): types = tuple(types) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index eb7745b2f6e..81063deb180 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -68,7 +68,6 @@ from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ from openerp import SUPERUSER_ID from query import Query -from openerp import SUPERUSER_ID _logger = logging.getLogger(__name__) _schema = logging.getLogger(__name__ + '.schema') @@ -1662,6 +1661,9 @@ class BaseModel(object): for field, strvalue in record.iteritems(): if field in (None, 'id', '.id'): continue + if not strvalue: + converted[field] = False + continue # In warnings and error messages, use translated string as # field name From a912bb31890a9d1d81f8b267e2ba2e0d0ca53dbe Mon Sep 17 00:00:00 2001 From: Saurang Suthar Date: Wed, 26 Sep 2012 12:33:39 +0530 Subject: [PATCH 018/171] [IMP]openerp:rename the group Partner Manager -> Contact Creation bzr revid: ssu@tinyerp.com-20120926070339-sr9lv02l5632ml1t --- openerp/addons/base/res/res_security.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/res/res_security.xml b/openerp/addons/base/res/res_security.xml index acec903c1a3..783243411ef 100644 --- a/openerp/addons/base/res/res_security.xml +++ b/openerp/addons/base/res/res_security.xml @@ -3,7 +3,7 @@ - Partner Manager + Contact Creation From 627bb128e04991fad61688b8b86ca82061a90977 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 26 Sep 2012 10:25:30 +0200 Subject: [PATCH 019/171] [IMP] adapt base_import to using Model.load instead of Model.import_data bzr revid: xmo@openerp.com-20120926082530-krhladsrn9mzs3lx --- addons/base_import/models.py | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/addons/base_import/models.py b/addons/base_import/models.py index 1e5ad002b84..733532ae095 100644 --- a/addons/base_import/models.py +++ b/addons/base_import/models.py @@ -307,22 +307,14 @@ class ir_import(orm.TransientModel): except ValueError, e: return [{ 'type': 'error', - 'message': str(e), + 'message': unicode(e), 'record': False, }] - try: - _logger.info('importing %d rows...', len(data)) - (code, record, message, _wat) = self.pool[record.res_model].import_data( - cr, uid, import_fields, data, context=context) - _logger.info('done') - - except Exception, e: - _logger.exception("Import failed") - # TODO: remove when exceptions stop being an "expected" - # behavior of import_data on some (most) invalid - # input. - code, record, message = -1, None, str(e) + _logger.info('importing %d rows...', len(data)) + 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 # an InternalError (ROLLBACK should work, maybe). Ignore that. @@ -339,14 +331,4 @@ class ir_import(orm.TransientModel): except psycopg2.InternalError: pass - if code != -1: - return [] - - # TODO: add key for error location? - # TODO: error not within normal preview, how to display? Re-preview - # with higher ``count``? - return [{ - 'type': 'error', - 'message': message, - 'record': record or False - }] + return import_result['messages'] From fd5d4e5dd34eeeec885ffd2fe5ae340c5bebb00c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 26 Sep 2012 13:42:35 +0200 Subject: [PATCH 020/171] [IMP] don't display deprecated fields in import window bzr revid: xmo@openerp.com-20120926114235-iqkmi23yk2v1hq7w --- addons/base_import/models.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/base_import/models.py b/addons/base_import/models.py index 733532ae095..16bb52d5856 100644 --- a/addons/base_import/models.py +++ b/addons/base_import/models.py @@ -83,6 +83,10 @@ class ir_import(orm.TransientModel): }] fields_got = self.pool[model].fields_get(cr, uid, context=context) for name, field in fields_got.iteritems(): + # an empty string means the field is deprecated, @deprecated must + # be absent or False to mean not-deprecated + if field.get('deprecated', False) is not False: + continue if field.get('readonly'): states = field.get('states') if not states: @@ -97,7 +101,7 @@ class ir_import(orm.TransientModel): 'id': name, 'name': name, 'string': field['string'], - # Y U NO ALWAYS HAVE REQUIRED + # Y U NO ALWAYS HAS REQUIRED 'required': bool(field.get('required')), 'fields': [], } From f6f37632ceb0ed2a6abd043c884e5e9de1cb8d51 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 26 Sep 2012 15:50:13 +0200 Subject: [PATCH 021/171] [IMP] errors display, dryrun management (add button) bzr revid: xmo@openerp.com-20120926135013-rkbvxluu6tqz9a1l --- addons/base_import/static/src/js/import.js | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index c2deb660ff2..9cb2f74bd48 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -63,7 +63,7 @@ openerp.base_import = function (instance) { {name: 'quoting', label: _lt("Quoting:"), value: '"'} ], events: { - 'change .oe_import_grid input': 'import_dryrun', + // 'change .oe_import_grid input': 'import_dryrun', 'change input.oe_import_file': 'file_update', 'change input.oe_import_has_header, .oe_import_options input': 'settings_updated', 'click a.oe_import_csv': function (e) { @@ -85,9 +85,10 @@ openerp.base_import = function (instance) { var self = this; this._super(parent, { buttons: [ - {text: _t("Import File"), click: function () { - self.do_import(); - }, 'class': 'oe_import_dialog_button'} + {text: _t("Import"), click: self.proxy('do_import'), + 'class': 'oe_import_dialog_button'}, + {text: _t("Validate"), click: self.proxy('import_dryrun'), + 'class': 'oe_import_dialog_button'} ] }); this.res_model = parent.model; @@ -134,11 +135,12 @@ openerp.base_import = function (instance) { .then(this.proxy('preview')); }, preview: function (result) { + this.$el.removeClass('oe_import_preview_error oe_import_error'); this.$el.toggleClass( 'oe_import_noheaders', !this.$('input.oe_import_has_header').prop('checked')); if (result.error) { - this.$el.addClass('oe_import_error'); + this.$el.addClass('oe_import_preview_error oe_import_error'); this.$('.oe_import_error_report').html( QWeb.render('ImportView.preview.error', result)); return; @@ -180,7 +182,7 @@ openerp.base_import = function (instance) { width: 'resolve', dropdownCssClass: 'oe_import_selector' }); - this.import_dryrun(); + //this.import_dryrun(); }, generate_fields_completion: function (root) { var basic = []; @@ -252,7 +254,6 @@ openerp.base_import = function (instance) { //- import itself call_import: function (options) { - var self = this; var fields = this.$('.oe_import_fields input.oe_import_match_field').map(function (index, el) { return $(el).select2('val') || false; }).get(); @@ -260,12 +261,12 @@ openerp.base_import = function (instance) { 'do', [this.id, fields, this.import_options()], options); }, import_dryrun: function () { -// this.call_import({ dryrun: true }) -// .then(this.proxy('render_import_errors')); + return this.call_import({ dryrun: true }) + .then(this.proxy('render_import_errors')); }, do_import: function () { var self = this; - this.call_import({ dryrun: false }).then(function (errors) { + return this.call_import({ dryrun: false }).then(function (errors) { if (_.isEmpty(errors)) { if (self.getParent().reload_content) { self.getParent().reload_content(); From 27aeae974aa4a001e7c8f13cd01f98a5ca295334 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 26 Sep 2012 18:50:52 +0200 Subject: [PATCH 022/171] [IMP] warning and error reporting during import bzr revid: xmo@openerp.com-20120926165052-7eq4ne62h63zbqqm --- addons/base_import/static/src/css/import.css | 6 ++- addons/base_import/static/src/js/import.js | 41 +++++++++++++++----- addons/base_import/static/src/xml/import.xml | 26 +++++++++++-- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/addons/base_import/static/src/css/import.css b/addons/base_import/static/src/css/import.css index f6258f5aa50..da61473fdba 100644 --- a/addons/base_import/static/src/css/import.css +++ b/addons/base_import/static/src/css/import.css @@ -10,7 +10,8 @@ .oe_import .oe_import_grid, .oe_import .oe_import_error_report, .oe_import .oe_import_with_file, -.oe_import .oe_import_noheaders { +.oe_import .oe_import_noheaders, +.oe_import .oe_import_report_more { display: none; } @@ -19,7 +20,8 @@ } .oe_import.oe_import_error .oe_import_error_report, .oe_import.oe_import_with_file .oe_import_with_file, -.oe_import.oe_import_noheaders .oe_import_noheaders { +.oe_import.oe_import_noheaders .oe_import_noheaders, +.oe_import .oe_import_report_showmore .oe_import_report_more { display: block; } diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 9cb2f74bd48..d5f059d0656 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -79,6 +79,10 @@ openerp.base_import = function (instance) { ? $el.next() : $el.parent().next()) .toggle(); + }, + 'click .oe_import_report a.oe_import_report_count': function (e) { + e.preventDefault(); + $(e.target).parent().toggleClass('oe_import_report_showmore'); } }, init: function (parent, dataset) { @@ -262,31 +266,50 @@ openerp.base_import = function (instance) { }, import_dryrun: function () { return this.call_import({ dryrun: true }) - .then(this.proxy('render_import_errors')); + .then(this.proxy('render_import_result')); }, do_import: function () { var self = this; - return this.call_import({ dryrun: false }).then(function (errors) { - if (_.isEmpty(errors)) { + return this.call_import({ dryrun: false }).then(function (message) { + if (!_.any(message, function (message) { + return message.type === 'error' })) { if (self.getParent().reload_content) { self.getParent().reload_content(); } self.close(); return; } - self.render_import_errors(errors); + self.render_import_result(message); }); }, - render_import_errors: function (errors) { - if (_.isEmpty(errors)) { + render_import_result: function (message) { + if (_.isEmpty(message)) { this.$el.removeClass('oe_import_error'); return; } - // import failed (or maybe just warnings, if we ever get - // warnings?) + // row indexes come back 0-indexed, spreadsheets + // display 1-indexed. + var offset = 1; + // offset more if header + if (this.import_options().header) { offset += 1; } + this.$el.addClass('oe_import_error'); this.$('.oe_import_error_report').html( - QWeb.render('ImportView.error', {errors: errors})); + QWeb.render('ImportView.error', { + errors: _(message).groupBy('message'), + at: function (rows) { + var from = rows.from + offset; + var to = rows.to + offset; + if (from === to) { + return _.str.sprintf(_t("at row %d"), from); + } + return _.str.sprintf(_t("between rows %d and %d"), + from, to); + }, + more: function (n) { + return _.str.sprintf(_t("(%d times more)"), n); + }, + })); }, }); }; diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml index 386348bea4b..5d36eae4d2d 100644 --- a/addons/base_import/static/src/xml/import.xml +++ b/addons/base_import/static/src/xml/import.xml @@ -86,12 +86,30 @@
    -
  • - - +
  • + + + + + + + +
      +
    • + + + +
    • +
+ + + + + + this.attr('t-if', 'widget.options.import_enabled'); From 4d7576393a3e0efadf639dbe9958725f76ae6f10 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 27 Sep 2012 09:48:45 +0200 Subject: [PATCH 023/171] [ADD] unstyled and incomplete handling of @moreinfo action triggering is still missing bzr revid: xmo@openerp.com-20120927074845-pb3osgymmtddglc0 --- addons/base_import/static/src/js/import.js | 35 +++++++++++++++++++- addons/base_import/static/src/xml/import.xml | 3 +- 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index d5f059d0656..ac81f89c7a9 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -83,6 +83,12 @@ openerp.base_import = function (instance) { 'click .oe_import_report a.oe_import_report_count': function (e) { e.preventDefault(); $(e.target).parent().toggleClass('oe_import_report_showmore'); + }, + 'click .oe_import_moreinfo_action a': function (e) { + e.preventDefault(); + // #data will parse the attribute on its own, we don't like that + var action = JSON.parse($(e.target).attr('data-action')); + console.log(action); } }, init: function (parent, dataset) { @@ -307,7 +313,34 @@ openerp.base_import = function (instance) { from, to); }, more: function (n) { - return _.str.sprintf(_t("(%d times more)"), n); + return _.str.sprintf(_t("(%d more)"), n); + }, + info: function (msg) { + if (typeof msg === 'string') { + return _.str.sprintf( + '
%s
', + _.str.escapeHTML(msg)); + } + if (msg instanceof Array) { + return _.str.sprintf( + '
%s
    %s
', + _.str.escapeHTML(_t("Here are the possible values:")), + _(msg).map(function (msg) { + return '
  • ' + + _.str.escapeHTML(msg) + + '
  • '; + }).join()); + } + // Final should be object, action descriptor + return [ + '' + ].join('') }, })); }, diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml index 5d36eae4d2d..2efd0b5a752 100644 --- a/addons/base_import/static/src/xml/import.xml +++ b/addons/base_import/static/src/xml/import.xml @@ -108,7 +108,8 @@ - + + From 4787939773e175475c03d70c4e32fdf0eeea1097 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 27 Sep 2012 11:49:50 +0200 Subject: [PATCH 024/171] [FIX] correctly transform 'get all possible values' action links to display list views as list views bzr revid: xmo@openerp.com-20120927094950-pwlgv2kt2a0jz8ji --- addons/base_import/static/src/js/import.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index ac81f89c7a9..8a73bcd5d71 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -86,9 +86,20 @@ openerp.base_import = function (instance) { }, 'click .oe_import_moreinfo_action a': function (e) { e.preventDefault(); - // #data will parse the attribute on its own, we don't like that + // #data will parse the attribute on its own, we don't like + // that sort of things var action = JSON.parse($(e.target).attr('data-action')); - console.log(action); + // FIXME: when JS-side clean_action + action.views = _(action.views).map(function (view) { + var id = view[0], type = view[1]; + return [ + id, + type !== 'tree' ? type + : action.view_type === 'form' ? 'list' + : 'tree' + ]; + }); + this.do_action(_.extend(action, {target: 'new'})); } }, init: function (parent, dataset) { @@ -329,7 +340,7 @@ openerp.base_import = function (instance) { return '
  • ' + _.str.escapeHTML(msg) + '
  • '; - }).join()); + }).join('')); } // Final should be object, action descriptor return [ From 9a721eba322dcdf6c3a7cab27f8538bd98d4d4cc Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 10 Oct 2012 17:38:49 +0200 Subject: [PATCH 025/171] [ADD] forgotten test file bzr revid: xmo@openerp.com-20121010153849-h91mv2dzjk6i0n55 --- openerp/tests/addons/test_impex/tests/contacts_big.json | 1 + 1 file changed, 1 insertion(+) create mode 100644 openerp/tests/addons/test_impex/tests/contacts_big.json diff --git a/openerp/tests/addons/test_impex/tests/contacts_big.json b/openerp/tests/addons/test_impex/tests/contacts_big.json new file mode 100644 index 00000000000..5dfe77ec04b --- /dev/null +++ b/openerp/tests/addons/test_impex/tests/contacts_big.json @@ -0,0 +1 @@ +[["88117cfa", "", "aa48ae52@df356352.com", ""], ["54455583", "0770708969", "75ce07b4@8c061041.com", ""], ["a4f9804e", "0794216989", "6a994598@402c461b.com", ""], ["0cfd7c89", "0788771669", "f2598da7@15f7036b.com", ""], ["220bfe63", "0767248573", "30d7e3f3@993ab043.com", ""], ["33bc0e31", "0747111853", "e86b7718@36a11f08.com", ""], ["ad3d1c39", "0707461083", "17ddff88@de2d370e.com", ""], ["06ba223e", "0793743830", "6c77d328@247685b0.com", ""], ["d457d407", "0723226290", "f497c7a2@c39adaaf.com", ""], ["b24f01d4", "0785727376", "e5c03c2c@be7b89f7.com", ""], ["666bbc2f", "0726730949", "1fbcb995@6b02d029.com", ""], ["8e29a5f9", "0769331825", "17771bb9@4e36debc.com", ""], ["330ee602", "0760467697", "86af9f82@bd3cdfba.com", ""], ["bcd48752", "0747166758", "ccc4c255@05124d59.com", ""], ["4a16c054", "0776729581", "c87cc075@6d84e9ae.com", ""], ["c065ff62", "0708945599", "41391787@d4c3369a.com", ""], ["8636e303", "0798878005", "520e2cee@3506cf0e.com", ""], ["0bdecf04", "0766303691", "7fb38fdd@46f90ec4.com", ""], ["e1ac6628", "0797335906", "5483d5cf@3372b5ea.com", ""], ["f0a24042", "0774112437", "4e99fd6b@7f727830.com", ""], ["41f18eb6", "0757100173", "f904520a@4d42ffde.com", ""], ["c4a8cde7", "", "aa344d15@3dd9bb6f.com", ""], ["abd14d72", "0778342908", "ed9cb7e3@c6fdb61c.com", ""], ["44c65e49", "0711762677", "ef6952c5@a9cd6711.com", ""], ["a3238267", "0758374338", "203081cd@e1263e29.com", ""], ["412069c4", "0745736692", "f2b989ed@67ac8c23.com", ""], ["b332577b", "0783480568", "4382789d@e1dd2623.com", ""], ["84fccf60", "", "ec2987d5@d777ea94.com", ""], ["c633db24", "0780843363", "9c6a9a39@3ba1bc58.com", ""], ["833bdb8f", "", "0c53568d@170c6826.com", ""], ["c6496a37", "0702644587", "808dc444@d3c87550.com", ""], ["3a9cf735", "0732254904", "825cb1f5@9599a25c.com", ""], ["d3bd7567", "0750010894", "3dd53f94@381f30ff.com", ""], ["ec9d38fa", "0722807920", "a63d63c9@9fc07187.com", ""], ["9e858edf", "0759964893", "ee8fc817@4969b884.com", ""], ["3820033b", "0713040363", "846b31dd@1fde871f.com", ""], ["78e3f6fa", "0713215706", "3a1bd069@a2165e02.com", ""], ["833bf332", "0732488435", "e962f524@7a67d172.com", ""], ["ebe0d200", "0791879047", "34a1610c@e30a9051.com", ""], ["e27d3d81", "0702888121", "a6c52338@56630271.com", ""], ["10423400", "0775217021", "24c5bd9d@3b4bbb75.com", ""], ["923f52a7", "0747670735", "19ee7b6b@fb8077b5.com", ""], ["fe50aca6", "0785899752", "99b1c2e4@30f03fdd.com", ""], ["bdbff3f2", "", "cc5cf567@c13f1531.com", ""], ["6dbbeeb9", "0755330628", "33efb58d@1ac1966a.com", ""], ["f88681ce", "", "4d01aa9a@461b4345.com", ""], ["8ff958c9", "0728877017", "c7327902@eae323df.com", ""], ["d8651703", "0711929482", "6aed493b@44b94bb2.com", ""], ["fcf4863f", "0721975645", "c8171bbf@2cefedab.com", ""], ["614949ae", "0708626227", "c54a3aca@bc635fa8.com", ""], ["7b0767ab", "0773868355", "2e5e7b82@bde16366.com", ""], ["ea0fb610", "0714907110", "3781b5b3@6ade96c7.com", ""], ["1833ee8b", "0754159809", "a1fd1724@0a180cd8.com", ""], ["277ea017", "0756815282", "ad93976a@022ff737.com", ""], ["67e662d9", "0789105520", "ecb672e5@df0e020e.com", ""], ["18f2288c", "", "25de55ef@a03da5ef.com", ""], ["32dcbf86", "0738646453", "88e37cac@ec308293.com", ""], ["242eb33b", "0702264598", "d132623d@6a8e65f4.com", ""], ["f2f8e50a", "0731234118", "10bdb9d0@a1dec378.com", ""], ["c414145d", "0723184859", "465e6323@083bdb6f.com", ""], ["1a6d51be", "0782912489", "7f69541f@5e17335f.com", ""], ["ef35d7bc", "0705243855", "0a8f6885@8a236f11.com", ""], ["ac48d3c2", "0714165417", "aa9c4199@1ab6a42c.com", ""], ["5ba7effd", "0731285368", "e7b91cfb@18ab2ca6.com", ""], ["c8349e83", "0711248806", "bf08adeb@72d14356.com", ""], ["ccb97edd", "", "b64dc525@fff1e0b6.com", ""], ["f6b196e8", "0754457697", "915a9cb0@75e21eed.com", ""], ["7a22e438", "0750066621", "4c2ba953@96df5597.com", ""], ["ffb4ecaf", "0791152499", "d9159dc2@6bdedc89.com", ""], ["64d655fb", "0756407008", "498110df@dfc1445a.com", ""], ["f27b441d", "0734603187", "4a23bbf1@ef4d212f.com", ""], ["2405cb10", "0761979851", "92b8250a@125bbd04.com", ""], ["fce58db4", "", "f6dfbd77@704c66d0.com", ""], ["2b16a16b", "0715217717", "d22db762@46605453.com", ""], ["4fbb5479", "0788391407", "e41e70f2@c575eeed.com", ""], ["5929e7ad", "0730836883", "38a50e2f@0cbd9fc0.com", ""], ["9fab30be", "0758240568", "f8422724@eef7fb6f.com", ""], ["57161fa2", "", "913ed6fb@1f9a591c.com", ""], ["787471f3", "0729709798", "45ed74ea@4209730d.com", ""], ["fce02cc8", "0717080949", "0430d0fa@09917e4b.com", ""], ["e5745b08", "", "2e337c3a@038a6c53.com", ""], ["bba2eeb4", "0754696079", "37c75466@05cbd7d1.com", ""], ["f072a1b0", "0774261408", "605c6350@5a7cc516.com", ""], ["95d2b9c0", "", "8935e406@c23ff41c.com", ""], ["8ac4ea3a", "0714072646", "734cfea9@fb198808.com", ""], ["5a523460", "", "2be9f139@0cf11f87.com", ""], ["5b10ec25", "0732185274", "098c0cf3@3676b22d.com", ""], ["8aa42976", "", "2942960c@1ae8ae04.com", ""], ["cdb88ecb", "0758961947", "0ea31305@77af1e9a.com", ""], ["0bd54356", "0784831852", "c437852e@c8e319ee.com", ""], ["1d1768af", "0793590304", "d66c02aa@649e09d1.com", ""], ["c19a3d43", "0749800307", "cf8fc137@761da752.com", ""], ["9c81b4c9", "", "ab84e443@09f1f4ea.com", ""], ["8811594d", "", "53698008@bcc6d45a.com", ""], ["97a8c2d1", "0715236942", "cf88f0ab@fa3f7a28.com", ""], ["fc7d6b5f", "0770870220", "b0e2ee90@41460794.com", ""], ["a158270a", "", "aaab6cf4@d31f8c47.com", ""], ["20bef3ca", "0744837638", "fca8c63c@8dae6b0a.com", ""], ["424b3e69", "", "7d2bb9c4@856ce530.com", ""], ["8ecc1d0a", "", "6df56a62@deac5fbf.com", ""], ["7e1f1b8c", "0795180887", "18d2a24d@a931a1cb.com", ""], ["4546b364", "0771517872", "77ac1ab3@3cd24c25.com", ""], ["a44b23b2", "", "55e75fcb@e20b079d.com", ""], ["90c52454", "0713222790", "e83be6ad@faaa1dcc.com", ""], ["f5fe091e", "0740461816", "fc4b6360@c84d9776.com", ""], ["01052d30", "0790737321", "421a7451@c44558f7.com", ""], ["a65634f3", "0733738433", "a7d57f5f@4f0007cc.com", ""], ["960aae3c", "", "3e2338e3@97fa70e8.com", ""], ["590858c2", "0736114828", "59cec23c@fb9704f4.com", ""], ["75725ca0", "0777217002", "5c7b484a@8c386315.com", ""], ["a16cf653", "0732018015", "a105f736@87a6c14b.com", ""], ["1ee991c1", "0759093690", "1cc3c001@d70dae0e.com", ""], ["48434804", "", "d3cee127@7dbf4217.com", ""], ["edfd871e", "0757718444", "d63d5c26@81eaeb47.com", ""], ["4fcc9a1c", "0738617045", "b114a376@9bd5a931.com", ""], ["b2001895", "0782251612", "a8868ffb@8114353b.com", ""], ["d61261f8", "0717555195", "ac613ffe@d4ee0d8c.com", ""], ["6327118e", "0739171459", "e047cf55@4920c1d3.com", ""], ["f6b14b7a", "0737868032", "418ed43d@b549551d.com", ""], ["22bc0aa9", "0759663409", "90494f38@f50ab03d.com", ""], ["3145a855", "0788697773", "8a0870bd@744b0c6b.com", ""], ["72f043d6", "0780961318", "859d6c8c@d6a29fe3.com", ""], ["98544981", "0707687449", "53a75fab@6ad934ef.com", ""], ["535ddbe3", "0751302080", "5d5dbab7@9b651502.com", ""], ["c3e5f1ac", "", "62955f00@74d50e7c.com", ""], ["6470f36e", "0776617461", "5f6d81a4@e535baa8.com", ""], ["ed39f4ae", "0766428270", "c73a731a@186ebf0c.com", ""], ["6aa1b596", "0720348914", "67d7e38b@5798f6cc.com", ""], ["f5a778ee", "0773231447", "5919b978@2768de0c.com", ""], ["9d530c2b", "0776812615", "4609c18c@aeca697d.com", ""], ["89a13571", "0744825918", "766ddb77@7dfc129d.com", ""], ["90dd9419", "0722654005", "22bef5d1@74801d84.com", ""], ["0d69e61d", "0779495819", "fa2fce6e@d3b9debf.com", ""], ["0a64ee31", "0741511488", "f48455f9@0163e8a5.com", ""], ["87f3e7fd", "0792428108", "a7763cb9@ae893b19.com", ""], ["99a66411", "0785175788", "3abf4513@c90ddc54.com", ""], ["d4d56088", "0748950722", "2dfc3b17@4af80b7c.com", ""], ["1f001808", "0733396761", "59ae4761@17a1bad6.com", ""], ["2d5cac56", "0744393937", "709feac1@1c16f3b0.com", ""], ["d0acb4ea", "0770269511", "66221771@3ff30570.com", ""], ["4f57869d", "0706718397", "1cd2ed99@0ba6e1d3.com", ""], ["a2e66c44", "", "753da3f0@41f8ab02.com", ""], ["843dc71f", "0757529013", "016ff59d@6c1f9655.com", ""], ["c314a28e", "0767460679", "cd2ff99c@8b178d14.com", ""], ["f0d6631d", "0719558518", "0725dfc6@8ce4d6ff.com", ""], ["aa522bd8", "0735252785", "57d3edd6@5eb1bc66.com", ""], ["3f69f804", "0717000930", "6ca23f02@d169a50e.com", ""], ["cc98acfa", "0796356276", "19579c3b@39e71005.com", ""], ["9e6dbdc2", "0769660138", "6349577f@fba33845.com", ""], ["fba0618b", "0758705886", "ae7a9890@e776386f.com", ""], ["f36d563f", "0744525165", "c76184f9@5dccdf5b.com", ""], ["d2df6db1", "0783917910", "f7726c1a@c9e9b839.com", ""], ["8645707d", "0724081473", "842f2369@be456fbb.com", ""], ["95b4296a", "0782845149", "af879705@e8e7bd93.com", ""], ["345ba50c", "0792384168", "ca3ccb91@faf01bf8.com", ""], ["e05de824", "0707660173", "79f09a3d@3deafa86.com", ""], ["de837ee8", "0772243198", "b25275dc@f057ff78.com", ""], ["ae9f1402", "0704963747", "253d5995@6c771579.com", ""], ["259c5532", "0781521724", "cec33738@874afdb1.com", ""], ["1bc82378", "0748931231", "7b3feccd@cd205def.com", ""], ["c73869a0", "0728684210", "790c4825@22921089.com", ""], ["725a20ee", "0716516714", "a5852a47@85846987.com", ""], ["f975f39e", "0732891142", "aea8acea@a6feaa85.com", ""], ["d0fbcd0b", "0753684058", "78f9d7f2@49082702.com", ""], ["d41da511", "0765105657", "bca42072@50aa0f9d.com", ""], ["42be8058", "0776465871", "15d0f988@805b0cb0.com", ""], ["269eda62", "0705841239", "67dc4d59@51389f5b.com", ""], ["2ffc5c2c", "0732437371", "c3b76c4c@9772ea3b.com", ""], ["258e1957", "0768636603", "476831de@98414c35.com", ""], ["a18a68f6", "", "136d0b0e@15864440.com", ""], ["56b9197d", "0734391703", "ab36de76@d7a2bc96.com", ""], ["e1acdbc5", "0790910000", "867827b4@fdffc111.com", ""], ["21e48ef2", "", "4a1bbb5c@29b1ac3a.com", ""], ["aec1a0ea", "0749338116", "64dbedc8@6e81a330.com", ""], ["b1afbf53", "0701600188", "5f1e3fcc@0c6cba23.com", ""], ["bc7e214f", "0725934639", "369caec7@ba1df297.com", ""], ["dd059d1e", "0774510615", "bac60ace@a354f53c.com", ""], ["cbe822f4", "0745859979", "01bebc6e@735bdad1.com", ""], ["961d21e8", "0741358098", "9348702e@ce9c1230.com", ""], ["c8ae2583", "", "3682409d@a15621c4.com", ""], ["c326d443", "0784986501", "0a505045@96938424.com", ""], ["3e51bb13", "0764438578", "b713c235@8e350663.com", ""], ["ad38c924", "0772825196", "ce8d0f19@b2d4ad9f.com", ""], ["65bd5021", "", "3f6318d5@91f006ed.com", ""], ["efd2705b", "0770828242", "736fe829@7c30bc29.com", ""], ["6ebdfef1", "0710559084", "e035b963@a163fb9d.com", ""], ["021358ef", "0711112569", "ecc20e51@547a7cc2.com", ""], ["548d9a59", "0790101525", "d42aa9de@6f91f22e.com", ""], ["262f96db", "0780153799", "094258c8@e03d9795.com", ""], ["a548913a", "0779869470", "6a6999ca@ca0a88d1.com", ""], ["e7beec03", "0796588859", "5e9940fa@eca859cc.com", ""], ["ae5a8077", "", "5ab3a073@18a45934.com", ""], ["eae22551", "0747781534", "6b04366d@7653fc99.com", ""], ["2fb9da57", "0799726898", "895646f6@2a66fe2e.com", ""], ["51d83f2f", "0704387855", "0d2ea467@62feebfc.com", ""], ["3da4bc6d", "0786428049", "db6a479a@37899df4.com", ""], ["c08101a4", "0751956133", "67852281@0bb68d55.com", ""], ["b5f8c156", "0754878556", "bad4eea7@4d5a817a.com", ""], ["ded80b77", "0772054646", "ebb2cd45@54262aec.com", ""], ["7b086cfd", "0776426442", "3b503434@d46b44fe.com", ""], ["7a64e1e8", "0740165572", "e9d50a03@38375fce.com", ""], ["ceaa584b", "0791796518", "b5839977@cfe3ae10.com", ""], ["9c4a8683", "0790187277", "f8e4fe02@02fc1038.com", ""], ["193f99b7", "", "915c1574@50758bc3.com", ""], ["6fb6c30a", "0720403051", "f4bcd3ca@a8ab8216.com", ""], ["9486800f", "0723518103", "a3886d22@6600cdda.com", ""], ["6e09f6bd", "0707095889", "868ffc60@46eb3135.com", ""], ["da1bd13c", "0756382253", "f34abc2c@d18ec367.com", ""], ["3910ae7d", "0786894789", "a8a751a5@9ddf61c7.com", ""], ["32e5be0c", "0738208660", "eab918a8@aeb7a8fe.com", ""], ["c6582498", "0744023415", "b893479d@9ad5c44f.com", ""], ["0c59f520", "", "d552d0fe@8675edcc.com", ""], ["3458ee7a", "", "c1145be6@4043406c.com", ""], ["91fad8e7", "0701771702", "a13640e5@2370e9a4.com", ""], ["2230118a", "0792290165", "529b9444@17c21068.com", ""], ["2df4bff7", "0783219548", "99a9af11@8e5a539d.com", ""], ["0a752397", "0723553802", "b094b6c9@2d8fb162.com", ""], ["918f92a0", "0790485640", "3984cea2@0750289f.com", ""], ["ff811166", "0756833896", "960e71f3@6ded22f4.com", ""], ["6578cd85", "0791455734", "f7e56f2a@35a68ac6.com", ""], ["fd21c33b", "0794847916", "23ae4c63@51515e19.com", ""], ["a12e519d", "0700174532", "d3b4264d@0c9d62f1.com", ""], ["96827dcd", "0780494940", "d70d9ab7@df66a83c.com", ""], ["8cd46009", "", "a8addc7c@c899c5f7.com", ""], ["982fb2ee", "0778368795", "b99de484@d262779b.com", ""], ["dbe5ea51", "0756105541", "ad90b51c@3f1af6d8.com", ""], ["3d963533", "0761709058", "85b32c2c@d99341a7.com", ""], ["064f2df0", "0791023088", "45d63e8f@ce0852ad.com", ""], ["e93edb9d", "0713294122", "b9b82603@a226aaba.com", ""], ["a94ada15", "0796377896", "5dd00c2d@794bcc18.com", ""], ["7de0f3db", "0748976042", "7714652c@3ef8b9d5.com", ""], ["6ff485d2", "0774873001", "77b5b11c@675bf1f7.com", ""], ["e472c175", "0761539211", "dc30792d@00a0ef22.com", ""], ["7a467ead", "0734394099", "e8d5a021@93926f82.com", ""], ["87749969", "0780899950", "dfe454aa@94a8fd44.com", ""], ["c24c8b06", "0777380273", "98dc8782@26872819.com", ""], ["a059abab", "0753758924", "581281a2@890e7bc6.com", ""], ["3b85bdd1", "0784193150", "cf3c82aa@e5df6c1b.com", ""], ["7cbc188d", "0704027570", "c6444b77@8ab56b47.com", ""], ["12ff649b", "0755744514", "a7ce16ba@c054924a.com", ""], ["cc77e0c9", "0724057991", "4e2473d1@4aca6e0e.com", ""], ["026e42e1", "0741456583", "09906c46@e32bed7b.com", ""], ["9e6550e3", "0740725000", "27ddd3ff@c0891335.com", ""], ["700be741", "0715655544", "bcc93b4c@de8854d5.com", ""], ["f15ce066", "0770746966", "aff2e8a9@19f37ff3.com", ""], ["d56faa79", "0780216109", "7119d43e@10e506f6.com", ""], ["d0d97e5c", "0799577373", "9c131514@a5f6fdee.com", ""], ["d3320f24", "0789091392", "e9b8d4a6@e2c9fa38.com", ""], ["c9b3e28b", "0753027473", "d4c85b77@26952326.com", ""], ["5f81787f", "0789741665", "ca7dbbcc@97fc237d.com", ""], ["dd720b39", "0799154501", "9cc0df66@70c2f5d7.com", ""], ["f584f902", "", "38a024f0@27413ff3.com", ""], ["bb37782d", "0788379130", "01ef864a@191fed62.com", ""], ["f6964d05", "0732597248", "32d25b28@6ae6e905.com", ""], ["cfdc9a87", "", "f453b3f6@4ccf20a3.com", ""], ["2c6f8592", "0773589537", "d71824f7@91dd37aa.com", ""], ["ba101360", "0748932503", "674abbfc@81b3b946.com", ""], ["3b35863b", "0759392115", "c249bcc4@e650abe8.com", ""], ["610da7a5", "0712310446", "f4a25391@d7759c5f.com", ""], ["1557e96a", "0796582698", "4aaf8b75@99c557d5.com", ""], ["98519b81", "", "a5050e55@917cd419.com", ""], ["ed820127", "0754946950", "dd82c2c1@7b1ab977.com", ""], ["b78147ee", "0765494006", "53795132@dd38aec7.com", ""], ["3d4859e8", "0782874657", "67b89627@d2269d32.com", ""], ["ce2366c6", "0719814269", "c6200859@0788dca5.com", ""], ["fba51219", "", "b1d107dd@b664bba3.com", ""], ["7469e033", "0742202666", "9cbedb4b@31ba3795.com", ""], ["20c151e6", "0746146553", "90851479@e8a420a4.com", ""], ["14499acc", "0731779256", "c5c930ab@8de927e9.com", ""], ["71297ace", "0771197563", "1bf4547d@f5c8f5b3.com", ""], ["ea630ed8", "0712073290", "245f14c9@9470fedc.com", ""], ["2c54ae4c", "0767896178", "76f988c7@07ade1eb.com", ""], ["e143bc1c", "0798189886", "68717fc3@c4ca3101.com", ""], ["1626281f", "0782558075", "d87d65f6@585b48b9.com", ""], ["3df7b85d", "", "4ef416c6@0e39f805.com", ""], ["101c2c3a", "0754160899", "c77a5ff2@e3dbfd0f.com", ""], ["0e433acb", "", "ce18b2e0@18a803fe.com", ""], ["b454958d", "0758649506", "daadf940@35a7b0e0.com", ""], ["54dd80e3", "", "499da17c@7a6a8e16.com", ""], ["c26e8c0b", "0705458229", "816d4c62@f196940f.com", ""], ["d6c7c2fb", "0712740449", "eb0f3861@028419f0.com", ""], ["ca236785", "0710117939", "74d89d0f@9681f6e2.com", ""], ["03e89273", "0770985481", "a767a13e@202e1c7e.com", ""], ["df528ea6", "0715236243", "4451e8a7@ab9e9cfc.com", ""], ["b369c3a1", "0720792641", "af8e60fb@affbf045.com", ""], ["0e3b3dcb", "0726931283", "b6c80dd6@377de27c.com", ""], ["72f56e12", "0749900046", "dfd0d53e@a04c2e07.com", ""], ["0b616a06", "0788777560", "e520ec34@5a88ce36.com", ""], ["7dcd72da", "0727744886", "c7ecb909@efc93b54.com", ""], ["b6749523", "0755931358", "76b2e187@777e9047.com", ""], ["349f347f", "0772053293", "aed364ad@8f6c1755.com", ""], ["b0a24d58", "0737012558", "b013a7fd@a6d27266.com", ""], ["924dcf44", "0761967844", "539d93ff@4e1741bd.com", ""], ["90781369", "0745086457", "bae1c94b@df7075b3.com", ""], ["7bbf6e08", "0762583195", "c54b4a41@f08e61ec.com", ""], ["696d9ce3", "0753759660", "9be913a2@efd894d9.com", ""], ["32dea399", "0730020519", "b76d0594@9127aaf5.com", ""], ["4aada6ab", "0716703652", "1172d8f6@70029987.com", ""], ["c2b450aa", "0786756810", "05fa85d3@6305e8e2.com", ""], ["f7220cb8", "0776056046", "b14c6af3@ebfcf7d3.com", ""], ["7da05a35", "0741271883", "abe23d1a@35fc89a5.com", ""], ["b277cd48", "0703025387", "ed28c019@feb1fe78.com", ""], ["55a16321", "0740030335", "a3db681b@de92450b.com", ""], ["646ff9aa", "0777457443", "ea0704a8@d6c557b9.com", ""], ["557c863d", "0752298537", "c412a0a1@4db33621.com", ""], ["324da6d3", "", "61e7386a@d4a90843.com", ""], ["144835ab", "", "70744ba9@000ee58a.com", ""], ["eda25157", "0734918749", "efdbfb3e@83133e46.com", ""], ["bf5d4b09", "0712427566", "4b13f413@18c93b1c.com", ""], ["c3745e8a", "0723463789", "e5ccf0c5@00118e4a.com", ""], ["11c0a9da", "0778743010", "865392aa@1c8da31d.com", ""], ["7d0eac7d", "0701997984", "f2990151@e8d055f3.com", ""], ["e701488a", "0712599226", "f0d54316@037ea274.com", ""], ["acac2543", "0771603428", "73cc7388@981dc1a0.com", ""], ["82f371b0", "0758909949", "6ebe0478@2b14af27.com", ""], ["896bb3a8", "", "83158633@aa3fe414.com", ""], ["9ce45cf4", "", "38772abe@078a889f.com", ""], ["fcb5a33a", "0769530741", "29a4e798@effcb6ef.com", ""], ["116d4f92", "0786255405", "e78e003a@cf7ebbc9.com", ""], ["35e5bd6c", "0700694228", "399e4cd6@eb7a9b08.com", ""], ["71df9408", "0798109987", "91433ee1@b3240820.com", ""], ["294e2aa4", "0700947677", "abca0340@4cd61d52.com", ""], ["9553d892", "", "3af85bbd@90ee53d6.com", ""], ["bbcdb933", "0773940915", "de196282@bf4432fe.com", ""], ["3ccbc198", "0742527356", "014618cd@78b4016f.com", ""], ["100d9496", "0786481835", "df9a72cc@123af588.com", ""], ["57cfe177", "0789499193", "f5f0de36@27d22e23.com", ""], ["cdffca88", "0714652673", "4102e3e2@26a0d112.com", ""], ["69c5f300", "0762631167", "14f5940e@fe984955.com", ""], ["819e988a", "0798148053", "b78ed8c4@ef4a213d.com", ""], ["f5c6a92a", "0774167414", "c996df73@8a8a0586.com", ""], ["3d3f9cc9", "0766973558", "f0a43735@47580cbd.com", ""], ["a30056ab", "0787961025", "04256d2d@459d56d8.com", ""], ["7dc0d98e", "0741838356", "2a597e04@f7089dd3.com", ""], ["e19f381b", "", "ce9c237b@4ec0fd84.com", ""], ["34b3c355", "", "77bcc11b@2e791c9a.com", ""], ["4bca2052", "0799612575", "dba5fc2f@600d7b82.com", ""], ["dbc71748", "0726348854", "efc05aff@df67c9ee.com", ""], ["6fee2352", "0786598805", "c8bad10b@f550bfa0.com", ""], ["b56c589c", "0722452846", "43e2ed1c@4e233028.com", ""], ["77f4b627", "0795147567", "6f26728c@015d1c78.com", ""], ["e6e1b439", "0720916434", "6cf4f2cd@d0daef5a.com", ""], ["c4e49a4c", "0787210516", "bfc6dbf7@8f315a9f.com", ""], ["3f717509", "0704228943", "7a9fd98d@112a27be.com", ""], ["8c0f2abf", "0739504671", "b2c6a55d@bf7bb207.com", ""], ["d0ec34db", "0745702321", "4a426510@7c7cc4ba.com", ""], ["cfe0f395", "0708421594", "df815b04@d27562ee.com", ""], ["4ab6a603", "0781614226", "8e7498c4@216eb70c.com", ""], ["53727fa1", "0778595217", "0fef75b6@7e3eee41.com", ""], ["1dd3c6c9", "0795888769", "c88ef6ef@9182a8c1.com", ""], ["5dbdf1c2", "0700853188", "8b2128ff@5a2fa2e4.com", ""], ["5cbf9768", "0752068105", "064d2e97@f4631505.com", ""], ["b81ffbce", "0737952148", "ef90cb7e@7f7bb2d3.com", ""], ["6c9426b4", "0765528647", "571533da@097b853a.com", ""], ["76b87a42", "0735376294", "4fab1c8a@1004c035.com", ""], ["cc67d10f", "", "df05e09c@10cdee9b.com", ""], ["91f2c079", "0727155396", "f70f99d0@e56d0e68.com", ""], ["7687c589", "0723819703", "f4a37237@8306ec54.com", ""], ["cc065203", "0777023625", "235ab1ff@ad59efef.com", ""], ["ada410b8", "0795063396", "d58735f0@1cfd2246.com", ""], ["b17fda79", "0761763470", "06024431@3a90405d.com", ""], ["82c9cf46", "0795927937", "cc7d157e@fe591e61.com", ""], ["1229f23f", "0766794577", "196fbca7@2762695d.com", ""], ["41ee3436", "0751875418", "e789384d@1e8c078b.com", ""], ["b59b1ae3", "0706674618", "eaa7394a@9ca599d5.com", ""], ["32a1572d", "0739902105", "076df342@f7257d5d.com", ""], ["87142aaf", "0773843071", "2d1ed5d0@745442e0.com", ""], ["fd302180", "0752363053", "69d00e9f@4361b9c7.com", ""], ["9b129111", "0711208227", "e3f41ba4@230ff3c7.com", ""], ["00c627e0", "0795414341", "878530d9@891a7047.com", ""], ["f9bd3b66", "0754134870", "36b72937@8fe2912f.com", ""], ["d89c11eb", "0760560969", "6ec530b9@d7f30799.com", ""], ["e2b6ef2f", "0711238958", "6250bb8e@23e30295.com", ""], ["811c4293", "", "81aeac97@4083c108.com", ""], ["96c90ed7", "", "19727897@698b879b.com", ""], ["b82a09af", "", "7527ccbc@4e351f9e.com", ""], ["17e61a75", "", "ffdd4947@541dcc9f.com", ""], ["f97fa25d", "0749784894", "8b419430@4ae116d0.com", ""], ["13b9a7da", "0748292813", "1d2e437d@1f7fd137.com", ""], ["e0879369", "0778630426", "fbc7e460@350c1a55.com", ""], ["1a74805d", "0749531731", "f06e561d@ebbec1ed.com", ""], ["eb5db9c6", "0707095397", "ec71e260@1ab509e1.com", ""], ["9da2dc8e", "0717471756", "23418425@2dffbc70.com", ""], ["a6f6f46f", "0703721541", "037414ed@c1759536.com", ""], ["765a5633", "", "4fd61a7f@e9fc7672.com", ""], ["3a717722", "0785655203", "82f8e536@4aab3d98.com", ""], ["34b938b7", "0712389450", "0ce3ed1c@8d5aec4e.com", ""], ["fa4eaa80", "0760264228", "487d193e@86c36a2b.com", ""], ["6ba54ee3", "0737464908", "ee4791dc@6e7e18cc.com", ""], ["3d02d6e8", "", "b2c13ef3@66a3948e.com", ""], ["ee3c7b2e", "", "2c51c4fc@97eaabe3.com", ""], ["943acb8a", "0775102341", "306ebc20@ad6a778a.com", ""], ["e86c14c3", "0705424432", "40c1518d@76aab128.com", ""], ["0d993e76", "0729633289", "a18fcc70@f04ad1c7.com", ""], ["fec3f43d", "0740081762", "e04dd4d1@615c78e6.com", ""], ["90645f43", "0737009757", "d8ed6970@a5baa38b.com", ""], ["723c41aa", "0732060795", "d42e8f6c@9f01caf6.com", ""], ["96235675", "0755010192", "30d98b6f@fad581d8.com", ""], ["1bfda78b", "0736195102", "f0ba5711@24ec477e.com", ""], ["9fde0750", "0737641391", "f73cf50d@108aad37.com", ""], ["4cf28ec2", "0715717575", "f18063da@f0625607.com", ""], ["96824bd2", "0754345078", "f7b7534d@5c430ea4.com", ""], ["57e1a524", "0775075206", "9b963917@f4c93815.com", ""], ["1a1b212d", "0796156113", "0da3bd5f@60d5c0a3.com", ""], ["73267af7", "", "20364468@e070797b.com", ""], ["c4fcd602", "", "c996b53c@8c9e56e7.com", ""], ["23944725", "0756128345", "3f1043b9@4e705983.com", ""], ["8665f6ee", "", "c562cd48@d44eb942.com", ""], ["5178e72f", "0791852636", "bbcae5ee@428b8ba9.com", ""], ["9de66e8d", "0753385146", "55874b2c@f115714e.com", ""], ["de112089", "0734935576", "789b1706@6d2b21b8.com", ""], ["4e8283b9", "0700426636", "081d48fc@797f0558.com", ""], ["081d4271", "", "99829160@285f8fd8.com", ""], ["69dd706e", "0771351294", "906cc914@01cacc1a.com", ""], ["5de2de11", "", "1edc2268@e01bd776.com", ""], ["e823c600", "", "ecda6d9d@74a5da65.com", ""], ["907e7635", "", "14cbd73f@3c5029c4.com", ""], ["d6236d57", "", "39f3d4ae@1b7b8200.com", ""], ["aa2630bb", "", "75535cee@5391b394.com", ""], ["c6cda97f", "0710755065", "ff4ab3b0@e7638240.com", ""], ["b6661f85", "", "067496c8@1c3d2df6.com", ""], ["c95cf777", "", "afa9a3cb@279aa011.com", ""], ["e575e109", "0785713068", "8601eb2f@7d69d9ef.com", ""], ["c4aed4d1", "0797133251", "0ebd0ca0@44b0db13.com", ""], ["d8799ad6", "0766336482", "c8a2db59@fc20467a.com", ""], ["fe041483", "0747021458", "f1341509@a0d4bfcb.com", ""], ["9c8975f4", "", "1adf2f38@6ef39e01.com", ""], ["fc572eb6", "0707977477", "39ce98ea@c55bd59e.com", ""], ["86b7eeac", "0714499420", "19318930@f8cab44a.com", ""], ["d64977ad", "0761071885", "11a682aa@69ca2391.com", ""], ["ea006f73", "", "dfa103d4@ec0cffab.com", ""], ["0137c08a", "", "4e98808d@f9cbe2ee.com", ""], ["4c3ef173", "", "ad18e541@9c961399.com", ""], ["3a90243d", "0702089544", "73d9e758@a8185f82.com", ""], ["552fcf54", "0711153208", "b425511b@1f62462e.com", ""], ["47f20958", "0709287769", "387a2338@24efeaca.com", ""], ["95568c92", "0753062212", "230b16d8@7c47aa21.com", ""], ["4e00e50d", "0705656877", "e0557aa4@17b9f5a3.com", ""], ["6a744fbc", "0752791693", "f1cb0467@fc01b034.com", ""], ["b00241b5", "0707154247", "8fb98d05@46674fd5.com", ""], ["40bbff33", "0798115729", "238dcde2@3a402b74.com", ""], ["2ff0e839", "0734208035", "b85bc3b0@fb7c2ace.com", ""], ["48db2fd2", "0746707903", "21bd36a0@967fc046.com", ""], ["82ce2569", "0712225501", "b578f1ac@ebb9345c.com", ""], ["bc8eb82a", "", "579a208c@3a587c16.com", ""], ["e6c2ceff", "", "cf4f1954@a02d98a5.com", ""], ["36cc5274", "0794054613", "6a6fba68@59b878ab.com", ""], ["a47d46b0", "0779573055", "7e3b995d@8ba62af1.com", ""], ["abc2eea3", "0770887487", "2d0b55d1@9b58d90f.com", ""], ["3aeec47e", "0798128924", "cc01849a@23ae0c8c.com", ""], ["438153a3", "", "fbf57f49@b868a49c.com", ""], ["c2e79acb", "0790684868", "eefb75e0@e0e96a2f.com", ""], ["975b014e", "0762757057", "aa6a37dc@a30b0c72.com", ""], ["e610d666", "0781184369", "4d3f4b20@97d8a401.com", ""], ["73495ac9", "0719719687", "7a018884@c2c13e7c.com", ""], ["9a445855", "0785545967", "2d196afc@ffff6d02.com", ""], ["ce2ab926", "0700868262", "2cd6763f@b6d56901.com", ""], ["c0fcbafb", "0706968744", "8ab42ade@5692f6e3.com", ""], ["0604c52c", "0772596476", "b71e2f2d@31e533dd.com", ""], ["b4fedfd6", "0700359913", "af1c4ae4@fcbb6631.com", ""], ["1e19fa35", "0742671783", "009e471f@8e4f29d9.com", ""], ["6bc8631a", "", "38a6dc3d@900b535a.com", ""], ["09c221a5", "0712627371", "6b081654@a5d250ba.com", ""], ["55d80d5c", "0710423548", "149c66a4@0adbe042.com", ""], ["bc6f4369", "", "28372894@dfa1fbcc.com", ""], ["7aa8a6a4", "0766166352", "9dbb5873@5731fed6.com", ""], ["5c781ef0", "0790623819", "2d629650@b57c316a.com", ""], ["6830c71c", "", "39897b3b@7ee8cde5.com", ""], ["6af8ee98", "0747825215", "5b56cf20@4c81a082.com", ""], ["5a8bcde3", "0763197608", "e69532c0@34630f3b.com", ""], ["8f9037fe", "0726240186", "df9e5814@e4fc4423.com", ""], ["fb13d5b0", "0721173617", "d475522e@c02f6cd8.com", ""], ["9ce57e77", "0774209520", "288a94c3@2a0fba22.com", ""], ["b54fd99f", "0735610926", "061e00aa@34301c70.com", ""], ["fe1a320c", "0776730222", "53770fab@f7c2cd27.com", ""], ["10c3b647", "", "ae78b399@9c814e5f.com", ""], ["d24be5da", "0797581905", "9394a573@bc5e7a67.com", ""], ["e61273a7", "0730217168", "ef553bd3@fbd3732e.com", ""], ["fb6472b6", "", "cdf00170@f2e89868.com", ""], ["b588bcf8", "0715004480", "21505721@1ce9749f.com", ""], ["36ef226a", "0720093875", "293e85b5@c81c476f.com", ""], ["007c51ec", "0770116496", "b1b68fa0@159369c4.com", ""], ["949c6074", "", "6e66fd1b@434382f3.com", ""], ["4e4f11d2", "0762955197", "66d29d82@531b2e83.com", ""], ["758c58d1", "", "9dd36cae@295af2b4.com", ""], ["99be0c22", "0794398863", "0249fb4e@4786d822.com", ""], ["1298fb9d", "0720206151", "d3c59051@5d59357f.com", ""], ["b97f877d", "0779925184", "f6414a10@13ffe42b.com", ""], ["3e653aa1", "0750192219", "b35e6fe5@1b6bf57b.com", ""], ["8fbab13c", "0732153634", "b0a7711e@52ecc07a.com", ""], ["1a3e75c1", "0743946942", "25786349@8c65834c.com", ""], ["6ca639a0", "0717692914", "ad3be222@b10a9147.com", ""], ["3c8dd318", "", "bcccd36e@cc70d14f.com", ""], ["a6d3e7cd", "", "aa9c8a70@963098a4.com", ""], ["6fc1b3a2", "", "d19b5d6b@934e24f1.com", ""], ["759261f1", "0709242692", "72f51517@87b24cae.com", ""], ["3d7a1d1e", "0715207809", "877c9255@1671ff51.com", ""], ["93278583", "0742390782", "21dca350@2cb6d4ef.com", ""], ["51f96553", "0748353883", "6be176c3@4e96ba7a.com", ""], ["a347fde6", "0741522657", "c133fafa@26d07149.com", ""], ["362bd97e", "", "60ac173a@10183062.com", ""], ["b1a7c0ff", "0764817452", "67e54fec@06bf6f87.com", ""], ["9ee4aa89", "0741377765", "1856ef66@cc9f3fcf.com", ""], ["49a7dbd1", "0783985348", "ce3970b4@ea82bad4.com", ""], ["206b0643", "0767461177", "a897ff67@8adf5df6.com", ""], ["596a240b", "0722068325", "82663950@9a960206.com", ""], ["dc852f2d", "", "381e1687@8bc841b8.com", ""], ["44dbefa1", "0774987561", "391ecd0c@26e9e890.com", ""], ["69e6e40c", "0713319482", "978839d0@5ad63633.com", ""], ["0121d5a7", "0714766437", "8abfc0e0@b6e025bf.com", ""], ["cd5200b5", "0764514081", "6304987a@1d2f7eb9.com", ""], ["536c4831", "", "c0e01f58@cc7a1d56.com", ""], ["31167693", "0712547344", "9e54c7e9@90b5ff9a.com", ""], ["6a240068", "0776430272", "4ae0eabc@345cd527.com", ""], ["c14f594b", "", "7351d1ff@31c0438e.com", ""], ["b290c6f6", "", "9368d514@98711325.com", ""], ["f870b7b6", "0789240289", "4f8593ba@f80fe1b6.com", ""], ["f9c1a49b", "0714414302", "02efd04f@a4f155cd.com", ""], ["51605326", "0704713807", "13a21489@ec8993c8.com", ""], ["88b5f2e2", "0794091627", "a2745c7e@a32c3b1d.com", ""], ["f3b6a422", "", "4e024c4c@022f5e1f.com", ""], ["c2cfc88a", "0761247770", "153b9fe1@9859b8a4.com", ""], ["597f3524", "0761882451", "1982d242@a8f03583.com", ""], ["d694bc4c", "", "864732d7@76277920.com", ""], ["8e859b4a", "", "d16f7f69@0261a0ef.com", ""], ["2d583afa", "", "876b76c6@e55415c1.com", ""], ["1bd3b1ae", "0761041162", "a2ebbee4@9a1b4f54.com", ""], ["a01d3c8b", "0703532365", "d6d7b818@bf578a2a.com", ""], ["6b8ff331", "0705773074", "e1e26a20@144139a8.com", ""], ["bef24803", "0750088813", "fde0039c@4ed43033.com", ""], ["253651f9", "0715013226", "3aebf85d@f7455c19.com", ""], ["34da6189", "0764808908", "03f2bd4a@c73ec94e.com", ""], ["c72f3422", "0754811376", "5941e3ce@769439b1.com", ""], ["1761f923", "0708249762", "20e78457@68b640c2.com", ""], ["a7ddd99c", "0754994431", "ad342d73@622fa530.com", ""], ["1b32b5cb", "0769400739", "510cf15d@1feaf7a5.com", ""], ["2ecd5127", "0788462377", "f2a96f71@c6dadef6.com", ""], ["b2e338ca", "0780233422", "0394850d@c7f808dd.com", ""], ["0999515c", "0708264022", "9214c832@73793633.com", ""], ["b4662820", "0791822790", "378843d9@faad02b6.com", ""], ["b6526e56", "0702391559", "ddd3922e@683a9bc3.com", ""], ["e1d0dfca", "0796434690", "bbea0324@a25f259b.com", ""], ["7507ff74", "0718970953", "957feb53@dd898780.com", ""], ["537fba55", "0712047297", "380f1934@1afa6994.com", ""], ["c03244c8", "0719594534", "d685945b@5f863d81.com", ""], ["7a0b4463", "0726964918", "7dada8d2@632a578b.com", ""], ["01f105bd", "0771635491", "0103cc3d@2c004125.com", ""], ["7c9baaea", "0793949807", "b0aae8e3@23796a56.com", ""], ["05f636c7", "0707511947", "36c7c75f@2d86967f.com", ""], ["71d917a2", "0784386161", "ad18b2df@9366e77f.com", ""], ["d417a204", "", "e3ef2306@48c768ce.com", ""], ["8d6d1bb3", "0705778200", "ee43e84c@f6df11fa.com", ""], ["f4c05035", "0772727935", "c0ced236@b4ea187c.com", ""], ["e6d75e18", "0788952581", "402b9e4d@0b37e829.com", ""], ["1dabeec9", "0727510448", "a0fac12e@0ea007ab.com", ""], ["f71fa168", "0759368909", "2b111301@22da70d4.com", ""], ["23b7c929", "0786848255", "b71bbd84@857f9ec8.com", ""], ["a085c14c", "0740213711", "d32148b7@b67a73ae.com", ""], ["9ac9fa3e", "0701707031", "42cbdd1a@175f318e.com", ""], ["b4e40dc9", "0715864237", "a15c733d@6669b4f7.com", ""], ["94020cfb", "0715193084", "ad45863a@2eaab246.com", ""], ["626c1ee7", "0750957414", "d66ae397@2e895e2f.com", ""], ["1eabc823", "0731567529", "20f8332e@b4b0703c.com", ""], ["e5a98097", "0718031536", "718b7ae2@8504fe2d.com", ""], ["69245c65", "0779488230", "fc645fef@635fbdcf.com", ""], ["247ce107", "0721718937", "2c662519@b0c33ecc.com", ""], ["c4925e4c", "0710565488", "36e178bb@4cb2efd9.com", ""], ["d0ef8f45", "0725811338", "f5404bce@9e12cd0a.com", ""], ["ed80a2b0", "0708451045", "6b9f4393@5ea5c209.com", ""], ["13aab094", "0711289294", "ce0972c1@c202849a.com", ""], ["0154dcda", "0734530650", "e225ca87@12039c6d.com", ""], ["004cecdb", "0741339169", "d85299ff@ce7f5fd2.com", ""], ["bbbcbeb3", "0723560169", "e31d0863@b105423d.com", ""], ["cef7c1a9", "0752786167", "3c76459a@4e492e4a.com", ""], ["c3cb922f", "0779170714", "24e2fd0a@cd086652.com", ""], ["a267897d", "0740299573", "c8ab2622@5d1d5225.com", ""], ["f73c9d67", "0754733442", "433436bc@eaa41a7e.com", ""], ["bb0022c6", "", "016e3dfc@ea644733.com", ""], ["060c6c8f", "0767126800", "24a3b50a@8c364ded.com", ""], ["9f5b5d9a", "0769176462", "20dc7747@ef1ad5b9.com", ""], ["43e67ec3", "0743483082", "8a5f94e8@5e7d39a0.com", ""], ["345e4a33", "0787677047", "3a0c802c@608dbe31.com", ""], ["ec2e7f7d", "0784990090", "23ec5d05@c4bebf51.com", ""], ["e239c87d", "0738679004", "8cfa7063@bcb2bc21.com", ""], ["271fc9e7", "0778470494", "c2d46c92@dcb3dbfc.com", ""], ["3b0585bd", "0795454441", "40772365@1d8f7187.com", ""], ["a1a58875", "0718370412", "2eb10d10@5ec547bd.com", ""], ["4c45c47c", "0739708464", "81d5c8ac@06447b51.com", ""], ["e57e8359", "0751620730", "4b2b8980@c95b69b5.com", ""], ["735270e1", "", "ee37dbee@25a4ff65.com", ""], ["4b44e00f", "0711946373", "0eb6a22a@b320af8f.com", ""], ["8a372c1e", "0770968660", "d6678940@ee818cf4.com", ""], ["88efebf9", "0720205646", "7d6d6bcd@87fecbbb.com", ""], ["6ebf214d", "0780223716", "d28759e2@e323f23f.com", ""], ["2eb46280", "0795896930", "8dae7897@d3854b08.com", ""], ["53bc97c3", "0730645626", "12518a5f@a1fdfaca.com", ""], ["fec1e211", "0754874523", "40655f66@4897f637.com", ""], ["1b8ef046", "0731698966", "cbed8d9a@893a95be.com", ""], ["70099e91", "0725267567", "b75efdd0@8dcf7f96.com", ""], ["00234cba", "0748766899", "9088d159@a1a1f4c1.com", ""], ["35ff23c6", "0747870199", "c961250c@d8a06d24.com", ""], ["9e7417f8", "0746801322", "ba7e50e4@cae69ff0.com", ""], ["3428f8d3", "", "56c21910@48bf2959.com", ""], ["f575e8a6", "0734183553", "4953d608@e2885866.com", ""], ["ee701421", "0762346436", "0eee7492@884b62a5.com", ""], ["197c760a", "0741186587", "8e5f6e2b@ea3cb8da.com", ""], ["691a46aa", "0708195042", "cf89f958@dfa6105e.com", ""], ["b03db786", "0716744355", "3f1c117f@a6c83666.com", ""], ["4be4d96b", "", "48195284@40467121.com", ""], ["dfcd1ada", "0783073475", "9785f27d@1df650f2.com", ""], ["4490c17c", "0786052205", "8e4bab0c@b98b8c4c.com", ""], ["f4e991f7", "0717102345", "e5a0a4fa@0483a00c.com", ""], ["b15003cd", "0771528694", "e012bb67@b1463416.com", ""], ["95e2a50d", "0796484412", "508dbc66@25ef9b72.com", ""], ["c2003313", "", "8856d03c@72d7544b.com", ""], ["276b4bc0", "0789810903", "ef87caba@2741b9cb.com", ""], ["15da925e", "0748450892", "656949ba@78234623.com", ""], ["77e171e3", "0786551610", "a86b4d19@110baa19.com", ""], ["9d9d0504", "0758342738", "0dc0e069@bb5e721e.com", ""], ["f4def247", "0798052097", "e66df9a7@af41f423.com", ""], ["c3ea412f", "0706797533", "4e28af08@8a754fb6.com", ""], ["53812400", "", "6c6215f1@45e33532.com", ""], ["369bfda8", "", "45cf4bdb@6d0980f6.com", ""], ["dbd3b821", "0765939770", "f448784d@35d292f5.com", ""], ["142f350f", "", "41c305cd@fdf34481.com", ""], ["8fcd1626", "0770444423", "323d955b@41ff7d99.com", ""], ["8789a603", "0751524803", "359bc02a@72af402c.com", ""], ["0a675687", "0789901250", "2323249a@619f78c5.com", ""], ["fb011bf6", "0750579472", "7152dcf1@668f96db.com", ""], ["a623d8fd", "0733289400", "482d64cd@8a4e2926.com", ""], ["6d961e75", "", "6fe4f5e1@e9239485.com", ""], ["4f39910e", "0746456899", "e85969c9@cca744af.com", ""], ["81a834a8", "0731458518", "8ffce3c8@8e7ae609.com", ""], ["70472837", "0775526068", "5dbfd52d@0d829cb6.com", ""], ["2d882041", "0708561610", "e5853dfb@b2187974.com", ""], ["deaf54bc", "", "a9dc3872@2506f15e.com", ""], ["a7226533", "0760964433", "2490dcc2@de364340.com", ""], ["36377c3b", "0759911971", "93465967@b06cdaa6.com", ""], ["31724c44", "0757697274", "b04d8ef1@544a5467.com", ""], ["51806270", "0742044114", "417f3dca@cab99b90.com", ""], ["e9b2295d", "", "d9e360f5@4d87b309.com", ""], ["0dee89bb", "0712052963", "4700d9dc@8b9161ce.com", ""], ["8a4b9e30", "0755847836", "b3d261a8@60e7e6d9.com", ""], ["f8f7bc44", "", "ffd58a0e@e40c3e44.com", ""], ["c5d5d713", "0790256137", "35f89e39@5893f2c1.com", ""], ["90f0cbd9", "0737155772", "6d8e7f05@40038b63.com", ""], ["eef1d1a9", "0742179171", "065c13a8@634d0446.com", ""], ["55faa2a4", "0748611843", "22382d4c@d11eb075.com", ""], ["f7913ace", "", "40be2255@400d8d4e.com", ""], ["0d47fd15", "0797162254", "06ec4142@00a16805.com", ""], ["906a5fb1", "0703201953", "4d249ff5@55ffe749.com", ""], ["4b5010c3", "0741317092", "25d81157@6027b9e1.com", ""], ["f79b697f", "0773396997", "2e116cf9@9a6bfcba.com", ""], ["c6d2d0f0", "", "a83108a9@40159f26.com", ""], ["da24f341", "0775328597", "4e7592d1@70f4d9c1.com", ""], ["b20edca2", "0789450137", "d326baf5@1baafa6e.com", ""], ["3e11a76a", "0776368949", "15a351bc@e4b229ff.com", ""], ["3e2fbdc3", "0717744150", "4ebbd18a@de822931.com", ""], ["7e6c9549", "0750887467", "2c5034ed@23402dc1.com", ""], ["9536e16d", "0713928189", "e6cd0a97@d20db0c5.com", ""], ["8358cf78", "0735864901", "4ea98e6a@e1052078.com", ""], ["d12cef0a", "0730971666", "a7f6061d@b6f2822f.com", ""], ["cc2ee54b", "0778562618", "61917c3e@032bcdb5.com", ""], ["21ef6091", "0763511033", "166d075e@522d8051.com", ""], ["f6b7a396", "0772929913", "13ae6bf9@c44fbe8b.com", ""], ["042555cf", "", "b11161b5@143fc047.com", ""], ["9739c45e", "0792544143", "9a57d50c@7545618b.com", ""], ["aad5cccf", "0781328164", "59e7123e@488f0065.com", ""], ["b886493a", "0785224849", "d6499e6f@0688b326.com", ""], ["2c99ee07", "0748501224", "1e582961@799a7165.com", ""], ["72545b19", "0715248877", "637c5be0@c9d62b4b.com", ""], ["6f038b04", "0717198089", "fa325ae7@624b634c.com", ""], ["4f8e0b2a", "0764277265", "cb4a5eab@35f7346a.com", ""], ["03fdeddd", "0721789994", "c0476d68@4d5cd7bd.com", ""], ["89d74e2d", "0760756190", "d6594d52@5dee56e4.com", ""], ["c24acb5f", "0781827089", "3a1ea265@8bd8e1cc.com", ""], ["d1bf9230", "0722180686", "9e582b1e@e8351d46.com", ""], ["9dd2e88d", "0711860229", "56658ca4@132ac241.com", ""], ["a07824cb", "0741457266", "76f4cfea@f25c3adc.com", ""], ["f3897859", "0710503175", "8f23c250@4540617e.com", ""], ["c09a14dd", "", "e9c464ac@f76cfe06.com", ""], ["c9a758e4", "0790149404", "b5fbb29e@2ef70cf1.com", ""], ["dd56f5d4", "0709704673", "8b24db91@663c1804.com", ""], ["1ccf7b2e", "0728115684", "d7610e72@e4a8b643.com", ""], ["32351dff", "0748120068", "3939d0ab@8fdf1e7d.com", ""], ["8c4053c5", "0714496140", "420781c7@8ccbf30e.com", ""], ["ce266357", "0784144894", "de2e719c@22b747ed.com", ""], ["3257e91e", "0783497860", "09f03ee1@a98f49a0.com", ""], ["0180d6f1", "0743825253", "ec4bf604@a0b51abc.com", ""], ["5360eee3", "0719368035", "bb270d1d@984f9d39.com", ""], ["d40e19a5", "", "5ccc6f37@d6870955.com", ""], ["a429a2e0", "", "9372812a@b1676aa3.com", ""], ["45dfc0c2", "0709417666", "fed1b00e@bf665131.com", ""], ["a173558c", "0758991067", "3f706fd9@301776a7.com", ""], ["ca87b3af", "0781867475", "98155b79@adcd5507.com", ""], ["036da0ce", "0778243834", "66467ae3@686faa6d.com", ""], ["c5ddfc00", "0750665015", "4e0a5924@9d77bef0.com", ""], ["40b39118", "0765997451", "7bdfe708@a3cee04d.com", ""], ["a5de4669", "0761205980", "bfac2446@e1515b33.com", ""], ["a62448b2", "0739220943", "3da472cc@73d9e74b.com", ""], ["3b04bf59", "0711824354", "b6ac5778@42772967.com", ""], ["ab56dd06", "0791996063", "3949b2d8@b08c4c5f.com", ""], ["9820b796", "0724164512", "ecad52ea@81041046.com", ""], ["894e5af9", "0706784563", "4b2ee10d@821e0a2e.com", ""], ["dd06ec54", "0782812141", "a2e91578@875cac30.com", ""], ["802f32a7", "0749216707", "8e7171df@898e4f66.com", ""], ["ff30e79e", "", "e09f32d2@e8bed799.com", ""], ["7070b09d", "0707078844", "bfb66733@308be7e8.com", ""], ["fde6085d", "0712529236", "c4750b1e@05266810.com", ""], ["87f4aafd", "0739190648", "1cb92876@1cf2f6df.com", ""], ["94a88efa", "0775446056", "f0ce2f87@410508db.com", ""], ["20f56909", "0740230843", "55997ce7@82f437b1.com", ""], ["1ee26c6c", "0713640395", "6357a25b@fb48f794.com", ""], ["ee1ba135", "0789826448", "097f6423@892f4d97.com", ""], ["7fa6baf5", "0752582669", "6e49ac6d@7012d5a1.com", ""], ["35fc8c1d", "0789186620", "0abe0f3f@2435673a.com", ""], ["0e7434e8", "0791384205", "96c88dc9@8e26852c.com", ""], ["6a899564", "", "65902813@1686d624.com", ""], ["90d1935e", "0756326458", "8ddc4a04@a5572b9d.com", ""], ["f8c69667", "", "c827107a@93f105af.com", ""], ["b2c511ea", "0749010920", "24bb2d69@f4287f85.com", ""], ["6ecc592e", "0788769694", "4c132913@931ce84d.com", ""], ["6722b9db", "0794262991", "52bed401@35003685.com", ""], ["6874d990", "0748452679", "b5dca8b6@97c1b768.com", ""], ["72740c63", "0721748699", "9e10c395@a98c8e1c.com", ""], ["ee0d78e0", "0795469696", "56d8a295@d6ef7c7e.com", ""], ["d5bcb2aa", "0741284724", "34c858aa@5f49689d.com", ""], ["5909a519", "0744679678", "88409af7@e19f4243.com", ""], ["f265acfa", "0792029151", "e34d1172@0e9bba98.com", ""], ["94a5602e", "0709917575", "0464515e@552c3013.com", ""], ["d0d483ad", "0764326930", "86b8c70f@a6382da0.com", ""], ["b9155373", "0725007352", "e3223601@f5a902c8.com", ""], ["600e630d", "", "f4e4d66b@bf8aa87c.com", ""], ["fe12bd24", "0772663541", "8b8b1297@1e02a9bf.com", ""], ["fc11b86a", "", "65f3c1ac@375002e4.com", ""], ["110d8fa4", "0775226701", "3d955f6b@ce7da885.com", ""], ["d3105be1", "0779779352", "ed2be0eb@ccc7485e.com", ""], ["fda61a0b", "0720274167", "6becb3e3@4deb6681.com", ""], ["17f8be17", "0799234608", "73f14970@cb957e59.com", ""], ["e9516a1d", "0760461815", "8ad30006@8df0fd1a.com", ""], ["60177106", "0760558146", "dc029d5c@b6ef2fd2.com", ""], ["3450866c", "0711798090", "cda58109@c2e25542.com", ""], ["06e26393", "0735961481", "305bcf19@6c3ec630.com", ""], ["708af001", "0728438353", "048b067d@80d72f50.com", ""], ["c40ec0a3", "0726279359", "a10d333e@9a89cb7c.com", ""], ["2a2eb847", "0741461630", "d3daf952@7a3408e4.com", ""], ["d01a1813", "0704645200", "f226e03a@f67c7d10.com", ""], ["819cb8a9", "0798337915", "cf70d59b@d1a42e77.com", ""], ["6fa32598", "0701415106", "4a225f04@2534e6a3.com", ""], ["4e441149", "", "456f82ab@ca0b04a5.com", ""], ["f7ff1a4a", "0701515624", "5918544a@4bb3704b.com", ""], ["5738b682", "0724843216", "04c74b30@ed510bac.com", ""], ["f10e2d6a", "0730727806", "415d4081@b1a88a68.com", ""], ["749e83bd", "", "15e0671b@76428121.com", ""], ["3d65b85b", "", "d1eb4053@c363e69a.com", ""], ["a0792a15", "0729947768", "c24f344e@41f87b82.com", ""], ["fb01d572", "0792621720", "9c750d18@7fb1f5ce.com", ""], ["e67acc97", "0789099675", "bdfbe8ff@e2ca97d6.com", ""], ["a9e6cd4b", "0708488791", "1826128f@98d76c9c.com", ""], ["819f95a3", "0788991943", "cf458e5d@44f3c946.com", ""], ["20204d4c", "0714720322", "1efe1d68@5dddd6aa.com", ""], ["a1ea35bb", "0749468567", "480f2ab1@72a338ed.com", ""], ["92ffc165", "0780038157", "4dd11686@daa7a1e3.com", ""], ["4ba3f501", "", "71b12e98@a76b654f.com", ""], ["d7f5285b", "0736359794", "fc0e04a5@dde7291b.com", ""], ["77bfe67f", "0742014858", "b3c8790c@cacf5548.com", ""], ["dbf35774", "0726444745", "2243d1d2@138dfd54.com", ""], ["e9f91a9f", "0760618445", "db7cbba8@4616651c.com", ""], ["04c6be09", "", "9adf3814@5e01afe2.com", ""], ["b57e7ee3", "0722578719", "1736e501@964d3424.com", ""], ["d8114397", "", "6bc5e72a@bc4d7e37.com", ""], ["7ab21681", "", "aa33cd18@d6af8a2b.com", ""], ["290e81e9", "", "4d6f5933@bb9b4aa8.com", ""], ["5a1f2ac9", "0778832081", "53b60c08@8973dcfb.com", ""], ["f8775a07", "0743005567", "3c53f38a@c0918592.com", ""], ["30c7cdb1", "0779422190", "a809f36f@1b3dc9e3.com", ""], ["1c669546", "0793826953", "0b2728a3@f2f4eee2.com", ""], ["229beef7", "", "97621f15@237e6070.com", ""], ["ab8c48d5", "", "8545951c@c7523008.com", ""], ["e4294ab1", "", "6d8bdfd5@e9c1c910.com", ""], ["c9c8fbe2", "0729515527", "bcd2115d@e4500586.com", ""], ["0b6463d3", "0725941332", "e768eb3b@fbd48ad0.com", ""], ["15bbab63", "", "a8fd2422@b236a910.com", ""], ["c181cff0", "0717305018", "fb4e37ed@b611bc36.com", ""], ["03f712cd", "", "a05576b0@e16b8368.com", ""], ["31105e59", "0790432797", "6e4a7e2a@d6902424.com", ""], ["5c868ce0", "0738424344", "36348d0f@342e6bed.com", ""], ["d47bd754", "0793838363", "dd9d97d7@54a56407.com", ""], ["660908ec", "0707537040", "ae9b6681@a9382430.com", ""], ["2c6290a0", "0759084568", "b011def2@e1ac3f5a.com", ""], ["a989f05a", "", "25e59710@79b7f417.com", ""], ["f3c16d3b", "", "ded9819d@a99d7dc3.com", ""], ["1adf7818", "0751467205", "b96aa90a@9791c269.com", ""], ["409cc67e", "0777500182", "5317c827@a2db4c45.com", ""], ["aabd103f", "", "e5d907aa@bcc71e10.com", ""], ["d23bf430", "0777169222", "85e8641e@cc5f3d8b.com", ""], ["69a2879f", "0743889293", "4b5f4141@04205775.com", ""], ["8a3ec274", "0756861440", "ee663794@82c1f4fe.com", ""], ["b4a3cd48", "", "fc399765@64ca060b.com", ""], ["e0ccaed3", "", "6388033c@824e8b90.com", ""], ["c3eee2b9", "", "c0162073@2c2fd2b2.com", ""], ["a73c64c4", "", "f688a2e5@6e30672c.com", ""], ["2d852e73", "0772174434", "7a42d0d9@72486cd3.com", ""], ["a12254f5", "0769384878", "caf08e44@d46ed98b.com", ""], ["f653b74f", "0763997467", "388c7213@1126f95d.com", ""], ["fc93ee15", "0774848627", "beabdaca@2d84271e.com", ""], ["73c04bfd", "0771623831", "8fe53552@31ce4032.com", ""], ["74a5f23f", "0789722672", "82930bac@7dc3e501.com", ""], ["796d3e3d", "0762835594", "1181180a@ec66e2f0.com", ""], ["1bc14fc5", "0704902009", "ed326523@23e42c30.com", ""], ["5c08dcb0", "0765115244", "dd5945cf@f297b86b.com", ""], ["566bf582", "0723354400", "ea731083@ef83ec7c.com", ""], ["f25208d4", "0789906022", "457e0601@4eb796dd.com", ""], ["114eeeb3", "0758751442", "14cfa76b@aee76d12.com", ""], ["2669d4a2", "", "5ea1e2ac@cf638cf0.com", ""], ["6202f1c1", "", "d93e09a6@d3898452.com", ""], ["5126ade5", "", "96601158@cfeca444.com", ""], ["a0cef71f", "", "e9366869@532d79f8.com", ""], ["41bda0e9", "", "c4206b97@966c822f.com", ""], ["a75dc6cc", "", "12906204@892175ee.com", ""], ["79636bcc", "", "c45c9aae@a303f7d9.com", ""], ["e40fb55e", "", "72fe75ec@c1ddfd45.com", ""], ["e0ddb510", "", "ea6c8b14@193b1240.com", ""], ["c7532313", "", "a9361a51@f562d91e.com", ""], ["0158d9f3", "", "1de7b7e6@876beb92.com", ""], ["b2394690", "", "fe1b9570@3cffd682.com", ""], ["79051273", "", "fbb85c72@4532b2d6.com", ""], ["1ea2aeae", "", "6e8d529a@fa5af449.com", ""], ["2c637d3a", "", "b633e7ca@b2e822e5.com", ""], ["ba3b59cc", "", "10e596a4@e2654efc.com", ""], ["631c774b", "", "03b0156e@5f3383bd.com", ""], ["6e4d8e1d", "", "b7026e82@015d71cc.com", ""], ["3d430d86", "", "6af17483@bf92c6e9.com", ""], ["ea26cfde", "", "aeee3cf2@e06f3387.com", ""], ["16596f6f", "", "d1445706@a3282036.com", ""], ["9bc58d49", "", "", ""], ["cc907372", "", "a923bc16@c3febc01.com", ""], ["605dd0be", "", "49daa341@8ee4c07e.com", ""], ["1b1114f0", "", "ec5a8a0b@489a7dd5.com", ""], ["28b0c962", "", "", ""], ["63370e89", "", "44d07879@3c414118.com", ""], ["f3f1db57", "", "", ""], ["ac49009b", "", "e5cd7740@ea261a3b.com", ""], ["9e46dcc0", "", "83a47ee1@a58545dc.com", ""], ["2e95367c", "", "8bee6be7@8906c53f.com", ""], ["ea22c7bb", "", "53b4805b@af24dcf7.com", ""], ["c90f526b", "", "d790585d@8d10cf2a.com", ""], ["7887a62b", "", "7120351b@ff19865b.com", ""], ["64cb3f9d", "", "", ""], ["9e591fa7", "", "c7730953@a714f3a7.com", ""], ["1f16cd07", "", "67bebc78@40ed8809.com", ""], ["287ec13a", "", "9e5e71cb@2bb1c891.com", ""], ["d164c461", "", "1cd6e067@14c6ada1.com", ""], ["36379fd3", "", "72a5809d@9b00b1bb.com", ""], ["9470c307", "", "2816f5ec@451ff875.com", ""], ["1655c648", "", "f06ec4a5@f5ab6d4c.com", ""], ["879040ac", "", "62e235cb@ec8a0c43.com", ""], ["50232cdf", "", "025cd190@2477e789.com", ""], ["5a35f64f", "", "", ""], ["777de661", "", "38640b10@41514086.com", ""], ["705fb8cf", "", "7284860f@b222b1f8.com", ""], ["fcb30c1b", "", "361fde21@1f411552.com", ""], ["079e7250", "", "6bbcefcd@2aeb37af.com", ""], ["4e47023c", "", "d23b6b4d@b0b56474.com", ""], ["1bce884e", "", "1dfab7b8@68a3a0ef.com", ""], ["7088e56a", "", "6570590b@e2a30ae0.com", ""], ["65ce20d2", "", "86106467@58070f61.com", ""], ["68d599aa", "", "e279dc2a@58d99123.com", ""], ["0142c624", "", "9abe7329@cb4a4022.com", ""], ["9e8bcae3", "", "840f16ca@1ed99fc8.com", ""], ["4b854254", "", "fbf12ee9@6499201a.com", ""], ["615d5c95", "", "abce7c9f@ade377af.com", ""], ["d389a4b2", "", "a78f0fc2@a6f959cd.com", ""], ["38be0158", "", "bfa96d44@f363eb7c.com", ""], ["f9e06f7c", "", "542bd29b@97746a9e.com", ""], ["832811a7", "", "9bf9010e@01957fac.com", ""], ["359a78e3", "", "3248a6d3@763ddb24.com", ""], ["27f9817c", "", "07d94000@2aca2301.com", ""], ["013a3bd4", "", "2224822e@fade404f.com", ""], ["a0ef9d39", "", "", ""], ["b024d6ee", "", "0daa6bb1@2ef82615.com", ""], ["a381acc3", "", "bad81f63@dc7acb79.com", ""], ["7610e7b9", "", "59829816@d8a49cb9.com", ""], ["4460acd9", "", "acf4b236@dd8a5e68.com", ""], ["5193bc70", "", "f5620364@f763b237.com", ""], ["9d134fef", "", "df0234b6@ae3b90cf.com", ""], ["8d8c4586", "", "f92d2add@0f93928c.com", ""], ["2d12e299", "", "99162fbe@59bd4569.com", ""], ["bcc4641b", "", "afff34a5@f2e5d247.com", ""], ["5c2243c3", "", "3ef0d599@f34a9ed2.com", ""], ["427913d9", "", "086fe7e8@925e9a12.com", ""], ["3a60c0ec", "", "66c4dfbc@cee34594.com", ""], ["ffca31e8", "", "0a4a245c@68e91e5e.com", ""], ["7df057ab", "", "335cabf9@a17a61b3.com", ""], ["71307e65", "", "c1de44cd@a1add235.com", ""], ["17bf2c39", "", "601cc244@f0efd775.com", ""], ["a8b9e2e1", "", "de175716@d0cfb7a0.com", ""], ["a294ae4d", "", "", ""], ["37859daf", "", "376f0f2b@369a7bcf.com", ""], ["01e1685f", "", "6fed663d@3672e214.com", ""], ["0a32e0c6", "", "0b0791c3@2c33f23e.com", ""], ["5b8ff540", "", "29599ad0@548964c2.com", ""], ["eec1094b", "", "dfa57b8f@80b6e55e.com", ""], ["acf49b4d", "", "4c20a1c0@c6d0e041.com", ""], ["63867c55", "", "", ""], ["873984c8", "", "1cf228b4@b15a1b5f.com", ""], ["ecde582c", "", "cc419b7b@60107818.com", ""], ["039d5e8f", "", "5da65ece@1bb69031.com", ""], ["21f04ac9", "", "8ee84896@25275182.com", ""], ["735349c7", "", "117051ac@3a4fd4d7.com", ""], ["273761c9", "", "0184bda8@198fe950.com", ""], ["200dff3c", "", "cb311031@7c6cc49c.com", ""], ["80a68462", "", "5f3c6275@44263f25.com", ""], ["53ace4f9", "", "733a3c0e@c7fd4969.com", ""], ["484e9625", "", "bfe97c09@d2ef03d6.com", ""], ["b798a822", "", "cd531d09@aa763932.com", ""], ["04cecc11", "", "e173c817@3977a28a.com", ""], ["2a910e01", "", "5e200dad@5cf70a26.com", ""], ["540b1452", "", "c9462faa@cb7652ed.com", ""], ["80261f65", "", "fea8b5bc@e5018e45.com", ""], ["9e4fa7c2", "", "95f3f9c9@f265aefc.com", ""], ["f0227223", "", "702f9660@2006f10b.com", ""], ["500bb986", "", "40a22a5c@ed4055c3.com", ""], ["34c14a29", "", "3d2633f6@206a53a0.com", ""], ["66a868a5", "", "7a2b1e68@9cf601fb.com", ""], ["af8d6986", "", "", ""], ["1fbefa32", "", "e948b9f8@c0fe216c.com", ""], ["ee799cb5", "", "01013bb3@229547a9.com", ""], ["b2ce8d2e", "", "fe3750e1@ee50e669.com", ""], ["ae7c0d5b", "0727494158", "68aad918@674f032f.com", ""], ["1c55c64e", "", "efc9a2bb@1fec90ba.com", ""], ["2eef74a3", "", "238d16ec@14658003.com", ""], ["5ba48a58", "", "c6d52520@65f96971.com", ""], ["e650ca0a", "", "76be4eba@a74dba94.com", ""], ["f977adbb", "0789591713", "68b0f662@c01e961d.com", ""], ["ac083f57", "0718005506", "6443f242@7ed34530.com", ""], ["0c7080de", "0791608592", "4444abf7@63b54852.com", ""], ["599039e0", "0717263596", "34898b46@b248d961.com", ""], ["33656705", "0788380435", "0d6c86a9@0d0b30a4.com", ""], ["f0d6f309", "0766220442", "22d01833@9bba21ab.com", ""], ["7c27f7a0", "0740179768", "69d0ce4f@829116df.com", ""], ["cba407c8", "0745165304", "4e94762c@6033decd.com", ""], ["3a9c0bcd", "0738794804", "ea1ca3ef@bd6258aa.com", ""], ["006b2bde", "0759645406", "822277ef@e36775c4.com", ""], ["fe832f26", "", "", ""], ["03c43ae1", "0776208246", "8e64a53e@d0aefbfb.com", ""], ["4ae6a913", "0798677379", "4affe084@f4ee6d7c.com", ""], ["5d3d3ca1", "0755000779", "0ea61d50@7ffd96e5.com", ""], ["dc58f75f", "0717795899", "119adc66@edbc9bac.com", ""], ["593f7e23", "", "d3161d49@fe2ad83f.com", ""], ["e5075ec4", "0760361665", "2f410a97@1b02d6a2.com", ""], ["a0d86ad4", "0765965949", "0fb55b69@cd1c2b77.com", ""], ["09e6e8a1", "0790479077", "cc542c67@9cb69e36.com", ""], ["77bb6d11", "0720961023", "60ff31ae@d9349e56.com", ""], ["e2f4aed5", "0735608325", "663b0400@671dff5d.com", ""], ["149211a9", "0706545912", "8481da49@233dfe8e.com", ""], ["59a53ba0", "", "3401405f@e1c370ad.com", ""], ["60a4c106", "0746099226", "7003f4bf@6a7770b2.com", ""], ["4483788d", "0728712830", "b30ee032@1694bb85.com", ""], ["ee8a3129", "0712964023", "34ee5c80@c82fb116.com", ""], ["c8bdde49", "", "3ac6c6dd@13bc3322.com", ""], ["65a477be", "0705359300", "a67e1d49@966dedb0.com", ""], ["d1a51add", "", "", ""], ["467392b8", "0765663197", "f878eeb4@7334e9fd.com", ""], ["61b4a1d6", "", "9d7e523e@e334c7d2.com", ""], ["16bbecf4", "0794827711", "39dff936@596fb073.com", ""], ["1cd25515", "0786915799", "d1c9de87@fb823959.com", ""], ["9d653b08", "0783707353", "75f1c3fa@dca1ac44.com", ""], ["cb93adc9", "0757349238", "7c3f098b@cf9bb4fe.com", ""], ["5f879d9e", "0764695115", "0a710345@b99b17e3.com", ""], ["8b840055", "", "", ""], ["d436dacc", "0796095598", "ea120899@3c8f6ea0.com", ""], ["1d0faf93", "0744424802", "3cf588a8@44544be8.com", ""], ["4ee8b8f7", "0764292501", "", ""]] \ No newline at end of file From 9d175bdfd60cda0335c0229876e46f72e7101249 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 27 Sep 2012 11:50:38 +0200 Subject: [PATCH 026/171] [FIX] postgres 9.2 added data at the end of 23502's error message, only use prefix matches not fullstring bzr revid: xmo@openerp.com-20120927095038-m2vm663nqj1s1pne --- openerp/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 81063deb180..641d21d6327 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -5376,7 +5376,7 @@ class ImportWarning(Warning): def convert_pgerror_23502(model, fields, info, e): m = re.match(r'^null value in column "(?P\w+)" violates ' - r'not-null constraint\n$', + r'not-null constraint\n', str(e)) if not m or m.group('field') not in fields: return {'message': unicode(e)} From f39730e918bc85c770dfa6edd52394351d54d006 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 27 Sep 2012 12:34:52 +0200 Subject: [PATCH 027/171] [FIX] make conversion errors for floats and integers more predictable Python turns out to have changed the wording of the messages between 2.6 and 2.7 also, these messages need to be translatable *and* use the human-readable field name bzr revid: xmo@openerp.com-20120927103452-cvgv3e8dsvcvnics --- openerp/addons/base/ir/ir_fields.py | 14 ++++++++++++-- openerp/tests/addons/test_impex/tests/test_load.py | 4 ++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 621dd93d469..ba6681bee15 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -100,10 +100,20 @@ class ir_fields_converter(orm.Model): return True def _str_to_integer(self, cr, uid, model, column, value, context=None): - return int(value) + try: + return int(value) + except ValueError: + raise ValueError( + _(u"'%s' does not seem to be an integer for field '%%(field)s'") + % value) def _str_to_float(self, cr, uid, model, column, value, context=None): - return float(value) + try: + return float(value) + except ValueError: + raise ValueError( + _(u"'%s' does not seem to be a number for field '%%(field)s'") + % value) def _str_to_char(self, cr, uid, model, column, value, context=None): return value diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 72442963af5..032dd65ef07 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -275,7 +275,7 @@ class test_integer_field(ImporterCase): 'rows': {'from': 0, 'to': 0}, 'record': 0, 'field': 'value', - 'message': u"invalid literal for int() with base 10: 'zorglub'", + 'message': u"'zorglub' does not seem to be an integer for field 'unknown'", }]) class test_float_field(ImporterCase): @@ -344,7 +344,7 @@ class test_float_field(ImporterCase): 'rows': {'from': 0, 'to': 0}, 'record': 0, 'field': 'value', - 'message': u"invalid literal for float(): foobar", + 'message': u"'foobar' does not seem to be a number for field 'unknown'", }]) class test_string_field(ImporterCase): From 87af7a63654b279d79a8b4eb3c5fb675ef7a8708 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 27 Sep 2012 16:43:41 +0200 Subject: [PATCH 028/171] [FIX] replace warnings capture by explicitly returning a list of warnings from converters makes converter code noisier, but ultimately simpler without having access to dynamically scoped variables bzr revid: xmo@openerp.com-20120927144341-j1dx3e0epf3i5ja6 --- openerp/addons/base/ir/ir_fields.py | 82 ++++++++++--------- openerp/osv/orm.py | 10 +-- .../addons/test_impex/tests/test_load.py | 2 +- 3 files changed, 47 insertions(+), 47 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index ba6681bee15..7a4c34c7f30 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -2,7 +2,6 @@ import functools import operator import itertools -import warnings from openerp.osv import orm, fields from openerp.tools.translate import _ @@ -38,9 +37,9 @@ class ir_fields_converter(orm.Model): By default, tries to get a method on itself with a name matching the pattern ``_$fromtype_$column._type`` and returns it. - Converter callables can either return a value to their caller or raise - ``ValueError``, which will be interpreted as a validation & conversion - failure. + Converter callables can either return a value and a list of warnings + to their caller or raise ``ValueError``, which will be interpreted as a + validation & conversion failure. ValueError can have either one or two parameters. The first parameter is mandatory, **must** be a unicode string and will be used as the @@ -53,10 +52,10 @@ class ir_fields_converter(orm.Model): client. If a converter can perform its function but has to make assumptions - about the data, it can send a warning to the user through signalling - an :class:`~openerp.osv.orm.ImportWarning` (via ``warnings.warn``). The - handling of a warning at the upper levels is the same as - ``ValueError`` above. + about the data, it can send a warning to the user through adding an + instance of :class:`~openerp.osv.orm.ImportWarning` to the second value + it returns. The handling of a warning at the upper levels is the same + as ``ValueError`` above. :param cr: openerp cursor :param uid: ID of user calling the converter @@ -84,7 +83,7 @@ class ir_fields_converter(orm.Model): self._get_translations(cr, uid, ['code'], u"true", context=context), self._get_translations(cr, uid, ['code'], u"yes", context=context), )) - if value.lower() in trues: return True + if value.lower() in trues: return True, [] # potentially broken casefolding? What about locales? falses = set(word.lower() for word in itertools.chain( @@ -92,16 +91,15 @@ class ir_fields_converter(orm.Model): self._get_translations(cr, uid, ['code'], u"false", context=context), self._get_translations(cr, uid, ['code'], u"no", context=context), )) - if value.lower() in falses: return False + if value.lower() in falses: return False, [] - warnings.warn(orm.ImportWarning( + return True, [orm.ImportWarning( _(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'") - % (value, yes))) - return True + % (value, yes))] def _str_to_integer(self, cr, uid, model, column, value, context=None): try: - return int(value) + return int(value), [] except ValueError: raise ValueError( _(u"'%s' does not seem to be an integer for field '%%(field)s'") @@ -109,20 +107,20 @@ class ir_fields_converter(orm.Model): def _str_to_float(self, cr, uid, model, column, value, context=None): try: - return float(value) + return float(value), [] except ValueError: raise ValueError( _(u"'%s' does not seem to be a number for field '%%(field)s'") % value) def _str_to_char(self, cr, uid, model, column, value, context=None): - return value + return value, [] def _str_to_text(self, cr, uid, model, column, value, context=None): - return value + return value, [] def _str_to_binary(self, cr, uid, model, column, value, context=None): - return value + return value, [] def _get_translations(self, cr, uid, types, src, context): types = tuple(types) @@ -151,7 +149,7 @@ class ir_fields_converter(orm.Model): cr, uid, ('selection', 'model', 'code'), label, context=context) labels.append(label) if value == unicode(item) or value in labels: - return item + return item, [] raise ValueError( _(u"Value '%s' not found in selection field '%%(field)s'") % ( value), { @@ -173,11 +171,14 @@ class ir_fields_converter(orm.Model): id :param value: value of the reference to match to an actual record :param context: OpenERP request context - :return: a pair of the matched database identifier (if any) and the - translated user-readable name for the field - :rtype: (ID|None, unicode) + :return: a pair of the matched database identifier (if any), the + translated user-readable name for the field and the list of + warnings + :rtype: (ID|None, unicode, list) """ + if context is None: context = {} id = None + warnings = [] RelatedModel = self.pool[column._obj] if subfield == '.id': field_type = _(u"database id") @@ -191,7 +192,7 @@ class ir_fields_converter(orm.Model): if '.' in value: module, xid = value.split('.', 1) else: - module, xid = '', value + module, xid = context.get('_import_current_module', ''), value ModelData = self.pool['ir.model.data'] try: md_id = ModelData._get_id(cr, uid, module, xid) @@ -206,7 +207,7 @@ class ir_fields_converter(orm.Model): cr, uid, name=value, operator='=', context=context) if ids: if len(ids) > 1: - warnings.warn(orm.ImportWarning( + warnings.append(orm.ImportWarning( _(u"Found multiple matches for field '%%(field)s' (%d matches)") % (len(ids)))) id, _name = ids[0] @@ -227,7 +228,7 @@ class ir_fields_converter(orm.Model): 'help': _(u"See all possible values") } }) - return id, field_type + return id, field_type, warnings def _referencing_subfield(self, record): """ Checks the record for the subfields allowing referencing (an @@ -236,8 +237,8 @@ class ir_fields_converter(orm.Model): returns the name of the correct subfield. :param record: - :return: the record subfield to use for referencing - :rtype: str + :return: the record subfield to use for referencing and a list of warnings + :rtype: str, list """ # Can import by name_get, external id or database id fieldset = set(record.iterkeys()) @@ -250,39 +251,42 @@ class ir_fields_converter(orm.Model): # only one field left possible, unpack [subfield] = fieldset - return subfield + return subfield, [] def _str_to_many2one(self, cr, uid, model, column, values, context=None): # Should only be one record, unpack [record] = values - subfield = self._referencing_subfield(record) + subfield, w1 = self._referencing_subfield(record) reference = record[subfield] - id, subfield_type = self.db_id_for( + id, subfield_type, w2 = self.db_id_for( cr, uid, model, column, subfield, reference, context=context) - return id + return id, w1 + w2 def _str_to_many2many(self, cr, uid, model, column, value, context=None): [record] = value - subfield = self._referencing_subfield(record) + subfield, warnings = self._referencing_subfield(record) ids = [] for reference in record[subfield].split(','): - id, subfield_type = self.db_id_for( + id, subfield_type, ws = self.db_id_for( cr, uid, model, column, subfield, reference, context=context) ids.append(id) - return [(6, 0, ids)] + warnings.extend(ws) + return [(6, 0, ids)], warnings def _str_to_one2many(self, cr, uid, model, column, records, context=None): commands = [] + warnings = [] if len(records) == 1 and exclude_ref_fields(records[0]) == {}: # only one row with only ref field, field=ref1,ref2,ref3 as in # m2o/m2m record = records[0] - subfield = self._referencing_subfield(record) + subfield, ws = self._referencing_subfield(record) + warnings.extend(ws) # transform [{subfield:ref1,ref2,ref3}] into # [{subfield:ref1},{subfield:ref2},{subfield:ref3}] records = ({subfield:item} for item in record[subfield].split(',')) @@ -292,10 +296,12 @@ class ir_fields_converter(orm.Model): refs = only_ref_fields(record) # there are ref fields in the record if refs: - subfield = self._referencing_subfield(refs) + subfield, w1 = self._referencing_subfield(refs) + warnings.extend(w1) reference = record[subfield] - id, subfield_type = self.db_id_for( + id, subfield_type, w2 = self.db_id_for( cr, uid, model, column, subfield, reference, context=context) + warnings.extend(w2) writable = exclude_ref_fields(record) if id: @@ -304,4 +310,4 @@ class ir_fields_converter(orm.Model): else: commands.append(CREATE(writable)) - return commands + return commands, warnings diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 641d21d6327..9bd04dd56a8 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1670,15 +1670,9 @@ class BaseModel(object): message_base = dict( extras, record=stream.index, field=field_names[field]) try: - with warnings.catch_warnings(record=True) as ws: - converted[field] = converters[field](strvalue) + converted[field], ws = converters[field](strvalue) - for warning in ws: - # bubble non-import warnings upward - if warning.category != ImportWarning: - warnings.warn(warning.message, warning.category) - continue - w = warning.message + for w in ws: if isinstance(w, basestring): # wrap warning string in an ImportWarning for # uniform handling diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 032dd65ef07..a5c3dcc5bbf 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -186,7 +186,7 @@ class test_boolean_field(ImporterCase): message(u"Unknown value '%s' for boolean field 'unknown', assuming 'yes'" % v[0], type='warning', from_=i, to_=i, record=i) for i, v in enumerate(trues) - if v[0] != 'true' if v[0] != 'yes' if v[0] != '1' + if v[0] not in ('true', 'yes', '1') ]) self.assertEqual( [True] * 10, From def7e61901dafc41cb0b404b1e74275b00deaa1b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 27 Sep 2012 17:42:44 +0200 Subject: [PATCH 029/171] [IMP] reimplement BaseModel.import_data on top of BaseModel.load adapt tests to assert the corrected behavior of load instead of import_data's broken behavior bzr revid: xmo@openerp.com-20120927154244-e56ygz2yytullg2l --- openerp/osv/orm.py | 196 +++--------------- .../addons/test_impex/tests/test_import.py | 175 +++++++--------- .../addons/test_impex/tests/test_load.py | 4 +- 3 files changed, 98 insertions(+), 277 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 9bd04dd56a8..934b6eaf2e4 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1254,189 +1254,43 @@ class BaseModel(object): :returns: 4-tuple in the form (return_code, errored_resource, error_message, unused) :rtype: (int, dict or 0, str or 0, str or 0) """ - if not context: - context = {} + context = dict(context) if context is not None else {} + context['_import_current_module'] = current_module + fields = map(fix_import_export_id_paths, fields) ir_model_data_obj = self.pool.get('ir.model.data') - # mode: id (XML id) or .id (database id) or False for name_get - def _get_id(model_name, id, current_module=False, mode='id'): - if mode=='.id': - id = int(id) - obj_model = self.pool.get(model_name) - ids = obj_model.search(cr, uid, [('id', '=', int(id))]) - if not len(ids): - raise Exception(_("Database ID doesn't exist: %s : %s") %(model_name, id)) - elif mode=='id': - if '.' in id: - module, xml_id = id.rsplit('.', 1) - else: - module, xml_id = current_module, id - record_id = ir_model_data_obj._get_id(cr, uid, module, xml_id) - ir_model_data = ir_model_data_obj.read(cr, uid, [record_id], ['res_id']) - if not ir_model_data: - raise ValueError('No references to %s.%s' % (module, xml_id)) - id = ir_model_data[0]['res_id'] - else: - obj_model = self.pool.get(model_name) - ids = obj_model.name_search(cr, uid, id, operator='=', context=context) - if not ids: - raise ValueError('No record found for %s' % (id,)) - id = ids[0][0] - return id + def log(m): + if m['type'] == 'error': + raise Exception(m['message']) - # IN: - # datas: a list of records, each record is defined by a list of values - # prefix: a list of prefix fields ['line_ids'] - # position: the line to process, skip is False if it's the first line of the current record - # OUT: - # (res, position, warning, res_id) with - # res: the record for the next line to process (including it's one2many) - # position: the new position for the next line - # res_id: the ID of the record if it's a modification - def process_liness(self, datas, prefix, current_module, model_name, fields_def, position=0, skip=0): - line = datas[position] - row = {} - warning = [] - data_res_id = False - xml_id = False - nbrmax = position+1 - - done = {} - for i, field in enumerate(fields): - res = False - if i >= len(line): - raise Exception(_('Please check that all your lines have %d columns.' - 'Stopped around line %d having %d columns.') % \ - (len(fields), position+2, len(line))) - if not line[i]: - continue - - if field[:len(prefix)] <> prefix: - if line[i] and skip: - return False - continue - field_name = field[len(prefix)] - - #set the mode for m2o, o2m, m2m : xml_id/id/name - if len(field) == len(prefix)+1: - mode = False - else: - mode = field[len(prefix)+1] - - # TODO: improve this by using csv.csv_reader - def many_ids(line, relation, current_module, mode): - res = [] - for db_id in line.split(config.get('csv_internal_sep')): - res.append(_get_id(relation, db_id, current_module, mode)) - return [(6,0,res)] - - # ID of the record using a XML ID - if field_name == 'id': - try: - data_res_id = _get_id(model_name, line[i], current_module) - except ValueError: - pass - xml_id = line[i] - continue - - # ID of the record using a database ID - elif field_name == '.id': - data_res_id = _get_id(model_name, line[i], current_module, '.id') - continue - - field_type = fields_def[field_name]['type'] - # recursive call for getting children and returning [(0,0,{})] or [(1,ID,{})] - if field_type == 'one2many': - if field_name in done: - continue - done[field_name] = True - relation = fields_def[field_name]['relation'] - relation_obj = self.pool.get(relation) - newfd = relation_obj.fields_get( cr, uid, context=context ) - pos = position - - res = [] - - first = 0 - while pos < len(datas): - res2 = process_liness(self, datas, prefix + [field_name], current_module, relation_obj._name, newfd, pos, first) - if not res2: - break - (newrow, pos, w2, data_res_id2, xml_id2) = res2 - nbrmax = max(nbrmax, pos) - warning += w2 - first += 1 - - if (not newrow) or not reduce(lambda x, y: x or y, newrow.values(), 0): - break - - res.append( (data_res_id2 and 1 or 0, data_res_id2 or 0, newrow) ) - - elif field_type == 'many2one': - relation = fields_def[field_name]['relation'] - res = _get_id(relation, line[i], current_module, mode) - - elif field_type == 'many2many': - relation = fields_def[field_name]['relation'] - res = many_ids(line[i], relation, current_module, mode) - - elif field_type == 'integer': - res = line[i] and int(line[i]) or 0 - elif field_type == 'boolean': - res = line[i].lower() not in ('0', 'false', 'off') - elif field_type == 'float': - res = line[i] and float(line[i]) or 0.0 - elif field_type == 'selection': - for key, val in fields_def[field_name]['selection']: - if tools.ustr(line[i]) in [tools.ustr(key), tools.ustr(val)]: - res = key - break - if line[i] and not res: - _logger.warning( - _("key '%s' not found in selection field '%s'"), - tools.ustr(line[i]), tools.ustr(field_name)) - warning.append(_("Key/value '%s' not found in selection field '%s'") % ( - tools.ustr(line[i]), tools.ustr(field_name))) - - else: - res = line[i] - - row[field_name] = res or False - - return row, nbrmax, warning, data_res_id, xml_id - - fields_def = self.fields_get(cr, uid, context=context) - - position = 0 if config.get('import_partial') and filename: with open(config.get('import_partial'), 'rb') as partial_import_file: data = pickle.load(partial_import_file) position = data.get(filename, 0) - while position o2m resolution skipped altogether, - # creates 2 records => remove/don't import columns sideshow columns, - # get completely different semantics - b, b1 = self.browse() + [b] = self.browse() self.assertEqual(b.const, 42) - self.assertEqual(values(b.value), []) - self.assertEqual(b1.const, 4) - self.assertEqual(values(b1.value), []) + # automatically forces link between core record and o2ms + self.assertEqual(values(b.value), [109, 262]) + self.assertEqual(values(b.value, field='parent_id'), [b, b]) def test_link_2(self): O2M_c = self.registry('export.one2many.child') @@ -838,21 +822,10 @@ class test_o2m(ImporterCase): ]), ok(2)) - (b,) = self.browse() - # if an id (db or xid) is provided, expectations that objects are - # *already* linked and emits UPDATE (1, id, {}). - # Noid => CREATE (0, ?, {}) - # TODO: xid ignored aside from getting corresponding db id? + [b] = self.browse() self.assertEqual(b.const, 42) - self.assertEqual(values(b.value), []) - - # FIXME: updates somebody else's records? - self.assertEqual( - O2M_c.read(self.cr, openerp.SUPERUSER_ID, id1), - {'id': id1, 'str': 'Bf', 'value': 1, 'parent_id': False}) - self.assertEqual( - O2M_c.read(self.cr, openerp.SUPERUSER_ID, id2), - {'id': id2, 'str': 'Me', 'value': 2, 'parent_id': False}) + self.assertEqual(values(b.value), [1, 2]) + self.assertEqual(values(b.value, field='parent_id'), [b, b]) class test_o2m_multiple(ImporterCase): model_name = 'export.one2many.multiple' @@ -866,16 +839,10 @@ class test_o2m_multiple(ImporterCase): ['', '14', ''], ]), ok(4)) - # Oh yeah, that's the stuff - (b, b1, b2) = self.browse() - self.assertEqual(values(b.child1), [11]) - self.assertEqual(values(b.child2), [21]) - self.assertEqual(values(b1.child1), [12]) - self.assertEqual(values(b1.child2), [22]) - - self.assertEqual(values(b2.child1), [13, 14]) - self.assertEqual(values(b2.child2), [23]) + [b] = self.browse() + self.assertEqual(values(b.child1), [11, 12, 13, 14]) + self.assertEqual(values(b.child2), [21, 22, 23]) def test_multi(self): self.assertEqual( @@ -888,11 +855,10 @@ class test_o2m_multiple(ImporterCase): ['', '', '23'], ]), ok(6)) - # What the actual fuck? - (b, b1) = self.browse() + + [b] = self.browse() self.assertEqual(values(b.child1), [11, 12, 13, 14]) - self.assertEqual(values(b.child2), [21]) - self.assertEqual(values(b1.child2), [22, 23]) + self.assertEqual(values(b.child2), [21, 22, 23]) def test_multi_fullsplit(self): self.assertEqual( @@ -906,12 +872,11 @@ class test_o2m_multiple(ImporterCase): ['', '', '23'], ]), ok(7)) - # oh wow - (b, b1) = self.browse() + + [b] = self.browse() self.assertEqual(b.const, 5) self.assertEqual(values(b.child1), [11, 12, 13, 14]) - self.assertEqual(b1.const, 36) - self.assertEqual(values(b1.child2), [21, 22, 23]) + self.assertEqual(values(b.child2), [21, 22, 23]) # function, related, reference: written to db as-is... # => function uses @type for value coercion/conversion diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index a5c3dcc5bbf..c141e62b65d 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -394,12 +394,14 @@ class test_unbound_string_field(ImporterCase): class test_required_string_field(ImporterCase): model_name = 'export.string.required' + @mute_logger('openerp.sql_db') def test_empty(self): result = self.import_(['value'], [[]]) self.assertEqual(result['messages'], [message( u"Missing required value for the field 'unknown'")]) self.assertIs(result['ids'], False) + @mute_logger('openerp.sql_db') def test_not_provided(self): result = self.import_(['const'], [['12']]) self.assertEqual(result['messages'], [message( @@ -920,7 +922,7 @@ class test_o2m_multiple(ImporterCase): ]) self.assertFalse(result['messages']) self.assertEqual(len(result['ids']), 1) - # Oh yeah, that's the stuff + [b] = self.browse() self.assertEqual(values(b.child1), [11, 12, 13, 14]) self.assertEqual(values(b.child2), [21, 22, 23]) From 98e4725ce07b6b76a7a5c15fbeaf6ae2df8014d0 Mon Sep 17 00:00:00 2001 From: Vishmita Date: Fri, 28 Sep 2012 12:07:48 +0530 Subject: [PATCH 030/171] [Fix]Pass event on click of download button. bzr revid: vja@tinyerp.com-20120928063748-cnsecd8mmevb2fbu --- addons/web/static/src/js/view_form.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index c888c13e6be..98a558661b5 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -4692,9 +4692,9 @@ instance.web.form.FieldBinaryFile = instance.web.form.FieldBinary.extend({ this._super(); if (this.get("effective_readonly")) { var self = this; - this.$el.find('a').click(function() { + this.$el.find('a').click(function(ev) { if (self.get('value')) { - self.on_save_as(); + self.on_save_as(ev); } return false; }); From aaff4459c438bdbb4b2d5f62bbd95a155904136c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 28 Sep 2012 08:56:54 +0200 Subject: [PATCH 031/171] [DOC] fixes and cleanups bzr revid: xmo@openerp.com-20120928065654-jj3ot17dz8vd3wum --- .bzrignore | 31 ++++++---- doc/api/user_img_specs.rst | 25 ++++++-- doc/conf.py | 24 +++---- openerp/osv/expression.py | 124 ++++++++++++++++++++----------------- openerp/osv/orm.py | 12 +++- openerp/osv/query.py | 14 ++--- 6 files changed, 135 insertions(+), 95 deletions(-) diff --git a/.bzrignore b/.bzrignore index ba5258506be..8531ad42bbb 100644 --- a/.bzrignore +++ b/.bzrignore @@ -1,14 +1,19 @@ -.* -*.egg-info -*.orig -*.vim +.*.swp +.bzrignore +.idea +.project +.pydevproject +.ropeproject +.settings +.DS_Store +openerp/addons/* +openerp/filestore* +.Python +*.pyc +*.pyo +bin/* build/ -RE:^bin/ -RE:^dist/ -RE:^include/ - -RE:^share/ -RE:^man/ -RE:^lib/ - -RE:^doc/_build/ +include/ +lib/ +share/ +doc/_build/* diff --git a/doc/api/user_img_specs.rst b/doc/api/user_img_specs.rst index a915529accd..c6729eed920 100644 --- a/doc/api/user_img_specs.rst +++ b/doc/api/user_img_specs.rst @@ -3,9 +3,24 @@ User avatar .. versionadded:: 7.0 -This revision adds an avatar for users. This replaces the use of gravatar to emulate avatars, used in views like the tasks kanban view. Two fields have been added to the res.users model: - - avatar_big, a binary field holding the image. It is base-64 encoded, and PIL-supported. Images stored are resized to 540x450 px, to limitate the binary field size. - - avatar, a function binary field holding an automatically resized version of the avatar_big field. It is also base-64 encoded, and PIL-supported. Dimensions of the resized avatar are 180x150. This field is used as an inteface to get and set the user avatar. -When changing the avatar through the avatar function field, the new image is automatically resized to 540x450, and stored in the avatar_big field. This triggers the function field, that will compute a 180x150 resized version of the image. +This revision adds an avatar for users. This replaces the use of +gravatar to emulate avatars, used in views like the tasks kanban +view. Two fields have been added to the res.users model: -An avatar field has been added to the users form view, as well as in Preferences. When creating a new user, a default avatar is chosen among 6 possible default images. +* ``avatar_big``, a binary field holding the image. It is base-64 + encoded, and PIL-supported. Images stored are resized to 540x450 px, + to limitate the binary field size. + +* ``avatar``, a function binary field holding an automatically resized + version of the avatar_big field. It is also base-64 encoded, and + PIL-supported. Dimensions of the resized avatar are 180x150. This + field is used as an inteface to get and set the user avatar. + +When changing the avatar through the avatar function field, the new +image is automatically resized to 540x450, and stored in the +avatar_big field. This triggers the function field, that will compute +a 180x150 resized version of the image. + +An avatar field has been added to the users form view, as well as in +Preferences. When creating a new user, a default avatar is chosen +among 6 possible default images. diff --git a/doc/conf.py b/doc/conf.py index a5ff6bed7e1..ac5b8907e0e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -16,9 +16,10 @@ import sys, os # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) sys.path.append(os.path.abspath('_themes')) -sys.path.insert(0, os.path.abspath('../addons')) -sys.path.insert(0, os.path.abspath('..')) +sys.path.append(os.path.abspath('..')) +sys.path.append(os.path.abspath('../openerp')) # -- General configuration ----------------------------------------------------- @@ -42,7 +43,7 @@ source_suffix = '.rst' master_doc = 'index' # General information about the project. -project = u'OpenERP Web Developers Documentation' +project = u'OpenERP Server Developers Documentation' copyright = u'2012, OpenERP s.a.' # The version info for the project you're documenting, acts as replacement for @@ -50,9 +51,9 @@ copyright = u'2012, OpenERP s.a.' # built documents. # # The short X.Y version. -version = '6.1' +version = '7.0' # The full version, including alpha/beta/rc tags. -release = '6.1' +release = '7.0b' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -170,7 +171,7 @@ html_sidebars = { #html_file_suffix = None # Output file base name for HTML help builder. -htmlhelp_basename = 'openerp-web-doc' +htmlhelp_basename = 'openerp-server-doc' # -- Options for LaTeX output -------------------------------------------------- @@ -189,7 +190,7 @@ latex_elements = { # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'openerp-web-doc.tex', u'OpenERP Web Developers Documentation', + ('index', 'openerp-server-doc.tex', u'OpenERP Server Developers Documentation', u'OpenERP s.a.', 'manual'), ] @@ -219,7 +220,7 @@ latex_documents = [ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'openerp-web-doc', u'OpenERP Web Developers Documentation', + ('index', 'openerp-server-doc', u'OpenERP Server Developers Documentation', [u'OpenERP s.a.'], 1) ] @@ -233,8 +234,8 @@ man_pages = [ # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ - ('index', 'OpenERPWebDocumentation', u'OpenERP Web Developers Documentation', - u'OpenERP s.a.', 'OpenERPWebDocumentation', 'Developers documentation for the openerp-web project.', + ('index', 'OpenERPServerDocumentation', u'OpenERP Server Developers Documentation', + u'OpenERP s.a.', 'OpenERPServerDocumentation', 'Developers documentation for the openobject-server project.', 'Miscellaneous'), ] @@ -247,11 +248,10 @@ texinfo_documents = [ # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' -todo_include_todos = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('http://docs.python.org/', None), - 'openerpserver': ('http://doc.openerp.com/trunk/developers/server', None), + 'openerpweb': ('http://doc.openerp.com/trunk/developers/web', None), 'openerpdev': ('http://doc.openerp.com/trunk/developers', None), } diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index e2a3391c8a4..5f2c0b915c1 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -22,102 +22,114 @@ """ Domain expression processing -The main duty of this module is to compile a domain expression into a SQL -query. A lot of things should be documented here, but as a first step in the -right direction, some tests in test_osv_expression.yml might give you some -additional information. +The main duty of this module is to compile a domain expression into a +SQL query. A lot of things should be documented here, but as a first +step in the right direction, some tests in test_osv_expression.yml +might give you some additional information. -For legacy reasons, a domain uses an inconsistent two-levels abstract syntax -(domains are regular Python data structures). At the first level, a domain -is an expression made of terms (sometimes called leaves) and (domain) operators -used in prefix notation. The available operators at this level are '!', '&', -and '|'. '!' is a unary 'not', '&' is a binary 'and', and '|' is a binary 'or'. -For instance, here is a possible domain. ( stands for an arbitrary term, -more on this later.) +For legacy reasons, a domain uses an inconsistent two-levels abstract +syntax (domains are regular Python data structures). At the first +level, a domain is an expression made of terms (sometimes called +leaves) and (domain) operators used in prefix notation. The available +operators at this level are '!', '&', and '|'. '!' is a unary 'not', +'&' is a binary 'and', and '|' is a binary 'or'. For instance, here +is a possible domain. ( stands for an arbitrary term, more on +this later.):: ['&', '!', , '|', , ] -It is equivalent to this pseudo code using infix notation: +It is equivalent to this pseudo code using infix notation:: (not ) and ( or ) -The second level of syntax deals with the term representation. A term is -a triple of the form (left, operator, right). That is, a term uses an infix -notation, and the available operators, and possible left and right operands -differ with those of the previous level. Here is a possible term: +The second level of syntax deals with the term representation. A term +is a triple of the form (left, operator, right). That is, a term uses +an infix notation, and the available operators, and possible left and +right operands differ with those of the previous level. Here is a +possible term:: ('company_id.name', '=', 'OpenERP') -The left and right operand don't have the same possible values. The left -operand is field name (related to the model for which the domain applies). -Actually, the field name can use the dot-notation to traverse relationships. -The right operand is a Python value whose type should match the used operator -and field type. In the above example, a string is used because the name field -of a company has type string, and because we use the '=' operator. When -appropriate, a 'in' operator can be used, and thus the right operand should be -a list. +The left and right operand don't have the same possible values. The +left operand is field name (related to the model for which the domain +applies). Actually, the field name can use the dot-notation to +traverse relationships. The right operand is a Python value whose +type should match the used operator and field type. In the above +example, a string is used because the name field of a company has type +string, and because we use the '=' operator. When appropriate, a 'in' +operator can be used, and thus the right operand should be a list. -Note: the non-uniform syntax could have been more uniform, but this would hide -an important limitation of the domain syntax. Say that the term representation -was ['=', 'company_id.name', 'OpenERP']. Used in a complete domain, this would -look like: +Note: the non-uniform syntax could have been more uniform, but this +would hide an important limitation of the domain syntax. Say that the +term representation was ['=', 'company_id.name', 'OpenERP']. Used in a +complete domain, this would look like:: - ['!', ['=', 'company_id.name', 'OpenERP']] + ['!', ['=', 'company_id.name', 'OpenERP']] -and you would be tempted to believe something like this would be possible: +and you would be tempted to believe something like this would be +possible:: - ['!', ['=', 'company_id.name', ['&', ..., ...]]] + ['!', ['=', 'company_id.name', ['&', ..., ...]]] -That is, a domain could be a valid operand. But this is not the case. A domain -is really limited to a two-level nature, and can not take a recursive form: a -domain is not a valid second-level operand. +That is, a domain could be a valid operand. But this is not the +case. A domain is really limited to a two-level nature, and can not +take a recursive form: a domain is not a valid second-level operand. Unaccent - Accent-insensitive search -OpenERP will use the SQL function 'unaccent' when available for the 'ilike' and -'not ilike' operators, and enabled in the configuration. -Normally the 'unaccent' function is obtained from the PostgreSQL 'unaccent' -contrib module[0]. +OpenERP will use the SQL function 'unaccent' when available for the +'ilike' and 'not ilike' operators, and enabled in the configuration. +Normally the 'unaccent' function is obtained from `the PostgreSQL +'unaccent' contrib module +`_. +.. todo: The following explanation should be moved in some external + installation guide -..todo: The following explanation should be moved in some external installation - guide +The steps to install the module might differ on specific PostgreSQL +versions. We give here some instruction for PostgreSQL 9.x on a +Ubuntu system. -The steps to install the module might differ on specific PostgreSQL versions. -We give here some instruction for PostgreSQL 9.x on a Ubuntu system. +Ubuntu doesn't come yet with PostgreSQL 9.x, so an alternative package +source is used. We use Martin Pitt's PPA available at +`ppa:pitti/postgresql +`_. -Ubuntu doesn't come yet with PostgreSQL 9.x, so an alternative package source -is used. We use Martin Pitt's PPA available at ppa:pitti/postgresql[1]. See -[2] for instructions. Basically: +.. code-block:: sh > sudo add-apt-repository ppa:pitti/postgresql > sudo apt-get update -Once the package list is up-to-date, you have to install PostgreSQL 9.0 and -its contrib modules. +Once the package list is up-to-date, you have to install PostgreSQL +9.0 and its contrib modules. + +.. code-block:: sh > sudo apt-get install postgresql-9.0 postgresql-contrib-9.0 When you want to enable unaccent on some database: +.. code-block:: sh + > psql9 -f /usr/share/postgresql/9.0/contrib/unaccent.sql -Here 'psql9' is an alias for the newly installed PostgreSQL 9.0 tool, together -with the correct port if necessary (for instance if PostgreSQL 8.4 is running -on 5432). (Other aliases can be used for createdb and dropdb.) +Here :program:`psql9` is an alias for the newly installed PostgreSQL +9.0 tool, together with the correct port if necessary (for instance if +PostgreSQL 8.4 is running on 5432). (Other aliases can be used for +createdb and dropdb.) + +.. code-block:: sh > alias psql9='/usr/lib/postgresql/9.0/bin/psql -p 5433' You can check unaccent is working: +.. code-block:: sh + > psql9 -c"select unaccent('hélène')" Finally, to instruct OpenERP to really use the unaccent function, you have to -start the server specifying the --unaccent flag. - -[0] http://developer.postgresql.org/pgdocs/postgres/unaccent.html -[1] https://launchpad.net/~pitti/+archive/postgresql -[2] https://launchpad.net/+help/soyuz/ppa-sources-list.html +start the server specifying the ``--unaccent`` flag. """ @@ -232,7 +244,7 @@ def is_leaf(element, internal=False): """ Test whether an object is a valid domain term. :param internal: allow or not the 'inselect' internal operator in the term. - This normally should be always left to False. + This normally should be always left to False. """ INTERNAL_OPS = TERM_OPERATORS + ('inselect',) return (isinstance(element, tuple) or isinstance(element, list)) \ diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 934b6eaf2e4..afabda817f4 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1218,7 +1218,11 @@ class BaseModel(object): return {'datas': datas} def import_data(self, cr, uid, fields, datas, mode='init', current_module='', noupdate=False, context=None, filename=None): - """Import given data in given module + """ + .. deprecated:: 7.0 + Use :meth:`~load` instead + + Import given data in given module This method is used when importing data via client menu. @@ -1302,6 +1306,10 @@ class BaseModel(object): ``False`` if there was an error and no id could be generated) and a list of messages. + The ids are those of the records created and saved (in database), in + the same order they were extracted from the file. They can be passed + directly to :meth:`~read` + Each message is a dictionary with the following keys: ``type`` @@ -5065,7 +5073,7 @@ class BaseModel(object): def is_transient(self): """ Return whether the model is transient. - See TransientModel. + See :class:`TransientModel`. """ return self._transient diff --git a/openerp/osv/query.py b/openerp/osv/query.py index 0b2bf47305a..252b301d531 100644 --- a/openerp/osv/query.py +++ b/openerp/osv/query.py @@ -73,15 +73,15 @@ class Query(object): :param connection: a tuple ``(lhs, table, lhs_col, col)``. The join corresponds to the SQL equivalent of:: - ``(lhs.lhs_col = table.col)`` + (lhs.lhs_col = table.col) :param outer: True if a LEFT OUTER JOIN should be used, if possible (no promotion to OUTER JOIN is supported in case the JOIN - was already present in the query, as for the moment - implicit INNER JOINs are only connected from NON-NULL - columns so it would not be correct (e.g. for - ``_inherits`` or when a domain criterion explicitly - adds filtering) + was already present in the query, as for the moment + implicit INNER JOINs are only connected from NON-NULL + columns so it would not be correct (e.g. for + ``_inherits`` or when a domain criterion explicitly + adds filtering) """ (lhs, table, lhs_col, col) = connection lhs = _quote(lhs) @@ -120,4 +120,4 @@ class Query(object): def __str__(self): return '' % self.get_sql() -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: \ No newline at end of file +# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From 879886decd0a64ac2e8d62bb9a6e409aa7f8abe8 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 28 Sep 2012 11:15:19 +0200 Subject: [PATCH 032/171] [ADD] some import doc bzr revid: xmo@openerp.com-20120928091519-ksdprqdq0tpklggw --- doc/import.rst | 231 +++++++++++++++++++++++++++++++++++++++++++++ doc/index.rst.inc | 1 + doc/o2m.txt | 13 +++ openerp/osv/orm.py | 31 ------ 4 files changed, 245 insertions(+), 31 deletions(-) create mode 100644 doc/import.rst create mode 100644 doc/o2m.txt diff --git a/doc/import.rst b/doc/import.rst new file mode 100644 index 00000000000..f3db5d50bcf --- /dev/null +++ b/doc/import.rst @@ -0,0 +1,231 @@ +.. _bulk-import: + +Bulk Import +=========== + +OpenERP has included a bulk import facility for CSV-ish files for a +long time. With 7.0, both the interface and internal implementation +have been redone, resulting in +:meth:`~openerp.osv.orm.BaseModel.load`. + +.. note:: + + the previous bulk-loading method, + :meth:`~openerp.osv.orm.BaseModel.import_data`, remains for + backwards compatibility but was re-implemented on top of + :meth:`~openerp.osv.orm.BaseModel.load`, while its interface is + unchanged its precise behavior has likely been altered for some + cases (it shouldn't throw exceptions anymore in many cases where + it previously did) + +This document attempts to explain the behavior and limitations of +:meth:`~openerp.osv.orm.BaseModel.load`. + +Data +==== + +The input ``data`` is a regular row-major matrix of strings (in Python +datatype terms, a ``list`` of rows, each row being a ``list`` of +``str``, all rows must be of equal length). Each row must be the same +length as the ``fields`` list preceding it in the argslist. + +Each field of ``fields`` maps to a (potentially relational and nested) +field of the model under import, and the corresponding column of the +``data`` matrix provides a value for the field for each record. + +Generally speaking each row of the input yields a record of output, +and each cell of a row yields a value for the corresponding field of +the row's record. There is currently one exception for this rule: + +One to Many fields +------------------ + +Because O2M fields contain multiple records "embedded" in the main +one, and these sub-records are fully dependent on the main record (are +no other references to the sub-records in the system), they have to be +spliced into the matrix somehow. This is done by adding lines composed +*only* of o2m record fields below the main record: + +.. literalinclude:: o2m.txt + +the sections in double-lines represent the span of two o2m +fields. During parsing, they are extracted into their own ``data`` +matrix for the o2m field they correspond to. + +Import process +============== + +Here are the phases of import. Note that the concept of "phases" is +fuzzy as it's currently more of a pipeline, each record moves through +the entire pipeline before the next one is processed. + +Extraction +---------- + +The first phase of the import is the extraction of the current row +(and potentially a section of rows following it if it has One to Many +fields) into a record dictionary. The keys are the ``fields`` +originally passed to :meth:`~openerp.osv.orm.BaseModel.load`, and the +values are either the string value at the corresponding cell (for +non-relational fields) or a list of sub-records (for all relational +fields). + +This phase also generates the ``rows`` indexes for any +:ref:`import-message` produced thereafter. + +Conversion +---------- + +This second phase takes the record dicts, extracts the :ref:`dbid` and +:ref:`xid` if present and attempts to convert each field to a type +matching what OpenERP expects to write. Empty fields (empty strings) +are replaced with the ``False`` value, and non-empty fields are +converted through +:class:`~openerp.addons.base.ir.ir_fields.ir_fields_converter`: + +Char, text and binary fields +++++++++++++++++++++++++++++ + +Are returned as-is, without any alteration. + +Boolean fields +++++++++++++++ + +The string value is compared (in a case-insensitive manner) to ``0``, +``false`` and ``no`` as well of any translation thereof loaded in the +database. If the value matches one of these, the field is set to +``False``. + +Otherwise the field is compared to ``1``, ``true`` and ``yes`` (and +any translation of these in the database). The field is always set to +``True``, but if the value does not match one of these a warning will +also be output. + +Integers and float fields ++++++++++++++++++++++++++ + +The field is parsed with Python's built-in conversion routines +(``int`` and ``float`` respectively), if the conversion fails an error +is generated. + +Selection fields +++++++++++++++++ + +The field is compared to 1. the values of the selection (first part of +each selection tuple) and 2. all translations of the selection label +found in the database. + +If one of these is matched, the corresponding value is set on the +field. + +Otherwise an error is generated. + +The same process applies to both list-type and function-type selection +fields. + +Many to One field ++++++++++++++++++ + +If the specified field is the relational field itself (``m2o``), the +value is used in a ``name_search``. The first record returned by +``name_search`` is used as the field's value. + +If ``name_search`` finds no value, an error is generated. If +``name_search`` finds multiple value, a warning is generated to warn +the user of ``name_search`` collisions. + +If the specified field is a :ref:`xid` (``m2o/id``), the +corresponding record it looked up in the database and used as the +field's value. If no record is found matching the provided external +ID, an error is generated. + +If the specified field is a :ref:`dbid` (``m2o/.id``), the process is +the same as for external ids (on database identifiers instead of +external ones). + +Many to Many field +++++++++++++++++++ + +The field's value is interpreted as a comma-separated list of names, +external ids or database ids. For each one, the process previously +used for the many to one field is applied. + +One to Many field ++++++++++++++++++ + +For each o2m record extracted, if the record has a ``name``, +:ref:`xid` or :ref:`dbid` the :ref:`dbid` is looked up and checked +through the same process as for m2o fields. + +If a :ref:`dbid` was found, a LINK_TO command is emmitted, followed by +an UPDATE with the non-db values for the relational field. + +Otherwise a CREATE command is emmitted. + +Create/Write +------------ + +If the conversion was successful, the converted record is then saved +to the database via ``(ir.model.data)._update``. + +Error handling +-------------- + +The import process will only catch 2 types of exceptions to convert +them to error messages: ``ValueError`` during the conversion process, +and sub-exceptions of ``psycopg2.Error`` during the create/write +process. + +The import process uses savepoint to: + +* protect the overall transaction from the failure of each ``_update`` + call, if an ``_update`` call fails the savepoint is rolled back and + the import process keeps going in order to obtain as many error + messages as possible during each run. + +* protect the import as a whole, a savepoint is created before + starting and if any error is generated that savepoint is rolled + back. The rest of the transaction (anything not within the import + process) will be left untouched. + +.. _import-message: +.. _import-messages: + +Messages +======== + +A message is a dictionary with 5 mandatory keys and one optional key: + +``type`` + the type of message, either ``warning`` or ``error``. Any + ``error`` message indicates the import failed and was rolled back. + +``message`` + the message's actual text, which should be translated and can be + shown to the user directly + +``rows`` + a dict with 2 keys ``from`` and ``to``, indicates the range of + rows in ``data`` which generated the message + +``record`` + a single integer, for warnings the index of the record which + generated the message (can be obtained from a non-false ``ids`` + result) + +``field`` + the name of the (logical) OpenERP field for which the error or + warning was generated + +``moreinfo`` (optional) + A string, a list or a dict, leading to more information about the + warning. + + * If ``moreinfo`` is a string, it is a supplementary warnings + message which should be hidden by default + * If ``moreinfo`` is a list, it provides a number of possible or + alternative values for the string + * If ``moreinfo`` is a dict, it is an OpenERP action descriptor + which can be executed to get more information about the issues + with the field. If present, the ``help`` key serves as a label + for the action (e.g. the text of the link). diff --git a/doc/index.rst.inc b/doc/index.rst.inc index 6182f6251e9..842888aecb2 100644 --- a/doc/index.rst.inc +++ b/doc/index.rst.inc @@ -5,6 +5,7 @@ OpenERP Server .. toctree:: :maxdepth: 1 + import test-framework Changed in 7.0 diff --git a/doc/o2m.txt b/doc/o2m.txt new file mode 100644 index 00000000000..32218e5ac85 --- /dev/null +++ b/doc/o2m.txt @@ -0,0 +1,13 @@ ++-------+-------+===========+===========+-------+-------+ +|value01|value02‖o2m/value01|o2m/value02‖value03|value04| ++-------+-------+-----------+-----------+-------+-------+ +| | ‖o2m/value11|o2m/value12‖ | | ++-------+-------+-----------+-----------+-------+-------+ +| | ‖o2m/value21|o2m/value22‖ | | ++-------+-------+===========+===========+-------+-------+ +|value11|value12‖o2m/value01|o2m/value02‖value13|value14| ++-------+-------+-----------+-----------+-------+-------+ +| | ‖o2m/value11|o2m/value12‖ | | ++-------+-------+===========+===========+-------+-------+ +|value21|value22| | |value23|value24| ++-------+-------+-----------+-----------+-------+-------+ diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index afabda817f4..da0c93762bc 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1310,37 +1310,6 @@ class BaseModel(object): the same order they were extracted from the file. They can be passed directly to :meth:`~read` - Each message is a dictionary with the following keys: - - ``type`` - the type of message, either ``warning`` or ``error``. Any ``error`` - message indicates the import failed and was rolled back. - ``message`` - the message's actual text, which should be translated and can be - shown to the user directly - ``rows`` - a dict with 2 keys ``from`` and ``to``, indicates the range of rows - in ``data`` which generated the message - ``record`` - a single integer, for warnings the index of the record which - generated the message (can be obtained from a non-false ``ids`` - result) - ``field`` - the name of the (logical) OpenERP field for which the error or - warning was generated - ``moreinfo`` (optional) - A string, a list or a dict, leading to more information about the - warning. - - * If ``moreinfo`` is a string, it is a supplementary warnings - message which should be hidden by default - * If ``moreinfo`` is a list, it provides a number of possible or - alternative values for the string - * If ``moreinfo`` is a dict, it is an OpenERP action descriptor - which can be executed to get more information about the issues - with the field. If present, the ``help`` key serves as a label - for the action (e.g. the text of the link). - :param cr: cursor for the request :param int uid: ID of the user attempting the data import :param fields: list of fields to import, at the same index as the corresponding data From 45ee8b434d6ea9d1f1570478a091ce0eab2cc37d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 1 Oct 2012 09:17:28 +0200 Subject: [PATCH 033/171] [FIX] account: incorrect check for parent_id value would try a read on parent_id=False, which blows up bzr revid: xmo@openerp.com-20121001071728-npq7ieoe3rpz44i7 --- addons/account/account.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/account/account.py b/addons/account/account.py index e5e29f178a3..4eb99dc4bd4 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -2522,14 +2522,14 @@ class account_account_template(osv.osv): ] def create(self, cr, uid, vals, context=None): - if 'parent_id' in vals: + if vals.get('parent_id'): parent = self.read(cr, uid, [vals['parent_id']], ['type']) if parent and parent[0]['type'] != 'view': raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'.")) return super(account_account_template, self).create(cr, uid, vals, context=context) def write(self, cr, uid, ids, vals, context=None): - if 'parent_id' in vals: + if vals.get('parent_id'): parent = self.read(cr, uid, [vals['parent_id']], ['type']) if parent and parent[0]['type'] != 'view': raise osv.except_osv(_('Warning!'), _("You may only select a parent account of type 'View'.")) From 49998b5f72f2fa8a33472cc849203278cb80bbf0 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 1 Oct 2012 09:59:31 +0200 Subject: [PATCH 034/171] [FIX] account: correctly handle 'false' close_method bzr revid: xmo@openerp.com-20121001075931-zyrmydt1s7uz7ihk --- addons/account/account.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/account/account.py b/addons/account/account.py index 4eb99dc4bd4..878f8759239 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -155,6 +155,7 @@ class account_account_type(osv.osv): return res def _save_report_type(self, cr, uid, account_type_id, field_name, field_value, arg, context=None): + field_value = field_value or 'none' obj_data = self.pool.get('ir.model.data') obj_financial_report = self.pool.get('account.financial.report') #unlink if it exists somewhere in the financial reports related to BS or PL From abf0579a9825a1a72a7525b26c80c87838fd6058 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 1 Oct 2012 12:52:35 +0200 Subject: [PATCH 035/171] [IMP] Invert order of buttons in import dialog, add 'or cancel' link bzr revid: xmo@openerp.com-20121001105235-0d954cxt9y80q89w --- addons/base_import/static/src/js/import.js | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 8a73bcd5d71..2a7e6f18d8a 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -106,10 +106,10 @@ openerp.base_import = function (instance) { var self = this; this._super(parent, { buttons: [ - {text: _t("Import"), click: self.proxy('do_import'), - 'class': 'oe_import_dialog_button'}, {text: _t("Validate"), click: self.proxy('import_dryrun'), - 'class': 'oe_import_dialog_button'} + 'class': 'oe_import_dialog_button'}, + {text: _t("Import"), click: self.proxy('do_import'), + 'class': 'oe_import_dialog_button oe_highlight'} ] }); this.res_model = parent.model; @@ -126,6 +126,16 @@ openerp.base_import = function (instance) { self.$('input[name=import_id]').val(id); }); }, + open: function () { + this._super.apply(this, arguments); + this.$el.dialog('widget').find('.ui-dialog-buttonset') + .append(_t(" or ")) + .append( + $(' + + or + Cancel + From e8a7d276bf700b3c880d4e961cfac1ea0b40c690 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 3 Oct 2012 14:16:19 +0200 Subject: [PATCH 045/171] [IMP] highlight toggling between validation and import buttons bzr revid: xmo@openerp.com-20121003121619-e6nbd78mfa5owycp --- addons/base_import/static/src/js/import.js | 6 ++++-- addons/base_import/static/src/xml/import.xml | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index dd8b3288de9..4135f303513 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -325,8 +325,10 @@ openerp.base_import = function (instance) { }); }, render_import_result: function (message) { - if (_.isEmpty(message)) { - this.$('.oe_import_import').addClass('oe_highlight'); + var no_messages = _.isEmpty(message); + this.$('.oe_import_import').toggleClass('oe_highlight', no_messages); + this.$('.oe_import_validate').toggleClass('oe_highlight', !no_messages); + if (no_messages) { message.push({ type: 'info', message: _t("Everything seems valid.") diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml index aab9482ee81..b0eea1fcf75 100644 --- a/addons/base_import/static/src/xml/import.xml +++ b/addons/base_import/static/src/xml/import.xml @@ -4,7 +4,7 @@
    """ - _header_a4 = _header_main % ('23.0cm', '27.6cm', '27.7cm', '27.7cm', '27.8cm', '27.2cm', '26.8cm', '26.0cm', '26.0cm', '25.6cm', '25.6cm', '25.5cm', '25.5cm') + _header_a4 = _header_main % ('23.0cm', '27.6cm', '27.7cm', '27.7cm', '27.8cm', '27.4cm', '25.8cm', '26.0cm', '26.0cm', '25.6cm', '25.6cm', '25.5cm', '25.5cm') _header_letter = _header_main % ('21.3cm', '25.9cm', '26.0cm', '26.0cm', '26.1cm', '25.5cm', '25.1cm', '24.3cm', '24.3cm', '23.9cm', '23.9cm', '23.8cm', '23.8cm') def onchange_paper_format(self, cr, uid, ids, paper_format, context=None): From 2b1384dcfcdef5a336eee4864a7185c4e41072eb Mon Sep 17 00:00:00 2001 From: Vishmita Date: Wed, 3 Oct 2012 18:22:38 +0530 Subject: [PATCH 047/171] [Fix]remove delete icon from attachment for google doc bzr revid: vja@tinyerp.com-20121003125238-dnhytzvxj13gnsrf --- addons/web/static/src/xml/base.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 3d979186aa3..f4c5de237ac 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -554,7 +554,7 @@ - x + x
  • From fa2bc110258b48842fa95d370440b9399dcf8571 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 3 Oct 2012 16:50:48 +0200 Subject: [PATCH 048/171] [IMP] formalize state transitions via an actual fsm bzr revid: xmo@openerp.com-20121003145048-wdoo712j551jow3t --- addons/base_import/__openerp__.py | 1 + .../lib/javascript-state-machine/LICENSE | 20 ++ .../lib/javascript-state-machine/README.md | 327 ++++++++++++++++++ .../javascript-state-machine/RELEASE_NOTES.md | 32 ++ .../lib/javascript-state-machine/Rakefile | 8 + .../lib/javascript-state-machine/index.html | 39 +++ .../javascript-state-machine/state-machine.js | 155 +++++++++ addons/base_import/static/src/js/import.js | 90 +++-- 8 files changed, 638 insertions(+), 34 deletions(-) create mode 100644 addons/base_import/static/lib/javascript-state-machine/LICENSE create mode 100644 addons/base_import/static/lib/javascript-state-machine/README.md create mode 100644 addons/base_import/static/lib/javascript-state-machine/RELEASE_NOTES.md create mode 100644 addons/base_import/static/lib/javascript-state-machine/Rakefile create mode 100644 addons/base_import/static/lib/javascript-state-machine/index.html create mode 100644 addons/base_import/static/lib/javascript-state-machine/state-machine.js diff --git a/addons/base_import/__openerp__.py b/addons/base_import/__openerp__.py index eff20bc90ab..bb527e7b49d 100644 --- a/addons/base_import/__openerp__.py +++ b/addons/base_import/__openerp__.py @@ -33,6 +33,7 @@ Re-implement openerp's file import system: ], 'js': [ 'static/lib/select2/select2.js', + 'static/lib/javascript-state-machine/state-machine.js', 'static/src/js/import.js', ], 'qweb': ['static/src/xml/import.xml'], diff --git a/addons/base_import/static/lib/javascript-state-machine/LICENSE b/addons/base_import/static/lib/javascript-state-machine/LICENSE new file mode 100644 index 00000000000..8ad703ca4ef --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2012 Jake Gordon and contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/addons/base_import/static/lib/javascript-state-machine/README.md b/addons/base_import/static/lib/javascript-state-machine/README.md new file mode 100644 index 00000000000..64c045e69d7 --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/README.md @@ -0,0 +1,327 @@ +Javascript Finite State Machine (v2.1.0) +======================================== + +This standalone javascript micro-framework provides a finite state machine for your pleasure. + + * You can find the [code here](https://github.com/jakesgordon/javascript-state-machine) + * You can find a [description here](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/) + * You can find a [working demo here](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/example/) + +Download +======== + +You can download [state-machine.js](https://github.com/jakesgordon/javascript-state-machine/raw/master/state-machine.js), +or the [minified version](https://github.com/jakesgordon/javascript-state-machine/raw/master/state-machine.min.js) + +Alternatively: + + git clone git@github.com:jakesgordon/javascript-state-machine + + + * All code is in state-machine.js + * Minified version provided in state-machine.min.js + * No 3rd party library is required + * Demo can be found in /index.html + * QUnit tests can be found in /test/index.html + +Usage +===== + +Include `state-machine.min.js` in your application. + +In its simplest form, create a standalone state machine using: + + var fsm = StateMachine.create({ + initial: 'green', + events: [ + { name: 'warn', from: 'green', to: 'yellow' }, + { name: 'panic', from: 'yellow', to: 'red' }, + { name: 'calm', from: 'red', to: 'yellow' }, + { name: 'clear', from: 'yellow', to: 'green' } + ]}); + +... will create an object with a method for each event: + + * fsm.warn() - transition from 'green' to 'yellow' + * fsm.panic() - transition from 'yellow' to 'red' + * fsm.calm() - transition from 'red' to 'yellow' + * fsm.clear() - transition from 'yellow' to 'green' + +along with the following members: + + * fsm.current - contains the current state + * fsm.is(s) - return true if state `s` is the current state + * fsm.can(e) - return true if event `e` can be fired in the current state + * fsm.cannot(e) - return true if event `e` cannot be fired in the current state + +Multiple 'from' and 'to' states for a single event +================================================== + +If an event is allowed **from** multiple states, and always transitions to the same +state, then simply provide an array of states in the `from` attribute of an event. However, +if an event is allowed from multiple states, but should transition **to** a different +state depending on the current state, then provide multiple event entries with +the same name: + + var fsm = StateMachine.create({ + initial: 'hungry', + events: [ + { name: 'eat', from: 'hungry', to: 'satisfied' }, + { name: 'eat', from: 'satisfied', to: 'full' }, + { name: 'eat', from: 'full', to: 'sick' }, + { name: 'rest', from: ['hungry', 'satisfied', 'full', 'sick'], to: 'hungry' }, + ]}); + +This example will create an object with 2 event methods: + + * fsm.eat() + * fsm.rest() + +The `rest` event will always transition to the `hungry` state, while the `eat` event +will transition to a state that is dependent on the current state. + +>> NOTE: The `rest` event could use a wildcard '*' for the 'from' state if it should be +allowed from any current state. + +>> NOTE: The `rest` event in the above example can also be specified as multiple events with +the same name if you prefer the verbose approach. + +Callbacks +========= + +4 callbacks are available if your state machine has methods using the following naming conventions: + + * onbefore**event** - fired before the event + * onleave**state** - fired when leaving the old state + * onenter**state** - fired when entering the new state + * onafter**event** - fired after the event + +You can affect the event in 3 ways: + + * return `false` from an `onbeforeevent` handler to cancel the event. + * return `false` from an `onleavestate` handler to cancel the event. + * return `ASYNC` from an `onleavestate` handler to perform an asynchronous state transition (see next section) + +For convenience, the 2 most useful callbacks can be shortened: + + * on**event** - convenience shorthand for onafter**event** + * on**state** - convenience shorthand for onenter**state** + +In addition, a generic `onchangestate()` callback can be used to call a single function for _all_ state changes: + +All callbacks will be passed the same arguments: + + * **event** name + * **from** state + * **to** state + * _(followed by any arguments you passed into the original event method)_ + +Callbacks can be specified when the state machine is first created: + + var fsm = StateMachine.create({ + initial: 'green', + events: [ + { name: 'warn', from: 'green', to: 'yellow' }, + { name: 'panic', from: 'yellow', to: 'red' }, + { name: 'calm', from: 'red', to: 'yellow' }, + { name: 'clear', from: 'yellow', to: 'green' } + ], + callbacks: { + onpanic: function(event, from, to, msg) { alert('panic! ' + msg); }, + onclear: function(event, from, to, msg) { alert('thanks to ' + msg); }, + ongreen: function(event, from, to) { document.body.className = 'green'; }, + onyellow: function(event, from, to) { document.body.className = 'yellow'; }, + onred: function(event, from, to) { document.body.className = 'red'; }, + } + }); + + fsm.panic('killer bees'); + fsm.clear('sedatives in the honey pots'); + ... + +Additionally, they can be added and removed from the state machine at any time: + + fsm.ongreen = null; + fsm.onyellow = null; + fsm.onred = null; + fsm.onchangestate = function(event, from, to) { document.body.className = to; }; + +Asynchronous State Transitions +============================== + +Sometimes, you need to execute some asynchronous code during a state transition and ensure the +new state is not entered until your code has completed. + +A good example of this is when you transition out of a `menu` state, perhaps you want to gradually +fade the menu away, or slide it off the screen and don't want to transition to your `game` state +until after that animation has been performed. + +You can now return `StateMachine.ASYNC` from your `onleavestate` handler and the state machine +will be _'put on hold'_ until you are ready to trigger the transition using the new `transition()` +method. + +For example, using jQuery effects: + + var fsm = StateMachine.create({ + + initial: 'menu', + + events: [ + { name: 'play', from: 'menu', to: 'game' }, + { name: 'quit', from: 'game', to: 'menu' } + ], + + callbacks: { + + onentermenu: function() { $('#menu').show(); }, + onentergame: function() { $('#game').show(); }, + + onleavemenu: function() { + $('#menu').fadeOut('fast', function() { + fsm.transition(); + }); + return StateMachine.ASYNC; // tell StateMachine to defer next state until we call transition (in fadeOut callback above) + }, + + onleavegame: function() { + $('#game').slideDown('slow', function() { + fsm.transition(); + }; + return StateMachine.ASYNC; // tell StateMachine to defer next state until we call transition (in slideDown callback above) + } + + } + }); + + +State Machine Classes +===================== + +You can also turn all instances of a _class_ into an FSM by applying +the state machine functionality to the prototype, including your callbacks +in your prototype, and providing a `startup` event for use when constructing +instances: + + MyFSM = function() { // my constructor function + this.startup(); + }; + + MyFSM.prototype = { + + onpanic: function(event, from, to) { alert('panic'); }, + onclear: function(event, from, to) { alert('all is clear'); }, + + // my other prototype methods + + }; + + StateMachine.create({ + target: MyFSM.prototype, + events: [ + { name: 'startup', from: 'none', to: 'green' }, + { name: 'warn', from: 'green', to: 'yellow' }, + { name: 'panic', from: 'yellow', to: 'red' }, + { name: 'calm', from: 'red', to: 'yellow' }, + { name: 'clear', from: 'yellow', to: 'green' } + ]}); + + +This should be easy to adjust to fit your appropriate mechanism for object construction. + +Initialization Options +====================== + +How the state machine should initialize can depend on your application requirements, so +the library provides a number of simple options. + +By default, if you dont specify any initial state, the state machine will be in the `'none'` +state and you would need to provide an event to take it out of this state: + + var fsm = StateMachine.create({ + events: [ + { name: 'startup', from: 'none', to: 'green' }, + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "none" + fsm.startup(); + alert(fsm.current); // "green" + +If you specify the name of your initial event (as in all the earlier examples), then an +implicit `startup` event will be created for you and fired when the state machine is constructed. + + var fsm = StateMachine.create({ + initial: 'green', + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "green" + +If your object already has a `startup` method you can use a different name for the initial event + + var fsm = StateMachine.create({ + initial: { state: 'green', event: 'init' }, + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "green" + +Finally, if you want to wait to call the initial state transition event until a later date you +can `defer` it: + + var fsm = StateMachine.create({ + initial: { state: 'green', event: 'init', defer: true }, + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.current); // "none" + fsm.init(); + alert(fsm.current); // "green" + +Of course, we have now come full circle, this last example is pretty much functionally the +same as the first example in this section where you simply define your own startup event. + +So you have a number of choices available to you when initializing your state machine. + +Handling Failures +====================== + +By default, if you try to call an event method that is not allowed in the current state, the +state machine will throw an exception. If you prefer to handle the problem yourself, you can +define a custom `error` handler: + + var fsm = StateMachine.create({ + initial: 'green', + error: function(eventName, from, to, args, errorCode, errorMessage) { + return 'event ' + eventName + ' was naughty :- ' + errorMessage; + }, + events: [ + { name: 'panic', from: 'green', to: 'red' }, + { name: 'calm', from: 'red', to: 'green' }, + ]}); + alert(fsm.calm()); // "event calm was naughty :- event not allowed in current state green" + +Release Notes +============= + +See [RELEASE NOTES](https://github.com/jakesgordon/javascript-state-machine/blob/master/RELEASE_NOTES.md) file. + +License +======= + +See [LICENSE](https://github.com/jakesgordon/javascript-state-machine/blob/master/LICENSE) file. + +Contact +======= + +If you have any ideas, feedback, requests or bug reports, you can reach me at +[jake@codeincomplete.com](mailto:jake@codeincomplete.com), or via +my website: [Code inComplete](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/) + + + + + diff --git a/addons/base_import/static/lib/javascript-state-machine/RELEASE_NOTES.md b/addons/base_import/static/lib/javascript-state-machine/RELEASE_NOTES.md new file mode 100644 index 00000000000..06abf402d38 --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/RELEASE_NOTES.md @@ -0,0 +1,32 @@ +Version 2.1.0 (January 7th 2012) +-------------------------------- + + * Wrapped in self executing function to be more easily used with loaders like `require.js` or `curl.js` (issue #15) + * Allow event to be cancelled by returning `false` from `onleavestate` handler (issue #13) - WARNING: this breaks backward compatibility for async transitions (you now need to return `StateMachine.ASYNC` instead of `false`) + * Added explicit return values for event methods (issue #12) + * Added support for wildcard events that can be fired 'from' any state (issue #11) + * Added support for no-op events that transition 'to' the same state (issue #5) + * extended custom error callback to handle any exceptions caused by caller provided callbacks + * added custom error callback to override exception when an illegal state transition is attempted (thanks to cboone) + * fixed typos (thanks to cboone) + * fixed issue #4 - ensure before/after event hooks are called even if the event doesn't result in a state change + +Version 2.0.0 (August 19th 2011) +-------------------------------- + + * adding support for asynchronous state transitions (see README) - with lots of qunit tests (see test/async.js). + * consistent arguments for ALL callbacks, first 3 args are ALWAYS event name, from state and to state, followed by whatever arguments the user passed to the original event method. + * added a generic `onchangestate(event,from,to)` callback to detect all state changes with a single function. + * allow callbacks to be declared at creation time (instead of having to attach them afterwards) + * renamed 'hooks' => 'callbacks' + * [read more...](http://codeincomplete.com/posts/2011/8/19/javascript_state_machine_v2/) + +Version 1.2.0 (June 21st 2011) +------------------------------ + * allows the same event to transition to different states, depending on the current state (see 'Multiple...' section in README.md) + * [read more...](http://codeincomplete.com/posts/2011/6/21/javascript_state_machine_v1_2_0/) + +Version 1.0.0 (June 1st 2011) +----------------------------- + * initial version + * [read more...](http://codeincomplete.com/posts/2011/6/1/javascript_state_machine/) diff --git a/addons/base_import/static/lib/javascript-state-machine/Rakefile b/addons/base_import/static/lib/javascript-state-machine/Rakefile new file mode 100644 index 00000000000..beb8702a7f0 --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/Rakefile @@ -0,0 +1,8 @@ + +desc "create minified version of state-machine.js" +task :minify do + require File.expand_path(File.join(File.dirname(__FILE__), 'minifier/minifier')) + Minifier.enabled = true + Minifier.minify('state-machine.js') +end + diff --git a/addons/base_import/static/lib/javascript-state-machine/index.html b/addons/base_import/static/lib/javascript-state-machine/index.html new file mode 100644 index 00000000000..2d6cb62617b --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/index.html @@ -0,0 +1,39 @@ + + + + Javascript Finite State Machine + + + + + + +
    + +

    Finite State Machine

    + +
    + + + + +
    + +
    +
    + +
    + dashed lines are asynchronous state transitions (3 seconds) +
    + + + +
    + + + + + + + diff --git a/addons/base_import/static/lib/javascript-state-machine/state-machine.js b/addons/base_import/static/lib/javascript-state-machine/state-machine.js new file mode 100644 index 00000000000..0d503ee7bdb --- /dev/null +++ b/addons/base_import/static/lib/javascript-state-machine/state-machine.js @@ -0,0 +1,155 @@ +(function (window) { + + StateMachine = { + + //--------------------------------------------------------------------------- + + VERSION: "2.1.0", + + //--------------------------------------------------------------------------- + + Result: { + SUCCEEDED: 1, // the event transitioned successfully from one state to another + NOTRANSITION: 2, // the event was successfull but no state transition was necessary + CANCELLED: 3, // the event was cancelled by the caller in a beforeEvent callback + ASYNC: 4, // the event is asynchronous and the caller is in control of when the transition occurs + }, + + Error: { + INVALID_TRANSITION: 100, // caller tried to fire an event that was innapropriate in the current state + PENDING_TRANSITION: 200, // caller tried to fire an event while an async transition was still pending + INVALID_CALLBACK: 300, // caller provided callback function threw an exception + }, + + WILDCARD: '*', + ASYNC: 'async', + + //--------------------------------------------------------------------------- + + create: function(cfg, target) { + + var initial = (typeof cfg.initial == 'string') ? { state: cfg.initial } : cfg.initial; // allow for a simple string, or an object with { state: 'foo', event: 'setup', defer: true|false } + var fsm = target || cfg.target || {}; + var events = cfg.events || []; + var callbacks = cfg.callbacks || {}; + var map = {}; + + var add = function(e) { + var from = (e.from instanceof Array) ? e.from : (e.from ? [e.from] : [StateMachine.WILDCARD]); // allow 'wildcard' transition if 'from' is not specified + map[e.name] = map[e.name] || {}; + for (var n = 0 ; n < from.length ; n++) + map[e.name][from[n]] = e.to || from[n]; // allow no-op transition if 'to' is not specified + }; + + if (initial) { + initial.event = initial.event || 'startup'; + add({ name: initial.event, from: 'none', to: initial.state }); + } + + for(var n = 0 ; n < events.length ; n++) + add(events[n]); + + for(var name in map) { + if (map.hasOwnProperty(name)) + fsm[name] = StateMachine.buildEvent(name, map[name]); + } + + for(var name in callbacks) { + if (callbacks.hasOwnProperty(name)) + fsm[name] = callbacks[name] + } + + fsm.current = 'none'; + fsm.is = function(state) { return this.current == state; }; + fsm.can = function(event) { return !this.transition && (map[event].hasOwnProperty(this.current) || map[event].hasOwnProperty(StateMachine.WILDCARD)); } + fsm.cannot = function(event) { return !this.can(event); }; + fsm.error = cfg.error || function(name, from, to, args, error, msg) { throw msg; }; // default behavior when something unexpected happens is to throw an exception, but caller can override this behavior if desired (see github issue #3) + + if (initial && !initial.defer) + fsm[initial.event](); + + return fsm; + + }, + + //=========================================================================== + + doCallback: function(fsm, func, name, from, to, args) { + if (func) { + try { + return func.apply(fsm, [name, from, to].concat(args)); + } + catch(e) { + return fsm.error(name, from, to, args, StateMachine.Error.INVALID_CALLBACK, "an exception occurred in a caller-provided callback function"); + } + } + }, + + beforeEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onbefore' + name], name, from, to, args); }, + afterEvent: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onafter' + name] || fsm['on' + name], name, from, to, args); }, + leaveState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onleave' + from], name, from, to, args); }, + enterState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onenter' + to] || fsm['on' + to], name, from, to, args); }, + changeState: function(fsm, name, from, to, args) { return StateMachine.doCallback(fsm, fsm['onchangestate'], name, from, to, args); }, + + + buildEvent: function(name, map) { + return function() { + + var from = this.current; + var to = map[from] || map[StateMachine.WILDCARD] || from; + var args = Array.prototype.slice.call(arguments); // turn arguments into pure array + + if (this.transition) + return this.error(name, from, to, args, StateMachine.Error.PENDING_TRANSITION, "event " + name + " inappropriate because previous transition did not complete"); + + if (this.cannot(name)) + return this.error(name, from, to, args, StateMachine.Error.INVALID_TRANSITION, "event " + name + " inappropriate in current state " + this.current); + + if (false === StateMachine.beforeEvent(this, name, from, to, args)) + return StateMachine.CANCELLED; + + if (from === to) { + StateMachine.afterEvent(this, name, from, to, args); + return StateMachine.NOTRANSITION; + } + + // prepare a transition method for use EITHER lower down, or by caller if they want an async transition (indicated by an ASYNC return value from leaveState) + var fsm = this; + this.transition = function() { + fsm.transition = null; // this method should only ever be called once + fsm.current = to; + StateMachine.enterState( fsm, name, from, to, args); + StateMachine.changeState(fsm, name, from, to, args); + StateMachine.afterEvent( fsm, name, from, to, args); + }; + + var leave = StateMachine.leaveState(this, name, from, to, args); + if (false === leave) { + this.transition = null; + return StateMachine.CANCELLED; + } + else if ("async" === leave) { + return StateMachine.ASYNC; + } + else { + if (this.transition) + this.transition(); // in case user manually called transition() but forgot to return ASYNC + return StateMachine.SUCCEEDED; + } + + }; + } + + }; // StateMachine + + //=========================================================================== + + if ("function" === typeof define) { + define("statemachine", [], function() { return StateMachine; }); + } + else { + window.StateMachine = StateMachine; + } + +}(this)); + diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 4135f303513..19d4e466cc7 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -71,14 +71,8 @@ openerp.base_import = function (instance) { ], events: { // 'change .oe_import_grid input': 'import_dryrun', - 'change input.oe_import_file': 'file_update', - 'change input.oe_import_has_header, .oe_import_options input': 'settings_updated', - 'click a.oe_import_csv': function (e) { - e.preventDefault(); - }, - 'click a.oe_import_export': function (e) { - e.preventDefault(); - }, + 'change input.oe_import_file': 'loaded_file', + 'change input.oe_import_has_header, .oe_import_options input': 'settings_changed', 'click a.oe_import_toggle': function (e) { e.preventDefault(); var $el = $(e.target); @@ -109,14 +103,15 @@ openerp.base_import = function (instance) { this.do_action(_.extend(action, {target: 'new'})); }, // buttons - 'click .oe_import_validate': 'import_dryrun', - 'click .oe_import_import': 'do_import', + 'click .oe_import_validate': 'validate', + 'click .oe_import_import': 'import', 'click .oe_import_cancel': function (e) { e.preventDefault(); this.exit(); } }, init: function (parent, params) { + var self = this; this._super.apply(this, arguments); this.res_model = params.model; // import object id @@ -150,42 +145,48 @@ openerp.base_import = function (instance) { }, //- File & settings change section - file_update: function (e) { + onfile_loaded: function () { this.$('.oe_import_button').prop('disabled', true); if (!this.$('input.oe_import_file').val()) { return; } this.$el.removeClass('oe_import_preview oe_import_error'); jsonp(this.$el, { url: '/base_import/set_file' - }, this.proxy('settings_updated')); + }, this.proxy('settings_changed')); }, - settings_updated: function () { + onpreviewing: function () { + var self = this; + this.$('.oe_import_button').prop('disabled', true); this.$el.addClass('oe_import_with_file'); // TODO: test that write // succeeded? - this.Import.call( - 'parse_preview', [this.id, this.import_options()]) - .then(this.proxy('preview')); - }, - preview: function (result) { this.$el.removeClass('oe_import_preview_error oe_import_error'); this.$el.toggleClass( 'oe_import_noheaders', !this.$('input.oe_import_has_header').prop('checked')); - if (result.error) { - this.$('.oe_import_options').show(); - this.$el.addClass('oe_import_preview_error oe_import_error'); - this.$('.oe_import_error_report').html( - QWeb.render('ImportView.preview.error', result)) - .get(0).scrollIntoView(); - return; - } + this.Import.call( + 'parse_preview', [this.id, this.import_options()]) + .then(function (result) { + var signal = result.error ? 'preview_failed' : 'preview_succeeded'; + self[signal](result); + }); + }, + onpreview_error: function (event, from, to, result) { + this.$('.oe_import_options').show(); + this.$el.addClass('oe_import_preview_error oe_import_error'); + this.$('.oe_import_error_report').html( + QWeb.render('ImportView.preview.error', result)) + .get(0).scrollIntoView(); + }, + onpreview_success: function (event, from, to, result) { + this.$('.oe_import_import').removeClass('oe_highlight'); + this.$('.oe_import_validate').addClass('oe_highlight'); this.$('.oe_import_button').prop('disabled', false); this.$el.addClass('oe_import_preview'); this.$('table').html(QWeb.render('ImportView.preview', result)); if (result.headers.length === 1) { this.$('.oe_import_options').show(); - this.render_import_result([{ + this.onresults(null, null, null, [{ type: 'warning', message: _t("A single column was found in the file, this often means the file separator is incorrect") }]); @@ -225,7 +226,6 @@ openerp.base_import = function (instance) { width: 'resolve', dropdownCssClass: 'oe_import_selector' }); - //this.import_dryrun(); }, generate_fields_completion: function (root) { var basic = []; @@ -303,28 +303,31 @@ openerp.base_import = function (instance) { return this.Import.call( 'do', [this.id, fields, this.import_options()], options); }, - import_dryrun: function () { + onvalidate: function () { return this.call_import({ dryrun: true }) - .then(this.proxy('render_import_result')); + .then(this.proxy('validated')); }, - do_import: function () { + onimport: function () { var self = this; return this.call_import({ dryrun: false }).then(function (message) { if (!_.any(message, function (message) { return message.type === 'error' })) { - self.exit(); + self['import_succeeded'](); return; } - self.render_import_result(message); + self['import_failed'](message); }); }, + onimported: function () { + this.exit(); + }, exit: function () { this.do_action({ type: 'ir.actions.client', tag: 'history_back' }); }, - render_import_result: function (message) { + onresults: function (event, from, to, message) { var no_messages = _.isEmpty(message); this.$('.oe_import_import').toggleClass('oe_highlight', no_messages); this.$('.oe_import_validate').toggleClass('oe_highlight', !no_messages); @@ -386,4 +389,23 @@ openerp.base_import = function (instance) { })).get(0).scrollIntoView(); }, }); + // FSM-ize DataImport + StateMachine.create({ + target: instance.web.DataImport.prototype, + events: [ + { name: 'loaded_file', + from: ['none', 'file_loaded', 'preview_error', 'preview_success', 'results'], + to: 'file_loaded' }, + { name: 'settings_changed', + from: ['file_loaded', 'preview_error', 'preview_success', 'results'], + to: 'previewing' }, + { name: 'preview_failed', from: 'previewing', to: 'preview_error' }, + { name: 'preview_succeeded', from: 'previewing', to: 'preview_success' }, + { name: 'validate', from: 'preview_success', to: 'validating' }, + { name: 'validated', from: 'validating', to: 'results' }, + { name: 'import', from: ['preview_success', 'results'], to: 'importing' }, + { name: 'import_succeeded', from: 'importing', to: 'imported'}, + { name: 'import_failed', from: 'importing', to: 'results' } + ] + }) }; From 9c9d9781093cf35bb5abec0d605a02bbd9b3f108 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 3 Oct 2012 17:24:44 +0200 Subject: [PATCH 049/171] [IMP] relational field 'more values' dialog list features force display of pager and search view, make list non-selectable bzr revid: xmo@openerp.com-20121003152444-phnn9sc9nqo1ecpn --- addons/base_import/static/src/js/import.js | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 19d4e466cc7..2867242749c 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -100,7 +100,15 @@ openerp.base_import = function (instance) { : 'tree' ]; }); - this.do_action(_.extend(action, {target: 'new'})); + this.do_action(_.extend(action, { + target: 'new', + flags: { + search_view: true, + display_title: true, + pager: true, + list: {selectable: false} + } + })); }, // buttons 'click .oe_import_validate': 'validate', From 601568f5f9431338886aa1aeda240a7137f16173 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 08:42:15 +0200 Subject: [PATCH 050/171] [FIX] don't import empty cells at all rather than set them to False, to allow defaults handling to do its job before actually creating the record bzr revid: xmo@openerp.com-20121004064215-fqgir3ovmte2v438 --- openerp/osv/orm.py | 1 - openerp/tests/addons/test_impex/models.py | 10 ++++++++++ .../tests/addons/test_impex/tests/test_load.py | 15 +++++++++++++++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index 4f21a159cdd..b0b0fad88fa 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1501,7 +1501,6 @@ class BaseModel(object): for field, strvalue in record.iteritems(): if field in (None, 'id', '.id'): continue if not strvalue: - converted[field] = False continue # In warnings and error messages, use translated string as diff --git a/openerp/tests/addons/test_impex/models.py b/openerp/tests/addons/test_impex/models.py index 3c85b56fff7..50340082d08 100644 --- a/openerp/tests/addons/test_impex/models.py +++ b/openerp/tests/addons/test_impex/models.py @@ -123,3 +123,13 @@ class Many2ManyChild(orm.Model): , context=context) if isinstance(name, basestring) and name.split(':')[0] == self._name else []) + +class SelectionWithDefault(orm.Model): + _name = 'export.selection.withdefault' + + _columns = { + 'value': fields.selection([(1, "Foo"), (2, "Bar")], required=True), + } + _defaults = { + 'value': 2, + } diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 18b26a16354..65c4b6e9e57 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -474,6 +474,21 @@ class test_selection(ImporterCase): u"Value '42' not found in selection field 'unknown'", moreinfo="Foo Bar Qux 4".split())]) +class test_selection_with_default(ImporterCase): + model_name = 'export.selection.withdefault' + + def test_skip_empty(self): + """ Empty cells should be entirely skipped so that default values can + be inserted by the ORM + """ + result = self.import_(['value'], [['']]) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) + + self.assertEqual( + values(self.read()), + [2]) + class test_selection_function(ImporterCase): model_name = 'export.selection.function' translations_fr = [ From 6aba62a847daf134ea56e5421eae9496485b06ec Mon Sep 17 00:00:00 2001 From: "Jalpesh Patel (OpenERP)" Date: Thu, 4 Oct 2012 12:32:27 +0530 Subject: [PATCH 051/171] [fix]fix problem of send RFQ bzr revid: pja@tinyerp.com-20121004070227-rqo5ec0mckj5vqx8 --- addons/email_template/email_template.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/email_template/email_template.py b/addons/email_template/email_template.py index 1dc79dc634c..37dca4ceddb 100644 --- a/addons/email_template/email_template.py +++ b/addons/email_template/email_template.py @@ -319,7 +319,7 @@ class email_template(osv.osv): ext = "." + format if not report_name.endswith(ext): report_name += ext - attachments.append(report_name, result) + attachments.append((report_name, result)) # Add template attachments for attach in template.attachment_ids: From 82e849e180c89698fd86fb1e6aa9a6d59119216e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 10:32:31 +0200 Subject: [PATCH 052/171] [FIX] base_import: should be possible to re-validate a file after a failed import or validation bzr revid: xmo@openerp.com-20121004083231-qyl8l58wyhb7g2c3 --- addons/base_import/static/src/js/import.js | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 2867242749c..937565a24e8 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -410,6 +410,7 @@ openerp.base_import = function (instance) { { name: 'preview_failed', from: 'previewing', to: 'preview_error' }, { name: 'preview_succeeded', from: 'previewing', to: 'preview_success' }, { name: 'validate', from: 'preview_success', to: 'validating' }, + { name: 'validate', from: 'results', to: 'validating' }, { name: 'validated', from: 'validating', to: 'results' }, { name: 'import', from: ['preview_success', 'results'], to: 'importing' }, { name: 'import_succeeded', from: 'importing', to: 'imported'}, From 8f3db5bcf1f1eb6eb505e9847536fd8d26e46497 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 12:22:36 +0200 Subject: [PATCH 053/171] [IMP] move file format options from below preview table to below file field bzr revid: xmo@openerp.com-20121004102236-auj053d38tp5fpvy --- addons/base_import/static/src/css/import.css | 2 +- addons/base_import/static/src/xml/import.xml | 26 +++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/addons/base_import/static/src/css/import.css b/addons/base_import/static/src/css/import.css index 25678cbe98a..21e598ff707 100644 --- a/addons/base_import/static/src/css/import.css +++ b/addons/base_import/static/src/css/import.css @@ -52,7 +52,7 @@ } .oe_import .oe_import_options label { display: inline-block; - width: 10em; + width: 8em; text-align: right; } diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml index b0eea1fcf75..91debd64897 100644 --- a/addons/base_import/static/src/xml/import.xml +++ b/addons/base_import/static/src/xml/import.xml @@ -24,6 +24,20 @@ +
    + + File Format Options… +
    +

    + + + +

    +
    +

    Map your data to OpenERP

    @@ -38,18 +52,6 @@
    - - File Format Options… -
    -

    - - - -

    -

    Frequently Asked Questions

    From 1b17b59f50b73c927f1283901003d6ea793785af Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 13:50:24 +0200 Subject: [PATCH 054/171] [IMP] base_import: provide an encoding picker bzr revid: xmo@openerp.com-20121004115024-m77q5c95i12q3qkm --- addons/base_import/static/src/js/import.js | 23 ++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 937565a24e8..551196678a3 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -128,6 +128,7 @@ openerp.base_import = function (instance) { }, start: function () { var self = this; + this.setup_encoding_picker(); return $.when( this._super(), @@ -139,6 +140,25 @@ openerp.base_import = function (instance) { }) ) }, + setup_encoding_picker: function () { + this.$('input.oe_import_encoding').select2({ + width: '160px', + query: function (q) { + var make = function (term) { return {id: term, text: term}; }; + var suggestions = _.map( + ('utf-8 utf-16 windows-1252 latin1 latin2 big5 ' + + 'gb18030 shift_jis windows-1251 koir8_r').split(/\s+/), + make); + if (q.term) { + suggestions.unshift(make(q.term)); + } + q.callback({results: suggestions}); + }, + initSelection: function (e, c) { + return c({id: 'utf-8', text: 'utf-8'}); + } + }).select2('val', 'utf-8'); + }, import_options: function () { var self = this; @@ -182,8 +202,7 @@ openerp.base_import = function (instance) { this.$('.oe_import_options').show(); this.$el.addClass('oe_import_preview_error oe_import_error'); this.$('.oe_import_error_report').html( - QWeb.render('ImportView.preview.error', result)) - .get(0).scrollIntoView(); + QWeb.render('ImportView.preview.error', result)); }, onpreview_success: function (event, from, to, result) { this.$('.oe_import_import').removeClass('oe_highlight'); From e77fad774b7c2d8c450259b9d35d9540c44e7997 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 13:58:22 +0200 Subject: [PATCH 055/171] [IMP] error messages for datetime error bzr revid: xmo@openerp.com-20121004115822-54mzaeqi05h0p4ve --- openerp/addons/base/ir/ir_fields.py | 6 ++++-- openerp/tests/addons/test_impex/tests/test_load.py | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 65d4a786178..05aa6782e9c 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -127,7 +127,8 @@ class ir_fields_converter(orm.Model): return value, [] except ValueError: raise ValueError( - _(u"'%s' does not seem to be a valid date for field '%%(field)s'") % value) + _(u"'%s' does not seem to be a valid date for field '%%(field)s'. Use the format '%s'") % + (value, u"2012-12-31")) def _str_to_datetime(self, cr, uid, model, column, value, context=None): try: @@ -135,7 +136,8 @@ class ir_fields_converter(orm.Model): return value, [] except ValueError: raise ValueError( - _(u"'%s' does not seem to be a valid datetime for field '%%(field)s'") % value) + _(u"'%s' does not seem to be a valid datetime for field '%%(field)s'. Use the format '%s'") % + (value, u"2012-12-31 23:59:59")) def _get_translations(self, cr, uid, types, src, context): types = tuple(types) diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 65c4b6e9e57..8bfcd20f4f3 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -1008,7 +1008,7 @@ class test_date(ImporterCase): result = self.import_(['value'], [['not really a date']]) self.assertEqual(result['messages'], [ message(u"'not really a date' does not seem to be a valid date " - u"for field 'unknown'")]) + u"for field 'unknown'. Use the format '2012-12-31'")]) self.assertIs(result['ids'], False) class test_datetime(ImporterCase): @@ -1028,7 +1028,8 @@ class test_datetime(ImporterCase): result = self.import_(['value'], [['not really a datetime']]) self.assertEqual(result['messages'], [ message(u"'not really a datetime' does not seem to be a valid " - u"datetime for field 'unknown'")]) + u"datetime for field 'unknown'. Use the format " + u"'2012-12-31 23:59:59'")]) self.assertIs(result['ids'], False) # function, related, reference: written to db as-is... From cb265f710c0b752e2928553ffc8a654ed2436ca5 Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Thu, 4 Oct 2012 17:57:11 +0530 Subject: [PATCH 056/171] [IMP]replace callenabled for on_record_loaded method. bzr revid: vme@tinyerp.com-20121004122711-hzymzdnzbyutxge5 --- addons/web/static/src/js/view_form.js | 20 ++++++---- addons/web_diagram/static/src/js/diagram.js | 42 +++++++++++++++++---- 2 files changed, 47 insertions(+), 15 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 1c32d5ef63e..279f44827ba 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -111,6 +111,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM self.on("change:actual_mode", self, self.init_pager); self.init_pager(); }); + self.on("record_load", self, self.on_record_loaded); instance.web.bus.on('clear_uncommitted_changes', this, function(e) { if (!this.can_be_discarded()) { e.preventDefault(); @@ -316,7 +317,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM fields.push('display_name'); return self.dataset.read_index(fields, { context: { 'bin_size': true, 'future_display_name' : true } - }).pipe(self.on_record_loaded); + }).pipe(function(r){self.trigger('record_load',r)}); }); } return shown.pipe(function() { @@ -364,6 +365,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM field._inhibit_on_change_flag = false; set_values.push(result); }); + console.log("on_record_loaded"); return $.when.apply(null, set_values).pipe(function() { if (!record.id) { // New record: Second pass in order to trigger the onchanges @@ -398,12 +400,13 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM * @return {$.Deferred} */ load_defaults: function () { + var self = this; var keys = _.keys(this.fields_view.fields); if (keys.length) { return this.dataset.default_get(keys) - .pipe(this.on_record_loaded); + .pipe(function(r){self.trigger('record_load',r)}); } - return this.on_record_loaded({}); + return self.trigger('record_load',{}); }, on_form_changed: function() { this.trigger("view_content_has_changed"); @@ -737,7 +740,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.trigger('history_back'); } else { this.to_view_mode(); - this.on_record_loaded(this.datarecord); + self.trigger('record_load',this.datarecord); } } return false; @@ -943,7 +946,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM fields.push('display_name'); return self.dataset.read_index(fields, { context : { 'bin_size' : true, 'future_display_name' : true } - }).pipe(self.on_record_loaded); + }).pipe(function(r){self.trigger('record_load',r)}); } }); }, @@ -3291,9 +3294,10 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ if (self.get("effective_readonly")) { $(".oe_form_buttons", controller.$el).children().remove(); } - controller.on_record_loaded.add_last(function() { - once.resolve(); - }); + controller.on("record_load", self, function(){ + console.log("view manager form"); + once.resolve(); + }); controller.on_pager_action.add_first(function() { self.save_any_view(); }); diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 543cc6a9c12..e2ec463ec8e 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -241,22 +241,35 @@ instance.web.DiagramView = instance.web.View.extend({ } ); - pop.on_write.add(function() { + pop.on_write_completed.add(function() { self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded); }); var form_fields = [self.parent_field]; var form_controller = pop.view_form; - form_controller.on_record_loaded.add_first(function() { + /*form_controller.on_record_loaded.add_first(function() { + console.log("web diagram add_first"); _.each(form_fields, function(fld) { if (!(fld in form_controller.fields)) { return; } var field = form_controller.fields[fld]; field.$input.prop('disabled', true); field.$drop_down.unbind(); - field.$menu_btn.unbind(); + //field.$menu_btn.unbind(); }); - }); + });*/ + form_controller.on("record_load", self, function(){ + console.log("web diagram add_first"); + _.each(form_fields, function(fld) { + if (!(fld in form_controller.fields)) { return; } + var field = form_controller.fields[fld]; + field.$input.prop('disabled', true); + field.$drop_down.unbind(); + //field.$menu_btn.unbind(); + }); + }); + + }, // Creates a popup to add a node to the diagram @@ -281,7 +294,15 @@ instance.web.DiagramView = instance.web.View.extend({ var form_controller = pop.view_form; var form_fields = [this.parent_field]; - form_controller.on_record_loaded.add_last(function() { + /*form_controller.on_record_loaded.add_last(function() { + _.each(form_fields, function(fld) { + if (!(fld in form_controller.fields)) { return; } + var field = form_controller.fields[fld]; + field.set_value(self.id); + field.dirty = true; + }); + });*/ + form_controller.on("record_load", self, function(){ _.each(form_fields, function(fld) { if (!(fld in form_controller.fields)) { return; } var field = form_controller.fields[fld]; @@ -340,12 +361,19 @@ instance.web.DiagramView = instance.web.View.extend({ var form_controller = pop.view_form; - form_controller.on_record_loaded.add_last(function () { + /*form_controller.on_record_loaded.add_last(function () { form_controller.fields[self.connectors.attrs.source].set_value(node_source_id); form_controller.fields[self.connectors.attrs.source].dirty = true; form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id); form_controller.fields[self.connectors.attrs.destination].dirty = true; - }); + });*/ + form_controller.on("record_load", self, function(){ + console.log("add_connector"); + form_controller.fields[self.connectors.attrs.source].set_value(node_source_id); + form_controller.fields[self.connectors.attrs.source].dirty = true; + form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id); + form_controller.fields[self.connectors.attrs.destination].dirty = true; + }); }, on_pager_action: function(action) { From 67a9cf851cb5405aa97c26284757018cf1f1ade4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 14:32:37 +0200 Subject: [PATCH 057/171] [IMP] use moreinfo key to provide more info as to what the boolean and date value should look like bzr revid: xmo@openerp.com-20121004123237-p5ht1uozaobxn8b7 --- openerp/addons/base/ir/ir_fields.py | 14 +++++++++----- openerp/tests/addons/test_impex/tests/test_load.py | 8 +++++--- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 05aa6782e9c..d949c00c420 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -99,7 +99,9 @@ class ir_fields_converter(orm.Model): return True, [orm.ImportWarning( _(u"Unknown value '%s' for boolean field '%%(field)s', assuming '%s'") - % (value, yes))] + % (value, yes), { + 'moreinfo': _(u"Use '1' for yes and '0' for no") + })] def _str_to_integer(self, cr, uid, model, column, value, context=None): try: @@ -127,8 +129,9 @@ class ir_fields_converter(orm.Model): return value, [] except ValueError: raise ValueError( - _(u"'%s' does not seem to be a valid date for field '%%(field)s'. Use the format '%s'") % - (value, u"2012-12-31")) + _(u"'%s' does not seem to be a valid date for field '%%(field)s'") % value, { + 'moreinfo': _(u"Use the format '%s'") % u"2012-12-31" + }) def _str_to_datetime(self, cr, uid, model, column, value, context=None): try: @@ -136,8 +139,9 @@ class ir_fields_converter(orm.Model): return value, [] except ValueError: raise ValueError( - _(u"'%s' does not seem to be a valid datetime for field '%%(field)s'. Use the format '%s'") % - (value, u"2012-12-31 23:59:59")) + _(u"'%s' does not seem to be a valid datetime for field '%%(field)s'") % value, { + 'moreinfo': _(u"Use the format '%s'") % u"2012-12-31 23:59:59" + }) def _get_translations(self, cr, uid, types, src, context): types = tuple(types) diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 8bfcd20f4f3..547675da2e4 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -184,6 +184,7 @@ class test_boolean_field(ImporterCase): self.assertEqual(len(result['ids']), 10) self.assertEqual(result['messages'], [ message(u"Unknown value '%s' for boolean field 'unknown', assuming 'yes'" % v[0], + moreinfo=u"Use '1' for yes and '0' for no", type='warning', from_=i, to_=i, record=i) for i, v in enumerate(trues) if v[0] not in ('true', 'yes', '1') @@ -1008,7 +1009,8 @@ class test_date(ImporterCase): result = self.import_(['value'], [['not really a date']]) self.assertEqual(result['messages'], [ message(u"'not really a date' does not seem to be a valid date " - u"for field 'unknown'. Use the format '2012-12-31'")]) + u"for field 'unknown'", + moreinfo=u"Use the format '2012-12-31'")]) self.assertIs(result['ids'], False) class test_datetime(ImporterCase): @@ -1028,8 +1030,8 @@ class test_datetime(ImporterCase): result = self.import_(['value'], [['not really a datetime']]) self.assertEqual(result['messages'], [ message(u"'not really a datetime' does not seem to be a valid " - u"datetime for field 'unknown'. Use the format " - u"'2012-12-31 23:59:59'")]) + u"datetime for field 'unknown'", + moreinfo=u"Use the format '2012-12-31 23:59:59'")]) self.assertIs(result['ids'], False) # function, related, reference: written to db as-is... From 25f695a650079d68fabb96a923c4d7411a27cd98 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 15:38:40 +0200 Subject: [PATCH 058/171] [IMP] pass restore callback to import action so that the listview can reload itself after coming back from an import (successful or not) and show the results of the import. bzr revid: xmo@openerp.com-20121004133840-a2uy4s5qhpqqvp8h --- addons/base_import/static/src/js/import.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/base_import/static/src/js/import.js b/addons/base_import/static/src/js/import.js index 551196678a3..5980eb2384c 100644 --- a/addons/base_import/static/src/js/import.js +++ b/addons/base_import/static/src/js/import.js @@ -53,6 +53,8 @@ openerp.base_import = function (instance) { params: { model: self.dataset.model } + }, void 0, void 0, function () { + self.reload(); }); return false; }); From 27f15d8af855baaf8d01d69615256804a61320fd Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 17:17:53 +0200 Subject: [PATCH 059/171] [IMP] 'more info' actions from relational fields conversion link to ir.model.data if failing to link with database or external ids bzr revid: xmo@openerp.com-20121004151753-wuyra1fjo31ur56g --- openerp/addons/base/ir/ir.xml | 1 + openerp/addons/base/ir/ir_fields.py | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/openerp/addons/base/ir/ir.xml b/openerp/addons/base/ir/ir.xml index ea549c39993..491bc9c8131 100644 --- a/openerp/addons/base/ir/ir.xml +++ b/openerp/addons/base/ir/ir.xml @@ -1019,6 +1019,7 @@ + diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index d949c00c420..2f557c4aaa9 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -201,13 +201,16 @@ class ir_fields_converter(orm.Model): if context is None: context = {} id = None warnings = [] - extras = {'moreinfo': { - 'type': 'ir.actions.act_window', 'target': 'new', - 'res_model': column._obj, 'view_mode': 'tree,form', - 'view_type': 'form', - 'views': [(False, 'tree', (False, 'form'))], - 'help': _(u"See all possible values") - }} + action = {'type': 'ir.actions.act_window', 'target': 'new', + 'view_mode': 'tree,form', 'view_type': 'form', + 'views': [(False, 'tree', (False, 'form'))], + 'help': _(u"See all possible values")} + if subfield is None: + action['res_model'] = column._obj + elif subfield in ('id', '.id'): + action['res_model'] = 'ir.model.data' + action['domain'] = [('model', '=', column._obj)] + RelatedModel = self.pool[column._obj] if subfield == '.id': field_type = _(u"database id") @@ -221,7 +224,7 @@ class ir_fields_converter(orm.Model): # type error raise ValueError( _(u"Invalid database id '%s' for the field '%%(field)s'") % value, - extras) + {'moreinfo': action}) elif subfield == 'id': field_type = _(u"external id") if '.' in value: @@ -252,7 +255,8 @@ class ir_fields_converter(orm.Model): if id is None: raise ValueError( _(u"No matching record found for %(field_type)s '%(value)s' in field '%%(field)s'") - % {'field_type': field_type, 'value': value}, extras) + % {'field_type': field_type, 'value': value}, + {'moreinfo': action}) return id, field_type, warnings def _referencing_subfield(self, record): From 1a54bd9e4bb4bd91e2049417a656a33907b18a55 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 4 Oct 2012 17:29:36 +0200 Subject: [PATCH 060/171] [FIX] import tests pertaining to failing to link a relational subfield by id, xid or name bzr revid: xmo@openerp.com-20121004152936-wsbwa4wbw9rii1an --- .../addons/test_impex/tests/test_load.py | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 547675da2e4..afa3e8dd8e3 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -12,16 +12,14 @@ def message(msg, type='error', from_=0, to_=0, record=0, field='value', **kwargs return dict(kwargs, type=type, rows={'from': from_, 'to': to_}, record=record, field=field, message=msg) -def moreaction(model): - return { - 'type': 'ir.actions.act_window', - 'target': 'new', - 'res_model': model, - 'view_mode': 'tree,form', - 'view_type': 'form', - 'views': [(False, 'tree', (False, 'form'))], - 'help': u"See all possible values" - } +def moreaction(**kwargs): + return dict(kwargs, + type='ir.actions.act_window', + target='new', + view_mode='tree,form', + view_type='form', + views=[(False, 'tree', (False, 'form'))], + help=u"See all possible values") def values(seq, field='value'): return [item[field] for item in seq] @@ -626,7 +624,7 @@ class test_m2o(ImporterCase): self.assertEqual(result['messages'], [ message(u"No matching record found for name '%s' in field 'unknown'" % id, from_=index, to_=index, record=index, - moreinfo=moreaction('export.integer')) + moreinfo=moreaction(res_model='export.integer')) for index, id in enumerate([integer_id1, integer_id2, integer_id1])]) self.assertIs(result['ids'], False) @@ -636,7 +634,8 @@ class test_m2o(ImporterCase): self.assertEqual(result['messages'], [ message(u"Invalid database id 'foo' for the field 'unknown'", - moreinfo=moreaction('export.integer')) + moreinfo=moreaction(res_model='ir.model.data', + domain=[('model','=','export.integer')])) ]) self.assertIs(result['ids'], False) @@ -654,19 +653,22 @@ class test_m2o(ImporterCase): result = self.import_(['value'], [['nameisnoexist:3']]) self.assertEqual(result['messages'], [message( u"No matching record found for name 'nameisnoexist:3' " - u"in field 'unknown'", moreinfo=moreaction('export.integer'))]) + u"in field 'unknown'", moreinfo=moreaction( + res_model='export.integer'))]) self.assertIs(result['ids'], False) result = self.import_(['value/id'], [['noxidhere']]) self.assertEqual(result['messages'], [message( u"No matching record found for external id 'noxidhere' " - u"in field 'unknown'", moreinfo=moreaction('export.integer'))]) + u"in field 'unknown'", moreinfo=moreaction( + res_model='ir.model.data', domain=[('model','=','export.integer')]))]) self.assertIs(result['ids'], False) result = self.import_(['value/.id'], [['66']]) self.assertEqual(result['messages'], [message( u"No matching record found for database id '66' " - u"in field 'unknown'", moreinfo=moreaction('export.integer'))]) + u"in field 'unknown'", moreinfo=moreaction( + res_model='ir.model.data', domain=[('model','=','export.integer')]))]) self.assertIs(result['ids'], False) def test_fail_multiple(self): @@ -718,7 +720,8 @@ class test_m2m(ImporterCase): result = self.import_(['value/.id'], [['42']]) self.assertEqual(result['messages'], [message( u"No matching record found for database id '42' in field " - u"'unknown'", moreinfo=moreaction('export.many2many.other'))]) + u"'unknown'", moreinfo=moreaction( + res_model='ir.model.data', domain=[('model','=','export.many2many.other')]))]) self.assertIs(result['ids'], False) def test_xids(self): @@ -744,7 +747,8 @@ class test_m2m(ImporterCase): result = self.import_(['value/id'], [['noxidforthat']]) self.assertEqual(result['messages'], [message( u"No matching record found for external id 'noxidforthat' in field" - u" 'unknown'", moreinfo=moreaction('export.many2many.other'))]) + u" 'unknown'", moreinfo=moreaction( + res_model='ir.model.data', domain=[('model','=','export.many2many.other')]))]) self.assertIs(result['ids'], False) def test_names(self): @@ -773,7 +777,8 @@ class test_m2m(ImporterCase): result = self.import_(['value'], [['wherethem2mhavenonames']]) self.assertEqual(result['messages'], [message( u"No matching record found for name 'wherethem2mhavenonames' in " - u"field 'unknown'", moreinfo=moreaction('export.many2many.other'))]) + u"field 'unknown'", moreinfo=moreaction( + res_model='export.many2many.other'))]) self.assertIs(result['ids'], False) def test_import_to_existing(self): @@ -807,7 +812,7 @@ class test_o2m(ImporterCase): [['5', s]]) self.assertEqual(result['messages'], [message( u"No matching record found for name '%s' in field 'unknown'" % s, - moreinfo=moreaction('export.one2many.child'))]) + moreinfo=moreaction(res_model='export.one2many.child'))]) self.assertIs(result['ids'], False) def test_single(self): From a3f1a64c45568fe0fe2f0ceecb41887c4b61a1a4 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 5 Oct 2012 10:34:40 +0200 Subject: [PATCH 061/171] [IMP] make base_import auto-install bzr revid: xmo@openerp.com-20121005083440-xenjci6qag7bkk3n --- addons/base_import/__openerp__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_import/__openerp__.py b/addons/base_import/__openerp__.py index bb527e7b49d..f5a707650d8 100644 --- a/addons/base_import/__openerp__.py +++ b/addons/base_import/__openerp__.py @@ -26,7 +26,7 @@ Re-implement openerp's file import system: 'author': 'OpenERP SA', 'depends': ['base'], 'installable': True, - 'auto_install': False, # set to true and allow uninstall? + 'auto_install': True, 'css': [ 'static/lib/select2/select2.css', 'static/src/css/import.css', From 50a7718e45014e5db1f2d304cc3b33a35c0baf8b Mon Sep 17 00:00:00 2001 From: "Jalpesh Patel (OpenERP)" Date: Fri, 5 Oct 2012 18:48:04 +0530 Subject: [PATCH 062/171] [IMP]send mail (automated purchase order notification mail) bzr revid: pja@tinyerp.com-20121005131804-qliwjans60m08lhx --- addons/mail/wizard/mail_compose_message.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index fbadf76f2fb..b0e2e89d795 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -81,7 +81,8 @@ class mail_compose_message(osv.TransientModel): elif composition_mode == 'comment' and model and res_id: vals = self.get_record_data(cr, uid, model, res_id, context=context) elif composition_mode == 'mass_mail' and model and active_ids: - vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'} + vals = self.pool.get('email.template').generate_email(cr, uid, context.get('default_template_id'), res_id, context=context) + vals.update({'content_subtype': 'html'}) else: vals = {'model': model, 'res_id': res_id} if composition_mode: From 3f0c2c1bb7f582498781e2f9306c12cc1e05637c Mon Sep 17 00:00:00 2001 From: "Atul Patel (OpenERP)" Date: Fri, 5 Oct 2012 19:01:29 +0530 Subject: [PATCH 063/171] [IMP]: Enble stock moves menu in stock. bzr revid: atp@tinyerp.com-20121005133129-iik7g60lme14ccm3 --- addons/stock/security/stock_security.xml | 2 ++ addons/stock/stock_view.xml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/stock/security/stock_security.xml b/addons/stock/security/stock_security.xml index c9f8a03a378..d8f9c2973db 100644 --- a/addons/stock/security/stock_security.xml +++ b/addons/stock/security/stock_security.xml @@ -21,6 +21,8 @@ Manage Logistic Serial Numbers + + diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index f5320a963c7..ad62cc3d4f0 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -272,7 +272,7 @@ + sequence="3" groups="stock.group_tracking_lot"/> Date: Sat, 6 Oct 2012 19:10:53 +0200 Subject: [PATCH 064/171] rm files, move postload hook bzr revid: al@openerp.com-20121006171053-qggp1r5263h7v7vd --- LICENSE.web | 661 ------------------ Makefile | 34 - README.web | 9 - addons/web/__init__.py | 33 +- addons/web/common/http.py | 24 + {doc => addons/web/doc}/Makefile | 0 {doc => addons/web/doc}/_static/openerp.png | Bin .../web/doc}/_templates/sidebarintro.html | 0 .../web/doc}/_templates/sidebarlogo.html | 0 {doc => addons/web/doc}/_themes/LICENSE | 0 {doc => addons/web/doc}/_themes/README | 0 .../web/doc}/_themes/flask/layout.html | 0 .../web/doc}/_themes/flask/relations.html | 0 .../doc}/_themes/flask/static/flasky.css_t | 0 .../doc}/_themes/flask/static/small_flask.css | 0 .../web/doc}/_themes/flask/theme.conf | 0 .../web/doc}/_themes/flask_small/layout.html | 0 .../_themes/flask_small/static/flasky.css_t | 0 .../web/doc}/_themes/flask_small/theme.conf | 0 .../web/doc}/_themes/flask_theme_support.py | 0 {doc => addons/web/doc}/addon-structure.txt | 0 {doc => addons/web/doc}/async.rst | 0 {doc => addons/web/doc}/changelog-7.0.rst | 0 {doc => addons/web/doc}/conf.py | 0 {doc => addons/web/doc}/form-notes.rst | 0 .../web/doc}/guides/client-action.rst | 0 {doc => addons/web/doc}/index.rst | 0 {doc => addons/web/doc}/list-view.rst | 0 {doc => addons/web/doc}/make.bat | 0 {doc => addons/web/doc}/rpc.rst | 0 {doc => addons/web/doc}/search-view.rst | 0 {doc => addons/web/doc}/widget.rst | 0 logging.json | 27 - openerp-web | 116 --- openerp-web.cfg | 3 - setup.py | 124 ---- 36 files changed, 27 insertions(+), 1004 deletions(-) delete mode 100644 LICENSE.web delete mode 100644 Makefile delete mode 100644 README.web rename {doc => addons/web/doc}/Makefile (100%) rename {doc => addons/web/doc}/_static/openerp.png (100%) rename {doc => addons/web/doc}/_templates/sidebarintro.html (100%) rename {doc => addons/web/doc}/_templates/sidebarlogo.html (100%) rename {doc => addons/web/doc}/_themes/LICENSE (100%) rename {doc => addons/web/doc}/_themes/README (100%) rename {doc => addons/web/doc}/_themes/flask/layout.html (100%) rename {doc => addons/web/doc}/_themes/flask/relations.html (100%) rename {doc => addons/web/doc}/_themes/flask/static/flasky.css_t (100%) rename {doc => addons/web/doc}/_themes/flask/static/small_flask.css (100%) rename {doc => addons/web/doc}/_themes/flask/theme.conf (100%) rename {doc => addons/web/doc}/_themes/flask_small/layout.html (100%) rename {doc => addons/web/doc}/_themes/flask_small/static/flasky.css_t (100%) rename {doc => addons/web/doc}/_themes/flask_small/theme.conf (100%) rename {doc => addons/web/doc}/_themes/flask_theme_support.py (100%) rename {doc => addons/web/doc}/addon-structure.txt (100%) rename {doc => addons/web/doc}/async.rst (100%) rename {doc => addons/web/doc}/changelog-7.0.rst (100%) rename {doc => addons/web/doc}/conf.py (100%) rename {doc => addons/web/doc}/form-notes.rst (100%) rename {doc => addons/web/doc}/guides/client-action.rst (100%) rename {doc => addons/web/doc}/index.rst (100%) rename {doc => addons/web/doc}/list-view.rst (100%) rename {doc => addons/web/doc}/make.bat (100%) rename {doc => addons/web/doc}/rpc.rst (100%) rename {doc => addons/web/doc}/search-view.rst (100%) rename {doc => addons/web/doc}/widget.rst (100%) delete mode 100644 logging.json delete mode 100755 openerp-web delete mode 100644 openerp-web.cfg delete mode 100755 setup.py diff --git a/LICENSE.web b/LICENSE.web deleted file mode 100644 index dba13ed2ddf..00000000000 --- a/LICENSE.web +++ /dev/null @@ -1,661 +0,0 @@ - GNU AFFERO GENERAL PUBLIC LICENSE - Version 3, 19 November 2007 - - Copyright (C) 2007 Free Software Foundation, Inc. - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - - Preamble - - The GNU Affero General Public License is a free, copyleft license for -software and other kinds of works, specifically designed to ensure -cooperation with the community in the case of network server software. - - The licenses for most software and other practical works are designed -to take away your freedom to share and change the works. By contrast, -our General Public Licenses are intended to guarantee your freedom to -share and change all versions of a program--to make sure it remains free -software for all its users. - - When we speak of free software, we are referring to freedom, not -price. Our General Public Licenses are designed to make sure that you -have the freedom to distribute copies of free software (and charge for -them if you wish), that you receive source code or can get it if you -want it, that you can change the software or use pieces of it in new -free programs, and that you know you can do these things. - - Developers that use our General Public Licenses protect your rights -with two steps: (1) assert copyright on the software, and (2) offer -you this License which gives you legal permission to copy, distribute -and/or modify the software. - - A secondary benefit of defending all users' freedom is that -improvements made in alternate versions of the program, if they -receive widespread use, become available for other developers to -incorporate. Many developers of free software are heartened and -encouraged by the resulting cooperation. However, in the case of -software used on network servers, this result may fail to come about. -The GNU General Public License permits making a modified version and -letting the public access it on a server without ever releasing its -source code to the public. - - The GNU Affero General Public License is designed specifically to -ensure that, in such cases, the modified source code becomes available -to the community. It requires the operator of a network server to -provide the source code of the modified version running there to the -users of that server. Therefore, public use of a modified version, on -a publicly accessible server, gives the public access to the source -code of the modified version. - - An older license, called the Affero General Public License and -published by Affero, was designed to accomplish similar goals. This is -a different license, not a version of the Affero GPL, but Affero has -released a new version of the Affero GPL which permits relicensing under -this license. - - The precise terms and conditions for copying, distribution and -modification follow. - - TERMS AND CONDITIONS - - 0. Definitions. - - "This License" refers to version 3 of the GNU Affero General Public License. - - "Copyright" also means copyright-like laws that apply to other kinds of -works, such as semiconductor masks. - - "The Program" refers to any copyrightable work licensed under this -License. Each licensee is addressed as "you". "Licensees" and -"recipients" may be individuals or organizations. - - To "modify" a work means to copy from or adapt all or part of the work -in a fashion requiring copyright permission, other than the making of an -exact copy. The resulting work is called a "modified version" of the -earlier work or a work "based on" the earlier work. - - A "covered work" means either the unmodified Program or a work based -on the Program. - - To "propagate" a work means to do anything with it that, without -permission, would make you directly or secondarily liable for -infringement under applicable copyright law, except executing it on a -computer or modifying a private copy. Propagation includes copying, -distribution (with or without modification), making available to the -public, and in some countries other activities as well. - - To "convey" a work means any kind of propagation that enables other -parties to make or receive copies. Mere interaction with a user through -a computer network, with no transfer of a copy, is not conveying. - - An interactive user interface displays "Appropriate Legal Notices" -to the extent that it includes a convenient and prominently visible -feature that (1) displays an appropriate copyright notice, and (2) -tells the user that there is no warranty for the work (except to the -extent that warranties are provided), that licensees may convey the -work under this License, and how to view a copy of this License. If -the interface presents a list of user commands or options, such as a -menu, a prominent item in the list meets this criterion. - - 1. Source Code. - - The "source code" for a work means the preferred form of the work -for making modifications to it. "Object code" means any non-source -form of a work. - - A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of -interfaces specified for a particular programming language, one that -is widely used among developers working in that language. - - The "System Libraries" of an executable work include anything, other -than the work as a whole, that (a) is included in the normal form of -packaging a Major Component, but which is not part of that Major -Component, and (b) serves only to enable use of the work with that -Major Component, or to implement a Standard Interface for which an -implementation is available to the public in source code form. A -"Major Component", in this context, means a major essential component -(kernel, window system, and so on) of the specific operating system -(if any) on which the executable work runs, or a compiler used to -produce the work, or an object code interpreter used to run it. - - The "Corresponding Source" for a work in object code form means all -the source code needed to generate, install, and (for an executable -work) run the object code and to modify the work, including scripts to -control those activities. However, it does not include the work's -System Libraries, or general-purpose tools or generally available free -programs which are used unmodified in performing those activities but -which are not part of the work. For example, Corresponding Source -includes interface definition files associated with source files for -the work, and the source code for shared libraries and dynamically -linked subprograms that the work is specifically designed to require, -such as by intimate data communication or control flow between those -subprograms and other parts of the work. - - The Corresponding Source need not include anything that users -can regenerate automatically from other parts of the Corresponding -Source. - - The Corresponding Source for a work in source code form is that -same work. - - 2. Basic Permissions. - - All rights granted under this License are granted for the term of -copyright on the Program, and are irrevocable provided the stated -conditions are met. This License explicitly affirms your unlimited -permission to run the unmodified Program. The output from running a -covered work is covered by this License only if the output, given its -content, constitutes a covered work. This License acknowledges your -rights of fair use or other equivalent, as provided by copyright law. - - You may make, run and propagate covered works that you do not -convey, without conditions so long as your license otherwise remains -in force. You may convey covered works to others for the sole purpose -of having them make modifications exclusively for you, or provide you -with facilities for running those works, provided that you comply with -the terms of this License in conveying all material for which you do -not control copyright. Those thus making or running the covered works -for you must do so exclusively on your behalf, under your direction -and control, on terms that prohibit them from making any copies of -your copyrighted material outside their relationship with you. - - Conveying under any other circumstances is permitted solely under -the conditions stated below. Sublicensing is not allowed; section 10 -makes it unnecessary. - - 3. Protecting Users' Legal Rights From Anti-Circumvention Law. - - No covered work shall be deemed part of an effective technological -measure under any applicable law fulfilling obligations under article -11 of the WIPO copyright treaty adopted on 20 December 1996, or -similar laws prohibiting or restricting circumvention of such -measures. - - When you convey a covered work, you waive any legal power to forbid -circumvention of technological measures to the extent such circumvention -is effected by exercising rights under this License with respect to -the covered work, and you disclaim any intention to limit operation or -modification of the work as a means of enforcing, against the work's -users, your or third parties' legal rights to forbid circumvention of -technological measures. - - 4. Conveying Verbatim Copies. - - You may convey verbatim copies of the Program's source code as you -receive it, in any medium, provided that you conspicuously and -appropriately publish on each copy an appropriate copyright notice; -keep intact all notices stating that this License and any -non-permissive terms added in accord with section 7 apply to the code; -keep intact all notices of the absence of any warranty; and give all -recipients a copy of this License along with the Program. - - You may charge any price or no price for each copy that you convey, -and you may offer support or warranty protection for a fee. - - 5. Conveying Modified Source Versions. - - You may convey a work based on the Program, or the modifications to -produce it from the Program, in the form of source code under the -terms of section 4, provided that you also meet all of these conditions: - - a) The work must carry prominent notices stating that you modified - it, and giving a relevant date. - - b) The work must carry prominent notices stating that it is - released under this License and any conditions added under section - 7. This requirement modifies the requirement in section 4 to - "keep intact all notices". - - c) You must license the entire work, as a whole, under this - License to anyone who comes into possession of a copy. This - License will therefore apply, along with any applicable section 7 - additional terms, to the whole of the work, and all its parts, - regardless of how they are packaged. This License gives no - permission to license the work in any other way, but it does not - invalidate such permission if you have separately received it. - - d) If the work has interactive user interfaces, each must display - Appropriate Legal Notices; however, if the Program has interactive - interfaces that do not display Appropriate Legal Notices, your - work need not make them do so. - - A compilation of a covered work with other separate and independent -works, which are not by their nature extensions of the covered work, -and which are not combined with it such as to form a larger program, -in or on a volume of a storage or distribution medium, is called an -"aggregate" if the compilation and its resulting copyright are not -used to limit the access or legal rights of the compilation's users -beyond what the individual works permit. Inclusion of a covered work -in an aggregate does not cause this License to apply to the other -parts of the aggregate. - - 6. Conveying Non-Source Forms. - - You may convey a covered work in object code form under the terms -of sections 4 and 5, provided that you also convey the -machine-readable Corresponding Source under the terms of this License, -in one of these ways: - - a) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by the - Corresponding Source fixed on a durable physical medium - customarily used for software interchange. - - b) Convey the object code in, or embodied in, a physical product - (including a physical distribution medium), accompanied by a - written offer, valid for at least three years and valid for as - long as you offer spare parts or customer support for that product - model, to give anyone who possesses the object code either (1) a - copy of the Corresponding Source for all the software in the - product that is covered by this License, on a durable physical - medium customarily used for software interchange, for a price no - more than your reasonable cost of physically performing this - conveying of source, or (2) access to copy the - Corresponding Source from a network server at no charge. - - c) Convey individual copies of the object code with a copy of the - written offer to provide the Corresponding Source. This - alternative is allowed only occasionally and noncommercially, and - only if you received the object code with such an offer, in accord - with subsection 6b. - - d) Convey the object code by offering access from a designated - place (gratis or for a charge), and offer equivalent access to the - Corresponding Source in the same way through the same place at no - further charge. You need not require recipients to copy the - Corresponding Source along with the object code. If the place to - copy the object code is a network server, the Corresponding Source - may be on a different server (operated by you or a third party) - that supports equivalent copying facilities, provided you maintain - clear directions next to the object code saying where to find the - Corresponding Source. Regardless of what server hosts the - Corresponding Source, you remain obligated to ensure that it is - available for as long as needed to satisfy these requirements. - - e) Convey the object code using peer-to-peer transmission, provided - you inform other peers where the object code and Corresponding - Source of the work are being offered to the general public at no - charge under subsection 6d. - - A separable portion of the object code, whose source code is excluded -from the Corresponding Source as a System Library, need not be -included in conveying the object code work. - - A "User Product" is either (1) a "consumer product", which means any -tangible personal property which is normally used for personal, family, -or household purposes, or (2) anything designed or sold for incorporation -into a dwelling. In determining whether a product is a consumer product, -doubtful cases shall be resolved in favor of coverage. For a particular -product received by a particular user, "normally used" refers to a -typical or common use of that class of product, regardless of the status -of the particular user or of the way in which the particular user -actually uses, or expects or is expected to use, the product. A product -is a consumer product regardless of whether the product has substantial -commercial, industrial or non-consumer uses, unless such uses represent -the only significant mode of use of the product. - - "Installation Information" for a User Product means any methods, -procedures, authorization keys, or other information required to install -and execute modified versions of a covered work in that User Product from -a modified version of its Corresponding Source. The information must -suffice to ensure that the continued functioning of the modified object -code is in no case prevented or interfered with solely because -modification has been made. - - If you convey an object code work under this section in, or with, or -specifically for use in, a User Product, and the conveying occurs as -part of a transaction in which the right of possession and use of the -User Product is transferred to the recipient in perpetuity or for a -fixed term (regardless of how the transaction is characterized), the -Corresponding Source conveyed under this section must be accompanied -by the Installation Information. But this requirement does not apply -if neither you nor any third party retains the ability to install -modified object code on the User Product (for example, the work has -been installed in ROM). - - The requirement to provide Installation Information does not include a -requirement to continue to provide support service, warranty, or updates -for a work that has been modified or installed by the recipient, or for -the User Product in which it has been modified or installed. Access to a -network may be denied when the modification itself materially and -adversely affects the operation of the network or violates the rules and -protocols for communication across the network. - - Corresponding Source conveyed, and Installation Information provided, -in accord with this section must be in a format that is publicly -documented (and with an implementation available to the public in -source code form), and must require no special password or key for -unpacking, reading or copying. - - 7. Additional Terms. - - "Additional permissions" are terms that supplement the terms of this -License by making exceptions from one or more of its conditions. -Additional permissions that are applicable to the entire Program shall -be treated as though they were included in this License, to the extent -that they are valid under applicable law. If additional permissions -apply only to part of the Program, that part may be used separately -under those permissions, but the entire Program remains governed by -this License without regard to the additional permissions. - - When you convey a copy of a covered work, you may at your option -remove any additional permissions from that copy, or from any part of -it. (Additional permissions may be written to require their own -removal in certain cases when you modify the work.) You may place -additional permissions on material, added by you to a covered work, -for which you have or can give appropriate copyright permission. - - Notwithstanding any other provision of this License, for material you -add to a covered work, you may (if authorized by the copyright holders of -that material) supplement the terms of this License with terms: - - a) Disclaiming warranty or limiting liability differently from the - terms of sections 15 and 16 of this License; or - - b) Requiring preservation of specified reasonable legal notices or - author attributions in that material or in the Appropriate Legal - Notices displayed by works containing it; or - - c) Prohibiting misrepresentation of the origin of that material, or - requiring that modified versions of such material be marked in - reasonable ways as different from the original version; or - - d) Limiting the use for publicity purposes of names of licensors or - authors of the material; or - - e) Declining to grant rights under trademark law for use of some - trade names, trademarks, or service marks; or - - f) Requiring indemnification of licensors and authors of that - material by anyone who conveys the material (or modified versions of - it) with contractual assumptions of liability to the recipient, for - any liability that these contractual assumptions directly impose on - those licensors and authors. - - All other non-permissive additional terms are considered "further -restrictions" within the meaning of section 10. If the Program as you -received it, or any part of it, contains a notice stating that it is -governed by this License along with a term that is a further -restriction, you may remove that term. If a license document contains -a further restriction but permits relicensing or conveying under this -License, you may add to a covered work material governed by the terms -of that license document, provided that the further restriction does -not survive such relicensing or conveying. - - If you add terms to a covered work in accord with this section, you -must place, in the relevant source files, a statement of the -additional terms that apply to those files, or a notice indicating -where to find the applicable terms. - - Additional terms, permissive or non-permissive, may be stated in the -form of a separately written license, or stated as exceptions; -the above requirements apply either way. - - 8. Termination. - - You may not propagate or modify a covered work except as expressly -provided under this License. Any attempt otherwise to propagate or -modify it is void, and will automatically terminate your rights under -this License (including any patent licenses granted under the third -paragraph of section 11). - - However, if you cease all violation of this License, then your -license from a particular copyright holder is reinstated (a) -provisionally, unless and until the copyright holder explicitly and -finally terminates your license, and (b) permanently, if the copyright -holder fails to notify you of the violation by some reasonable means -prior to 60 days after the cessation. - - Moreover, your license from a particular copyright holder is -reinstated permanently if the copyright holder notifies you of the -violation by some reasonable means, this is the first time you have -received notice of violation of this License (for any work) from that -copyright holder, and you cure the violation prior to 30 days after -your receipt of the notice. - - Termination of your rights under this section does not terminate the -licenses of parties who have received copies or rights from you under -this License. If your rights have been terminated and not permanently -reinstated, you do not qualify to receive new licenses for the same -material under section 10. - - 9. Acceptance Not Required for Having Copies. - - You are not required to accept this License in order to receive or -run a copy of the Program. Ancillary propagation of a covered work -occurring solely as a consequence of using peer-to-peer transmission -to receive a copy likewise does not require acceptance. However, -nothing other than this License grants you permission to propagate or -modify any covered work. These actions infringe copyright if you do -not accept this License. Therefore, by modifying or propagating a -covered work, you indicate your acceptance of this License to do so. - - 10. Automatic Licensing of Downstream Recipients. - - Each time you convey a covered work, the recipient automatically -receives a license from the original licensors, to run, modify and -propagate that work, subject to this License. You are not responsible -for enforcing compliance by third parties with this License. - - An "entity transaction" is a transaction transferring control of an -organization, or substantially all assets of one, or subdividing an -organization, or merging organizations. If propagation of a covered -work results from an entity transaction, each party to that -transaction who receives a copy of the work also receives whatever -licenses to the work the party's predecessor in interest had or could -give under the previous paragraph, plus a right to possession of the -Corresponding Source of the work from the predecessor in interest, if -the predecessor has it or can get it with reasonable efforts. - - You may not impose any further restrictions on the exercise of the -rights granted or affirmed under this License. For example, you may -not impose a license fee, royalty, or other charge for exercise of -rights granted under this License, and you may not initiate litigation -(including a cross-claim or counterclaim in a lawsuit) alleging that -any patent claim is infringed by making, using, selling, offering for -sale, or importing the Program or any portion of it. - - 11. Patents. - - A "contributor" is a copyright holder who authorizes use under this -License of the Program or a work on which the Program is based. The -work thus licensed is called the contributor's "contributor version". - - A contributor's "essential patent claims" are all patent claims -owned or controlled by the contributor, whether already acquired or -hereafter acquired, that would be infringed by some manner, permitted -by this License, of making, using, or selling its contributor version, -but do not include claims that would be infringed only as a -consequence of further modification of the contributor version. For -purposes of this definition, "control" includes the right to grant -patent sublicenses in a manner consistent with the requirements of -this License. - - Each contributor grants you a non-exclusive, worldwide, royalty-free -patent license under the contributor's essential patent claims, to -make, use, sell, offer for sale, import and otherwise run, modify and -propagate the contents of its contributor version. - - In the following three paragraphs, a "patent license" is any express -agreement or commitment, however denominated, not to enforce a patent -(such as an express permission to practice a patent or covenant not to -sue for patent infringement). To "grant" such a patent license to a -party means to make such an agreement or commitment not to enforce a -patent against the party. - - If you convey a covered work, knowingly relying on a patent license, -and the Corresponding Source of the work is not available for anyone -to copy, free of charge and under the terms of this License, through a -publicly available network server or other readily accessible means, -then you must either (1) cause the Corresponding Source to be so -available, or (2) arrange to deprive yourself of the benefit of the -patent license for this particular work, or (3) arrange, in a manner -consistent with the requirements of this License, to extend the patent -license to downstream recipients. "Knowingly relying" means you have -actual knowledge that, but for the patent license, your conveying the -covered work in a country, or your recipient's use of the covered work -in a country, would infringe one or more identifiable patents in that -country that you have reason to believe are valid. - - If, pursuant to or in connection with a single transaction or -arrangement, you convey, or propagate by procuring conveyance of, a -covered work, and grant a patent license to some of the parties -receiving the covered work authorizing them to use, propagate, modify -or convey a specific copy of the covered work, then the patent license -you grant is automatically extended to all recipients of the covered -work and works based on it. - - A patent license is "discriminatory" if it does not include within -the scope of its coverage, prohibits the exercise of, or is -conditioned on the non-exercise of one or more of the rights that are -specifically granted under this License. You may not convey a covered -work if you are a party to an arrangement with a third party that is -in the business of distributing software, under which you make payment -to the third party based on the extent of your activity of conveying -the work, and under which the third party grants, to any of the -parties who would receive the covered work from you, a discriminatory -patent license (a) in connection with copies of the covered work -conveyed by you (or copies made from those copies), or (b) primarily -for and in connection with specific products or compilations that -contain the covered work, unless you entered into that arrangement, -or that patent license was granted, prior to 28 March 2007. - - Nothing in this License shall be construed as excluding or limiting -any implied license or other defenses to infringement that may -otherwise be available to you under applicable patent law. - - 12. No Surrender of Others' Freedom. - - If conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot convey a -covered work so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you may -not convey it at all. For example, if you agree to terms that obligate you -to collect a royalty for further conveying from those to whom you convey -the Program, the only way you could satisfy both those terms and this -License would be to refrain entirely from conveying the Program. - - 13. Remote Network Interaction; Use with the GNU General Public License. - - Notwithstanding any other provision of this License, if you modify the -Program, your modified version must prominently offer all users -interacting with it remotely through a computer network (if your version -supports such interaction) an opportunity to receive the Corresponding -Source of your version by providing access to the Corresponding Source -from a network server at no charge, through some standard or customary -means of facilitating copying of software. This Corresponding Source -shall include the Corresponding Source for any work covered by version 3 -of the GNU General Public License that is incorporated pursuant to the -following paragraph. - - Notwithstanding any other provision of this License, you have -permission to link or combine any covered work with a work licensed -under version 3 of the GNU General Public License into a single -combined work, and to convey the resulting work. The terms of this -License will continue to apply to the part which is the covered work, -but the work with which it is combined will remain governed by version -3 of the GNU General Public License. - - 14. Revised Versions of this License. - - The Free Software Foundation may publish revised and/or new versions of -the GNU Affero General Public License from time to time. Such new versions -will be similar in spirit to the present version, but may differ in detail to -address new problems or concerns. - - Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU Affero General -Public License "or any later version" applies to it, you have the -option of following the terms and conditions either of that numbered -version or of any later version published by the Free Software -Foundation. If the Program does not specify a version number of the -GNU Affero General Public License, you may choose any version ever published -by the Free Software Foundation. - - If the Program specifies that a proxy can decide which future -versions of the GNU Affero General Public License can be used, that proxy's -public statement of acceptance of a version permanently authorizes you -to choose that version for the Program. - - Later license versions may give you additional or different -permissions. However, no additional obligations are imposed on any -author or copyright holder as a result of your choosing to follow a -later version. - - 15. Disclaimer of Warranty. - - THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY -APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT -HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY -OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, -THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM -IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF -ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. Limitation of Liability. - - IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING -WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS -THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY -GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE -USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF -DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD -PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), -EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF -SUCH DAMAGES. - - 17. Interpretation of Sections 15 and 16. - - If the disclaimer of warranty and limitation of liability provided -above cannot be given local legal effect according to their terms, -reviewing courts shall apply local law that most closely approximates -an absolute waiver of all civil liability in connection with the -Program, unless a warranty or assumption of liability accompanies a -copy of the Program in return for a fee. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Programs - - If you develop a new program, and you want it to be of the greatest -possible use to the public, the best way to achieve this is to make it -free software which everyone can redistribute and change under these terms. - - To do so, attach the following notices to the program. It is safest -to attach them to the start of each source file to most effectively -state the exclusion of warranty; and each file should have at least -the "copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - 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 . - -Also add information on how to contact you by electronic and paper mail. - - If your software can interact with users remotely through a computer -network, you should also make sure that it provides a way for users to -get its source. For example, if your program is a web application, its -interface could display a "Source" link that leads users to an archive -of the code. There are many ways you could offer source, and different -solutions will be better for different programs; see section 13 for the -specific requirements. - - You should also get your employer (if you work as a programmer) or school, -if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU AGPL, see -. diff --git a/Makefile b/Makefile deleted file mode 100644 index 4beb20d3eed..00000000000 --- a/Makefile +++ /dev/null @@ -1,34 +0,0 @@ -.PHONY: all doc release clean - -HOST = 127.0.0.1 -PORT = 8080 - -all: run - -run: - python openerp-web.py -a ${HOST} -p ${PORT} - -release: - python setup.py sdist - -install: - python setup.py install - -clean: - @find . -name '*.pyc' -exec rm -f {} + - @find . -name '*.pyo' -exec rm -f {} + - @find . -name '*.swp' -exec rm -f {} + - @find . -name '*~' -exec rm -f {} + - @rm -rf build - @rm -rf dist - @rm -rf *.egg-info - -doc: - make -C doc html - -cloc: - cloc addons/*/common/*.py addons/*/controllers/*.py addons/*/static/src/*.js addons/*/static/src/js/*.js addons/*/static/src/css/*.css addons/*/static/src/xml/*.xml - -blamestat: - echo addons/*/common/*.py addons/*/controllers/*.py addons/*/static/src/js/*.js addons/*/static/src/css/*.css addons/*/static/src/xml/*.xml | xargs -t -n 1 bzr blame -v --long --all | awk '{print $2}' | sort | uniq -c | sort -n - diff --git a/README.web b/README.web deleted file mode 100644 index 72393bacd34..00000000000 --- a/README.web +++ /dev/null @@ -1,9 +0,0 @@ -OpenERP Web ------------ - -To build the documentation use: - -$ make doc - -then look at doc/build/html/index.html - diff --git a/addons/web/__init__.py b/addons/web/__init__.py index 621931e9167..c4337b37d2c 100644 --- a/addons/web/__init__.py +++ b/addons/web/__init__.py @@ -1,31 +1,4 @@ -import logging - -from . import common -from . import controllers - -_logger = logging.getLogger(__name__) - -class Options(object): - pass - -def wsgi_postload(): - import openerp - import os - import tempfile - import getpass - _logger.info("embedded mode") - o = Options() - o.dbfilter = openerp.tools.config['dbfilter'] - o.server_wide_modules = openerp.conf.server_wide_modules or ['web'] - try: - username = getpass.getuser() - except Exception: - username = "unknown" - o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) - o.addons_path = openerp.modules.module.ad_paths - o.serve_static = True - o.backend = 'local' - - app = common.http.Root(o) - openerp.wsgi.register_wsgi_handler(app) +import common +import controllers +wsgi_postload = common.http.wsgi_postload diff --git a/addons/web/common/http.py b/addons/web/common/http.py index 4955876894d..3b9fdf302f3 100644 --- a/addons/web/common/http.py +++ b/addons/web/common/http.py @@ -579,4 +579,28 @@ class Root(object): ps = '/' return None +class Options(object): + pass + +def wsgi_postload(): + import openerp + import tempfile + import getpass + _logger.info("embedded mode") + o = Options() + o.dbfilter = openerp.tools.config['dbfilter'] + o.server_wide_modules = openerp.conf.server_wide_modules or ['web'] + try: + username = getpass.getuser() + except Exception: + username = "unknown" + o.session_storage = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username) + o.addons_path = openerp.modules.module.ad_paths + o.serve_static = True + o.backend = 'local' + + app = Root(o) + openerp.wsgi.register_wsgi_handler(app) + + # vim:et:ts=4:sw=4: diff --git a/doc/Makefile b/addons/web/doc/Makefile similarity index 100% rename from doc/Makefile rename to addons/web/doc/Makefile diff --git a/doc/_static/openerp.png b/addons/web/doc/_static/openerp.png similarity index 100% rename from doc/_static/openerp.png rename to addons/web/doc/_static/openerp.png diff --git a/doc/_templates/sidebarintro.html b/addons/web/doc/_templates/sidebarintro.html similarity index 100% rename from doc/_templates/sidebarintro.html rename to addons/web/doc/_templates/sidebarintro.html diff --git a/doc/_templates/sidebarlogo.html b/addons/web/doc/_templates/sidebarlogo.html similarity index 100% rename from doc/_templates/sidebarlogo.html rename to addons/web/doc/_templates/sidebarlogo.html diff --git a/doc/_themes/LICENSE b/addons/web/doc/_themes/LICENSE similarity index 100% rename from doc/_themes/LICENSE rename to addons/web/doc/_themes/LICENSE diff --git a/doc/_themes/README b/addons/web/doc/_themes/README similarity index 100% rename from doc/_themes/README rename to addons/web/doc/_themes/README diff --git a/doc/_themes/flask/layout.html b/addons/web/doc/_themes/flask/layout.html similarity index 100% rename from doc/_themes/flask/layout.html rename to addons/web/doc/_themes/flask/layout.html diff --git a/doc/_themes/flask/relations.html b/addons/web/doc/_themes/flask/relations.html similarity index 100% rename from doc/_themes/flask/relations.html rename to addons/web/doc/_themes/flask/relations.html diff --git a/doc/_themes/flask/static/flasky.css_t b/addons/web/doc/_themes/flask/static/flasky.css_t similarity index 100% rename from doc/_themes/flask/static/flasky.css_t rename to addons/web/doc/_themes/flask/static/flasky.css_t diff --git a/doc/_themes/flask/static/small_flask.css b/addons/web/doc/_themes/flask/static/small_flask.css similarity index 100% rename from doc/_themes/flask/static/small_flask.css rename to addons/web/doc/_themes/flask/static/small_flask.css diff --git a/doc/_themes/flask/theme.conf b/addons/web/doc/_themes/flask/theme.conf similarity index 100% rename from doc/_themes/flask/theme.conf rename to addons/web/doc/_themes/flask/theme.conf diff --git a/doc/_themes/flask_small/layout.html b/addons/web/doc/_themes/flask_small/layout.html similarity index 100% rename from doc/_themes/flask_small/layout.html rename to addons/web/doc/_themes/flask_small/layout.html diff --git a/doc/_themes/flask_small/static/flasky.css_t b/addons/web/doc/_themes/flask_small/static/flasky.css_t similarity index 100% rename from doc/_themes/flask_small/static/flasky.css_t rename to addons/web/doc/_themes/flask_small/static/flasky.css_t diff --git a/doc/_themes/flask_small/theme.conf b/addons/web/doc/_themes/flask_small/theme.conf similarity index 100% rename from doc/_themes/flask_small/theme.conf rename to addons/web/doc/_themes/flask_small/theme.conf diff --git a/doc/_themes/flask_theme_support.py b/addons/web/doc/_themes/flask_theme_support.py similarity index 100% rename from doc/_themes/flask_theme_support.py rename to addons/web/doc/_themes/flask_theme_support.py diff --git a/doc/addon-structure.txt b/addons/web/doc/addon-structure.txt similarity index 100% rename from doc/addon-structure.txt rename to addons/web/doc/addon-structure.txt diff --git a/doc/async.rst b/addons/web/doc/async.rst similarity index 100% rename from doc/async.rst rename to addons/web/doc/async.rst diff --git a/doc/changelog-7.0.rst b/addons/web/doc/changelog-7.0.rst similarity index 100% rename from doc/changelog-7.0.rst rename to addons/web/doc/changelog-7.0.rst diff --git a/doc/conf.py b/addons/web/doc/conf.py similarity index 100% rename from doc/conf.py rename to addons/web/doc/conf.py diff --git a/doc/form-notes.rst b/addons/web/doc/form-notes.rst similarity index 100% rename from doc/form-notes.rst rename to addons/web/doc/form-notes.rst diff --git a/doc/guides/client-action.rst b/addons/web/doc/guides/client-action.rst similarity index 100% rename from doc/guides/client-action.rst rename to addons/web/doc/guides/client-action.rst diff --git a/doc/index.rst b/addons/web/doc/index.rst similarity index 100% rename from doc/index.rst rename to addons/web/doc/index.rst diff --git a/doc/list-view.rst b/addons/web/doc/list-view.rst similarity index 100% rename from doc/list-view.rst rename to addons/web/doc/list-view.rst diff --git a/doc/make.bat b/addons/web/doc/make.bat similarity index 100% rename from doc/make.bat rename to addons/web/doc/make.bat diff --git a/doc/rpc.rst b/addons/web/doc/rpc.rst similarity index 100% rename from doc/rpc.rst rename to addons/web/doc/rpc.rst diff --git a/doc/search-view.rst b/addons/web/doc/search-view.rst similarity index 100% rename from doc/search-view.rst rename to addons/web/doc/search-view.rst diff --git a/doc/widget.rst b/addons/web/doc/widget.rst similarity index 100% rename from doc/widget.rst rename to addons/web/doc/widget.rst diff --git a/logging.json b/logging.json deleted file mode 100644 index 1f982a6d4d0..00000000000 --- a/logging.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": 1, - "formatters": { - "simple": { - "format": "[%(asctime)s] %(levelname)s:%(name)s:%(message)s" - } - }, - "handlers": { - "console": { - "class": "logging.StreamHandler", - "level": "DEBUG", - "formatter": "simple", - "stream": "ext://sys.stdout" - } - }, - "loggers": { - "web": { - }, - "web.common.openerplib": { - "level": "INFO" - } - }, - "root": { - "level": "DEBUG", - "handlers": ["console"] - } -} diff --git a/openerp-web b/openerp-web deleted file mode 100755 index 7e536a99442..00000000000 --- a/openerp-web +++ /dev/null @@ -1,116 +0,0 @@ -#!/usr/bin/env python -import json -import logging -import logging.config -import optparse -import os -import sys -import tempfile - -import werkzeug.serving -import werkzeug.contrib.fixers - -optparser = optparse.OptionParser() -optparser.add_option("-s", "--session-path", dest="session_storage", - default=os.path.join(tempfile.gettempdir(), "oe-sessions"), - help="Directory used for session storage", metavar="DIR") -optparser.add_option("--server-host", dest="server_host", - default='127.0.0.1', help="OpenERP server hostname", metavar="HOST") -optparser.add_option("--server-port", dest="server_port", default=8069, - help="OpenERP server port", type="int", metavar="NUMBER") -optparser.add_option("--db-filter", dest="dbfilter", default='.*', - help="Filter listed databases", metavar="REGEXP") -optparser.add_option('--addons-path', dest='addons_path', default=[], action='append', - help="Path to addons directory", metavar="PATH") -optparser.add_option('--load', dest='server_wide_modules', default=['web'], action='append', - help="Load an additional module before login (by default only 'web' is loaded)", metavar="MODULE") - -server_options = optparse.OptionGroup(optparser, "Server configuration") -server_options.add_option("-p", "--port", dest="socket_port", default=8002, - help="listening port", type="int", metavar="NUMBER") -server_options.add_option('--reloader', dest='reloader', - default=False, action='store_true', - help="Reload application when python files change") -server_options.add_option('--no-serve-static', dest='serve_static', - default=True, action='store_false', - help="Do not serve static files via this server") -server_options.add_option('--multi-threaded', dest='threaded', - default=False, action='store_true', - help="Spawn one thread per HTTP request") -server_options.add_option('--proxy-mode', dest='proxy_mode', - default=False, action='store_true', - help="Enable correct behavior when behind a reverse proxy") -optparser.add_option_group(server_options) - -logging_opts = optparse.OptionGroup(optparser, "Logging") -logging_opts.add_option("--log-level", dest="log_level", type="choice", - default='debug', help="Global logging level", metavar="LOG_LEVEL", - choices=['debug', 'info', 'warning', 'error', 'critical']) -logging_opts.add_option("--log-config", dest="log_config", default=os.path.join(os.path.dirname(__file__), "logging.json"), - help="Logging configuration file", metavar="FILE") -optparser.add_option_group(logging_opts) - -testing_opts = optparse.OptionGroup(optparser, "Testing") -testing_opts.add_option('--test-mode', dest='test_mode', - action='store_true', default=False, - help="Starts test mode, which provides a few" - " (utterly unsafe) APIs for testing purposes and" - " sets up a special connector which always raises" - " errors on tentative server access. These errors" - " serialize RPC query information (service," - " method, arguments list) in the fault_code" - " attribute of the error object returned to the" - " client. This lets javascript code assert the" \ - " XMLRPC consequences of its queries.") -optparser.add_option_group(testing_opts) - -if __name__ == "__main__": - (options, args) = optparser.parse_args(sys.argv[1:]) - - if not options.addons_path: - path_root = os.path.dirname(os.path.abspath(__file__)) - path_addons = os.path.join(path_root, 'addons') - if os.path.exists(path_addons): - options.addons_path.append(path_addons) - - options.addons_path = [ - path[:-1] if path[-1] in r'\/' else path - for path in options.addons_path - if os.path.exists(path) - ] - - for path_addons in options.addons_path: - if path_addons not in sys.path: - sys.path.insert(0, path_addons) - - try: - import web.common.http - except ImportError: - optparser.error('Error Importing base web module. Check correctness of --addons-path.') - - options.backend = 'xmlrpc' - os.environ["TZ"] = "UTC" - - if options.test_mode: - import web.test_support - import web.test_support.controllers - options.connector = web.test_support.TestConnector() - logging.getLogger('werkzeug').setLevel(logging.WARNING) - - if sys.version_info >= (2, 7) and os.path.exists(options.log_config): - with open(options.log_config) as file: - dct = json.load(file) - logging.config.dictConfig(dct) - logging.getLogger().setLevel(getattr(logging, options.log_level.upper())) - else: - logging.basicConfig(level=getattr(logging, options.log_level.upper())) - - app = web.common.http.Root(options) - - if options.proxy_mode: - app = werkzeug.contrib.fixers.ProxyFix(app) - - werkzeug.serving.run_simple( - '0.0.0.0', options.socket_port, app, - use_reloader=options.reloader, threaded=options.threaded) - diff --git a/openerp-web.cfg b/openerp-web.cfg deleted file mode 100644 index 424d1a8c082..00000000000 --- a/openerp-web.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[global] - - diff --git a/setup.py b/setup.py deleted file mode 100755 index 18d341039ea..00000000000 --- a/setup.py +++ /dev/null @@ -1,124 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -############################################################################## -# -# OpenERP, Open Source Management Solution -# Copyright (C) 2004-2010 Tiny SPRL (). -# -# 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 . -# -############################################################################## - -import glob, os, re, setuptools, sys -from os.path import join, isfile - -# List all data files -def data(): - files = [] - for root, dirnames, filenames in os.walk('openerp'): - for filename in filenames: - if not re.match(r'.*(\.pyc|\.pyo|\~)$',filename): - files.append(os.path.join(root, filename)) - d = {} - for v in files: - k=os.path.dirname(v) - if k in d: - d[k].append(v) - else: - d[k]=[v] - r = d.items() - if os.name == 'nt': - r.append(("Microsoft.VC90.CRT", glob.glob('C:\Microsoft.VC90.CRT\*.*'))) - - import babel - r.append(("localedata", - glob.glob(os.path.join(os.path.dirname(babel.__file__), "localedata" , '*')))) - - return r - -def gen_manifest(): - file_list="\n".join(data()) - open('MANIFEST','w').write(file_list) - -if os.name == 'nt': - sys.path.append("C:\Microsoft.VC90.CRT") - -def py2exe_options(): - if os.name == 'nt': - import py2exe - return { - "console" : [ { "script": "openerp-server", "icon_resources": [(1, join("install","openerp-icon.ico"))], }], - 'options' : { - "py2exe": { - "skip_archive": 1, - "optimize": 2, - "dist_dir": 'dist', - "packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "email", "encodings", "imaplib", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml", ], - "excludes" : ["Tkconstants","Tkinter","tcl"], - } - } - } - else: - return {} - -execfile(join(os.path.dirname(__file__), 'openerp', 'release.py')) - -setuptools.setup( - name = 'openerp', - version = version, - description = description, - long_description = long_desc, - url = url, - author = author, - author_email = author_email, - classifiers = filter(None, classifiers.split("\n")), - license = license, - scripts = ['openerp-server'], - data_files = data(), - packages = setuptools.find_packages(), - dependency_links = ['http://download.gna.org/pychart/'], - #include_package_data = True, - install_requires = [ - 'pychart', - 'babel', - 'docutils', - 'feedparser', - 'gdata', - 'lxml < 3', - 'mako', - 'psutil', - 'psycopg2', - 'pydot', - 'python-dateutil < 2', - 'python-ldap', - 'python-openid', - 'pytz', - 'pywebdav', - 'pyyaml', - 'reportlab', - 'simplejson', - 'vatnumber', - 'vobject', - 'werkzeug', - 'xlwt', - 'zsi', - ], - extras_require = { - 'SSL' : ['pyopenssl'], - }, - **py2exe_options() -) - - -# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From e676beaa3851a4476876206b8217db87a7acd92f Mon Sep 17 00:00:00 2001 From: "Jalpesh Patel (OpenERP)" Date: Mon, 8 Oct 2012 11:41:46 +0530 Subject: [PATCH 065/171] [IMP]check condition bzr revid: pja@tinyerp.com-20121008061146-jz78eocp772xztob --- addons/mail/wizard/mail_compose_message.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index b0e2e89d795..87050ebb771 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -81,8 +81,11 @@ class mail_compose_message(osv.TransientModel): elif composition_mode == 'comment' and model and res_id: vals = self.get_record_data(cr, uid, model, res_id, context=context) elif composition_mode == 'mass_mail' and model and active_ids: - vals = self.pool.get('email.template').generate_email(cr, uid, context.get('default_template_id'), res_id, context=context) - vals.update({'content_subtype': 'html'}) + if context.get('default_template_id'): + vals = email_template_obj.generate_email(cr, uid, context.get('default_template_id'), res_id, context=context) + vals.update({'content_subtype': 'html'}) + else: + vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'} else: vals = {'model': model, 'res_id': res_id} if composition_mode: From e846176dfab08134de5727afee1dc7ec81f8f508 Mon Sep 17 00:00:00 2001 From: "Jalpesh Patel (OpenERP)" Date: Mon, 8 Oct 2012 12:47:31 +0530 Subject: [PATCH 066/171] [IMP]default_get method in mail_compose_message bzr revid: pja@tinyerp.com-20121008071731-ra4p1a2s5aiu4v4f --- addons/mail/wizard/mail_compose_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 87050ebb771..d4dcafeb038 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -82,7 +82,7 @@ class mail_compose_message(osv.TransientModel): vals = self.get_record_data(cr, uid, model, res_id, context=context) elif composition_mode == 'mass_mail' and model and active_ids: if context.get('default_template_id'): - vals = email_template_obj.generate_email(cr, uid, context.get('default_template_id'), res_id, context=context) + vals = self.pool.get('email.template').generate_email(cr, uid, context.get('default_template_id'), res_id, context=context) vals.update({'content_subtype': 'html'}) else: vals = {'model': model, 'res_id': res_id, 'content_subtype': 'html'} From 6b30915df24f7f5082d0ad3a636e57de12d4bb7d Mon Sep 17 00:00:00 2001 From: Vishmita Date: Mon, 8 Oct 2012 12:57:33 +0530 Subject: [PATCH 067/171] [Fix]replace callenabled for on_button_cancel bzr revid: vja@tinyerp.com-20121008072733-kveozkvheu9kiti2 --- addons/web_kanban/static/src/js/kanban.js | 4 ++-- addons/web_view_editor/static/src/js/view_editor.js | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 41cc366226f..dae3d7b214d 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -194,7 +194,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ }); var am = instance.webclient.action_manager; var form = am.dialog_widget.views.form.controller; - form.on_button_cancel.add_last(am.dialog.on_close); + form.on("on_button_cancel",self,am.dialog.on_close); form.on_created.add_last(function(r) { (new instance.web.DataSet(self, self.group_by_field.relation)).name_get([r.result]).then(function(new_record) { am.dialog.on_close(); @@ -672,7 +672,7 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ }); var am = instance.webclient.action_manager; var form = am.dialog_widget.views.form.controller; - form.on_button_cancel.add_last(am.dialog.on_close); + form.on("on_button_cancel",self,am.dialog.on_close); form.on_saved.add_last(function() { am.dialog.on_close(); self.view.do_reload(); diff --git a/addons/web_view_editor/static/src/js/view_editor.js b/addons/web_view_editor/static/src/js/view_editor.js index e65f0ab5d38..f26e12a55d7 100644 --- a/addons/web_view_editor/static/src/js/view_editor.js +++ b/addons/web_view_editor/static/src/js/view_editor.js @@ -1014,9 +1014,7 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({ var action_manager = new instance.web.ActionManager(self); $.when(action_manager.do_action(action)).then(function() { var controller = action_manager.dialog_widget.views['form'].controller; - controller.on_button_cancel.add_last(function(){ - action_manager.destroy() - }); + controller.on("on_button_cancel",self,action_manager.destroy); controller.do_save.add_last(function(){ action_manager.destroy(); var value =controller.fields.name.get('value'); From 64759e988f6cc164c4118801ef82cbad8d82a0d1 Mon Sep 17 00:00:00 2001 From: "Atul Patel (OpenERP)" Date: Mon, 8 Oct 2012 13:06:08 +0530 Subject: [PATCH 068/171] [FIX]: Remove groups to enable stock moves menu bzr revid: atp@tinyerp.com-20121008073608-0rnmqkz82631tbxj --- addons/stock/security/stock_security.xml | 2 -- addons/stock/stock_view.xml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/addons/stock/security/stock_security.xml b/addons/stock/security/stock_security.xml index d8f9c2973db..c9f8a03a378 100644 --- a/addons/stock/security/stock_security.xml +++ b/addons/stock/security/stock_security.xml @@ -21,8 +21,6 @@ Manage Logistic Serial Numbers - - diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index ad62cc3d4f0..1a3f8e57d5c 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -272,7 +272,7 @@ + sequence="3"/> Date: Mon, 8 Oct 2012 11:12:43 +0200 Subject: [PATCH 069/171] [IMP] crm: lead search view bzr revid: qdp-launchpad@openerp.com-20121008091243-f0ktqoee67y6kb3x --- addons/crm/crm_lead_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index f91a8756598..1a099fcb803 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -359,7 +359,7 @@ - + From 777243bef32482803e8ccbc6735293c19187a54a Mon Sep 17 00:00:00 2001 From: Vishmita Date: Mon, 8 Oct 2012 15:03:15 +0530 Subject: [PATCH 070/171] [Fix]Improve code bzr revid: vja@tinyerp.com-20121008093315-tnfnrr0rhum2yyoy --- addons/web_view_editor/static/src/js/view_editor.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/web_view_editor/static/src/js/view_editor.js b/addons/web_view_editor/static/src/js/view_editor.js index f26e12a55d7..b3a4f0dcfac 100644 --- a/addons/web_view_editor/static/src/js/view_editor.js +++ b/addons/web_view_editor/static/src/js/view_editor.js @@ -1014,7 +1014,9 @@ instance.web_view_editor.ViewEditor = instance.web.Widget.extend({ var action_manager = new instance.web.ActionManager(self); $.when(action_manager.do_action(action)).then(function() { var controller = action_manager.dialog_widget.views['form'].controller; - controller.on("on_button_cancel",self,action_manager.destroy); + controller.on("on_button_cancel",self,function(){ + action_manager.destroy(); + }); controller.do_save.add_last(function(){ action_manager.destroy(); var value =controller.fields.name.get('value'); From f893835350de746d054913cdb30b923f8fa2f0c5 Mon Sep 17 00:00:00 2001 From: ggh-openerp Date: Mon, 8 Oct 2012 15:19:24 +0530 Subject: [PATCH 071/171] [IMP] Remove add_last in on_created method bzr revid: ggh@tinyerp.com-20121008094924-uawry36sh9ffz8ut --- addons/web/static/src/js/view_form.js | 8 +++++--- addons/web_calendar/static/src/js/calendar.js | 2 +- addons/web_kanban/static/src/js/kanban.js | 4 ++-- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index b41039361c0..366b2e7a9e9 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -131,6 +131,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM e.preventDefault(); } }); + self.on('on_rec_create', self, self.on_created); }, destroy: function() { _.each(this.get_widgets(), function(w) { @@ -778,7 +779,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM var def = $.Deferred(); $.when(this.has_been_loaded).then(function() { self.dataset.call('copy', [self.datarecord.id, {}, self.dataset.context]).then(function(new_id) { - return self.on_created({ result : new_id }); + return self.trigger('on_rec_create',{ result : new_id }); }).then(function() { return self.to_edit_mode(); }).then(function() { @@ -859,7 +860,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM if (!self.datarecord.id) { // Creation save save_deferral = self.dataset.create(values).pipe(function(r) { - return self.on_created(r, prepend_on_create); + return self.trigger('on_rec_create', r, prepend_on_create); }, null); } else if (_.isEmpty(values) && ! self.force_dirty) { // Not dirty, noop save @@ -1622,12 +1623,13 @@ instance.web.form.FormDialog = instance.web.Dialog.extend({ return this; }, start: function() { + var self = this; this._super(); this.form = new instance.web.FormView(this, this.dataset, this.view_id, { pager: false }); this.form.appendTo(this.$el); - this.form.on_created.add_last(this.on_form_dialog_saved); + this.form.on('on_rec_create', self, this.on_form_dialog_saved); this.form.on_saved.add_last(this.on_form_dialog_saved); return this; }, diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index 81533413312..35cac441b35 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -458,7 +458,7 @@ instance.web_calendar.CalendarFormDialog = instance.web.Dialog.extend({ pager: false }); var def = this.form.appendTo(this.$el); - this.form.on_created.add_last(this.on_form_dialog_saved); + this.form.on('on_rec_create', self, this.on_form_dialog_saved); this.form.on_saved.add_last(this.on_form_dialog_saved); this.form.on_button_cancel = function() { self.close(); diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 41cc366226f..45f5c98bf51 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -195,7 +195,7 @@ instance.web_kanban.KanbanView = instance.web.View.extend({ var am = instance.webclient.action_manager; var form = am.dialog_widget.views.form.controller; form.on_button_cancel.add_last(am.dialog.on_close); - form.on_created.add_last(function(r) { + form.on('on_rec_create', self, function(r) { (new instance.web.DataSet(self, self.group_by_field.relation)).name_get([r.result]).then(function(new_record) { am.dialog.on_close(); var domain = self.dataset.domain.slice(0); @@ -673,7 +673,7 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ var am = instance.webclient.action_manager; var form = am.dialog_widget.views.form.controller; form.on_button_cancel.add_last(am.dialog.on_close); - form.on_saved.add_last(function() { + form.on('on_saved', self, function() { am.dialog.on_close(); self.view.do_reload(); }); From 0cfb72791dd068f1cb349a024208d6b4b7c486b3 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 12:00:32 +0200 Subject: [PATCH 072/171] [IMP] renamed filter 'Inbox' into 'Unread Messages' which is clearer bzr revid: qdp-launchpad@openerp.com-20121008100032-mh7sd740fhsbjemr --- addons/base_calendar/crm_meeting_view.xml | 2 +- addons/crm/crm_lead_view.xml | 6 +++--- addons/event/event_view.xml | 4 ++-- addons/hr_recruitment/hr_recruitment_view.xml | 2 +- addons/project/project_view.xml | 4 ++-- addons/project_issue/project_issue_view.xml | 2 +- addons/purchase/purchase_view.xml | 2 +- addons/sale/sale_view.xml | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/base_calendar/crm_meeting_view.xml b/addons/base_calendar/crm_meeting_view.xml index adcfc23a520..3782141fd3f 100644 --- a/addons/base_calendar/crm_meeting_view.xml +++ b/addons/base_calendar/crm_meeting_view.xml @@ -279,7 +279,7 @@ - + diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 1a099fcb803..0c6d1d1fe96 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -356,7 +356,7 @@ - + @@ -569,10 +569,10 @@ - + - + diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index 003072616f7..a290172d499 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -339,7 +339,7 @@ - + @@ -529,7 +529,7 @@ - + diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml index 08e5f31a2d2..d48086f5663 100644 --- a/addons/hr_recruitment/hr_recruitment_view.xml +++ b/addons/hr_recruitment/hr_recruitment_view.xml @@ -208,7 +208,7 @@ - + diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index bd63eec84c5..686ae08ee73 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -163,7 +163,7 @@ - + @@ -619,7 +619,7 @@ - + diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index 3214b93c8d3..4cd9e2c19ff 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -198,7 +198,7 @@ - + diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml index 7702d182b09..bfff06f5eaf 100644 --- a/addons/purchase/purchase_view.xml +++ b/addons/purchase/purchase_view.xml @@ -323,7 +323,7 @@ - + diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 5505a6a88b5..4323261a19d 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -337,7 +337,7 @@ - + From da443ff975c233d37126cda2d86d4d20a4f93226 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 12:05:47 +0200 Subject: [PATCH 073/171] [IMP] hr: removed job and manager from hr.employee search view bzr revid: qdp-launchpad@openerp.com-20121008100547-nlos20k0xyz3f72e --- addons/hr/hr_view.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index 69567902763..89c327f4d13 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -112,8 +112,6 @@ - - From 109d418de05226bbd242745c6f55010b7f6a8cb7 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 12:11:31 +0200 Subject: [PATCH 074/171] [IMP] hr_expense: search view bzr revid: qdp-launchpad@openerp.com-20121008101131-r34boe2joemso1mu --- addons/hr_expense/hr_expense_view.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml index 388e5262f6e..2032d3f0567 100644 --- a/addons/hr_expense/hr_expense_view.xml +++ b/addons/hr_expense/hr_expense_view.xml @@ -155,11 +155,12 @@ - + + + - From d058f68e2f9c2d301880219880ba9e32cb64dfd0 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 12:11:44 +0200 Subject: [PATCH 075/171] [IMP] account: search view bzr revid: qdp-launchpad@openerp.com-20121008101144-bx103sqfkhd03a21 --- addons/account/account_invoice_view.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 26e18271dc2..3c2922ff141 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -458,7 +458,6 @@ - From 4dd9a36f6e88d23344deaecbf997fc2ec63a64cc Mon Sep 17 00:00:00 2001 From: ggh-openerp Date: Mon, 8 Oct 2012 16:33:46 +0530 Subject: [PATCH 076/171] [IMP] Remove add_last in on_saved method bzr revid: ggh@tinyerp.com-20121008110346-r0dzmjy3161bxglb --- addons/web/static/src/js/view_form.js | 5 +++-- addons/web_calendar/static/src/js/calendar.js | 2 +- addons/web_kanban/static/src/js/kanban.js | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index b41039361c0..ce2e1ac5689 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -131,6 +131,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM e.preventDefault(); } }); + self.on("on_rec_save", self, self.on_saved); }, destroy: function() { _.each(this.get_widgets(), function(w) { @@ -868,7 +869,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM self.force_dirty = false; // Write save save_deferral = self.dataset.write(self.datarecord.id, values, {}).pipe(function(r) { - return self.on_saved(r); + return self.trigger('on_rec_save', r); }, null); } return save_deferral; @@ -1628,7 +1629,7 @@ instance.web.form.FormDialog = instance.web.Dialog.extend({ }); this.form.appendTo(this.$el); this.form.on_created.add_last(this.on_form_dialog_saved); - this.form.on_saved.add_last(this.on_form_dialog_saved); + this.form.on('on_rec_save', this, this.on_form_dialog_saved); return this; }, select_id: function(id) { diff --git a/addons/web_calendar/static/src/js/calendar.js b/addons/web_calendar/static/src/js/calendar.js index 81533413312..37c49462b59 100644 --- a/addons/web_calendar/static/src/js/calendar.js +++ b/addons/web_calendar/static/src/js/calendar.js @@ -459,7 +459,7 @@ instance.web_calendar.CalendarFormDialog = instance.web.Dialog.extend({ }); var def = this.form.appendTo(this.$el); this.form.on_created.add_last(this.on_form_dialog_saved); - this.form.on_saved.add_last(this.on_form_dialog_saved); + this.form.on('on_rec_save', self, this.on_form_dialog_saved); this.form.on_button_cancel = function() { self.close(); } diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index 41cc366226f..438b1f7cb14 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -673,7 +673,7 @@ instance.web_kanban.KanbanGroup = instance.web.Widget.extend({ var am = instance.webclient.action_manager; var form = am.dialog_widget.views.form.controller; form.on_button_cancel.add_last(am.dialog.on_close); - form.on_saved.add_last(function() { + form.on('on_rec_save', self, function() { am.dialog.on_close(); self.view.do_reload(); }); From bc3009d590af243ee28bb1e6994bcb2fae6d2bba Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 8 Oct 2012 14:03:51 +0200 Subject: [PATCH 077/171] [FIX] mail: default alias_domain should be web.base.url bzr revid: odo@openerp.com-20121008120351-fqmjckrjmy52xdji --- addons/mail/data/mail_demo.xml | 5 ----- addons/mail/res_config.py | 13 +++++++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/addons/mail/data/mail_demo.xml b/addons/mail/data/mail_demo.xml index 1350fd33547..7bda32c002b 100644 --- a/addons/mail/data/mail_demo.xml +++ b/addons/mail/data/mail_demo.xml @@ -42,10 +42,5 @@ This month you also get 250 EUR of eco-vouchers if you have been in the company comment - - mail.catchall.domain - demo.openerp.com - - diff --git a/addons/mail/res_config.py b/addons/mail/res_config.py index ce2f75687ba..91606e1c720 100644 --- a/addons/mail/res_config.py +++ b/addons/mail/res_config.py @@ -19,6 +19,8 @@ # ############################################################################## +import urlparse + from openerp.osv import osv, fields class project_configuration(osv.TransientModel): @@ -31,9 +33,16 @@ class project_configuration(osv.TransientModel): } def get_default_alias_domain(self, cr, uid, ids, context=None): - return {'alias_domain': self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context)} + alias_domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "mail.catchall.domain", context=context) + if not alias_domain: + domain = self.pool.get("ir.config_parameter").get_param(cr, uid, "web.base.url", context=context) + try: + alias_domain = urlparse.urlsplit(domain).netloc.split(':')[0] + except Exception: + pass + return {'alias_domain': alias_domain} def set_alias_domain(self, cr, uid, ids, context=None): config_parameters = self.pool.get("ir.config_parameter") for record in self.browse(cr, uid, ids, context=context): - config_parameters.set_param(cr, uid, "mail.catchall.domain", record.alias_domain or '', context=context) \ No newline at end of file + config_parameters.set_param(cr, uid, "mail.catchall.domain", record.alias_domain or '', context=context) From 43595fbeb1f2020ab7e1f545c0dbf6a579bf77b2 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 8 Oct 2012 14:04:21 +0200 Subject: [PATCH 078/171] [FIX] mail: default email From should consider user email if there is no alias bzr revid: odo@openerp.com-20121008120421-k9aj7g6vsr3zu5u2 --- addons/mail/mail_mail.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index e9a569a8192..5b4a096e969 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -63,10 +63,12 @@ class mail_mail(osv.Model): } def _get_default_from(self, cr, uid, context=None): - cur = self.pool.get('res.users').browse(cr, uid, uid, context=context) - if not cur.alias_domain: - raise osv.except_osv(_('Invalid Action!'), _('Unable to send email, set an alias domain in your server settings.')) - return cur.alias_name + '@' + cur.alias_domain + this = self.pool.get('res.users').browse(cr, uid, uid, context=context) + if this.alias_domain: + return '%s@%s' % (this.alias, this.alias_domain) + elif this.email: + return this.email + raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) _defaults = { 'state': 'outgoing', From d54120565152cc0f1c8164bf7538802f7d527332 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 8 Oct 2012 15:02:45 +0200 Subject: [PATCH 079/171] [FIX] portal: add test email address for outgoing emails sent during tests bzr revid: odo@openerp.com-20121008130245-7n1ed3i2ez9bzniy --- addons/portal/tests/test_portal.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/addons/portal/tests/test_portal.py b/addons/portal/tests/test_portal.py index 653a05d40aa..10392be53ff 100644 --- a/addons/portal/tests/test_portal.py +++ b/addons/portal/tests/test_portal.py @@ -49,6 +49,9 @@ class test_portal(test_mail.TestMailMockups): self.user_chell = self.res_users.browse(cr, uid, self.user_chell_id) self.partner_chell_id = self.user_chell.partner_id.id + # Set an email address for the user running the tests, used as Sender for outgoing mails + self.res_users.write(cr, uid, uid, {'email': 'test@localhost'}) + def test_00_access_rights(self): """ Test basic mail_message and mail_group access rights for portal users. """ cr, uid = self.cr, self.uid From a369130a35341779d0876da02aaa49b284f8f09f Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 15:08:31 +0200 Subject: [PATCH 080/171] [IMP] crm: remove 'sale team' from res.partner search view bzr revid: qdp-launchpad@openerp.com-20121008130831-hjm46vehb54tqc74 --- addons/crm/res_partner_view.xml | 16 ---------------- addons/hr_expense/hr_expense_view.xml | 2 +- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/addons/crm/res_partner_view.xml b/addons/crm/res_partner_view.xml index 84a0bd4cf49..4b313bcef71 100644 --- a/addons/crm/res_partner_view.xml +++ b/addons/crm/res_partner_view.xml @@ -15,28 +15,12 @@ - - view.res.partner.tree.crm.inherited2 - res.partner - - - - - - - - - view.res.partner.search.crm.inherited3 res.partner - - - - diff --git a/addons/hr_expense/hr_expense_view.xml b/addons/hr_expense/hr_expense_view.xml index 2032d3f0567..67d629eee6a 100644 --- a/addons/hr_expense/hr_expense_view.xml +++ b/addons/hr_expense/hr_expense_view.xml @@ -158,7 +158,7 @@ - + From 775fe69fc10603c8b859e2c2554827f74f0405fc Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:08:56 +0200 Subject: [PATCH 081/171] [IMP] base_import: always open CSV link in new window bzr revid: xmo@openerp.com-20121008130856-fw0p92lofx7u27b2 --- addons/base_import/static/src/xml/import.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml index 91debd64897..adca0579f8f 100644 --- a/addons/base_import/static/src/xml/import.xml +++ b/addons/base_import/static/src/xml/import.xml @@ -18,7 +18,7 @@

    Upload your file

    Select the .CSV + class="oe_import_csv" target="_blank">.CSV file to import. If you need a sample importable file, you can use the export tool to generate one.

    From 350d365ba087288b36bf55a6097bc4738115719c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:14:07 +0200 Subject: [PATCH 082/171] [IMP] remove docstring for cr, uid following odo review bzr revid: xmo@openerp.com-20121008131407-5hnxkn17lycfoqzu --- openerp/addons/base/ir/ir_fields.py | 4 ---- openerp/osv/orm.py | 5 ----- 2 files changed, 9 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 2f557c4aaa9..401808dfc30 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -61,8 +61,6 @@ class ir_fields_converter(orm.Model): it returns. The handling of a warning at the upper levels is the same as ``ValueError`` above. - :param cr: openerp cursor - :param uid: ID of user calling the converter :param column: column object to generate a value for :type column: :class:`fields._column` :param type fromtype: type to convert to something fitting for ``column`` @@ -183,8 +181,6 @@ class ir_fields_converter(orm.Model): """ Finds a database id for the reference ``value`` in the referencing subfield ``subfield`` of the provided column of the provided model. - :param cr: OpenERP cursor - :param uid: OpenERP user id :param model: model to which the column belongs :param column: relational column for which references are provided :param subfield: a relational subfield allowing building of refs to diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index b0b0fad88fa..a0188c29e51 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1310,8 +1310,6 @@ class BaseModel(object): the same order they were extracted from the file. They can be passed directly to :meth:`~read` - :param cr: cursor for the request - :param int uid: ID of the user attempting the data import :param fields: list of fields to import, at the same index as the corresponding data :type fields: list(str) :param data: row-major matrix of data to import @@ -1377,8 +1375,6 @@ class BaseModel(object): * None is the name_get for the record (to use with name_create/name_search) * "id" is the External ID for the record * ".id" is the Database ID for the record - - :param ImportLogger logger: """ columns = dict((k, v.column) for k, v in self._all_columns.iteritems()) # Fake columns to avoid special cases in extractor @@ -1448,7 +1444,6 @@ class BaseModel(object): strings) into forms which can be written to the database (via self.create or (ir.model.data)._update) - :param ImportLogger parent_logger: :returns: a list of triplets of (id, xid, record) :rtype: list((int|None, str|None, dict)) """ From 53786034e3d38b8b187c6ea537af98b869872648 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:15:45 +0200 Subject: [PATCH 083/171] [FIX] docstring typo for name of converter methods on ir_fields, following odo review bzr revid: xmo@openerp.com-20121008131545-u5sdlolmmgr8db2y --- openerp/addons/base/ir/ir_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 401808dfc30..d20841df9a7 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -39,7 +39,7 @@ class ir_fields_converter(orm.Model): value acceptable for a write() on the column ``column``. By default, tries to get a method on itself with a name matching the - pattern ``_$fromtype_$column._type`` and returns it. + pattern ``_$fromtype_to_$column._type`` and returns it. Converter callables can either return a value and a list of warnings to their caller or raise ``ValueError``, which will be interpreted as a From 00c7d9f4c02d8ca79ccd7f42c954f09367837275 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:20:54 +0200 Subject: [PATCH 084/171] [FIX] incorrect views spec in relational fields 'more info' action, spotted by odo bzr revid: xmo@openerp.com-20121008132054-y57mg2rhxg6hkx2s --- openerp/addons/base/ir/ir_fields.py | 2 +- openerp/tests/addons/test_impex/tests/test_load.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index d20841df9a7..e6d9c46fbb8 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -199,7 +199,7 @@ class ir_fields_converter(orm.Model): warnings = [] action = {'type': 'ir.actions.act_window', 'target': 'new', 'view_mode': 'tree,form', 'view_type': 'form', - 'views': [(False, 'tree', (False, 'form'))], + 'views': [(False, 'tree'), (False, 'form')], 'help': _(u"See all possible values")} if subfield is None: action['res_model'] = column._obj diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index afa3e8dd8e3..18958969dfb 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -18,7 +18,7 @@ def moreaction(**kwargs): target='new', view_mode='tree,form', view_type='form', - views=[(False, 'tree', (False, 'form'))], + views=[(False, 'tree'), (False, 'form')], help=u"See all possible values") def values(seq, field='value'): From 12f4150195c057c68d96c1430333b56d6ca7d903 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:30:06 +0200 Subject: [PATCH 085/171] [IMP] avoid reimplementing (ir.model.data).get_object_reference() by hand, caught by odo bzr revid: xmo@openerp.com-20121008133006-zsjdvax07zsrjwm6 --- openerp/addons/base/ir/ir_fields.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index e6d9c46fbb8..3e63228210c 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -229,11 +229,8 @@ class ir_fields_converter(orm.Model): module, xid = context.get('_import_current_module', ''), value ModelData = self.pool['ir.model.data'] try: - md_id = ModelData._get_id(cr, uid, module, xid) - model_data = ModelData.read(cr, uid, [md_id], ['res_id'], - context=context) - if model_data: - id = model_data[0]['res_id'] + _model, id = ModelData.get_object_reference( + cr, uid, module, xid) except ValueError: pass # leave id is None elif subfield is None: field_type = _(u"name") From 443b5b9767cc35e3ab58b1283cacf38b9757c146 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:31:16 +0200 Subject: [PATCH 086/171] [FIX] missing translation mark on exception message bzr revid: xmo@openerp.com-20121008133116-da2n77kbk9evaa7g --- openerp/addons/base/ir/ir_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 3e63228210c..3d9276fde2f 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -243,7 +243,7 @@ class ir_fields_converter(orm.Model): % (len(ids)))) id, _name = ids[0] else: - raise Exception(u"Unknown sub-field '%s'" % subfield) + raise Exception(_(u"Unknown sub-field '%s'") % subfield) if id is None: raise ValueError( From afee5b63eeb7c913cd2fa0b5efa4c869c1af4651 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:33:11 +0200 Subject: [PATCH 087/171] [IMP] use readable relation helper to generate a '6' m2m command bzr revid: xmo@openerp.com-20121008133311-ejaix536syks41oo --- openerp/addons/base/ir/ir_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 3d9276fde2f..e714e6c11f9 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -297,7 +297,7 @@ class ir_fields_converter(orm.Model): cr, uid, model, column, subfield, reference, context=context) ids.append(id) warnings.extend(ws) - return [(6, 0, ids)], warnings + return REPLACE_WITH(ids), warnings def _str_to_one2many(self, cr, uid, model, column, records, context=None): commands = [] From e8af13f0e88024230007c2345268946352546565 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 8 Oct 2012 15:38:23 +0200 Subject: [PATCH 088/171] [IMP] don't pass in a default lang to _get_source if no value is provided in the context, caught by odo bzr revid: xmo@openerp.com-20121008133823-vhypgddiicztai3a --- openerp/osv/orm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index a0188c29e51..e890f6ebbe8 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1453,7 +1453,7 @@ class BaseModel(object): Translation = self.pool['ir.translation'] field_names = dict( (f, (Translation._get_source(cr, uid, self._name + ',' + f, 'field', - context.get('lang', False) or 'en_US') + context.get('lang')) or column.string or f)) for f, column in columns.iteritems()) converters = dict( From 635c3225dbf78dd6ff548b9f979f549a49a663da Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 8 Oct 2012 16:08:57 +0200 Subject: [PATCH 089/171] [FIX] project_long_term: demo project with phases should be marked as `use_phases` bzr revid: odo@openerp.com-20121008140857-zd3kcyq8t2ewyfno --- addons/project_long_term/project_long_term_demo.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/project_long_term/project_long_term_demo.xml b/addons/project_long_term/project_long_term_demo.xml index bca8231fdaa..fda481d5a11 100644 --- a/addons/project_long_term/project_long_term_demo.xml +++ b/addons/project_long_term/project_long_term_demo.xml @@ -7,6 +7,7 @@ + From a9b9412f182c3bc7e53b7a001d5edfa0b21b24bf Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 16:26:19 +0200 Subject: [PATCH 090/171] [IMP] crm: remove 'country_id' from res.partner search view bzr revid: qdp-launchpad@openerp.com-20121008142619-mxqis0ubc8m3o4z9 --- addons/crm/res_partner_view.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/addons/crm/res_partner_view.xml b/addons/crm/res_partner_view.xml index 4b313bcef71..b7aa334a9c3 100644 --- a/addons/crm/res_partner_view.xml +++ b/addons/crm/res_partner_view.xml @@ -21,9 +21,6 @@ - - - From 6210cf76a6fba0d3054dcbe837e0824858eeea4b Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 16:27:57 +0200 Subject: [PATCH 091/171] [IMP] base: module search view imp bzr revid: qdp-launchpad@openerp.com-20121008142757-c3597to9h42e56tl --- openerp/addons/base/module/module_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/module/module_view.xml b/openerp/addons/base/module/module_view.xml index 729d3ef7b4c..c3b3684f07c 100644 --- a/openerp/addons/base/module/module_view.xml +++ b/openerp/addons/base/module/module_view.xml @@ -39,7 +39,7 @@ + 'ilike', self)]" string="Module"/> From 5db1b37797c2e2e8ed2743e89cac8dd7489b6d8d Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Mon, 8 Oct 2012 16:47:22 +0200 Subject: [PATCH 092/171] [IMP] crm: opportunity search view imp bzr revid: qdp-launchpad@openerp.com-20121008144722-r6th49qhfzq8jmbf --- addons/crm/crm_lead_view.xml | 3 +-- addons/crm_partner_assign/crm_lead_view.xml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 0c6d1d1fe96..6a152f44b67 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -580,9 +580,8 @@ domain="['|', ('section_id.user_id','=',uid), ('section_id.member_ids', 'in', [uid])]" context="{'invisible_section': False}" help="Opportunities that are assigned to either me or one of the sale teams I manage" /> - - + diff --git a/addons/crm_partner_assign/crm_lead_view.xml b/addons/crm_partner_assign/crm_lead_view.xml index cd2e3f08c9a..2cffb6fb899 100644 --- a/addons/crm_partner_assign/crm_lead_view.xml +++ b/addons/crm_partner_assign/crm_lead_view.xml @@ -56,7 +56,7 @@ - + From 5300124eb0cf643297971e4c7d9d6756ffd40c62 Mon Sep 17 00:00:00 2001 From: Antonin Bourguignon Date: Mon, 8 Oct 2012 18:49:24 +0200 Subject: [PATCH 093/171] [FIX] wrong var assignation lp bug: https://launchpad.net/bugs/1022509 fixed bzr revid: abo@openerp.com-20121008164924-6pfc9k8lzt11uw9m --- addons/board/static/src/js/dashboard.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 43d7ca76d39..6037c8c08fa 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -173,7 +173,7 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({ }, on_load_action: function(result, index, action_attrs) { var self = this, - action = result.result, + action = result, view_mode = action_attrs.view_mode; if (action_attrs.context && action_attrs.context['dashboard_merge_domains_contexts'] === false) { From b586a20460b0401b329027735127ec7267579680 Mon Sep 17 00:00:00 2001 From: Vishmita Date: Tue, 9 Oct 2012 10:52:33 +0530 Subject: [PATCH 094/171] [FIX]Improve Code bzr revid: vja@tinyerp.com-20121009052233-hmcjqfur2uoi1fq4 --- addons/web/static/src/xml/base.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 11d2f3da869..544fc0b8061 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -548,7 +548,7 @@ - x + x
  • From 5ab21e13a36902c13905ff92bb1cc0d728dbd467 Mon Sep 17 00:00:00 2001 From: ggh-openerp Date: Tue, 9 Oct 2012 11:46:18 +0530 Subject: [PATCH 095/171] [IMP] Remove add_last in on_unlink method bzr revid: ggh@tinyerp.com-20121009061618-ohsjlzf14jpqws2q --- addons/web/static/src/js/data.js | 4 +++- addons/web/static/src/js/view_form.js | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/data.js b/addons/web/static/src/js/data.js index 220d42737da..79f909c0908 100644 --- a/addons/web/static/src/js/data.js +++ b/addons/web/static/src/js/data.js @@ -690,9 +690,11 @@ instance.web.DataSet = instance.web.CallbackEnabled.extend({ instance.web.DataSetStatic = instance.web.DataSet.extend({ init: function(parent, model, context, ids) { + var self = this; this._super(parent, model, context); // all local records this.ids = ids || []; + self.on("on_rec_unlink", self, self.on_unlink); }, read_slice: function (fields, options) { options = options || {}; @@ -711,7 +713,7 @@ instance.web.DataSetStatic = instance.web.DataSet.extend({ } }, unlink: function(ids) { - this.on_unlink(ids); + this.trigger('on_rec_unlink', ids); return $.Deferred().resolve({result: true}); }, on_unlink: function(ids) { diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 6a16c06095d..0266af3d40e 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3998,7 +3998,7 @@ instance.web.form.FieldMany2Many = instance.web.form.AbstractField.extend({ this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation); this.dataset.m2m = this; - this.dataset.on_unlink.add_last(function(ids) { + this.dataset.on('on_rec_unlink', self, function(ids) { self.dataset_changed(); }); @@ -4131,7 +4131,7 @@ instance.web.form.FieldMany2ManyKanban = instance.web.form.AbstractField.extend( this.dataset = new instance.web.form.Many2ManyDataSet(this, this.field.relation); this.dataset.m2m = this; - this.dataset.on_unlink.add_last(function(ids) { + this.dataset.on('on_rec_unlink', self, function(ids) { self.dataset_changed(); }); From 35c0e73eb4e5bc6b169b643b46ebcb899b0f9468 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Oct 2012 09:09:34 +0200 Subject: [PATCH 096/171] [FIX] m2m command in import command helpers create a single command, still need the list around commands and the tests would have caught it, if I'd run them... bzr revid: xmo@openerp.com-20121009070934-7i1hxuzumdu8x1jy --- openerp/addons/base/ir/ir_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index e714e6c11f9..1c14fdb3c4d 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -297,7 +297,7 @@ class ir_fields_converter(orm.Model): cr, uid, model, column, subfield, reference, context=context) ids.append(id) warnings.extend(ws) - return REPLACE_WITH(ids), warnings + return [REPLACE_WITH(ids)], warnings def _str_to_one2many(self, cr, uid, model, column, records, context=None): commands = [] From 10345423b16f2a16c43fda671024e6f793eb7c1b Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Oct 2012 10:54:46 +0200 Subject: [PATCH 097/171] [FIX] treat imported datetime as local to the context-provided or user's datetime bzr revid: xmo@openerp.com-20121009085446-obfpn4c5r250zonc --- openerp/addons/base/ir/ir_fields.py | 38 +++++++++++-- .../addons/test_impex/tests/test_load.py | 53 ++++++++++++++++++- 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/openerp/addons/base/ir/ir_fields.py b/openerp/addons/base/ir/ir_fields.py index 1c14fdb3c4d..94a0be9e86c 100644 --- a/openerp/addons/base/ir/ir_fields.py +++ b/openerp/addons/base/ir/ir_fields.py @@ -1,10 +1,14 @@ # -*- coding: utf-8 -*- +import datetime import functools import operator import itertools -import psycopg2 import time -from openerp.osv import orm, fields + +import psycopg2 +import pytz + +from openerp.osv import orm from openerp.tools.translate import _ from openerp.tools.misc import DEFAULT_SERVER_DATE_FORMAT,\ DEFAULT_SERVER_DATETIME_FORMAT @@ -131,16 +135,42 @@ class ir_fields_converter(orm.Model): 'moreinfo': _(u"Use the format '%s'") % u"2012-12-31" }) + def _input_tz(self, cr, uid, context): + # if there's a tz in context, try to use that + if context.get('tz'): + try: + return pytz.timezone(context['tz']) + except pytz.UnknownTimeZoneError: + pass + + # if the current user has a tz set, try to use that + user = self.pool['res.users'].read( + cr, uid, [uid], ['tz'], context=context)[0] + if user['tz']: + try: + return pytz.timezone(user['tz']) + except pytz.UnknownTimeZoneError: + pass + + # fallback if no tz in context or on user: UTC + return pytz.UTC + def _str_to_datetime(self, cr, uid, model, column, value, context=None): + if context is None: context = {} try: - time.strptime(value, DEFAULT_SERVER_DATETIME_FORMAT) - return value, [] + parsed_value = datetime.datetime.strptime( + value, DEFAULT_SERVER_DATETIME_FORMAT) except ValueError: raise ValueError( _(u"'%s' does not seem to be a valid datetime for field '%%(field)s'") % value, { 'moreinfo': _(u"Use the format '%s'") % u"2012-12-31 23:59:59" }) + input_tz = self._input_tz(cr, uid, context)# Apply input tz to the parsed naive datetime + dt = input_tz.localize(parsed_value, is_dst=False) + # And convert to UTC before reformatting for writing + return dt.astimezone(pytz.UTC).strftime(DEFAULT_SERVER_DATETIME_FORMAT), [] + def _get_translations(self, cr, uid, types, src, context): types = tuple(types) # Cache translations so they don't have to be reloaded from scratch on diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 18958969dfb..043337dd027 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -1039,5 +1039,54 @@ class test_datetime(ImporterCase): moreinfo=u"Use the format '2012-12-31 23:59:59'")]) self.assertIs(result['ids'], False) -# function, related, reference: written to db as-is... -# => function uses @type for value coercion/conversion + def test_checktz1(self): + """ Imported date should be interpreted as being in the tz provided by + the context + """ + # write dummy tz in user (Asia/Hovd UTC+0700), should be superseded by + # context + self.registry('res.users').write( + self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID], + {'tz': 'Asia/Hovd'}) + + # UTC+1400 + result = self.import_( + ['value'], [['2012-02-03 11:11:11']], {'tz': 'Pacific/Kiritimati'}) + self.assertFalse(result['messages']) + self.assertEqual( + values(self.read(domain=[('id', 'in', result['ids'])])), + ['2012-02-02 21:11:11']) + + # UTC-0930 + result = self.import_( + ['value'], [['2012-02-03 11:11:11']], {'tz': 'Pacific/Marquesas'}) + self.assertFalse(result['messages']) + self.assertEqual( + values(self.read(domain=[('id', 'in', result['ids'])])), + ['2012-02-03 20:41:11']) + + def test_usertz(self): + """ If the context does not hold a timezone, the importing user's tz + should be used + """ + # UTC +1000 + self.registry('res.users').write( + self.cr, openerp.SUPERUSER_ID, [openerp.SUPERUSER_ID], + {'tz': 'Asia/Yakutsk'}) + + result = self.import_( + ['value'], [['2012-02-03 11:11:11']]) + self.assertFalse(result['messages']) + self.assertEqual( + values(self.read(domain=[('id', '=', result['ids'])])), + ['2012-02-03 01:11:11']) + + def test_notz(self): + """ If there is no tz either in the context or on the user, falls back + to UTC + """ + result = self.import_(['value'], [['2012-02-03 11:11:11']]) + self.assertFalse(result['messages']) + self.assertEqual( + values(self.read(domain=[('id', '=', result['ids'])])), + ['2012-02-03 11:11:11']) From 2f6ad073ba6f100c2699fdae42449178f876a061 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 9 Oct 2012 11:08:51 +0200 Subject: [PATCH 098/171] [FIX] problem with parsing in field monetary bzr revid: nicolas.vanhoren@openerp.com-20121009090851-021w63udra7mwa2a --- addons/web/static/src/js/view_form.js | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 6a16c06095d..0044a89fa3a 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2214,7 +2214,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we var self = this; var $input = this.$el.find('input'); $input.change(function() { - self.set({'value': instance.web.parse_value($input.val(), self)}); + self.set({'value': self.parse_value($input.val())}); }); this.setupFocus($input); }, @@ -2223,7 +2223,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we this.render_value(); }, render_value: function() { - var show_value = instance.web.format_value(this.get('value'), this, ''); + var show_value = this.format_value(this.get('value'), ''); if (!this.get("effective_readonly")) { this.$el.find('input').val(show_value); } else { @@ -2236,7 +2236,7 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we is_syntax_valid: function() { if (!this.get("effective_readonly")) { try { - var value_ = instance.web.parse_value(this.$el.find('input').val(), this, ''); + var value_ = this.parse_value(this.$el.find('input').val(), ''); return true; } catch(e) { return false; @@ -2244,6 +2244,12 @@ instance.web.form.FieldChar = instance.web.form.AbstractField.extend(instance.we } return true; }, + parse_value: function(val, def) { + return instance.web.parse_value(val, this, def); + }, + format_value: function(val, def) { + return instance.web.format_value(val, this, def); + }, is_false: function() { return this.get('value') === '' || this._super(); }, @@ -5021,6 +5027,12 @@ instance.web.form.FieldMonetary = instance.web.form.FieldFloat.extend({ self.set({"currency_info": res}); }); }, + parse_value: function(val, def) { + return instance.web.parse_value(val, {type: "float"}, def); + }, + format_value: function(val, def) { + return instance.web.format_value(val, {type: "float"}, def); + }, }); /** From bcefce701e8c1edd32de5268798838f89f757f72 Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 9 Oct 2012 11:21:29 +0200 Subject: [PATCH 099/171] [IMP] account: monetary widget for bank statements bzr revid: qdp-launchpad@openerp.com-20121009092129-w4pjhhuhxpabdjqg --- addons/account/account_bank_statement.py | 5 +++-- addons/account/account_view.xml | 9 +++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 8b2a9aff257..549363ce269 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -461,9 +461,10 @@ class account_bank_statement(osv.osv): return {} balance_start = self._compute_balance_end_real(cr, uid, journal_id, context=context) - journal_data = self.pool.get('account.journal').read(cr, uid, journal_id, ['company_id'], context=context) + journal_data = self.pool.get('account.journal').read(cr, uid, journal_id, ['company_id', 'currency'], context=context) company_id = journal_data['company_id'] - return {'value': {'balance_start': balance_start, 'company_id': company_id}} + currency_id = journal_data['currency'] or self.pool.get('res.company').browse(cr, uid, company_id[0], context=context).currency_id.id + return {'value': {'balance_start': balance_start, 'company_id': company_id, 'currency': currency_id}} def unlink(self, cr, uid, ids, context=None): stat = self.read(cr, uid, ids, ['state'], context=context) diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index 1c40bebba52..3391158696a 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -633,13 +633,10 @@ + - From 510eea5305ae80832f32d804464c841f6e52420e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Oct 2012 11:25:19 +0200 Subject: [PATCH 100/171] [CHG] make empty cells clear out the corresponding field on import, rather than skip them bzr revid: xmo@openerp.com-20121009092519-ge86yp3nnkqe9gic --- openerp/osv/orm.py | 1 + openerp/tests/addons/test_impex/models.py | 4 +++- .../tests/addons/test_impex/tests/test_load.py | 16 +++++++++++++--- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index e890f6ebbe8..4c84b611629 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -1496,6 +1496,7 @@ class BaseModel(object): for field, strvalue in record.iteritems(): if field in (None, 'id', '.id'): continue if not strvalue: + converted[field] = False continue # In warnings and error messages, use translated string as diff --git a/openerp/tests/addons/test_impex/models.py b/openerp/tests/addons/test_impex/models.py index 50340082d08..48bd9948a0f 100644 --- a/openerp/tests/addons/test_impex/models.py +++ b/openerp/tests/addons/test_impex/models.py @@ -128,8 +128,10 @@ class SelectionWithDefault(orm.Model): _name = 'export.selection.withdefault' _columns = { - 'value': fields.selection([(1, "Foo"), (2, "Bar")], required=True), + 'const': fields.integer(), + 'value': fields.selection([(1, "Foo"), (2, "Bar")]), } _defaults = { + 'const': 4, 'value': 2, } diff --git a/openerp/tests/addons/test_impex/tests/test_load.py b/openerp/tests/addons/test_impex/tests/test_load.py index 043337dd027..066fea62942 100644 --- a/openerp/tests/addons/test_impex/tests/test_load.py +++ b/openerp/tests/addons/test_impex/tests/test_load.py @@ -476,14 +476,24 @@ class test_selection(ImporterCase): class test_selection_with_default(ImporterCase): model_name = 'export.selection.withdefault' - def test_skip_empty(self): - """ Empty cells should be entirely skipped so that default values can - be inserted by the ORM + def test_empty(self): + """ Empty cells should set corresponding field to False """ result = self.import_(['value'], [['']]) self.assertFalse(result['messages']) self.assertEqual(len(result['ids']), 1) + self.assertEqual( + values(self.read()), + [False]) + + def test_default(self): + """ Non-provided cells should set corresponding field to default + """ + result = self.import_(['const'], [['42']]) + self.assertFalse(result['messages']) + self.assertEqual(len(result['ids']), 1) + self.assertEqual( values(self.read()), [2]) From dfad511a559e851a8471e93cecc998131ee143f4 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Tue, 9 Oct 2012 11:30:35 +0200 Subject: [PATCH 101/171] [IMP] put better error message when widget options are not valid JSON bzr revid: nicolas.vanhoren@openerp.com-20121009093035-1y3fxr95qo2thryq --- addons/web/static/src/js/view_form.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 0044a89fa3a..8e7364ee50a 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2068,7 +2068,11 @@ instance.web.form.AbstractField = instance.web.form.FormWidget.extend(instance.w this.field = this.field_manager.get_field_desc(this.name); this.widget = this.node.attrs.widget; this.string = this.node.attrs.string || this.field.string || this.name; - this.options = JSON.parse(this.node.attrs.options || '{}'); + try { + this.options = JSON.parse(this.node.attrs.options || '{}'); + } catch (e) { + throw new Error(_.str.sprintf(_t("Widget options for field '%s' are not valid JSON."), this.name)); + } this.set({'value': false}); this.on("change:value", this, function() { From f168998a49cbced240962f1aca86f2c89f665549 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Oct 2012 11:44:11 +0200 Subject: [PATCH 102/171] [FIX] l10n_uk: import doesn't skip empty cells anymore, so fix import which relied on default value being set on empty cell bzr revid: xmo@openerp.com-20121009094411-6eji2zzp3r1afx9h --- addons/l10n_uk/data/account.account.type.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/l10n_uk/data/account.account.type.csv b/addons/l10n_uk/data/account.account.type.csv index 0827975b22f..379656ece39 100644 --- a/addons/l10n_uk/data/account.account.type.csv +++ b/addons/l10n_uk/data/account.account.type.csv @@ -10,4 +10,4 @@ "account_type_output_tax","Output Tax","Output Tax","liability","Unreconciled" "account_type_input_tax","Input Tax","Input Tax","asset","Unreconciled" "account_type_profit_and_loss","Profit and Loss","Profit and Loss","none","None" -"account_type_view","View","view",,"None" +"account_type_view","View","view","none","None" From 61106da6e82626e2507aca424c3dded6760077d1 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Oct 2012 12:03:26 +0200 Subject: [PATCH 103/171] [FIX] import docs: clarify handling of empty cells bzr revid: xmo@openerp.com-20121009100326-bil1rw8904ze7opk --- doc/import.rst | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/doc/import.rst b/doc/import.rst index f3db5d50bcf..bc654170659 100644 --- a/doc/import.rst +++ b/doc/import.rst @@ -78,10 +78,17 @@ Conversion This second phase takes the record dicts, extracts the :ref:`dbid` and :ref:`xid` if present and attempts to convert each field to a type -matching what OpenERP expects to write. Empty fields (empty strings) -are replaced with the ``False`` value, and non-empty fields are -converted through -:class:`~openerp.addons.base.ir.ir_fields.ir_fields_converter`: +matching what OpenERP expects to write. + +* Empty fields (empty strings) are replaced with the ``False`` value + +* Non-empty fields are converted through + :class:`~openerp.addons.base.ir.ir_fields.ir_fields_converter` + +.. note:: if a field is specified in the import, its default will *never* be + used. If some records need to have a value and others need to use + the model's default, either specify that default explicitly or do + the import in two phases. Char, text and binary fields ++++++++++++++++++++++++++++ From e9aef291108da25a883042676f801b9650bd300d Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Oct 2012 12:06:54 +0200 Subject: [PATCH 104/171] [FIX] titles of import doc bzr revid: xmo@openerp.com-20121009100654-iqhppxeo9fijnceb --- doc/import.rst | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/doc/import.rst b/doc/import.rst index bc654170659..44e99e88d3e 100644 --- a/doc/import.rst +++ b/doc/import.rst @@ -22,7 +22,7 @@ This document attempts to explain the behavior and limitations of :meth:`~openerp.osv.orm.BaseModel.load`. Data -==== +---- The input ``data`` is a regular row-major matrix of strings (in Python datatype terms, a ``list`` of rows, each row being a ``list`` of @@ -38,7 +38,7 @@ and each cell of a row yields a value for the corresponding field of the row's record. There is currently one exception for this rule: One to Many fields ------------------- +++++++++++++++++++ Because O2M fields contain multiple records "embedded" in the main one, and these sub-records are fully dependent on the main record (are @@ -53,14 +53,14 @@ fields. During parsing, they are extracted into their own ``data`` matrix for the o2m field they correspond to. Import process -============== +-------------- Here are the phases of import. Note that the concept of "phases" is fuzzy as it's currently more of a pipeline, each record moves through the entire pipeline before the next one is processed. Extraction ----------- +++++++++++ The first phase of the import is the extraction of the current row (and potentially a section of rows following it if it has One to Many @@ -74,7 +74,7 @@ This phase also generates the ``rows`` indexes for any :ref:`import-message` produced thereafter. Conversion ----------- +++++++++++ This second phase takes the record dicts, extracts the :ref:`dbid` and :ref:`xid` if present and attempts to convert each field to a type @@ -91,12 +91,12 @@ matching what OpenERP expects to write. the import in two phases. Char, text and binary fields -++++++++++++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Are returned as-is, without any alteration. Boolean fields -++++++++++++++ +~~~~~~~~~~~~~~ The string value is compared (in a case-insensitive manner) to ``0``, ``false`` and ``no`` as well of any translation thereof loaded in the @@ -109,14 +109,14 @@ any translation of these in the database). The field is always set to also be output. Integers and float fields -+++++++++++++++++++++++++ +~~~~~~~~~~~~~~~~~~~~~~~~~ The field is parsed with Python's built-in conversion routines (``int`` and ``float`` respectively), if the conversion fails an error is generated. Selection fields -++++++++++++++++ +~~~~~~~~~~~~~~~~ The field is compared to 1. the values of the selection (first part of each selection tuple) and 2. all translations of the selection label @@ -131,7 +131,7 @@ The same process applies to both list-type and function-type selection fields. Many to One field -+++++++++++++++++ +~~~~~~~~~~~~~~~~~ If the specified field is the relational field itself (``m2o``), the value is used in a ``name_search``. The first record returned by @@ -151,14 +151,14 @@ the same as for external ids (on database identifiers instead of external ones). Many to Many field -++++++++++++++++++ +~~~~~~~~~~~~~~~~~~ The field's value is interpreted as a comma-separated list of names, external ids or database ids. For each one, the process previously used for the many to one field is applied. One to Many field -+++++++++++++++++ +~~~~~~~~~~~~~~~~~ For each o2m record extracted, if the record has a ``name``, :ref:`xid` or :ref:`dbid` the :ref:`dbid` is looked up and checked @@ -170,13 +170,13 @@ an UPDATE with the non-db values for the relational field. Otherwise a CREATE command is emmitted. Create/Write ------------- +++++++++++++ If the conversion was successful, the converted record is then saved to the database via ``(ir.model.data)._update``. Error handling --------------- +++++++++++++++ The import process will only catch 2 types of exceptions to convert them to error messages: ``ValueError`` during the conversion process, @@ -199,7 +199,7 @@ The import process uses savepoint to: .. _import-messages: Messages -======== +-------- A message is a dictionary with 5 mandatory keys and one optional key: From 1b7d0029221be19c6b23634f60d6f8109db852d8 Mon Sep 17 00:00:00 2001 From: Vishmita Date: Tue, 9 Oct 2012 15:40:28 +0530 Subject: [PATCH 105/171] [IMP]replace callenabled for on_mode_switch bzr revid: vja@tinyerp.com-20121009101028-z5andmt2pi0973za --- addons/web/static/src/js/view_form.js | 2 +- addons/web/static/src/js/views.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index b41039361c0..db68b8d7a23 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3370,7 +3370,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ } def.resolve(); }); - this.viewmanager.on_mode_switch.add_first(function(n_mode, b, c, d, e) { + this.viewmanager.on('on_mode_switch',self,function(n_mode, b, c, d, e) { $.when(self.save_any_view()).then(function() { if(n_mode === "list") $.async_when().then(function() {self.reload_current_view();}); diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 21f539fed1b..18d7e9d5666 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -552,7 +552,7 @@ instance.web.ViewManager = instance.web.Widget.extend({ add_breadcrumb: function(on_reverse_breadcrumb) { var self = this; var views = [this.active_view || this.views_src[0].view_type]; - this.on_mode_switch.add(function(mode) { + this.on('on_mode_switch',self,function(mode) { var last = views.slice(-1)[0]; if (mode !== last) { if (mode !== 'form') { From 96ffc4401a18c1d4b798c01970d0f3b40258a206 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Tue, 9 Oct 2012 12:10:51 +0200 Subject: [PATCH 106/171] [IMP] document date and datetime conversions bzr revid: xmo@openerp.com-20121009101051-rsdq3u3ues022wuc --- doc/import.rst | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/doc/import.rst b/doc/import.rst index 44e99e88d3e..bffb1af334b 100644 --- a/doc/import.rst +++ b/doc/import.rst @@ -169,6 +169,31 @@ an UPDATE with the non-db values for the relational field. Otherwise a CREATE command is emmitted. +Date fields +~~~~~~~~~~~ + +The value's format is checked against +:data:`~openerp.tools.misc.DEFAULT_SERVER_DATE_FORMAT`, an error is +generated if it does not match the specified format. + +Datetime fields +~~~~~~~~~~~~~~~ + +The value's format is checked against +:data:`~openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT`, an error +is generated if it does not match. + +The value is then interpreted as a datetime in the user's +timezone. The timezone is specified thus: + +* If the import ``context`` contains a ``tz`` key with a valid + timezone name, this is the timezone of the datetime. + +* Otherwise if the user performing the import has a ``tz`` attribute + set to a valid timezone name, this is the timezone of the datetime. + +* Otherwise interpret the datetime as being in the ``UTC`` timezone. + Create/Write ++++++++++++ From 42ece6cdb779bfdaed041768736a521dbc2aadc3 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 9 Oct 2012 12:35:35 +0200 Subject: [PATCH 107/171] [FIX] project: leftover typos in mail processing methods bzr revid: odo@openerp.com-20121009103535-ohvpd1u81hcc5izi --- addons/project/project.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/addons/project/project.py b/addons/project/project.py index 6b02a018293..98c385c0ce3 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -1173,11 +1173,10 @@ class task(base_stage, osv.osv): """ Override to updates the document according to the email. """ if custom_values is None: custom_values = {} custom_values.update({ - 'name': subject, + 'name': msg.get('subject'), 'planned_hours': 0.0, - 'subject': msg.get('subject'), }) - return super(project_tasks,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) + return super(task,self).message_new(cr, uid, msg, custom_values=custom_values, context=context) def message_update(self, cr, uid, ids, msg, update_vals=None, context=None): """ Override to update the task according to the email. """ @@ -1202,7 +1201,7 @@ class task(base_stage, osv.osv): act = 'do_%s' % res.group(2).lower() if act: getattr(self,act)(cr, uid, ids, context=context) - return super(project_tasks,self).message_update(cr, uid, msg, update_vals=update_vals, context=context) + return super(task,self).message_update(cr, uid, msg, update_vals=update_vals, context=context) # --------------------------------------------------- # OpenChatter methods and notifications From fb3a8ca573e4ce66ad562e1b504a9abd4fa0f695 Mon Sep 17 00:00:00 2001 From: Antonin Bourguignon Date: Tue, 9 Oct 2012 14:23:49 +0200 Subject: [PATCH 108/171] [REM] commented code bzr revid: abo@openerp.com-20121009122349-ex5ypwulksc7ibx4 --- addons/project/report/project_cumulative.xml | 30 -------------------- 1 file changed, 30 deletions(-) diff --git a/addons/project/report/project_cumulative.xml b/addons/project/report/project_cumulative.xml index 3531b5ffc27..dc556be22e7 100644 --- a/addons/project/report/project_cumulative.xml +++ b/addons/project/report/project_cumulative.xml @@ -29,21 +29,6 @@ - - - - - - - - - - - - - - - project.task.history.cumulative.search @@ -78,20 +63,5 @@ id="menu_action_view_task_history_cumulative" parent="base.menu_project_report" /> - - - - - - - - - - - - - - - From abac1cae455e3380fee45a2d9503427ed6fa3269 Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Tue, 9 Oct 2012 18:05:32 +0530 Subject: [PATCH 109/171] [IMP]improvements to record load event. bzr revid: vme@tinyerp.com-20121009123532-bwlrg4ldtprd1w2d --- addons/web/static/src/js/view_form.js | 3 +- addons/web_diagram/static/src/js/diagram.js | 34 +++------------------ addons/web_tests/static/src/js/web_tests.js | 6 ++-- 3 files changed, 8 insertions(+), 35 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index e55258d45d1..45e49366ba2 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -380,7 +380,6 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM field._inhibit_on_change_flag = false; set_values.push(result); }); - console.log("on_record_loaded"); return $.when.apply(null, set_values).pipe(function() { if (!record.id) { // New record: Second pass in order to trigger the onchanges @@ -755,7 +754,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM this.trigger('history_back'); } else { this.to_view_mode(); - self.trigger('record_load',this.datarecord); + this.trigger('record_load',this.datarecord); } } return false; diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 083dfd56dc0..6c32a12384a 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -239,24 +239,13 @@ instance.web.DiagramView = instance.web.View.extend({ title: _t("Open: ") + title } ); - - pop.on_write_completed.add(function() { + pop.on('on_write_complete', self, function() { self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded); }); - + var form_fields = [self.parent_field]; var form_controller = pop.view_form; - /*form_controller.on_record_loaded.add_first(function() { - console.log("web diagram add_first"); - _.each(form_fields, function(fld) { - if (!(fld in form_controller.fields)) { return; } - var field = form_controller.fields[fld]; - field.$input.prop('disabled', true); - field.$drop_down.unbind(); - //field.$menu_btn.unbind(); - }); - });*/ form_controller.on("record_load", self, function(){ console.log("web diagram add_first"); _.each(form_fields, function(fld) { @@ -264,7 +253,6 @@ instance.web.DiagramView = instance.web.View.extend({ var field = form_controller.fields[fld]; field.$input.prop('disabled', true); field.$drop_down.unbind(); - //field.$menu_btn.unbind(); }); }); @@ -293,14 +281,6 @@ instance.web.DiagramView = instance.web.View.extend({ var form_controller = pop.view_form; var form_fields = [this.parent_field]; - /*form_controller.on_record_loaded.add_last(function() { - _.each(form_fields, function(fld) { - if (!(fld in form_controller.fields)) { return; } - var field = form_controller.fields[fld]; - field.set_value(self.id); - field.dirty = true; - }); - });*/ form_controller.on("record_load", self, function(){ _.each(form_fields, function(fld) { if (!(fld in form_controller.fields)) { return; } @@ -324,7 +304,7 @@ instance.web.DiagramView = instance.web.View.extend({ title: _t("Open: ") + title } ); - pop.on_write.add(function() { + pop.on('on_write_complete', self, function() { self.dataset.read_index(_.keys(self.fields_view.fields)).pipe(self.on_diagram_loaded); }); }, @@ -360,14 +340,8 @@ instance.web.DiagramView = instance.web.View.extend({ var form_controller = pop.view_form; - /*form_controller.on_record_loaded.add_last(function () { - form_controller.fields[self.connectors.attrs.source].set_value(node_source_id); - form_controller.fields[self.connectors.attrs.source].dirty = true; - form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id); - form_controller.fields[self.connectors.attrs.destination].dirty = true; - });*/ + form_controller.on("record_load", self, function(){ - console.log("add_connector"); form_controller.fields[self.connectors.attrs.source].set_value(node_source_id); form_controller.fields[self.connectors.attrs.source].dirty = true; form_controller.fields[self.connectors.attrs.destination].set_value(node_dest_id); diff --git a/addons/web_tests/static/src/js/web_tests.js b/addons/web_tests/static/src/js/web_tests.js index 5b9c1d02503..8c146644372 100644 --- a/addons/web_tests/static/src/js/web_tests.js +++ b/addons/web_tests/static/src/js/web_tests.js @@ -23,14 +23,14 @@ openerp.web_tests = function (instance) { on_everything_loaded: function (slice) { var records = slice[0].records; if (!records.length) { - this.form.on_record_loaded({}); + this.form.trigger("record_load",{}); return; } - this.form.on_record_loaded(records[0]); + this.form.trigger("record_load", records[0]) _(records.slice(1)).each(function (record, index) { this.dataset.index = index+1; this.form.reposition($('
    ').appendTo(this.$el)); - this.form.on_record_loaded(record); + this.form.trigger("record_load", record) }, this); } }); From 5645a84a1651b9f2104ec22e62033de70fa8a334 Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Tue, 9 Oct 2012 18:19:08 +0530 Subject: [PATCH 110/171] [IMP]replace add* method. bzr revid: vme@tinyerp.com-20121009124908-r33qqpx15ea39p78 --- addons/board/static/src/js/dashboard.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/board/static/src/js/dashboard.js b/addons/board/static/src/js/dashboard.js index 43d7ca76d39..bba076e8594 100644 --- a/addons/board/static/src/js/dashboard.js +++ b/addons/board/static/src/js/dashboard.js @@ -25,9 +25,9 @@ instance.web.form.DashBoard = instance.web.form.FormWidget.extend({ }).bind('sortstop', self.do_save_dashboard); var old_title = this.__parentedParent.get('title'); - this.__parentedParent.on_record_loaded.add_last(function(){ + this.__parentedParent.on('record_load', this, function(){ self.__parentedParent.set({ 'title' : old_title}); - }); + }) // Events this.$el.find('.oe_dashboard_link_reset').click(this.on_reset); this.$el.find('.oe_dashboard_link_change_layout').click(this.on_change_layout); From 91f97769790fbd59bd6360c0392a4c1c1cece0cd Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Tue, 9 Oct 2012 18:26:47 +0530 Subject: [PATCH 111/171] [rem]console bzr revid: vme@tinyerp.com-20121009125647-zwx4fj95fjs0y34u --- addons/web_diagram/static/src/js/diagram.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/web_diagram/static/src/js/diagram.js b/addons/web_diagram/static/src/js/diagram.js index 6c32a12384a..3b9e81fdf6e 100644 --- a/addons/web_diagram/static/src/js/diagram.js +++ b/addons/web_diagram/static/src/js/diagram.js @@ -247,7 +247,6 @@ instance.web.DiagramView = instance.web.View.extend({ var form_controller = pop.view_form; form_controller.on("record_load", self, function(){ - console.log("web diagram add_first"); _.each(form_fields, function(fld) { if (!(fld in form_controller.fields)) { return; } var field = form_controller.fields[fld]; From 70ebec1cd62b532fb21da247f2deb2d8e13e9db1 Mon Sep 17 00:00:00 2001 From: Fabien Meghazi Date: Tue, 9 Oct 2012 14:56:53 +0200 Subject: [PATCH 112/171] [IMP] Do not update auth_oauth service providers bzr revid: fme@openerp.com-20121009125653-p9i84663xygtb1r0 --- addons/auth_oauth/auth_oauth_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/auth_oauth/auth_oauth_data.xml b/addons/auth_oauth/auth_oauth_data.xml index 83e9df2e44d..c07359a062c 100644 --- a/addons/auth_oauth/auth_oauth_data.xml +++ b/addons/auth_oauth/auth_oauth_data.xml @@ -1,6 +1,6 @@ - + OpenERP Accounts From b8d7b8cb0ac768220eb28b3823b02d63027267ca Mon Sep 17 00:00:00 2001 From: "Vidhin Mehta (OpenERP)" Date: Tue, 9 Oct 2012 18:30:30 +0530 Subject: [PATCH 113/171] [REM]one more console. bzr revid: vme@tinyerp.com-20121009130030-8437spz1j1eogh8x --- addons/web/static/src/js/view_form.js | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 45e49366ba2..43afe3a3bf1 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -3372,7 +3372,6 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ $(".oe_form_buttons", controller.$el).children().remove(); } controller.on("record_load", self, function(){ - console.log("view manager form"); once.resolve(); }); controller.on_pager_action.add_first(function() { From 099825109ef0795693d6fce665891e0641d2766d Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 9 Oct 2012 15:19:11 +0200 Subject: [PATCH 114/171] [FIX] mail.message: catch more error cases when trying to resolve msg author + lint cleanup bzr revid: odo@openerp.com-20121009131911-6lo2knsrh2qgrxv8 --- addons/mail/mail_message.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 9c873a31097..98b7ef9f36d 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -180,7 +180,6 @@ class mail_message(osv.Model): fields allow to have the foreign record name without having to check external access rights). """ - child_nbr = len(msg.child_ids) has_voted = False vote_ids = self.pool.get('res.users').name_get(cr, SUPERUSER_ID, [user.id for user in msg.vote_user_ids], context=context) for vote in vote_ids: @@ -194,7 +193,7 @@ class mail_message(osv.Model): try: author_id = self.pool.get('res.partner').name_get(cr, uid, [msg.author_id.id], context=context)[0] is_author = uid == msg.author_id.user_ids[0].id - except (orm.except_orm, osv.except_osv): + except Exception: author_id = False is_author = False try: From fac6e9246be0f03915c199a2f9277658dd501c95 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 9 Oct 2012 15:31:32 +0200 Subject: [PATCH 115/171] [FIX] mail: allow overriding message_post in threads + fix res.users' delegation to res.partner bzr revid: odo@openerp.com-20121009133132-at5j8pm8engxqi10 --- addons/mail/mail_thread.py | 2 +- addons/mail/res_users.py | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 9f53b4c08b1..8dee944751c 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -427,7 +427,7 @@ class mail_thread(osv.AbstractModel): model_pool.message_update(cr, user_id, [thread_id], msg, context=context) else: thread_id = model_pool.message_new(cr, user_id, msg, custom_values, context=context) - self.message_post(cr, uid, [thread_id], context=context, **msg) + model_pool.message_post(cr, uid, [thread_id], context=context, **msg) return thread_id def message_new(self, cr, uid, msg_dict, custom_values=None, context=None): diff --git a/addons/mail/res_users.py b/addons/mail/res_users.py index 545198f4c7f..f870604b03a 100644 --- a/addons/mail/res_users.py +++ b/addons/mail/res_users.py @@ -110,9 +110,19 @@ class res_users(osv.Model): alias_pool.unlink(cr, uid, alias_ids, context=context) return res - def message_post(self, cr, uid, thread_id, **kwargs): - partner_id = self.pool.get('res.users').browse(cr, uid, thread_id)[0].partner_id.id - return self.pool.get('res.partner').message_post(cr, uid, partner_id, **kwargs) + def message_post(self, cr, uid, thread_id, context=None, **kwargs): + assert thread_id, "res.users does not support posting global messages" + if context and 'thread_model' in context: + context['thread_model'] = 'res.partner' + if isinstance(thread_id, (list, tuple)): + thread_id = thread_id[0] + partner_id = self.pool.get('res.users').browse(cr, uid, thread_id).partner_id.id + return self.pool.get('res.partner').message_post(cr, uid, partner_id, context=context, **kwargs) + + def message_update(self, cr, uid, ids, msg_dict, update_vals=None, context=None): + partner_id = self.pool.get('res.users').browse(cr, uid, ids)[0].partner_id.id + return self.pool.get('res.partner').message_update(cr, uid, [partner_id], msg_dict, + update_vals=update_vals, context=context) class res_users_mail_group(osv.Model): """ Update of res.users class From 0ce1ef32a872bd1f942dd73ee11214374b6ce520 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 9 Oct 2012 15:40:20 +0200 Subject: [PATCH 116/171] [FIX] mail: typos -> message-id header is mapped to message_id column bzr revid: odo@openerp.com-20121009134020-9wf96yszv8qcmbm1 --- addons/mail/mail_thread.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 8dee944751c..2261b940944 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -422,7 +422,7 @@ class mail_thread(osv.AbstractModel): model_pool = self.pool.get(model) assert thread_id and hasattr(model_pool, 'message_update') or hasattr(model_pool, 'message_new'), \ "Undeliverable mail with Message-Id %s, model %s does not accept incoming emails" % \ - (msg['message-id'], model) + (msg['message_id'], model) if thread_id and hasattr(model_pool, 'message_update'): model_pool.message_update(cr, user_id, [thread_id], msg, context=context) else: @@ -538,7 +538,7 @@ class mail_thread(osv.AbstractModel): field may not be present if missing in original message:: - { 'message-id': msg_id, + { 'message_id': msg_id, 'subject': subject, 'from': from, 'to': to, From cb57169c97fa8577d2b2e24789b563f2066f059e Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 9 Oct 2012 15:44:14 +0200 Subject: [PATCH 117/171] [FIX] mail.mail: typo in previous commits bzr revid: odo@openerp.com-20121009134414-923kv1ciw00pxh6v --- addons/mail/mail_mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_mail.py b/addons/mail/mail_mail.py index 5b4a096e969..741184ef7a3 100644 --- a/addons/mail/mail_mail.py +++ b/addons/mail/mail_mail.py @@ -65,7 +65,7 @@ class mail_mail(osv.Model): def _get_default_from(self, cr, uid, context=None): this = self.pool.get('res.users').browse(cr, uid, uid, context=context) if this.alias_domain: - return '%s@%s' % (this.alias, this.alias_domain) + return '%s@%s' % (this.alias_name, this.alias_domain) elif this.email: return this.email raise osv.except_osv(_('Invalid Action!'), _("Unable to send email, please configure the sender's email address or alias.")) From 7f516d2835e3f2ba9a9b466d7ad4a92251566a20 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Tue, 9 Oct 2012 15:50:40 +0200 Subject: [PATCH 118/171] [FIX] mail.message: incorrect message-id generation, id/model were swapped For backwards-compatibility reasons we need to stick with this format: `res_id-model`, even if reversing the values would look more logical bzr revid: odo@openerp.com-20121009135040-spvejc1l4mos25yv --- addons/mail/mail_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 98b7ef9f36d..7e77b2f2e8c 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -501,7 +501,7 @@ class mail_message(osv.Model): def create(self, cr, uid, values, context=None): if not values.get('message_id') and values.get('res_id') and values.get('model'): - values['message_id'] = tools.generate_tracking_message_id('%(model)s-%(res_id)s' % values) + values['message_id'] = tools.generate_tracking_message_id('%(res_id)s-%(model)s' % values) newid = super(mail_message, self).create(cr, uid, values, context) self._notify(cr, SUPERUSER_ID, newid, context=context) return newid From c98de15a09c4d2dda02426ccb0bda9f6d37677bd Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 9 Oct 2012 16:26:07 +0200 Subject: [PATCH 119/171] [IMP] osv/orm.py: evaluate in python the 'options' attribute of a field in an xml view, before enclosing it in json. This allow to use python instead of json syntax in the definition of the options of a widget, which is a much more consistent behavior bzr revid: qdp-launchpad@openerp.com-20121009142607-rk3l00atu3w3kksp --- openerp/addons/base/res/res_bank_view.xml | 4 ++-- openerp/addons/base/res/res_company_view.xml | 4 ++-- openerp/addons/base/res/res_country_view.xml | 2 +- openerp/addons/base/res/res_partner_view.xml | 6 +++--- openerp/osv/orm.py | 8 ++++++-- openerp/tools/safe_eval.py | 2 +- 6 files changed, 15 insertions(+), 11 deletions(-) diff --git a/openerp/addons/base/res/res_bank_view.xml b/openerp/addons/base/res/res_bank_view.xml index ac91684d4d9..33b98c690c6 100644 --- a/openerp/addons/base/res/res_bank_view.xml +++ b/openerp/addons/base/res/res_bank_view.xml @@ -106,8 +106,8 @@
    - - + + diff --git a/openerp/addons/base/res/res_company_view.xml b/openerp/addons/base/res/res_company_view.xml index cac53b7a76a..b8cb12d3dd0 100644 --- a/openerp/addons/base/res/res_company_view.xml +++ b/openerp/addons/base/res/res_company_view.xml @@ -46,10 +46,10 @@
    - +
    - +
    @@ -177,7 +177,7 @@ + options='{"no_open": True}' attrs="{'invisible': [('is_company','=', True)]}" /> diff --git a/openerp/osv/orm.py b/openerp/osv/orm.py index c713c0378ea..8bc5c386527 100644 --- a/openerp/osv/orm.py +++ b/openerp/osv/orm.py @@ -60,7 +60,7 @@ import openerp import openerp.netsvc as netsvc import openerp.tools as tools from openerp.tools.config import config -from openerp.tools.safe_eval import safe_eval as eval +from openerp.tools.safe_eval import const_eval, safe_eval as eval from openerp.tools.translate import _ from openerp import SUPERUSER_ID from query import Query @@ -1732,7 +1732,11 @@ class BaseModel(object): field = model_fields.get(node.get('name')) if field: transfer_field_to_modifiers(field, modifiers) - + if node.get('options'): + try: + node.set('options', simplejson.dumps(const_eval(node.get('options')))) + except Exception, msg: + raise except_orm('Invalide Python code in %s'%(node.get('options')), msg[0]) elif node.tag in ('form', 'tree'): result = self.view_header_get(cr, user, False, node.tag, context) diff --git a/openerp/tools/safe_eval.py b/openerp/tools/safe_eval.py index 601fd95200a..6bc92e57465 100644 --- a/openerp/tools/safe_eval.py +++ b/openerp/tools/safe_eval.py @@ -46,7 +46,7 @@ _ALLOWED_MODULES = ['_strptime', 'time'] _CONST_OPCODES = set(opmap[x] for x in [ 'POP_TOP', 'ROT_TWO', 'ROT_THREE', 'ROT_FOUR', 'DUP_TOP', 'DUP_TOPX', 'POP_BLOCK','SETUP_LOOP', 'BUILD_LIST', 'BUILD_MAP', 'BUILD_TUPLE', - 'LOAD_CONST', 'RETURN_VALUE', 'STORE_SUBSCR'] if x in opmap) + 'LOAD_CONST', 'RETURN_VALUE', 'STORE_SUBSCR', 'STORE_MAP', 'LOAD_NAME'] if x in opmap) _EXPR_OPCODES = _CONST_OPCODES.union(set(opmap[x] for x in [ 'UNARY_POSITIVE', 'UNARY_NEGATIVE', 'UNARY_NOT', From fbbc0bad19c57bfbf5df9351373c8edd0ead3ded Mon Sep 17 00:00:00 2001 From: "Quentin (OpenERP)" Date: Tue, 9 Oct 2012 16:34:33 +0200 Subject: [PATCH 120/171] [IMP] all: replaced 'true' by 'True' in 'options' attribute in views, because now the syntax of these options must be expressed in python instead of json bzr revid: qdp-launchpad@openerp.com-20121009143433-s4y6isbewqdn2fu2 --- addons/account/account_invoice_view.xml | 2 +- addons/account/account_view.xml | 2 +- .../account_voucher/voucher_sales_purchase_view.xml | 2 +- addons/crm/crm_lead_view.xml | 12 ++++++------ addons/document_page/document_page_view.xml | 2 +- .../wizard/document_page_show_diff_view.xml | 2 +- .../wizard/email_template_preview_view.xml | 2 +- addons/event/event_view.xml | 4 ++-- addons/hr/hr_view.xml | 8 ++++---- addons/portal_crm/wizard/contact_view.xml | 4 ++-- addons/project/project_view.xml | 6 +++--- addons/project_gtd/project_gtd_view.xml | 4 ++-- addons/project_gtd/wizard/project_gtd_fill_view.xml | 2 +- addons/sale/sale_view.xml | 4 ++-- 14 files changed, 28 insertions(+), 28 deletions(-) diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 26e18271dc2..48737bd5fef 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -322,7 +322,7 @@ + options='{"always_reload": True}'/> diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index 3391158696a..d2680a8456f 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -635,7 +635,7 @@ - + diff --git a/addons/account_voucher/voucher_sales_purchase_view.xml b/addons/account_voucher/voucher_sales_purchase_view.xml index 4c388730644..a362c553ccd 100644 --- a/addons/account_voucher/voucher_sales_purchase_view.xml +++ b/addons/account_voucher/voucher_sales_purchase_view.xml @@ -73,7 +73,7 @@ - + diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index f91a8756598..f7acb6b557d 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -160,17 +160,17 @@
    - +
    - +
    @@ -488,7 +488,7 @@
    diff --git a/addons/event/event_view.xml b/addons/event/event_view.xml index b46fafe270f..cbfc670c9cf 100644 --- a/addons/event/event_view.xml +++ b/addons/event/event_view.xml @@ -126,10 +126,10 @@
    - +
    - + diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index 69567902763..caa893aea6e 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -42,14 +42,14 @@ - + - + @@ -57,14 +57,14 @@ - + - + diff --git a/addons/portal_crm/wizard/contact_view.xml b/addons/portal_crm/wizard/contact_view.xml index 1776632d7ab..778e7f9562e 100644 --- a/addons/portal_crm/wizard/contact_view.xml +++ b/addons/portal_crm/wizard/contact_view.xml @@ -35,8 +35,8 @@ - - + +

    diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index bd63eec84c5..9d3b137f7c1 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -103,7 +103,7 @@ - + @@ -311,7 +311,7 @@ - +
    @@ -401,7 +401,7 @@ - + diff --git a/addons/project_gtd/project_gtd_view.xml b/addons/project_gtd/project_gtd_view.xml index fdb8ae8495c..a7026da8854 100644 --- a/addons/project_gtd/project_gtd_view.xml +++ b/addons/project_gtd/project_gtd_view.xml @@ -92,8 +92,8 @@ - - + +
    diff --git a/addons/project_gtd/wizard/project_gtd_fill_view.xml b/addons/project_gtd/wizard/project_gtd_fill_view.xml index 8807c00a76b..626dfb699b8 100644 --- a/addons/project_gtd/wizard/project_gtd_fill_view.xml +++ b/addons/project_gtd/wizard/project_gtd_fill_view.xml @@ -8,7 +8,7 @@
    - + diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index 5505a6a88b5..0055deb8d3d 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -214,7 +214,7 @@
  • From b1b8447a6b7afe9a78a42a0cdcee58384690fd09 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Thu, 11 Oct 2012 02:02:17 +0200 Subject: [PATCH 168/171] [FIX] login always set params for potential redirection bzr revid: al@openerp.com-20121011000217-ck3xsquoain1z8ja --- addons/web/static/src/js/chrome.js | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 80693d26e46..ffec074a975 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1016,15 +1016,13 @@ instance.web.WebClient = instance.web.Client.extend({ }, show_login: function() { this.toggle_bars(false); - + + var state = $.bbq.getState(true); var action = { 'type': 'ir.actions.client', - 'tag': 'login' + 'tag': 'login', + 'params': state }; - var state = $.bbq.getState(true); - if (state.action === "login") { - action.params = state; - } this.action_manager.do_action(action); this.action_manager.inner_widget.on('login_successful', this, function() { From 462eccb10d3f3c4c374ad6c305c89dcc5eb57cdf Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Thu, 11 Oct 2012 02:05:34 +0200 Subject: [PATCH 169/171] [FIX] auth_signup pass on the state bzr revid: al@openerp.com-20121011000534-w3wa7m3mh1znylde --- addons/auth_signup/controllers/main.py | 4 ++-- addons/auth_signup/res_users.py | 12 ++++-------- addons/auth_signup/static/src/js/auth_signup.js | 1 + 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py index cf72e581049..54dcc5b4362 100644 --- a/addons/auth_signup/controllers/main.py +++ b/addons/auth_signup/controllers/main.py @@ -42,7 +42,7 @@ class Controller(openerp.addons.web.http.Controller): return user_info @openerp.addons.web.http.httprequest - def signup(self, req, dbname, token, name, login, password): + def signup(self, req, dbname, token, name, login, password, state=''): """ sign up a user (new or existing), and log it in """ url = '/' registry = RegistryManager.get(dbname) @@ -52,7 +52,7 @@ class Controller(openerp.addons.web.http.Controller): values = {'name': name, 'login': login, 'password': password} credentials = res_users.signup(cr, openerp.SUPERUSER_ID, values, token) cr.commit() - return login_and_redirect(req, *credentials) + return login_and_redirect(req, *credentials, redirect_url='/#%s'%state) except Exception as e: # signup error _logger.exception('error when signup') diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 7b663bd0e33..59196c4da87 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -18,6 +18,10 @@ # along with this program. If not, see # ############################################################################## +import random +import time +import urllib +import urlparse import openerp from openerp.osv import osv, fields @@ -25,11 +29,6 @@ from openerp import SUPERUSER_ID from openerp.tools.misc import DEFAULT_SERVER_DATETIME_FORMAT from openerp.tools.safe_eval import safe_eval -import time -import random -import urllib -import urlparse - def random_token(): # the token has an entropy of about 120 bits (6 bits/char * 20 chars) chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' @@ -38,7 +37,6 @@ def random_token(): def now(): return time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - class res_partner(osv.Model): _inherit = 'res.partner' @@ -132,8 +130,6 @@ class res_partner(osv.Model): res['email'] = partner.email or '' return res - - class res_users(osv.Model): _inherit = 'res.users' diff --git a/addons/auth_signup/static/src/js/auth_signup.js b/addons/auth_signup/static/src/js/auth_signup.js index 509fafa35cc..0974b73292d 100644 --- a/addons/auth_signup/static/src/js/auth_signup.js +++ b/addons/auth_signup/static/src/js/auth_signup.js @@ -90,6 +90,7 @@ openerp.auth_signup = function(instance) { name: name, login: login, password: password, + state: $.param(this.params) }; var url = "/auth_signup/signup?" + $.param(params); window.location = url; From f44ea704fe562dc346b280c6f546e5bc8d630387 Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Thu, 11 Oct 2012 02:37:29 +0200 Subject: [PATCH 170/171] [IMP] CrashManager use event dispatcher and cleanups bzr revid: al@openerp.com-20121011003729-jjbh5se1cpsachvm --- addons/web/static/src/js/chrome.js | 20 ++++++++++---------- addons/web/static/src/js/corelib.js | 9 ++++----- addons/web/static/src/js/view_form.js | 10 +++------- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index ffec074a975..b605f4ad4e1 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -181,7 +181,7 @@ instance.web.Dialog = instance.web.Widget.extend({ }); instance.web.CrashManager = instance.web.CallbackEnabled.extend({ - on_rpc_error: function(error) { + rpc_error: function(error) { if (error.data.fault_code) { var split = ("" + error.data.fault_code).split('\n')[0].split(' -- '); if (split.length > 1) { @@ -190,12 +190,12 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({ } } if (error.code === 200 && error.type) { - this.on_managed_error(error); + this.show_warning(error); } else { - this.on_traceback(error); + this.show_error(error); } }, - on_managed_error: function(error) { + show_warning: function(error) { instance.web.dialog($('
    ' + QWeb.render('CrashManager.warning', {error: error}) + '
    '), { title: "OpenERP " + _.str.capitalize(error.type), buttons: [ @@ -203,7 +203,7 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({ ] }); }, - on_traceback: function(error) { + show_error: function(error) { var self = this; var buttons = {}; buttons[_t("Ok")] = function() { @@ -219,8 +219,8 @@ instance.web.CrashManager = instance.web.CallbackEnabled.extend({ }).open(); dialog.$el.html(QWeb.render('CrashManager.error', {session: instance.session, error: error})); }, - on_javascript_exception: function(exception) { - this.on_traceback({ + show_message: function(exception) { + this.show_error({ type: _t("Client Error"), message: exception, data: {debug: ""} @@ -236,7 +236,7 @@ instance.web.Loading = instance.web.Widget.extend({ this.blocked_ui = false; this.session.on("request", this, this.request_call); this.session.on("response", this, this.response_call); - this.session.on("error", this, this.response_call); + this.session.on("response_failed", this, this.response_call); }, destroy: function() { this.on_rpc_event(-this.count); @@ -957,7 +957,7 @@ instance.web.Client = instance.web.Widget.extend({ show_common: function() { var self = this; this.crashmanager = new instance.web.CrashManager(); - instance.session.on_rpc_error.add(this.crashmanager.on_rpc_error); + instance.session.on('error', this.crashmanager, this.crashmanager.rpc_error); self.notification = new instance.web.Notification(this); self.notification.appendTo(self.$el); self.loading = new instance.web.Loading(self); @@ -1007,7 +1007,7 @@ instance.web.WebClient = instance.web.Client.extend({ var self = this; this._super(); window.onerror = function (message, file, line) { - self.crashmanager.on_traceback({ + self.crashmanager.show_error({ type: _t("Client Error"), message: message, data: {debug: file + ':' + line} diff --git a/addons/web/static/src/js/corelib.js b/addons/web/static/src/js/corelib.js index 7a9af79b4fb..b5fbacc7927 100644 --- a/addons/web/static/src/js/corelib.js +++ b/addons/web/static/src/js/corelib.js @@ -980,7 +980,8 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ triggers: { 'request': 'Request sent', 'response': 'Response received', - 'error': 'HTTP Error response or timeout received', + 'response_failed': 'HTTP Error response or timeout received', + 'error': 'The received response is an JSON-RPC error', }, /** * @constructs instance.web.JsonRPC @@ -1336,7 +1337,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ } }, function(jqXHR, textStatus, errorThrown) { - self.trigger('error'); + self.trigger('response_failed', jqXHR); var error = { code: -32098, message: "XmlHttpRequestError " + errorThrown, @@ -1348,7 +1349,7 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ deferred.fail(function() { deferred.fail(function(error, event) { if (!event.isDefaultPrevented()) { - self.on_rpc_error(error, event); + self.trigger('error', error, event); } }); }); @@ -1434,8 +1435,6 @@ instance.web.JsonRPC = instance.web.CallbackEnabled.extend({ return deferred; } }, - on_rpc_error: function(error) { - }, get_url: function (file) { return this.prefix + file; }, diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 2b4569eea3f..afb5e258c0b 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -615,7 +615,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return self.on_processed_onchange(response, processed); } catch(e) { console.error(e); - instance.webclient.crashmanager.on_javascript_exception(e); + instance.webclient.crashmanager.show_message(e); return $.Deferred().reject(); } }); @@ -650,7 +650,7 @@ instance.web.FormView = instance.web.View.extend(instance.web.form.FieldManagerM return $.Deferred().resolve(); } catch(e) { console.error(e); - instance.webclient.crashmanager.on_javascript_exception(e); + instance.webclient.crashmanager.show_message(e); return $.Deferred().reject(); } }, @@ -3316,11 +3316,7 @@ instance.web.form.FieldOne2Many = instance.web.form.AbstractField.extend({ var views = []; _.each(modes, function(mode) { if (! _.include(["list", "tree", "graph", "kanban"], mode)) { - try { - throw new Error(_.str.sprintf("View type '%s' is not supported in One2Many.", mode)); - } catch(e) { - instance.webclient.crashmanager.on_javascript_exception(e) - } + throw new Error(_.str.sprintf("View type '%s' is not supported in One2Many.", mode)); } var view = { view_id: false, From fc94df2af5146d2780da3b07bf1fad78e6831889 Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of openerp <> Date: Thu, 11 Oct 2012 05:00:45 +0000 Subject: [PATCH 171/171] Launchpad automatic translations update. bzr revid: launchpad_translations_on_behalf_of_openerp-20121011050045-qe3vhe34wrjw1xb8 --- addons/hr_payroll/i18n/es_MX.po | 2302 ++++++++++---------------- addons/l10n_in_hr_payroll/i18n/bn.po | 926 +---------- addons/l10n_in_hr_payroll/i18n/gu.po | 926 +---------- addons/l10n_in_hr_payroll/i18n/hi.po | 926 +---------- addons/l10n_in_hr_payroll/i18n/ta.po | 926 +---------- addons/l10n_in_hr_payroll/i18n/te.po | 926 +---------- 6 files changed, 861 insertions(+), 6071 deletions(-) diff --git a/addons/hr_payroll/i18n/es_MX.po b/addons/hr_payroll/i18n/es_MX.po index a0f4837d947..24b8d615a63 100644 --- a/addons/hr_payroll/i18n/es_MX.po +++ b/addons/hr_payroll/i18n/es_MX.po @@ -1,1759 +1,1119 @@ -# Spanish translation for openobject-addons -# Copyright (c) 2010 Rosetta Contributors and Canonical Ltd 2010 +# Spanish (Mexico) translation for openobject-addons +# Copyright (c) 2012 Rosetta Contributors and Canonical Ltd 2012 # This file is distributed under the same license as the openobject-addons package. -# FIRST AUTHOR , 2010. +# FIRST AUTHOR , 2012. # msgid "" msgstr "" "Project-Id-Version: openobject-addons\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2011-01-11 11:15+0000\n" -"PO-Revision-Date: 2011-01-18 11:08+0000\n" -"Last-Translator: Jordi Esteve (www.zikzakmedia.com) " -"\n" -"Language-Team: Spanish \n" +"POT-Creation-Date: 2012-02-08 01:37+0100\n" +"PO-Revision-Date: 2012-10-10 21:29+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Spanish (Mexico) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2011-09-05 05:46+0000\n" -"X-Generator: Launchpad (build 13830)\n" +"X-Launchpad-Export-Date: 2012-10-11 05:00+0000\n" +"X-Generator: Launchpad (build 16118)\n" #. module: hr_payroll -#: report:employees.salary:0 -msgid "E-mail Address" -msgstr "Dirección de correo electrónico" +#: field:hr.payslip.line,condition_select:0 +#: field:hr.salary.rule,condition_select:0 +msgid "Condition Based on" +msgstr "" #. module: hr_payroll -#: view:hr.allounce.deduction.categoty:0 -msgid "Based" -msgstr "Basado" +#: selection:hr.contract,schedule_pay:0 +msgid "Monthly" +msgstr "" #. module: hr_payroll -#: field:hr.contract,net:0 -#: field:hr.employee,net:0 -#: field:hr.payroll.register,net:0 -#: field:hr.payslip,net:0 -#: report:salary.structure:0 -msgid "Net Salary" -msgstr "Salario neto" +#: view:hr.payslip:0 field:hr.payslip,line_ids:0 +#: model:ir.actions.act_window,name:hr_payroll.act_contribution_reg_payslip_lines +msgid "Payslip Lines" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.line:0 +#: model:ir.model,name:hr_payroll.model_hr_salary_rule_category +#: report:paylip.details:0 +msgid "Salary Rule Category" +msgstr "" + +#. module: hr_payroll +#: help:hr.salary.rule.category,parent_id:0 +msgid "" +"Linking a salary category to its parent is used only for the reporting " +"purpose." +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 view:hr.payslip.line:0 view:hr.salary.rule:0 +msgid "Group By..." +msgstr "" #. module: hr_payroll #: view:hr.payslip:0 -msgid "Recompute Sheet" -msgstr "Recalcular plantilla" +msgid "States" +msgstr "" #. module: hr_payroll -#: report:employees.salary:0 -msgid "Employees Salary Details" -msgstr "Detalles salario de los empleados" +#: field:hr.payslip.line,input_ids:0 view:hr.salary.rule:0 +#: field:hr.salary.rule,input_ids:0 +msgid "Inputs" +msgstr "" #. module: hr_payroll -#: report:employees.salary:0 -msgid "Allowances with Basic:" -msgstr "Primas con base:" +#: field:hr.payslip.line,parent_rule_id:0 +#: field:hr.salary.rule,parent_rule_id:0 +msgid "Parent Salary Rule" +msgstr "" #. module: hr_payroll -#: report:employees.salary:0 -#: report:salary.structure:0 -msgid "Department" -msgstr "Departamento" - -#. module: hr_payroll -#: report:employees.salary:0 -msgid "Deductions:" -msgstr "Deducciones:" - -#. module: hr_payroll -#: field:company.contribution,gratuity:0 -msgid "Use for Gratuity ?" -msgstr "¿Uso de propina?" - -#. module: hr_payroll -#: field:hr.contract,working_days_per_week:0 -#: field:hr.payslip,working_days:0 -#: report:payslip.pdf:0 -msgid "Working Days" -msgstr "Días de trabajo" - -#. module: hr_payroll -#: selection:hr.allounce.deduction.categoty,type:0 -#: selection:hr.payslip.line,type:0 -msgid "Loan" -msgstr "Préstamo" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "Salary Payment Register" -msgstr "Registro de pago de nómina" - -#. module: hr_payroll -#: field:hr.employee,slip_ids:0 -#: view:hr.payroll.register:0 -#: field:hr.payroll.register,line_ids:0 +#: field:hr.employee,slip_ids:0 view:hr.payslip:0 view:hr.payslip.run:0 +#: field:hr.payslip.run,slip_ids:0 #: model:ir.actions.act_window,name:hr_payroll.act_hr_employee_payslip_list msgid "Payslips" -msgstr "Nóminas" - -#. module: hr_payroll -#: model:ir.actions.report.xml,name:hr_payroll.year_salary_report -msgid "Year Salary Report" -msgstr "Informe anual de salarios" - -#. module: hr_payroll -#: selection:hr.payroll.register,state:0 -#: selection:hr.payslip,state:0 -msgid "Paid Salary" -msgstr "Nómina pagada" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "(" -msgstr "(" - -#. module: hr_payroll -#: field:company.contribution,company_id:0 -#: field:hr.allounce.deduction.categoty,company_id:0 -#: field:hr.contibution.register,company_id:0 -#: field:hr.holidays.status,company_id:0 -#: field:hr.payroll.advice,company_id:0 -#: field:hr.payroll.register,company_id:0 -#: field:hr.payroll.structure,company_id:0 -#: field:hr.payslip,company_id:0 -msgid "Company" -msgstr "Compañía" - -#. module: hr_payroll -#: report:payroll.advice:0 -msgid "The Manager" -msgstr "El responsable" - -#. module: hr_payroll -#: view:hr.payroll.advice:0 -msgid "Letter Details" -msgstr "Detalles de la carta" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -#: report:payslip.pdf:0 -msgid "," -msgstr "," - -#. module: hr_payroll -#: view:hr.payroll.advice:0 -#: view:hr.payroll.register:0 -#: view:hr.payslip:0 -msgid "Set to Draft" -msgstr "Cambiar a borrador" - -#. module: hr_payroll -#: code:addons/hr_payroll/hr_payroll.py:180 -#: code:addons/hr_payroll/hr_payroll.py:195 -#: code:addons/hr_payroll/hr_payroll.py:285 -#: code:addons/hr_payroll/hr_payroll.py:835 -#: code:addons/hr_payroll/hr_payroll.py:1111 -#: code:addons/hr_payroll/hr_payroll.py:1126 -#: code:addons/hr_payroll/hr_payroll.py:1410 -#, python-format -msgid "Variable Error: %s " -msgstr "Error variable: %s " - -#. module: hr_payroll -#: view:hr.passport:0 -msgid "Expire" -msgstr "Vencimiento" - -#. module: hr_payroll -#: selection:hr.holidays.status,type:0 -msgid "Half-Pay Holiday" -msgstr "Vacaciones con media paga" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -#: field:hr.payslip,other_pay:0 -msgid "Others" -msgstr "Otros" - -#. module: hr_payroll -#: field:hr.payslip.line,slip_id:0 -#: model:ir.model,name:hr_payroll.model_hr_payslip -#: report:payslip.pdf:0 -msgid "Pay Slip" -msgstr "Nómina" - -#. module: hr_payroll -#: report:salary.structure:0 -msgid "Contract Detail:" -msgstr "Detalles del contrato:" - -#. module: hr_payroll -#: field:hr.payslip,igross:0 -#: field:hr.payslip,inet:0 -msgid "Calculaton Field" -msgstr "Campo de cálculo" - -#. module: hr_payroll -#: help:hr.payroll.advice,bank_id:0 -#: help:hr.payroll.register,bank_id:0 -msgid "Select the Bank Address from whcih the salary is going to be paid" -msgstr "Seleccione la dirección del banco de donde se pagará la nómina." - -#. module: hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice.line,advice_id:0 -msgid "Bank Advice" -msgstr "Aviso bancario" - -#. module: hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:hr.payroll.register,state:0 -#: selection:hr.payslip,state:0 -msgid "Reject" -msgstr "Rechazado" - -#. module: hr_payroll -#: selection:hr.allounce.deduction.categoty,type:0 -#: selection:hr.payslip.line,type:0 -msgid "Leaves" -msgstr "Ausencias" - -#. module: hr_payroll -#: field:hr.contibution.register.line,register_id:0 -#: view:hr.payslip:0 -#: field:hr.payslip,register_id:0 -#: report:payslip.pdf:0 -msgid "Register" -msgstr "Registro" - -#. module: hr_payroll -#: constraint:hr.employee:0 -msgid "" -"Error ! You cannot select a department for which the employee is the manager." msgstr "" -"¡Error! No puede seleccionar un departamento que tenga el empleado como " -"responsable." #. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Total Deductions" -msgstr "Total deducciones" +#: field:hr.payroll.structure,parent_id:0 +#: field:hr.salary.rule.category,parent_id:0 +msgid "Parent" +msgstr "" #. module: hr_payroll -#: field:company.contribution.line,value:0 -#: field:hr.payslip.line.line,value:0 -msgid "Value" -msgstr "Valor" +#: report:paylip.details:0 report:payslip:0 +msgid "(" +msgstr "" #. module: hr_payroll -#: report:payroll.advice:0 -msgid "Name of the Employee" -msgstr "Nombre del empleado" +#: field:hr.contribution.register,company_id:0 +#: field:hr.payroll.structure,company_id:0 field:hr.payslip,company_id:0 +#: field:hr.payslip.line,company_id:0 field:hr.salary.rule,company_id:0 +#: field:hr.salary.rule.category,company_id:0 +msgid "Company" +msgstr "" #. module: hr_payroll -#: view:hr.contibution.register:0 -msgid "Register Lines" -msgstr "Líneas de registro" +#: view:hr.payslip:0 +msgid "Done Slip" +msgstr "" + +#. module: hr_payroll +#: report:paylip.details:0 report:payslip:0 +msgid "," +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 view:hr.payslip.run:0 +msgid "Set to Draft" +msgstr "" + +#. module: hr_payroll +#: model:ir.model,name:hr_payroll.model_hr_salary_rule +msgid "hr.salary.rule" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip,payslip_run_id:0 +#: model:ir.model,name:hr_payroll.model_hr_payslip_run +msgid "Payslip Batches" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.employees:0 +msgid "" +"This wizard will generate payslips for all selected employee(s) based on the " +"dates and credit note specified on Payslips Run." +msgstr "" + +#. module: hr_payroll +#: report:contribution.register.lines:0 report:paylip.details:0 +#: report:payslip:0 +msgid "Quantity/Rate" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.input,payslip_id:0 field:hr.payslip.line,slip_id:0 +#: field:hr.payslip.worked_days,payslip_id:0 +#: model:ir.model,name:hr_payroll.model_hr_payslip report:payslip:0 +msgid "Pay Slip" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.employees:0 +msgid "Generate" +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip.line,amount_percentage_base:0 +#: help:hr.salary.rule,amount_percentage_base:0 +msgid "result will be affected to a variable" +msgstr "" + +#. module: hr_payroll +#: report:contribution.register.lines:0 +msgid "Total:" +msgstr "" + +#. module: hr_payroll +#: model:ir.actions.act_window,name:hr_payroll.act_children_salary_rules +msgid "All Children Rules" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 view:hr.salary.rule:0 +msgid "Input Data" +msgstr "" + +#. module: hr_payroll +#: constraint:hr.payslip:0 +msgid "Payslip 'Date From' must be before 'Date To'." +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 view:hr.salary.rule.category:0 +msgid "Notes" +msgstr "" #. module: hr_payroll #: view:hr.payslip:0 msgid "Salary Computation" -msgstr "Cálculo de la nómina" - -#. module: hr_payroll -#: field:hr.payroll.advice.line,amount:0 -#: report:payroll.advice:0 -#: report:salary.structure:0 -msgid "Amount" -msgstr "Importe" - -#. module: hr_payroll -#: code:addons/hr_payroll/hr_payroll.py:1225 -#, python-format -msgid "Please check configuration of %s, payroll head is missing" msgstr "" -"Por favor, compruebe la configuración de %s, falta la cabecera de la nómina" #. module: hr_payroll -#: selection:company.contribution,amount_type:0 -msgid "Percentage" -msgstr "Porcentaje" +#: report:contribution.register.lines:0 field:hr.payslip.input,amount:0 +#: field:hr.payslip.line,amount:0 report:paylip.details:0 report:payslip:0 +msgid "Amount" +msgstr "" #. module: hr_payroll -#: view:company.contribution:0 -#: view:hr.allounce.deduction.categoty:0 -msgid "Other Information" -msgstr "Otra información" - -#. module: hr_payroll -#: field:hr.passport,country_id:0 -msgid "Country of Issue" -msgstr "País de emisión" - -#. module: hr_payroll -#: field:hr.contibution.register.line,emp_deduction:0 -msgid "Employee Deduction" -msgstr "Deducción del empleado" - -#. module: hr_payroll -#: selection:hr.allounce.deduction.categoty,type:0 -#: selection:hr.payslip.line,type:0 -msgid "Other Deduction" -msgstr "Otras deducciones" - -#. module: hr_payroll -#: selection:hr.holidays.status,type:0 -msgid "Paid Holiday" -msgstr "Vacaciones pagada" - -#. module: hr_payroll -#: view:company.contribution:0 -#: view:hr.allounce.deduction.categoty:0 -#: view:hr.passport:0 -#: view:hr.payslip:0 -msgid "Group By..." -msgstr "Agrupar por..." - -#. module: hr_payroll -#: field:hr.passport,date_expire:0 -msgid "Passport Expire Date" -msgstr "Fecha expiración pasaporte" - -#. module: hr_payroll -#: selection:hr.holidays.status,type:0 -msgid "Un-Paid Holiday" -msgstr "Vacaciones no pagadas" - -#. module: hr_payroll -#: view:hr.passport:0 -msgid "Valid From" -msgstr "Válido desde" - -#. module: hr_payroll -#: help:hr.payslip,igross:0 -#: help:hr.payslip,inet:0 -msgid "" -"Calculation field used for internal calculation, do not place this on form" -msgstr "Campo usado en cálculos internos, no lo coloque en un formulario." - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Amount (in words) :" -msgstr "Importe (en palabras):" - -#. module: hr_payroll -#: field:hr.holidays.status,type:0 -msgid "Payment" -msgstr "Pago" - -#. module: hr_payroll -#: view:hr.payroll.structure:0 -#: view:hr.payslip:0 -#: field:hr.payslip,line_ids:0 -#: view:hr.payslip.line:0 +#: view:hr.payslip:0 view:hr.payslip.line:0 #: model:ir.model,name:hr_payroll.model_hr_payslip_line msgid "Payslip Line" -msgstr "Línea de nómina" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Identification No" -msgstr "Nº identificación" - -#. module: hr_payroll -#: view:hr.allounce.deduction.categoty:0 -#: field:hr.allounce.deduction.categoty,base:0 -msgid "Based on" -msgstr "Basado en" - -#. module: hr_payroll -#: selection:hr.payroll.register,state:0 -#: selection:hr.payslip,state:0 -msgid "Wating for Verification" -msgstr "Esperando verificación" - -#. module: hr_payroll -#: model:ir.module.module,shortdesc:hr_payroll.module_meta_information -msgid "Human Resource Payroll" -msgstr "Nóminas de recursos humanos" - -#. module: hr_payroll -#: report:payroll.advice:0 -msgid "Total:" -msgstr "Total:" - -#. module: hr_payroll -#: view:hr.payslip:0 -msgid "Posted" -msgstr "Fijado" - -#. module: hr_payroll -#: model:ir.module.module,description:hr_payroll.module_meta_information -msgid "" -"Generic Payroll system\n" -" * Employee Details\n" -" * Employee Contracts\n" -" * Passport based Contract\n" -" * Allowances / Deductions\n" -" * Allow to configure Basic / Grows / Net Salary\n" -" * Employee Payslip\n" -" * Monthly Payroll Register\n" -" * Integrated with Holiday Management\n" -" " msgstr "" -"Sistema de nóminas genérico\n" -" * Detalles de empleado\n" -" * Contratos de empleado\n" -" * Contratos basados en pasaportes\n" -" * Complementos/deducciones\n" -" * Permite configurar salario base/bruto/neto\n" -" * Nómina de empleado\n" -" * Registro mensual de nóminas\n" -" * Integrado con la gestión de vacaciones\n" -" " #. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_holidays_status -msgid "Leave Type" -msgstr "Tipo de ausencia" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "Date :" -msgstr "Fecha :" - -#. module: hr_payroll -#: field:hr.payslip.line,total:0 -msgid "Sub Total" -msgstr "Subtotal" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Payments -" -msgstr "Pagos -" - -#. module: hr_payroll -#: field:hr.contract,visa_no:0 -msgid "Visa No" -msgstr "Número de Visa" - -#. module: hr_payroll -#: field:company.contribution.line,from_val:0 -#: report:employees.salary:0 -#: field:hr.payslip.line.line,from_val:0 -#: report:year.salary:0 -msgid "From" -msgstr "Desde" - -#. module: hr_payroll -#: field:hr.payroll.advice.line,bysal:0 -#: report:payroll.advice:0 -msgid "By Salary" -msgstr "Por salario" - -#. module: hr_payroll -#: field:hr.payroll.employees.detail,date_to:0 -#: field:hr.payroll.year.salary,date_to:0 -#: report:salary.structure:0 -msgid "End Date" -msgstr "Fecha final" - -#. module: hr_payroll -#: model:ir.actions.report.xml,name:hr_payroll.salary_payslip -msgid "Employee PaySlip" -msgstr "Nómina del empleado" - -#. module: hr_payroll -#: field:hr.payslip,leaves:0 -msgid "Leave Deductions" -msgstr "Deducciones por ausencia" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -#: report:payroll.advice:0 -msgid "Authorised Signature" -msgstr "Firma autorizada" - -#. module: hr_payroll -#: selection:hr.payslip.line,amount_type:0 -msgid "Function Value" -msgstr "Valor de función" - -#. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_contibution_register_line -msgid "Contribution Register Line" -msgstr "Línea de registro de contribución" - -#. module: hr_payroll -#: report:salary.structure:0 -msgid "Notes:" -msgstr "Notas:" - -#. module: hr_payroll -#: field:hr.payroll.advice,state:0 -#: field:hr.payroll.register,state:0 -#: field:hr.payslip,state:0 -msgid "State" -msgstr "Estado" - -#. module: hr_payroll -#: view:hr.payroll.advice:0 -msgid "Paymeny Lines" -msgstr "Líneas de pago" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Other Lines" -msgstr "Otras líneas" - -#. module: hr_payroll -#: view:company.contribution:0 -#: view:hr.allounce.deduction.categoty:0 -#: view:hr.payroll.structure:0 #: view:hr.payslip:0 -#: view:hr.payslip.line:0 -msgid "Function Arguments" -msgstr "Argumentos de la función" +msgid "Other Information" +msgstr "" #. module: hr_payroll -#: model:ir.actions.act_window,name:hr_payroll.action_hr_company_contribution_tree -#: model:ir.ui.menu,name:hr_payroll.menu_hr_company_contribution_tree -msgid "Company Contributions" -msgstr "Contribuciones de la compañía" +#: help:hr.payslip.line,amount_select:0 help:hr.salary.rule,amount_select:0 +msgid "The computation method for the rule amount." +msgstr "" #. module: hr_payroll -#: field:hr.contibution.register.line,employee_id:0 -#: field:hr.passport,employee_id:0 -#: field:hr.payroll.advice.line,employee_id:0 -#: field:hr.payslip,employee_id:0 -#: field:hr.payslip.line,employee_id:0 +#: view:payslip.lines.contribution.register:0 +msgid "Contribution Register's Payslip Lines" +msgstr "" + +#. module: hr_payroll +#: code:addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py:52 +#, python-format +msgid "Warning !" +msgstr "" + +#. module: hr_payroll +#: report:paylip.details:0 +msgid "Details by Salary Rule Category:" +msgstr "" + +#. module: hr_payroll +#: report:paylip.details:0 report:payslip:0 +msgid "Note" +msgstr "" + +#. module: hr_payroll +#: field:hr.payroll.structure,code:0 field:hr.payslip,number:0 +#: report:paylip.details:0 report:payslip:0 +msgid "Reference" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 +msgid "Draft Slip" +msgstr "" + +#. module: hr_payroll +#: code:addons/hr_payroll/hr_payroll.py:422 +#, python-format +msgid "Normal Working Days paid at 100%" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,condition_range_max:0 +#: field:hr.salary.rule,condition_range_max:0 +msgid "Maximum Range" +msgstr "" + +#. module: hr_payroll +#: report:paylip.details:0 report:payslip:0 +msgid "Identification No" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip,struct_id:0 +msgid "Structure" +msgstr "" + +#. module: hr_payroll +#: help:hr.employee,total_wage:0 +msgid "Sum of all current contract's wage of employee." +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 +msgid "Total Working Days" +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip.line,code:0 help:hr.salary.rule,code:0 +msgid "" +"The code of salary rules can be used as reference in computation of other " +"rules. In that case, it is case sensitive." +msgstr "" + +#. module: hr_payroll +#: selection:hr.contract,schedule_pay:0 +msgid "Weekly" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,rate:0 +msgid "Rate (%)" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 +msgid "Confirm" +msgstr "" + +#. module: hr_payroll +#: model:ir.actions.report.xml,name:hr_payroll.payslip_report +msgid "Employee PaySlip" +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip.line,condition_range_max:0 +#: help:hr.salary.rule,condition_range_max:0 +msgid "The maximum amount, applied for this rule." +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip.line,condition_python:0 +#: help:hr.salary.rule,condition_python:0 +msgid "" +"Applied this rule for calculation if condition is true. You can specify " +"condition like basic > 1000." +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.employees:0 +msgid "Payslips by Employees" +msgstr "" + +#. module: hr_payroll +#: selection:hr.contract,schedule_pay:0 +msgid "Quarterly" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip,state:0 field:hr.payslip.run,state:0 +msgid "State" +msgstr "" + +#. module: hr_payroll +#: help:hr.salary.rule,quantity:0 +msgid "" +"It is used in computation for percentage and fixed amount.For e.g. A rule " +"for Meal Voucher having fixed amount of 1€ per worked day can have its " +"quantity defined in expression like worked_days.WORK100.number_of_days." +msgstr "" + +#. module: hr_payroll +#: view:hr.salary.rule:0 +msgid "Search Salary Rule" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip,employee_id:0 field:hr.payslip.line,employee_id:0 #: model:ir.model,name:hr_payroll.model_hr_employee msgid "Employee" -msgstr "Empleado" +msgstr "" #. module: hr_payroll -#: field:hr.payslip.line,base:0 -msgid "Formula" -msgstr "Fórmula" +#: selection:hr.contract,schedule_pay:0 +msgid "Semi-annually" +msgstr "" #. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_payroll_advice_line -msgid "Bank Advice Lines" -msgstr "Líneas de notificación bancaria" +#: view:hr.salary.rule:0 +msgid "Children Definition" +msgstr "" #. module: hr_payroll -#: view:company.contribution:0 -#: view:hr.allounce.deduction.categoty:0 -#: field:hr.allounce.deduction.categoty,type:0 -#: field:hr.payslip.line,type:0 -#: report:salary.structure:0 -msgid "Type" -msgstr "Tipo" - -#. module: hr_payroll -#: report:payslip.pdf:0 +#: report:paylip.details:0 report:payslip:0 msgid "Email" -msgstr "Correo electrónico" +msgstr "" #. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "#" -msgstr "#" +#: view:hr.payslip.run:0 +msgid "Search Payslip Batches" +msgstr "" #. module: hr_payroll -#: code:addons/hr_payroll/hr_payroll.py:469 -#: code:addons/hr_payroll/hr_payroll.py:1225 -#, python-format -msgid "Error !" -msgstr "¡ Error !" +#: field:hr.payslip.line,amount_percentage_base:0 +#: field:hr.salary.rule,amount_percentage_base:0 +msgid "Percentage based on" +msgstr "" #. module: hr_payroll -#: view:hr.payroll.register:0 -#: view:hr.payslip:0 -msgid "Verify Sheet" -msgstr "Comprobar hoja" +#: help:hr.payslip.line,amount_percentage:0 +#: help:hr.salary.rule,amount_percentage:0 +msgid "For example, enter 50.0 to apply a percentage of 50%" +msgstr "" #. module: hr_payroll -#: help:hr.contract,working_days_per_week:0 -msgid "No of Working days / week for an employee" -msgstr "Número de días/semanas trabajados para un empleado." +#: field:hr.payslip,paid:0 +msgid "Made Payment Order ? " +msgstr "" + +#. module: hr_payroll +#: report:contribution.register.lines:0 +msgid "PaySlip Lines by Contribution Register" +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip,state:0 +msgid "" +"* When the payslip is created the state is 'Draft'. \n" +"* If the payslip is under verification, the state is 'Waiting'. " +"\n" +"* If the payslip is confirmed then state is set to 'Done'. \n" +"* When user cancel payslip the state is 'Rejected'." +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.worked_days,number_of_days:0 +msgid "Number of Days" +msgstr "" #. module: hr_payroll -#: selection:hr.payroll.register,state:0 #: selection:hr.payslip,state:0 -msgid "New Slip" -msgstr "Nuevo justificante" +msgid "Rejected" +msgstr "" #. module: hr_payroll -#: field:hr.payslip,basic:0 -msgid "Net Basic" -msgstr "Base neta" +#: view:hr.payroll.structure:0 field:hr.payroll.structure,rule_ids:0 +#: view:hr.salary.rule:0 +#: model:ir.actions.act_window,name:hr_payroll.action_salary_rule_form +#: model:ir.ui.menu,name:hr_payroll.menu_action_hr_salary_rule_form +msgid "Salary Rules" +msgstr "" #. module: hr_payroll -#: field:hr.contract,gross:0 -#: field:hr.employee,gross:0 -#: field:hr.payroll.register,grows:0 -#: field:hr.payslip,grows:0 -#: report:salary.structure:0 -msgid "Gross Salary" -msgstr "Salario bruto" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Total Earnings" -msgstr "Ingresos totales" - -#. module: hr_payroll -#: model:ir.actions.act_window,name:hr_payroll.action_hr_payroll_employees_detail -#: model:ir.ui.menu,name:hr_payroll.menu_hr_payroll_employees_detail -msgid "Employee Salary Statement" -msgstr "Declaración salario del empleado" - -#. module: hr_payroll -#: selection:hr.allounce.deduction.categoty,type:0 -#: selection:hr.payslip.line,type:0 -msgid "Other Payment" -msgstr "Otros pagos" - -#. module: hr_payroll -#: field:hr.employee,advantages_net:0 -#: report:payslip.pdf:0 -#: report:salary.structure:0 -msgid "Deductions" -msgstr "Deducciones" - -#. module: hr_payroll -#: report:payroll.advice:0 -msgid "C/D" -msgstr "C/D" - -#. module: hr_payroll -#: field:hr.contract,permit_no:0 -msgid "Work Permit No" -msgstr "Nº permiso trabajo" - -#. module: hr_payroll -#: field:hr.payroll.advice,line_ids:0 -msgid "Employee Salary" -msgstr "Salario empleado" - -#. module: hr_payroll -#: field:hr.payroll.advice,chaque_nos:0 -msgid "Chaque Nos" -msgstr "Cada uno de nuestros" - -#. module: hr_payroll -#: field:hr.contibution.register,monthly_total_by_emp:0 -msgid "Total By Employee" -msgstr "Total por empleado" - -#. module: hr_payroll -#: view:company.contribution:0 -#: selection:company.contribution,amount_type:0 -#: selection:company.contribution.line,amount_type:0 -#: selection:hr.payslip.line,amount_type:0 -#: selection:hr.payslip.line.line,amount_type:0 -msgid "Fixed Amount" -msgstr "Importe fijo" - -#. module: hr_payroll -#: field:company.contribution.line,to_val:0 -#: report:employees.salary:0 -#: field:hr.payslip.line.line,to_val:0 -#: report:year.salary:0 -msgid "To" -msgstr "Para" - -#. module: hr_payroll -#: code:addons/hr_payroll/hr_payroll.py:180 -#: code:addons/hr_payroll/hr_payroll.py:195 -#: code:addons/hr_payroll/hr_payroll.py:285 -#: code:addons/hr_payroll/hr_payroll.py:835 -#: code:addons/hr_payroll/hr_payroll.py:1111 -#: code:addons/hr_payroll/hr_payroll.py:1126 -#: code:addons/hr_payroll/hr_payroll.py:1410 +#: code:addons/hr_payroll/hr_payroll.py:337 #, python-format -msgid "Variable Error !" -msgstr "¡Error de variable!" +msgid "Refund: " +msgstr "" #. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_payroll_employees_detail -msgid "hr.payroll.employees.detail" -msgstr "rrhh.nomina.empleados.detalle" +#: model:ir.model,name:hr_payroll.model_payslip_lines_contribution_register +msgid "PaySlip Lines by Contribution Registers" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 selection:hr.payslip,state:0 view:hr.payslip.run:0 +msgid "Done" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,appears_on_payslip:0 +#: field:hr.salary.rule,appears_on_payslip:0 +msgid "Appears on Payslip" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,amount_fix:0 +#: selection:hr.payslip.line,amount_select:0 field:hr.salary.rule,amount_fix:0 +#: selection:hr.salary.rule,amount_select:0 +msgid "Fixed Amount" +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip.line,active:0 help:hr.salary.rule,active:0 +msgid "" +"If the active field is set to false, it will allow you to hide the salary " +"rule without removing it." +msgstr "" #. module: hr_payroll -#: view:hr.payroll.register:0 #: view:hr.payslip:0 -msgid "Pay Salary" -msgstr "Pagar nómina" +msgid "Worked Days & Inputs" +msgstr "" #. module: hr_payroll -#: field:hr.payroll.advice.line,name:0 -msgid "Bank Account A/C" -msgstr "Cuenta bancaria" +#: field:hr.payslip,details_by_salary_rule_category:0 +msgid "Details by Salary Rule Category" +msgstr "" #. module: hr_payroll -#: view:hr.contibution.register:0 -msgid "Contribution Lines" -msgstr "Líneas de contribución" +#: model:ir.actions.act_window,name:hr_payroll.action_payslip_lines_contribution_register +msgid "PaySlip Lines" +msgstr "" #. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "For the month of" -msgstr "Para el mes de" +#: help:hr.payslip.line,register_id:0 help:hr.salary.rule,register_id:0 +msgid "Eventual third party involved in the salary payment of the employees." +msgstr "" #. module: hr_payroll -#: view:hr.allounce.deduction.categoty:0 -#: selection:hr.allounce.deduction.categoty,type:0 -#: field:hr.payroll.register,deduction:0 -#: report:hr.payroll.register.sheet:0 -#: field:hr.payslip,deduction:0 -#: selection:hr.payslip.line,type:0 -msgid "Deduction" -msgstr "Deducción" +#: field:hr.payslip.worked_days,number_of_hours:0 +msgid "Number of Hours" +msgstr "" #. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_payroll_advice -msgid "Bank Advice Note" -msgstr "Observación aviso bancario" +#: view:hr.payslip:0 +msgid "PaySlip Batch" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,condition_range_min:0 +#: field:hr.salary.rule,condition_range_min:0 +msgid "Minimum Range" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,child_ids:0 field:hr.salary.rule,child_ids:0 +msgid "Child Salary Rule" +msgstr "" + +#. module: hr_payroll +#: report:contribution.register.lines:0 field:hr.payslip,date_to:0 +#: field:hr.payslip.run,date_end:0 report:paylip.details:0 report:payslip:0 +#: field:payslip.lines.contribution.register,date_to:0 +msgid "Date To" +msgstr "" + +#. module: hr_payroll +#: selection:hr.payslip.line,condition_select:0 +#: selection:hr.salary.rule,condition_select:0 +msgid "Range" +msgstr "" + +#. module: hr_payroll +#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payroll_structure_tree +#: model:ir.ui.menu,name:hr_payroll.menu_hr_payroll_structure_tree +msgid "Salary Structures Hierarchy" +msgstr "" #. module: hr_payroll -#: view:hr.payroll.register:0 #: view:hr.payslip:0 msgid "Payslip" -msgstr "Nómina" +msgstr "" #. module: hr_payroll #: constraint:hr.contract:0 msgid "Error! contract start-date must be lower then contract end-date." msgstr "" -"¡Error! La fecha de inicio de contrato debe ser menor que la fecha de " -"finalización." #. module: hr_payroll -#: selection:hr.allounce.deduction.categoty,type:0 -#: selection:hr.payslip.line,type:0 -msgid "Loan Installment" -msgstr "Entrega anticipo" +#: view:hr.contract:0 +msgid "Payslip Info" +msgstr "" #. module: hr_payroll -#: view:hr.payroll.register:0 -msgid "Complete HR Checking" -msgstr "Comprobación RH completa" +#: model:ir.actions.act_window,name:hr_payroll.act_payslip_lines +msgid "Payslip Computation Details" +msgstr "" #. module: hr_payroll -#: report:payroll.advice:0 -msgid "Yours Sincerely" -msgstr "Atentamente" +#: code:addons/hr_payroll/hr_payroll.py:872 +#, python-format +msgid "Wrong python code defined for salary rule %s (%s) " +msgstr "" #. module: hr_payroll -#: report:payroll.advice:0 -msgid "SI. No." -msgstr "Nº SS" +#: model:ir.model,name:hr_payroll.model_hr_payslip_input +msgid "Payslip Input" +msgstr "" #. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Net Amount" -msgstr "Importe neto" +#: view:hr.salary.rule.category:0 +#: model:ir.actions.act_window,name:hr_payroll.action_hr_salary_rule_category +#: model:ir.ui.menu,name:hr_payroll.menu_hr_salary_rule_category +msgid "Salary Rule Categories" +msgstr "" #. module: hr_payroll -#: report:salary.structure:0 -msgid "Salary Structure:" -msgstr "Estructura salarial:" +#: help:hr.payslip.input,contract_id:0 +#: help:hr.payslip.worked_days,contract_id:0 +msgid "The contract for which applied this input" +msgstr "" #. module: hr_payroll -#: model:ir.actions.report.xml,name:hr_payroll.year_employees_detail -msgid "Employees Salary Detail" -msgstr "Detalle salario empleado" +#: view:hr.salary.rule:0 +msgid "Computation" +msgstr "" #. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_payslip_line_line -msgid "Function Line" -msgstr "Línea de función" +#: help:hr.payslip.input,amount:0 +msgid "" +"It is used in computation. For e.g. A rule for sales having 1% commission of " +"basic salary for per product can defined in expression like result = " +"inputs.SALEURO.amount * contract.wage*0.01." +msgstr "" #. module: hr_payroll -#: view:hr.payroll.advice:0 -#: selection:hr.payroll.advice,state:0 -#: selection:hr.payroll.register,state:0 -#: selection:hr.payslip,state:0 -msgid "Confirm Sheet" -msgstr "Hoja de confirmación" - -#. module: hr_payroll -#: report:employees.salary:0 -msgid "Others:" -msgstr "Otros:" - -#. module: hr_payroll -#: view:company.contribution:0 -#: selection:company.contribution,amount_type:0 -msgid "Function Calculation" -msgstr "Función de cálculo" - -#. module: hr_payroll -#: field:hr.payslip,worked_days:0 -#: report:payslip.pdf:0 -msgid "Worked Day" -msgstr "Días trabajados" - -#. module: hr_payroll -#: field:hr.contibution.register,monthly_total_by_comp:0 -msgid "Total By Company" -msgstr "Total por compañía" - -#. module: hr_payroll -#: field:hr.payroll.advice.line,flag:0 -msgid "D/C" -msgstr "D/C" - -#. module: hr_payroll -#: view:hr.passport:0 -msgid "Country & Address" -msgstr "País y Dirección" - -#. module: hr_payroll -#: report:employees.salary:0 -msgid "Employee Code" -msgstr "Código de empleado" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Basic Salary – Leaves" -msgstr "Salario base - Ausencias" - -#. module: hr_payroll -#: field:company.contribution,amount_type:0 -#: field:company.contribution.line,amount_type:0 -#: field:hr.payslip.line,amount_type:0 -#: field:hr.payslip.line.line,amount_type:0 -#: report:salary.structure:0 +#: view:hr.payslip.line:0 field:hr.payslip.line,amount_select:0 +#: field:hr.salary.rule,amount_select:0 msgid "Amount Type" -msgstr "Tipo de importe" +msgstr "" #. module: hr_payroll -#: view:company.contribution:0 -#: field:hr.payslip.line,category_id:0 +#: field:hr.payslip.line,category_id:0 view:hr.salary.rule:0 +#: field:hr.salary.rule,category_id:0 msgid "Category" -msgstr "Categoría" +msgstr "" #. module: hr_payroll -#: view:company.contribution:0 -#: view:hr.allounce.deduction.categoty:0 -#: field:hr.payslip.line,company_contrib:0 -#: model:ir.model,name:hr_payroll.model_company_contribution -msgid "Company Contribution" -msgstr "Contribución compañía" +#: help:hr.payslip.run,credit_note:0 +msgid "" +"If its checked, indicates that all payslips generated from here are refund " +"payslips." +msgstr "" #. module: hr_payroll -#: field:company.contribution,category_id:0 -msgid "Heads" -msgstr "Cabeceras" +#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payroll_structure_list_form +#: model:ir.ui.menu,name:hr_payroll.menu_hr_payroll_structure_view +msgid "Salary Structures" +msgstr "" #. module: hr_payroll -#: model:ir.actions.report.xml,name:hr_payroll.year_payroll_register -msgid "Print Statement" -msgstr "Imprimir declaración" +#: view:hr.payslip.run:0 +msgid "Draft Payslip Batches" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip:0 selection:hr.payslip,state:0 view:hr.payslip.run:0 +#: selection:hr.payslip.run,state:0 +msgid "Draft" +msgstr "" + +#. module: hr_payroll +#: report:contribution.register.lines:0 field:hr.payslip,date_from:0 +#: field:hr.payslip.run,date_start:0 report:paylip.details:0 report:payslip:0 +#: field:payslip.lines.contribution.register,date_from:0 +msgid "Date From" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.run:0 +msgid "Done Payslip Batches" +msgstr "" + +#. module: hr_payroll +#: report:paylip.details:0 +msgid "Payslip Lines by Contribution Register:" +msgstr "" + +#. module: hr_payroll +#: view:hr.salary.rule:0 +msgid "Conditions" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,amount_percentage:0 +#: selection:hr.payslip.line,amount_select:0 +#: field:hr.salary.rule,amount_percentage:0 +#: selection:hr.salary.rule,amount_select:0 +msgid "Percentage (%)" +msgstr "" #. module: hr_payroll #: view:hr.payslip:0 -msgid "Draft" -msgstr "Borrador" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Earnings" -msgstr "Ingresos" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -#: report:salary.structure:0 -msgid "Basic" -msgstr "Básico" - -#. module: hr_payroll -#: model:ir.actions.act_window,name:hr_payroll.action_hr_passport_tree -msgid "All Passports" -msgstr "Todos los pasaportes" - -#. module: hr_payroll -#: model:ir.actions.act_window,name:hr_payroll.action_hr_payroll_year_salary -#: model:ir.ui.menu,name:hr_payroll.menu_wizard_print_year_salary -msgid "Salary Register" -msgstr "Registro salarial" - -#. module: hr_payroll -#: report:employees.salary:0 -#: report:hr.payroll.register.sheet:0 -msgid "Employee Name" -msgstr "Nombre del empleado" - -#. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_passport -msgid "Passport Detail" -msgstr "Detalle pasaporte" - -#. module: hr_payroll -#: selection:hr.payslip.line,amount_type:0 -msgid "Percentage (%)" -msgstr "Porcentaje (%)" - -#. module: hr_payroll -#: field:hr.payroll.advice,register_id:0 -#: view:hr.payroll.register:0 -#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payroll_register_form -#: model:ir.model,name:hr_payroll.model_hr_payroll_register -#: model:ir.ui.menu,name:hr_payroll.hr_menu_payroll_register -msgid "Payroll Register" -msgstr "Registro de nóminas" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -#: report:payroll.advice:0 -msgid "For" -msgstr "Para" - -#. module: hr_payroll -#: field:hr.passport,contracts_ids:0 -msgid "Contracts" -msgstr "Contratos" +msgid "Worked Day" +msgstr "" #. module: hr_payroll #: view:hr.payroll.structure:0 msgid "Employee Function" -msgstr "Función del empleado" +msgstr "" #. module: hr_payroll -#: view:hr.payslip:0 -msgid "Paid" -msgstr "Pagado" - -#. module: hr_payroll -#: view:hr.payslip:0 -msgid "Approve Sheet" -msgstr "Aprobar hoja" - -#. module: hr_payroll -#: field:hr.payslip,paid:0 -msgid "Paid ? " -msgstr "¿Pagado? " - -#. module: hr_payroll -#: view:hr.holidays.status:0 -msgid "Validation" -msgstr "Validación" - -#. module: hr_payroll -#: report:employees.salary:0 -msgid "Title" -msgstr "Título" - -#. module: hr_payroll -#: view:company.contribution:0 -msgid "Search Company Contribution" -msgstr "Buscar contribución de la compañía" - -#. module: hr_payroll -#: field:hr.allounce.deduction.categoty,user_id:0 -msgid "User" -msgstr "Usuario" - -#. module: hr_payroll -#: view:hr.payroll.advice:0 -msgid "Payment Lines" -msgstr "Líneas de pago" +#: field:hr.payslip,credit_note:0 field:hr.payslip.run,credit_note:0 +msgid "Credit Note" +msgstr "" #. module: hr_payroll #: view:hr.payslip:0 msgid "Compute Sheet" -msgstr "Calcular hoja" +msgstr "" #. module: hr_payroll -#: field:company.contribution,active:0 -#: field:hr.payroll.register,active:0 +#: field:hr.payslip.line,active:0 field:hr.salary.rule,active:0 msgid "Active" -msgstr "Activo" +msgstr "" #. module: hr_payroll -#: help:hr.allounce.deduction.categoty,condition:0 -msgid "Applied this head for calculation if condition is true" -msgstr "Aplicar este encabezado en el cálculo si la condición es verdadera" - -#. module: hr_payroll -#: report:year.salary:0 -msgid "Yearly Salary Details" -msgstr "Detalles del salario anual" +#: view:hr.salary.rule:0 +msgid "Child Rules" +msgstr "" #. module: hr_payroll #: constraint:hr.employee:0 msgid "Error ! You cannot create recursive Hierarchy of Employees." -msgstr "¡Error! No se puede crear una jerarquía recursiva de empleados." +msgstr "" #. module: hr_payroll -#: field:hr.allounce.deduction.categoty,condition:0 -msgid "Condition" -msgstr "Condición" +#: model:ir.actions.report.xml,name:hr_payroll.payslip_details_report +msgid "PaySlip Details" +msgstr "" #. module: hr_payroll -#: selection:hr.payroll.register,state:0 -#: selection:hr.payslip,state:0 -msgid "Wating for HR Verification" -msgstr "Esperando verificación de RRHH" +#: help:hr.payslip.line,condition_range_min:0 +#: help:hr.salary.rule,condition_range_min:0 +msgid "The minimum amount, applied for this rule." +msgstr "" #. module: hr_payroll -#: report:payroll.advice:0 -msgid "Payment Advice:" -msgstr "Aviso de pago:" +#: selection:hr.payslip.line,condition_select:0 +#: selection:hr.salary.rule,condition_select:0 +msgid "Python Expression" +msgstr "" #. module: hr_payroll -#: view:hr.payroll.register:0 -msgid "Compute" -msgstr "Calcular" - -#. module: hr_payroll -#: report:employees.salary:0 -#: field:hr.payslip,deg_id:0 -#: report:payslip.pdf:0 -#: report:salary.structure:0 +#: report:paylip.details:0 report:payslip:0 msgid "Designation" -msgstr "Designación" +msgstr "" #. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "HR Manager" -msgstr "Responsable de RRHH" +#: code:addons/hr_payroll/wizard/hr_payroll_payslips_by_employees.py:52 +#, python-format +msgid "You must select employee(s) to generate payslip(s)" +msgstr "" #. module: hr_payroll -#: field:hr.contract,basic:0 -#: field:hr.employee,basic:0 -#: field:hr.payslip,basic_before_leaves:0 -#: report:payslip.pdf:0 -msgid "Basic Salary" -msgstr "Salario base" - -#. module: hr_payroll -#: field:hr.allounce.deduction.categoty,code:0 -msgid "Category Code" -msgstr "Código categoría" - -#. module: hr_payroll -#: view:hr.payroll.register:0 -msgid "Salary Information" -msgstr "Información del salario" - -#. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_allounce_deduction_categoty -#: model:ir.model,name:hr_payroll.model_hr_allounce_deduction_categoty_line -msgid "Allowance Deduction Categoty" -msgstr "Categoría prima/deducción" +#: code:addons/hr_payroll/hr_payroll.py:861 +#, python-format +msgid "Wrong quantity defined for salary rule %s (%s)" +msgstr "" #. module: hr_payroll #: view:hr.payslip:0 msgid "Companies" -msgstr "Compañías" +msgstr "" #. module: hr_payroll -#: report:payslip.pdf:0 +#: report:paylip.details:0 report:payslip:0 msgid "Authorized Signature" -msgstr "Firma autorizada" +msgstr "" #. module: hr_payroll -#: field:hr.payslip,contract_id:0 +#: field:hr.payslip,contract_id:0 field:hr.payslip.input,contract_id:0 +#: field:hr.payslip.line,contract_id:0 +#: field:hr.payslip.worked_days,contract_id:0 #: model:ir.model,name:hr_payroll.model_hr_contract msgid "Contract" -msgstr "Contrato" +msgstr "" #. module: hr_payroll -#: selection:hr.payroll.advice,state:0 -msgid "Draft Sheet" -msgstr "Hoja borrador" +#: report:paylip.details:0 report:payslip:0 +msgid "Credit" +msgstr "" #. module: hr_payroll -#: selection:hr.payroll.year.salary,salary_on:0 -msgid "Next Month Date" -msgstr "Fecha próximo mes" +#: field:hr.contract,schedule_pay:0 +msgid "Scheduled Pay" +msgstr "" #. module: hr_payroll -#: field:hr.contibution.register.line,date:0 -#: field:hr.payroll.advice,date:0 -#: field:hr.payroll.register,date:0 -#: field:hr.payslip,date:0 -msgid "Date" -msgstr "Fecha" +#: code:addons/hr_payroll/hr_payroll.py:861 +#: code:addons/hr_payroll/hr_payroll.py:866 +#: code:addons/hr_payroll/hr_payroll.py:872 +#: code:addons/hr_payroll/hr_payroll.py:889 +#: code:addons/hr_payroll/hr_payroll.py:895 +#, python-format +msgid "Error" +msgstr "" #. module: hr_payroll -#: field:hr.contract,visa_expire:0 -msgid "Visa Expire Date" -msgstr "Fecha expiración visado" +#: field:hr.payslip.line,condition_python:0 +#: field:hr.salary.rule,condition_python:0 +msgid "Python Condition" +msgstr "" #. module: hr_payroll -#: view:hr.passport:0 -msgid "Search Passport" -msgstr "Buscar pasaporte" - -#. module: hr_payroll -#: report:employees.salary:0 -#: report:salary.structure:0 -msgid "Phone No." -msgstr "Nº Teléfono" - -#. module: hr_payroll -#: field:company.contribution,contribute_per:0 -#: field:company.contribution.line,contribution_id:0 -#: view:hr.allounce.deduction.categoty:0 -#: view:hr.contibution.register:0 -#: view:hr.contibution.register.line:0 +#: view:hr.contribution.register:0 msgid "Contribution" -msgstr "Contribución" +msgstr "" #. module: hr_payroll -#: field:hr.allounce.deduction.categoty,state:0 -msgid "Label" -msgstr "Etiqueta" +#: code:addons/hr_payroll/hr_payroll.py:347 +#, python-format +msgid "Refund Payslip" +msgstr "" + +#. module: hr_payroll +#: field:hr.rule.input,input_id:0 +#: model:ir.model,name:hr_payroll.model_hr_rule_input +msgid "Salary Rule Input" +msgstr "" + +#. module: hr_payroll +#: code:addons/hr_payroll/hr_payroll.py:895 +#, python-format +msgid "Wrong python condition defined for salary rule %s (%s)" +msgstr "" + +#. module: hr_payroll +#: field:hr.payslip.line,quantity:0 field:hr.salary.rule,quantity:0 +msgid "Quantity" +msgstr "" #. module: hr_payroll -#: view:hr.payroll.structure:0 #: view:hr.payslip:0 -#: view:hr.payslip.line:0 +msgid "Refund" +msgstr "" + +#. module: hr_payroll +#: view:hr.salary.rule:0 msgid "Company Contribution" -msgstr "Contribución compañía" +msgstr "" #. module: hr_payroll -#: report:employees.salary:0 -#: report:salary.structure:0 -msgid "Other No." -msgstr "Otro Nº" - -#. module: hr_payroll -#: field:company.contribution,code:0 -#: field:hr.contibution.register.line,code:0 -#: field:hr.holidays.status,code:0 -#: field:hr.payroll.structure,code:0 -#: field:hr.payslip.line,code:0 -#: report:payslip.pdf:0 -#: report:salary.structure:0 +#: report:contribution.register.lines:0 field:hr.payslip.input,code:0 +#: field:hr.payslip.line,code:0 field:hr.payslip.worked_days,code:0 +#: field:hr.rule.input,code:0 field:hr.salary.rule,code:0 +#: field:hr.salary.rule.category,code:0 report:paylip.details:0 +#: report:payslip:0 msgid "Code" -msgstr "Código" - -#. module: hr_payroll -#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_bank_advice_tree -#: model:ir.ui.menu,name:hr_payroll.hr_menu_payment_advice -msgid "Payment Advice" -msgstr "Aviso de pago" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "Number :" -msgstr "Número :" - -#. module: hr_payroll -#: help:hr.allounce.deduction.categoty,base:0 -msgid "" -"This will use to computer the % fields values, in general its on basic, but " -"You can use all heads code field in small letter as a variable name i.e. " -"hra, ma, lta, etc...., also you can use, static varible basic" msgstr "" -"Esto se utiliza para calcular los valores de los campos %, en general en su " -"base, pero puede utilizar todas las cabeceras de los campos de código en " -"letra pequeña como un nombre de variable, por ejemplo hra, ma, lta, etc., " -"también puede utilizar variable estática básica." #. module: hr_payroll -#: view:hr.payslip:0 -msgid "Computation Overview" -msgstr "Resumen del cálculo" +#: field:hr.payslip.line,amount_python_compute:0 +#: selection:hr.payslip.line,amount_select:0 +#: field:hr.salary.rule,amount_python_compute:0 +#: selection:hr.salary.rule,amount_select:0 +msgid "Python Code" +msgstr "" #. module: hr_payroll -#: field:hr.payroll.year.salary,salary_on:0 -msgid "Salary On" -msgstr "Salario sobre" +#: field:hr.payslip.input,sequence:0 field:hr.payslip.line,sequence:0 +#: field:hr.payslip.worked_days,sequence:0 field:hr.salary.rule,sequence:0 +msgid "Sequence" +msgstr "" #. module: hr_payroll -#: field:hr.payroll.advice,number:0 -#: field:hr.payroll.register,number:0 -#: field:hr.payslip,number:0 -msgid "Number" -msgstr "Número" +#: report:contribution.register.lines:0 report:paylip.details:0 +msgid "Register Name" +msgstr "" #. module: hr_payroll -#: field:hr.contract,struct_id:0 -#: field:hr.employee,line_ids:0 -#: view:hr.payroll.structure:0 -#: field:hr.payroll.structure,line_ids:0 -#: view:hr.payslip:0 +#: view:hr.salary.rule:0 +msgid "General" +msgstr "" + +#. module: hr_payroll +#: code:addons/hr_payroll/hr_payroll.py:664 +#, python-format +msgid "Salary Slip of %s for %s" +msgstr "" + +#. module: hr_payroll +#: model:ir.model,name:hr_payroll.model_hr_payslip_employees +msgid "Generate payslips for all selected employees" +msgstr "" + +#. module: hr_payroll +#: field:hr.contract,struct_id:0 view:hr.payroll.structure:0 view:hr.payslip:0 #: view:hr.payslip.line:0 -#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_employee_grade_form -#: model:ir.actions.report.xml,name:hr_payroll.salary_structure_register #: model:ir.model,name:hr_payroll.model_hr_payroll_structure -#: model:ir.ui.menu,name:hr_payroll.menu_hr_employee_function msgid "Salary Structure" -msgstr "Estructura salarial" - -#. module: hr_payroll -#: field:hr.contibution.register,register_line_ids:0 -msgid "Register Line" -msgstr "Línea registro" - -#. module: hr_payroll -#: view:hr.payroll.register:0 -#: view:hr.payslip:0 -msgid "Cancel" -msgstr "Cancelar" - -#. module: hr_payroll -#: view:hr.payroll.employees.detail:0 -#: view:hr.payroll.year.salary:0 -msgid "Close" -msgstr "Cerrar" - -#. module: hr_payroll -#: field:hr.payslip.line,amount:0 -msgid "Amount / Percentage" -msgstr "Importe / Porcentaje" - -#. module: hr_payroll -#: field:hr.employee,advantages_gross:0 -#: report:hr.payroll.register.sheet:0 -#: report:salary.structure:0 -msgid "Allowances" -msgstr "Primas" - -#. module: hr_payroll -#: selection:hr.payroll.year.salary,salary_on:0 -msgid "Current Month Date" -msgstr "Fecha mes actual" - -#. module: hr_payroll -#: report:salary.structure:0 -msgid "Salary" -msgstr "Salario" - -#. module: hr_payroll -#: field:hr.contract,passport_id:0 -#: field:hr.passport,name:0 -msgid "Passport No" -msgstr "Número de pasaporte" - -#. module: hr_payroll -#: view:hr.passport:0 -msgid "Passport" -msgstr "Pasaporte" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "Total Salary" -msgstr "Salario total" - -#. module: hr_payroll -#: report:payroll.advice:0 -msgid "for period" -msgstr "por periodo" - -#. module: hr_payroll -#: field:hr.holidays.status,head_id:0 -msgid "Payroll Head" -msgstr "Cabecera nómina" - -#. module: hr_payroll -#: field:company.contribution,register_id:0 -#: model:ir.actions.act_window,name:hr_payroll.action_contibution_register_form -#: model:ir.model,name:hr_payroll.model_hr_contibution_register -#: model:ir.ui.menu,name:hr_payroll.menu_action_hr_contibution_register_form -msgid "Contribution Register" -msgstr "Registro de contribución" - -#. module: hr_payroll -#: report:salary.structure:0 -msgid "E-mail" -msgstr "Correo electrónico" - -#. module: hr_payroll -#: view:hr.allounce.deduction.categoty:0 -#: model:ir.actions.act_window,name:hr_payroll.hr_allounce_deduction_tree -#: model:ir.ui.menu,name:hr_payroll.menu_hr_allounce_deduction_tree -msgid "Salary Heads" -msgstr "Cabeceras salario" - -#. module: hr_payroll -#: view:hr.payroll.employees.detail:0 -#: view:hr.payroll.year.salary:0 -msgid "Print Report" -msgstr "Imprimir informe" - -#. module: hr_payroll -#: field:company.contribution,line_ids:0 -#: view:hr.payroll.structure:0 -#: view:hr.payslip:0 -#: view:hr.payslip.line:0 -#: field:hr.payslip.line,line_ids:0 -msgid "Calculations" -msgstr "Cálculos" - -#. module: hr_payroll -#: help:company.contribution,contribute_per:0 -msgid "" -"Define Company contribution ratio 1.00=100% contribution, If Employee " -"Contribute 5% then company will and here 0.50 defined then company will " -"contribute 50% on employee 5% contribution" msgstr "" -"Define el ratio de contribución de la compañía 1,00=100% de la contribución. " -"Si el empleado contribuye con el 5%, entonces si aquí la compañía tiene un " -"ratio de 0.50, la compañía contribuirá con el 50% del 5% de la contribución " -"del empleado." #. module: hr_payroll -#: view:hr.payslip:0 -msgid "Other Informations" -msgstr "Otra información" +#: field:hr.contribution.register,register_line_ids:0 +msgid "Register Line" +msgstr "" #. module: hr_payroll -#: view:hr.contibution.register:0 -msgid "Month" -msgstr "Mes" +#: view:hr.payslip:0 view:hr.payslip.employees:0 +#: view:payslip.lines.contribution.register:0 +msgid "Cancel" +msgstr "" #. module: hr_payroll -#: view:hr.passport:0 -msgid "Issue" -msgstr "Incidencia" +#: view:hr.payslip.run:0 selection:hr.payslip.run,state:0 +msgid "Close" +msgstr "" #. module: hr_payroll -#: view:hr.allounce.deduction.categoty:0 -msgid "Dynamic Computation" -msgstr "Cálculo Dinámico" +#: help:hr.payslip,struct_id:0 +msgid "" +"Defines the rules that have to be applied to this payslip, accordingly to " +"the contract chosen. If you let empty the field contract, this field isn't " +"mandatory anymore and thus the rules applied will be all the rules set on " +"the structure of all contracts of the employee valid for the chosen period" +msgstr "" #. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Basic Salary without Leave:" -msgstr "Salario base sin ausencias:" +#: field:hr.payroll.structure,children_ids:0 +#: field:hr.salary.rule.category,children_ids:0 +msgid "Children" +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip,credit_note:0 +msgid "Indicates this payslip has a refund of another" +msgstr "" + +#. module: hr_payroll +#: selection:hr.contract,schedule_pay:0 +msgid "Bi-monthly" +msgstr "" + +#. module: hr_payroll +#: report:paylip.details:0 +msgid "Pay Slip Details" +msgstr "" + +#. module: hr_payroll +#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payslip_form +#: model:ir.ui.menu,name:hr_payroll.menu_department_tree +msgid "Employee Payslips" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.line:0 field:hr.payslip.line,register_id:0 +#: field:hr.salary.rule,register_id:0 +#: model:ir.model,name:hr_payroll.model_hr_contribution_register +msgid "Contribution Register" +msgstr "" + +#. module: hr_payroll +#: view:payslip.lines.contribution.register:0 +msgid "Print" +msgstr "" + +#. module: hr_payroll +#: model:ir.actions.act_window,help:hr_payroll.action_contribution_register_form +msgid "" +"A contribution register is a third party involved in the salary payment of " +"the employees. It can be the social security, the estate or anyone that " +"collect or inject money on payslips." +msgstr "" + +#. module: hr_payroll +#: code:addons/hr_payroll/hr_payroll.py:889 +#, python-format +msgid "Wrong range condition defined for salary rule %s (%s)" +msgstr "" #. module: hr_payroll -#: view:company.contribution:0 -#: view:hr.allounce.deduction.categoty:0 -#: view:hr.payroll.structure:0 -#: view:hr.payslip:0 #: view:hr.payslip.line:0 -#: field:hr.payslip.line,function_id:0 -msgid "Function" -msgstr "Función" +msgid "Calculations" +msgstr "" #. module: hr_payroll #: view:hr.payslip:0 -msgid "States" -msgstr "Estados" +msgid "Worked Days" +msgstr "" #. module: hr_payroll -#: report:payroll.advice:0 -msgid "Dear Sir/Madam," -msgstr "Apreciado Sr./Sra.," - -#. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_allounce_deduction_categoty -msgid "Allowance Deduction Heads" -msgstr "Cabeceras prima/deducción" - -#. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "Gross Sal." -msgstr "Sal. bruto" - -#. module: hr_payroll -#: view:company.contribution:0 -#: field:company.contribution,note:0 -#: view:hr.allounce.deduction.categoty:0 -#: field:hr.allounce.deduction.categoty,note:0 -#: view:hr.contibution.register:0 -#: field:hr.contibution.register,note:0 -#: view:hr.passport:0 -#: field:hr.passport,note:0 -#: field:hr.payroll.advice,note:0 -#: field:hr.payroll.register,note:0 -#: view:hr.payroll.structure:0 -#: field:hr.payroll.structure,note:0 #: view:hr.payslip:0 -#: field:hr.payslip,note:0 -#: view:hr.payslip.line:0 -#: field:hr.payslip.line,note:0 +msgid "Search Payslips" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.run:0 +#: model:ir.actions.act_window,name:hr_payroll.action_hr_payslip_run_tree +#: model:ir.ui.menu,name:hr_payroll.menu_hr_payslip_run +msgid "Payslips Batches" +msgstr "" + +#. module: hr_payroll +#: view:hr.contribution.register:0 field:hr.contribution.register,note:0 +#: field:hr.payroll.structure,note:0 field:hr.payslip,name:0 +#: field:hr.payslip,note:0 field:hr.payslip.input,name:0 +#: view:hr.payslip.line:0 field:hr.payslip.line,note:0 +#: field:hr.payslip.worked_days,name:0 field:hr.rule.input,name:0 +#: view:hr.salary.rule:0 field:hr.salary.rule,note:0 +#: field:hr.salary.rule.category,note:0 msgid "Description" -msgstr "Descripción" +msgstr "" #. module: hr_payroll -#: field:hr.payroll.employees.detail,date_from:0 -#: field:hr.payroll.year.salary,date_from:0 -#: report:salary.structure:0 -msgid "Start Date" -msgstr "Fecha inicio" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Deduction -" -msgstr "Deducción -" - -#. module: hr_payroll -#: report:payslip.pdf:0 +#: report:paylip.details:0 report:payslip:0 msgid ")" -msgstr ")" +msgstr "" #. module: hr_payroll -#: view:hr.contibution.register:0 +#: view:hr.contribution.register:0 +#: model:ir.actions.act_window,name:hr_payroll.action_contribution_register_form +#: model:ir.ui.menu,name:hr_payroll.menu_action_hr_contribution_register_form msgid "Contribution Registers" -msgstr "Registros de contribución" +msgstr "" #. module: hr_payroll #: model:ir.ui.menu,name:hr_payroll.menu_hr_payroll_reporting #: model:ir.ui.menu,name:hr_payroll.menu_hr_root_payroll #: model:ir.ui.menu,name:hr_payroll.payroll_configure msgid "Payroll" -msgstr "Nómina" +msgstr "" #. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_contract_wage_type -msgid "Wage Type" -msgstr "Tipo de salario" +#: model:ir.actions.report.xml,name:hr_payroll.contribution_register +msgid "PaySlip Lines By Conribution Register" +msgstr "" #. module: hr_payroll -#: report:hr.payroll.register.sheet:0 -msgid "Net Sal." -msgstr "Sal. neto" - -#. module: hr_payroll -#: sql_constraint:hr.passport:0 -msgid "The Passport No must be unique !" -msgstr "¡El número de pasaporte debe ser único!" - -#. module: hr_payroll -#: field:hr.allounce.deduction.categoty,name:0 -msgid "Category Name" -msgstr "Nombre categoría" - -#. module: hr_payroll -#: model:ir.model,name:hr_payroll.model_hr_payroll_year_salary -msgid "hr.payroll.year.salary" -msgstr "hr.nómina.año.salario" - -#. module: hr_payroll -#: report:employees.salary:0 -#: field:hr.passport,address_id:0 -#: report:payslip.pdf:0 -#: report:salary.structure:0 -msgid "Address" -msgstr "Dirección" - -#. module: hr_payroll -#: field:hr.payslip.line.line,slipline_id:0 -msgid "Slip Line" -msgstr "Línea justificante" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Number of Leaves" -msgstr "Número de hojas" - -#. module: hr_payroll -#: report:employees.salary:0 -#: field:hr.payroll.advice,bank_id:0 -#: field:hr.payroll.register,bank_id:0 -#: report:salary.structure:0 -msgid "Bank" -msgstr "Banco" - -#. module: hr_payroll -#: view:hr.payroll.advice:0 -msgid "Cancel Sheet" -msgstr "Cancelar hoja" - -#. module: hr_payroll -#: selection:hr.allounce.deduction.categoty,type:0 -#: selection:hr.payslip.line,type:0 -msgid "Advance" -msgstr "Adelanto" - -#. module: hr_payroll -#: report:salary.structure:0 -msgid "Special Allowances and Deductions For Employee:" -msgstr "Primas y deducciones especiales por empleado:" - -#. module: hr_payroll -#: field:company.contribution,name:0 -#: field:company.contribution.line,name:0 -#: field:hr.contibution.register,name:0 -#: field:hr.contibution.register.line,name:0 -#: field:hr.payroll.advice,name:0 -#: field:hr.payroll.register,name:0 -#: field:hr.payroll.structure,name:0 -#: field:hr.payslip,name:0 -#: field:hr.payslip.line,name:0 -#: field:hr.payslip.line.line,name:0 -#: report:payslip.pdf:0 -#: report:salary.structure:0 -#: report:year.salary:0 -msgid "Name" -msgstr "Nombre" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Leaved Deduction" -msgstr "Hojas de deducción" - -#. module: hr_payroll -#: view:hr.passport:0 -msgid "Country" -msgstr "País" - -#. module: hr_payroll -#: view:hr.passport:0 -#: view:hr.payroll.employees.detail:0 -#: field:hr.payroll.employees.detail,employee_ids:0 -#: view:hr.payroll.year.salary:0 -#: field:hr.payroll.year.salary,employee_ids:0 -#: view:hr.payslip:0 -msgid "Employees" -msgstr "Empleados" - -#. module: hr_payroll -#: report:payroll.advice:0 -msgid "Bank Account" -msgstr "Cuenta bancaria" - -#. module: hr_payroll -#: help:company.contribution,register_id:0 -msgid "Contribution register based on company" -msgstr "Registro de contribución basado en la empresa" - -#. module: hr_payroll -#: help:hr.allounce.deduction.categoty,sequence:0 -msgid "Use to arrange calculation sequence" -msgstr "Se utiliza para organizar la secuencia de cálculo" - -#. module: hr_payroll -#: field:hr.payslip,total_pay:0 -msgid "Total Payment" -msgstr "Pago total" - -#. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Leave Deductions Line:" -msgstr "Línea de deducciones por ausencias:" - -#. module: hr_payroll -#: selection:hr.payroll.register,state:0 #: selection:hr.payslip,state:0 -msgid "Wating for Account Verification" -msgstr "Esperando por verificación de la cuenta" +msgid "Waiting" +msgstr "" #. module: hr_payroll -#: field:hr.contibution.register.line,comp_deduction:0 -msgid "Company Deduction" -msgstr "Deducción de la Compañía" +#: report:paylip.details:0 report:payslip:0 +msgid "Address" +msgstr "" #. module: hr_payroll -#: view:hr.holidays.status:0 -msgid "Payroll Configurtion" -msgstr "Configuración nómina" - -#. module: hr_payroll -#: code:addons/hr_payroll/hr_payroll.py:469 +#: code:addons/hr_payroll/hr_payroll.py:866 #, python-format -msgid "Please define bank account for the %s employee" -msgstr "Por favor, defina la cuenta bancaria para el empleado %s" +msgid "Wrong percentage base or quantity defined for salary rule %s (%s)" +msgstr "" #. module: hr_payroll -#: field:hr.passport,date_issue:0 -msgid "Passport Issue Date" -msgstr "Fecha de emisión del pasaporte" +#: field:hr.payslip,worked_days_line_ids:0 +#: model:ir.model,name:hr_payroll.model_hr_payslip_worked_days +msgid "Payslip Worked Days" +msgstr "" #. module: hr_payroll -#: view:hr.allounce.deduction.categoty:0 -#: selection:hr.allounce.deduction.categoty,type:0 -#: field:hr.payroll.register,allounce:0 -#: field:hr.payslip,allounce:0 -#: selection:hr.payslip.line,type:0 -msgid "Allowance" -msgstr "Prima" +#: view:hr.salary.rule.category:0 +msgid "Salary Categories" +msgstr "" #. module: hr_payroll -#: field:hr.payslip,holiday_days:0 -msgid "No of Leaves" -msgstr "N º de hojas" +#: report:contribution.register.lines:0 field:hr.contribution.register,name:0 +#: field:hr.payroll.structure,name:0 field:hr.payslip.line,name:0 +#: field:hr.payslip.run,name:0 field:hr.salary.rule,name:0 +#: field:hr.salary.rule.category,name:0 report:paylip.details:0 +#: report:payslip:0 +msgid "Name" +msgstr "" #. module: hr_payroll -#: field:hr.employee,otherid:0 -msgid "Other Id" -msgstr "Otro id" +#: view:hr.payroll.structure:0 +msgid "Payroll Structures" +msgstr "" #. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Bank Details" -msgstr "Detalles bancarios" +#: view:hr.payslip:0 view:hr.payslip.employees:0 +#: field:hr.payslip.employees,employee_ids:0 view:hr.payslip.line:0 +msgid "Employees" +msgstr "" #. module: hr_payroll -#: report:payslip.pdf:0 -msgid "Slip ID" -msgstr "ID justificante" +#: report:paylip.details:0 report:payslip:0 +msgid "Bank Account" +msgstr "" #. module: hr_payroll -#: field:company.contribution.line,sequence:0 -#: field:hr.allounce.deduction.categoty,sequence:0 -#: field:hr.payslip.line,sequence:0 -#: field:hr.payslip.line.line,sequence:0 -msgid "Sequence" -msgstr "Secuencia" +#: help:hr.payslip.line,sequence:0 help:hr.salary.rule,sequence:0 +msgid "Use to arrange calculation sequence" +msgstr "" #. module: hr_payroll -#: model:ir.actions.act_window,name:hr_payroll.action_view_hr_payslip_form -#: model:ir.ui.menu,name:hr_payroll.menu_department_tree -msgid "Employee Payslip" -msgstr "Nómina de los empleados" +#: help:hr.payslip.line,condition_range:0 +#: help:hr.salary.rule,condition_range:0 +msgid "" +"This will be used to compute the % fields values; in general it is on basic, " +"but you can also use categories code fields in lowercase as a variable names " +"(hra, ma, lta, etc.) and the variable basic." +msgstr "" #. module: hr_payroll -#: view:hr.payroll.advice:0 -msgid "Letter Content" -msgstr "Contenido carta" +#: selection:hr.contract,schedule_pay:0 +msgid "Annually" +msgstr "" #. module: hr_payroll -#: view:hr.payroll.employees.detail:0 -#: view:hr.payroll.year.salary:0 -msgid "Year Salary" -msgstr "Salario anual" +#: field:hr.payslip,input_line_ids:0 +msgid "Payslip Inputs" +msgstr "" #. module: hr_payroll -#: view:hr.payroll.register:0 -msgid "Allowance / Deduction" -msgstr "Prima / Deducción" +#: field:hr.payslip.line,salary_rule_id:0 +msgid "Rule" +msgstr "" #. module: hr_payroll -#: model:ir.actions.report.xml,name:hr_payroll.payroll_advice -msgid "Bank Payment Advice" -msgstr "Aviso de pago del banco" +#: model:ir.actions.act_window,name:hr_payroll.action_hr_salary_rule_category_tree_view +#: model:ir.ui.menu,name:hr_payroll.menu_hr_salary_rule_category_tree_view +msgid "Salary Rule Categories Hierarchy" +msgstr "" + +#. module: hr_payroll +#: report:contribution.register.lines:0 field:hr.payslip.line,total:0 +#: report:paylip.details:0 report:payslip:0 +msgid "Total" +msgstr "" + +#. module: hr_payroll +#: help:hr.payslip.line,appears_on_payslip:0 +#: help:hr.salary.rule,appears_on_payslip:0 +msgid "Used for the display of rule on payslip" +msgstr "" + +#. module: hr_payroll +#: view:hr.payslip.line:0 +msgid "Search Payslip Lines" +msgstr "" #. module: hr_payroll #: view:hr.payslip:0 -msgid "Search Payslips" -msgstr "Buscar nóminas" +msgid "Details By Salary Rule Category" +msgstr "" #. module: hr_payroll -#: report:employees.salary:0 -#: field:hr.contibution.register.line,total:0 -#: report:year.salary:0 -msgid "Total" -msgstr "Total" +#: help:hr.payslip.input,code:0 help:hr.payslip.worked_days,code:0 +#: help:hr.rule.input,code:0 +msgid "The code that can be used in the salary rules" +msgstr "" #. module: hr_payroll -#: view:company.contribution:0 -#: view:hr.allounce.deduction.categoty:0 -#: field:hr.allounce.deduction.categoty,contribute_ids:0 -msgid "Contributions" -msgstr "Contribuciones" +#: view:hr.payslip.run:0 +#: model:ir.actions.act_window,name:hr_payroll.action_hr_payslip_by_employees +msgid "Generate Payslips" +msgstr "" -#~ msgid "Categoty Code" -#~ msgstr "Código de categoría" +#. module: hr_payroll +#: selection:hr.contract,schedule_pay:0 +msgid "Bi-weekly" +msgstr "" -#~ msgid "Categoty Name" -#~ msgstr "Nombre categoría" +#. module: hr_payroll +#: field:hr.employee,total_wage:0 +msgid "Total Basic Salary" +msgstr "" -#~ msgid "Invalid model name in the action definition." -#~ msgstr "Nombre del modelo inválido en la definición de acción." +#. module: hr_payroll +#: selection:hr.payslip.line,condition_select:0 +#: selection:hr.salary.rule,condition_select:0 +msgid "Always True" +msgstr "" -#~ msgid "Analytic Account for Salary Analysis" -#~ msgstr "Cuenta analítica para análisis de nóminas" +#. module: hr_payroll +#: report:contribution.register.lines:0 +msgid "PaySlip Name" +msgstr "" -#~ msgid "Employee No" -#~ msgstr "Núm. trabajador" - -#~ msgid "Employee Payable Account" -#~ msgstr "Cuenta a pagar del trabajador" - -#~ msgid "Account" -#~ msgstr "Cuenta" - -#, python-format -#~ msgid "Warning !" -#~ msgstr "¡ Aviso !" - -#~ msgid "Payment From" -#~ msgstr "Pago desde" - -#~ msgid "Keep empty to use the period of the validation(Payslip) date." -#~ msgstr "" -#~ "Deje en blanco para usar el período de la fecha de validación de la nómina" - -#~ msgid "Analytic Account" -#~ msgstr "Cuenta analítica" - -#~ msgid "Force Period" -#~ msgstr "Forzar período" - -#~ msgid "Accounting Information" -#~ msgstr "Información contable" - -#, python-format -#~ msgid "Integrity Error !" -#~ msgstr "¡Error de integridad!" - -#~ msgid "Miscelleanous" -#~ msgstr "Misceláneos" - -#~ msgid "Error! You can not create recursive analytic accounts." -#~ msgstr "¡Error! No puede crear cuentas analíticas recursivas." - -#~ msgid "Account Lines" -#~ msgstr "Movimientos" - -#~ msgid "Accounting Informations" -#~ msgstr "Información contable" - -#~ msgid "Print" -#~ msgstr "Imprimir" - -#~ msgid "Invalid XML for View Architecture!" -#~ msgstr "¡XML inválido para la definición de la vista!" - -#~ msgid "Accounting Lines" -#~ msgstr "Movimientos contables" - -#~ msgid "Bank Journal" -#~ msgstr "Diario bancario" - -#~ msgid "Expense" -#~ msgstr "Gastos" - -#~ msgid "Payment Entries" -#~ msgstr "Asientos de pago" - -#~ msgid "Salary Account" -#~ msgstr "Cuenta de nómina" - -#~ msgid "Employee Contract" -#~ msgstr "Contrato de trabajador" - -#~ msgid "" -#~ "The Object name must start with x_ and not contain any special character !" -#~ msgstr "" -#~ "¡El nombre del objeto debe empezar con x_ y no contener ningún carácter " -#~ "especial!" - -#~ msgid "General Account" -#~ msgstr "Cuenta general" - -#~ msgid "Process" -#~ msgstr "Proceso" - -#~ msgid "Employee Account" -#~ msgstr "Cuenta trabajador" - -#~ msgid "General Information" -#~ msgstr "Información general" - -#~ msgid "Accounting Details" -#~ msgstr "Detalles de contabilidad" - -#~ msgid "Account Information" -#~ msgstr "Información de la cuenta" - -#~ msgid "Generalities" -#~ msgstr "Generalidades" - -#~ msgid "Contract Duration" -#~ msgstr "Duración del contrato" - -#~ msgid "Notes" -#~ msgstr "Notas" - -#~ msgid "Fiscal Year" -#~ msgstr "Ejercicio fiscal" +#. module: hr_payroll +#: field:hr.payslip.line,condition_range:0 +#: field:hr.salary.rule,condition_range:0 +msgid "Range Based on" +msgstr "" diff --git a/addons/l10n_in_hr_payroll/i18n/bn.po b/addons/l10n_in_hr_payroll/i18n/bn.po index ea9fcfd7bb3..0fd88c3813e 100644 --- a/addons/l10n_in_hr_payroll/i18n/bn.po +++ b/addons/l10n_in_hr_payroll/i18n/bn.po @@ -6,928 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: OpenERP Server 7.0alpha\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 06:46+0000\n" -"PO-Revision-Date: 2012-08-17 06:46+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2012-09-10 09:14+0000\n" +"PO-Revision-Date: 2012-10-10 12:31+0000\n" +"Last-Translator: Fabien (Open ERP) \n" "Language-Team: Bengali \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-10-11 05:00+0000\n" +"X-Generator: Launchpad (build 16118)\n" -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "E-mail Address" -msgstr "" -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,employee_bank_no:0 -msgid "Employee Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in draft state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Title" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Payment Advice from" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail -msgid "Hr Salary Employee By Category Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employees Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Allowances with Basic:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Department" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Deductions:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "A/C no." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,driver_salay:0 -msgid "Driver Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail -msgid "Yearly Salary by Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list -msgid "Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "March" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "(" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,company_id:0 -#: field:hr.payroll.advice.line,company_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,company_id:0 -msgid "Company" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "The Manager" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Letter Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "," -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Set to Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,number_of_year:0 -msgid "Total years of work experience" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "to" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Total :" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payslip.run,available_advice:0 -msgid "Made Payment Advice?" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Advices which are paid using NEFT transfer" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,tds:0 -msgid "Amount for Tax Deduction at Source" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip -msgid "Pay Slip" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,day:0 -msgid "Day" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Month of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.payslip:0 -msgid "Payslip 'Date From' must be before 'Date To'." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,batch_id:0 -msgid "Batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Other Information" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Cancelled" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "For" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Details by Salary Rule Category:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,voluntary_provident_fund:0 -msgid "VPF computed as percentage(%)" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,number:0 -#: report:paylip.details.in:0 -msgid "Reference" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Group By..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,medical_insurance:0 -msgid "Medical Insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Identification No" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Confirmed" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,bysal:0 -#: field:payment.advice.report,bysal:0 -#: report:payroll.advice:0 -msgid "By Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Confirm" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,chaque_nos:0 -#: field:payment.advice.report,cheque_nos:0 -msgid "Cheque Numbers" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:res.company:0 -msgid "Error! You can not create recursive companies." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month -msgid "Yearly Salary by Head" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#, python-format -msgid "You can not confirm Payment advice without advice lines." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,state:0 -#: field:payment.advice.report,state:0 -msgid "State" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Yours Sincerely" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,medical_insurance:0 -msgid "Deduction towards company provided medical insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line -msgid "Bank Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Email" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payslip.run,available_advice:0 -msgid "If this box is checked which means that Payment Advice exists for current batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Error !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Print" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run -msgid "Payslip Batches" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,debit_credit:0 -#: report:payroll.advice:0 -msgid "C/D" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.employee.bymonth:0 -msgid "Yearly Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice -msgid "Print Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,line_ids:0 -msgid "Employee Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "July" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Configuration" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree -#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice -msgid "Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice -#: view:payment.advice.report:0 -msgid "Advices Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc:0 -msgid "IFSC" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,tds:0 -msgid "TDS" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,join_date:0 -msgid "Join Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.contract:0 -msgid "Error! Contract start-date must be less than contract end-date." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:res.company,dearness_allowance:0 -msgid "Dearness Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "August" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Deduction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Search Payment advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "SI. No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in confirm state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "December" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Sheet" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,month:0 -msgid "Month" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Non Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "or" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month -msgid "Hr Salary Employee By Month Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,category_id:0 -msgid "Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#, python-format -msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "To Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,number_of_year:0 -msgid "No. of Years of Service" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Note" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Salary Rule Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: selection:hr.payroll.advice,state:0 -#: view:payment.advice.report:0 -#: selection:payment.advice.report,state:0 -msgid "Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,voluntary_provident_fund:0 -msgid "Voluntary Provident Fund" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report -msgid "Payment Advice Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Status" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,city_type:0 -msgid "Type of City" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:res.company,dearness_allowance:0 -msgid "Check this box if your company provide Dearness Allowance to employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc_code:0 -#: field:payment.advice.report,ifsc_code:0 -#: report:payroll.advice:0 -msgid "IFSC Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "June" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,nbr:0 -msgid "# Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report -msgid "PaySlip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,date:0 -#: field:payment.advice.report,date:0 -msgid "Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "November" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Extended Filters..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all -msgid "This report performs analysis on Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "October" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Designation" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "January" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "Pay Head Employee Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_res_company -msgid "Companies" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:payroll.advice:0 -msgid "Authorized Signature" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract -msgid "Contract" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice.line:0 -msgid "Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "To," -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,driver_salay:0 -msgid "Check this box if you provide allowance for driver" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice.line,advice_id:0 -#: field:hr.payslip,advice_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice -msgid "Bank Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Other No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Draft Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,neft:0 -msgid "Check this box if your company use online transfer for salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,number:0 -msgid "Number" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "September" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Cancel" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Day of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.employee:0 -msgid "Error! You cannot create recursive hierarchy of Employee(s)." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "This wizard will print report which display a pay head employee breakup for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Payslip Batches ready to be Adviced" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Pay Slip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Total Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,employee_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee -#: view:payment.advice.report:0 -#: field:payment.advice.report,employee_id:0 -msgid "Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Compute Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,join_date:0 -msgid "Joining date of employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Dear Sir/Madam," -msgstr "Dear Sir/Madam," - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,note:0 -msgid "Description" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid ")" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Payroll" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "NEFT" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Address" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,bank_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,bank_id:0 -#: report:payroll.advice:0 -#: report:salary.detail.byyear:0 -msgid "Bank" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,end_date:0 -#: field:yearly.salary.detail,date_to:0 -msgid "End Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "February" -msgstr "" - -#. module: l10n_in_hr_payroll -#: sql_constraint:res.company:0 -msgid "The company name must be unique !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,name:0 -#: report:paylip.details.in:0 -#: field:payment.advice.report,name:0 -#: report:salary.employee.bymonth:0 -msgid "Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: field:hr.salary.employee.month,employee_ids:0 -#: view:yearly.salary.detail:0 -#: field:yearly.salary.detail,employee_ids:0 -msgid "Employees" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "April" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Name of the Employe" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Please define bank account for the %s employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,start_date:0 -#: field:yearly.salary.detail,date_from:0 -msgid "Start Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,bank_id:0 -msgid "Select the Bank from which the salary is going to be paid" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "Employee Pay Head Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Phone No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Credit" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,name:0 -#: report:payroll.advice:0 -msgid "Bank Account No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,date:0 -msgid "Advice Date is used to search Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "May" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Create Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,year:0 -msgid "Year" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,neft:0 -#: field:payment.advice.report,neft:0 -msgid "NEFT Transaction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "Total" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "form period" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Year of Payment Advices" -msgstr "" \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/gu.po b/addons/l10n_in_hr_payroll/i18n/gu.po index 2c321b13191..844c492e28b 100644 --- a/addons/l10n_in_hr_payroll/i18n/gu.po +++ b/addons/l10n_in_hr_payroll/i18n/gu.po @@ -6,928 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: OpenERP Server 7.0alpha\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 06:46+0000\n" -"PO-Revision-Date: 2012-08-17 06:46+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2012-09-10 09:14+0000\n" +"PO-Revision-Date: 2012-10-10 12:31+0000\n" +"Last-Translator: Fabien (Open ERP) \n" "Language-Team: Gujarati \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-10-11 05:00+0000\n" +"X-Generator: Launchpad (build 16118)\n" -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "E-mail Address" -msgstr "" -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,employee_bank_no:0 -msgid "Employee Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in draft state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Title" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Payment Advice from" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail -msgid "Hr Salary Employee By Category Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employees Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Allowances with Basic:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Department" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Deductions:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "A/C no." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,driver_salay:0 -msgid "Driver Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail -msgid "Yearly Salary by Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list -msgid "Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "March" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "(" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,company_id:0 -#: field:hr.payroll.advice.line,company_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,company_id:0 -msgid "Company" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "The Manager" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Letter Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "," -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Set to Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,number_of_year:0 -msgid "Total years of work experience" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "to" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Total :" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payslip.run,available_advice:0 -msgid "Made Payment Advice?" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Advices which are paid using NEFT transfer" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,tds:0 -msgid "Amount for Tax Deduction at Source" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip -msgid "Pay Slip" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,day:0 -msgid "Day" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Month of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.payslip:0 -msgid "Payslip 'Date From' must be before 'Date To'." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,batch_id:0 -msgid "Batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Other Information" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Cancelled" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "For" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Details by Salary Rule Category:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,voluntary_provident_fund:0 -msgid "VPF computed as percentage(%)" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,number:0 -#: report:paylip.details.in:0 -msgid "Reference" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Group By..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,medical_insurance:0 -msgid "Medical Insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Identification No" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Confirmed" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,bysal:0 -#: field:payment.advice.report,bysal:0 -#: report:payroll.advice:0 -msgid "By Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Confirm" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,chaque_nos:0 -#: field:payment.advice.report,cheque_nos:0 -msgid "Cheque Numbers" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:res.company:0 -msgid "Error! You can not create recursive companies." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month -msgid "Yearly Salary by Head" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#, python-format -msgid "You can not confirm Payment advice without advice lines." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,state:0 -#: field:payment.advice.report,state:0 -msgid "State" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Yours Sincerely" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,medical_insurance:0 -msgid "Deduction towards company provided medical insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line -msgid "Bank Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Email" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payslip.run,available_advice:0 -msgid "If this box is checked which means that Payment Advice exists for current batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Error !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Print" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run -msgid "Payslip Batches" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,debit_credit:0 -#: report:payroll.advice:0 -msgid "C/D" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.employee.bymonth:0 -msgid "Yearly Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice -msgid "Print Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,line_ids:0 -msgid "Employee Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "July" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Configuration" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree -#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice -msgid "Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice -#: view:payment.advice.report:0 -msgid "Advices Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc:0 -msgid "IFSC" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,tds:0 -msgid "TDS" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,join_date:0 -msgid "Join Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.contract:0 -msgid "Error! Contract start-date must be less than contract end-date." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:res.company,dearness_allowance:0 -msgid "Dearness Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "August" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Deduction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Search Payment advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "SI. No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in confirm state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "December" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Sheet" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,month:0 -msgid "Month" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Non Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "or" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month -msgid "Hr Salary Employee By Month Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,category_id:0 -msgid "Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#, python-format -msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "To Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,number_of_year:0 -msgid "No. of Years of Service" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Note" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Salary Rule Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: selection:hr.payroll.advice,state:0 -#: view:payment.advice.report:0 -#: selection:payment.advice.report,state:0 -msgid "Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,voluntary_provident_fund:0 -msgid "Voluntary Provident Fund" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report -msgid "Payment Advice Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Status" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,city_type:0 -msgid "Type of City" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:res.company,dearness_allowance:0 -msgid "Check this box if your company provide Dearness Allowance to employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc_code:0 -#: field:payment.advice.report,ifsc_code:0 -#: report:payroll.advice:0 -msgid "IFSC Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "June" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,nbr:0 -msgid "# Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report -msgid "PaySlip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,date:0 -#: field:payment.advice.report,date:0 -msgid "Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "November" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Extended Filters..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all -msgid "This report performs analysis on Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "October" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Designation" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "January" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "Pay Head Employee Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_res_company -msgid "Companies" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:payroll.advice:0 -msgid "Authorized Signature" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract -msgid "Contract" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice.line:0 -msgid "Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "To," -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,driver_salay:0 -msgid "Check this box if you provide allowance for driver" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice.line,advice_id:0 -#: field:hr.payslip,advice_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice -msgid "Bank Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Other No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Draft Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,neft:0 -msgid "Check this box if your company use online transfer for salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,number:0 -msgid "Number" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "September" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Cancel" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Day of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.employee:0 -msgid "Error! You cannot create recursive hierarchy of Employee(s)." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "This wizard will print report which display a pay head employee breakup for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Payslip Batches ready to be Adviced" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Pay Slip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Total Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,employee_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee -#: view:payment.advice.report:0 -#: field:payment.advice.report,employee_id:0 -msgid "Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Compute Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,join_date:0 -msgid "Joining date of employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Dear Sir/Madam," -msgstr "Dear Sir/Madam," - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,note:0 -msgid "Description" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid ")" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Payroll" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "NEFT" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Address" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,bank_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,bank_id:0 -#: report:payroll.advice:0 -#: report:salary.detail.byyear:0 -msgid "Bank" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,end_date:0 -#: field:yearly.salary.detail,date_to:0 -msgid "End Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "February" -msgstr "" - -#. module: l10n_in_hr_payroll -#: sql_constraint:res.company:0 -msgid "The company name must be unique !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,name:0 -#: report:paylip.details.in:0 -#: field:payment.advice.report,name:0 -#: report:salary.employee.bymonth:0 -msgid "Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: field:hr.salary.employee.month,employee_ids:0 -#: view:yearly.salary.detail:0 -#: field:yearly.salary.detail,employee_ids:0 -msgid "Employees" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "April" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Name of the Employe" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Please define bank account for the %s employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,start_date:0 -#: field:yearly.salary.detail,date_from:0 -msgid "Start Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,bank_id:0 -msgid "Select the Bank from which the salary is going to be paid" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "Employee Pay Head Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Phone No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Credit" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,name:0 -#: report:payroll.advice:0 -msgid "Bank Account No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,date:0 -msgid "Advice Date is used to search Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "May" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Create Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,year:0 -msgid "Year" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,neft:0 -#: field:payment.advice.report,neft:0 -msgid "NEFT Transaction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "Total" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "form period" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Year of Payment Advices" -msgstr "" \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/hi.po b/addons/l10n_in_hr_payroll/i18n/hi.po index d7cc9dc1519..b13e24e05f9 100644 --- a/addons/l10n_in_hr_payroll/i18n/hi.po +++ b/addons/l10n_in_hr_payroll/i18n/hi.po @@ -6,928 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: OpenERP Server 7.0alpha\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 06:46+0000\n" -"PO-Revision-Date: 2012-08-17 06:46+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2012-09-10 09:14+0000\n" +"PO-Revision-Date: 2012-10-10 12:31+0000\n" +"Last-Translator: Fabien (Open ERP) \n" "Language-Team: Hindi \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-10-11 05:00+0000\n" +"X-Generator: Launchpad (build 16118)\n" -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "E-mail Address" -msgstr "" -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,employee_bank_no:0 -msgid "Employee Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in draft state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Title" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Payment Advice from" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail -msgid "Hr Salary Employee By Category Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employees Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Allowances with Basic:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Department" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Deductions:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "A/C no." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,driver_salay:0 -msgid "Driver Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail -msgid "Yearly Salary by Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list -msgid "Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "March" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "(" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,company_id:0 -#: field:hr.payroll.advice.line,company_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,company_id:0 -msgid "Company" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "The Manager" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Letter Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "," -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Set to Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,number_of_year:0 -msgid "Total years of work experience" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "to" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Total :" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payslip.run,available_advice:0 -msgid "Made Payment Advice?" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Advices which are paid using NEFT transfer" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,tds:0 -msgid "Amount for Tax Deduction at Source" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip -msgid "Pay Slip" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,day:0 -msgid "Day" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Month of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.payslip:0 -msgid "Payslip 'Date From' must be before 'Date To'." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,batch_id:0 -msgid "Batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Other Information" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Cancelled" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "For" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Details by Salary Rule Category:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,voluntary_provident_fund:0 -msgid "VPF computed as percentage(%)" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,number:0 -#: report:paylip.details.in:0 -msgid "Reference" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Group By..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,medical_insurance:0 -msgid "Medical Insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Identification No" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Confirmed" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,bysal:0 -#: field:payment.advice.report,bysal:0 -#: report:payroll.advice:0 -msgid "By Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Confirm" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,chaque_nos:0 -#: field:payment.advice.report,cheque_nos:0 -msgid "Cheque Numbers" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:res.company:0 -msgid "Error! You can not create recursive companies." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month -msgid "Yearly Salary by Head" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#, python-format -msgid "You can not confirm Payment advice without advice lines." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,state:0 -#: field:payment.advice.report,state:0 -msgid "State" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Yours Sincerely" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,medical_insurance:0 -msgid "Deduction towards company provided medical insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line -msgid "Bank Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Email" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payslip.run,available_advice:0 -msgid "If this box is checked which means that Payment Advice exists for current batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Error !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Print" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run -msgid "Payslip Batches" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,debit_credit:0 -#: report:payroll.advice:0 -msgid "C/D" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.employee.bymonth:0 -msgid "Yearly Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice -msgid "Print Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,line_ids:0 -msgid "Employee Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "July" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Configuration" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree -#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice -msgid "Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice -#: view:payment.advice.report:0 -msgid "Advices Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc:0 -msgid "IFSC" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,tds:0 -msgid "TDS" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,join_date:0 -msgid "Join Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.contract:0 -msgid "Error! Contract start-date must be less than contract end-date." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:res.company,dearness_allowance:0 -msgid "Dearness Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "August" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Deduction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Search Payment advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "SI. No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in confirm state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "December" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Sheet" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,month:0 -msgid "Month" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Non Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "or" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month -msgid "Hr Salary Employee By Month Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,category_id:0 -msgid "Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#, python-format -msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "To Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,number_of_year:0 -msgid "No. of Years of Service" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Note" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Salary Rule Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: selection:hr.payroll.advice,state:0 -#: view:payment.advice.report:0 -#: selection:payment.advice.report,state:0 -msgid "Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,voluntary_provident_fund:0 -msgid "Voluntary Provident Fund" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report -msgid "Payment Advice Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Status" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,city_type:0 -msgid "Type of City" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:res.company,dearness_allowance:0 -msgid "Check this box if your company provide Dearness Allowance to employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc_code:0 -#: field:payment.advice.report,ifsc_code:0 -#: report:payroll.advice:0 -msgid "IFSC Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "June" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,nbr:0 -msgid "# Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report -msgid "PaySlip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,date:0 -#: field:payment.advice.report,date:0 -msgid "Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "November" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Extended Filters..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all -msgid "This report performs analysis on Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "October" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Designation" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "January" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "Pay Head Employee Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_res_company -msgid "Companies" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:payroll.advice:0 -msgid "Authorized Signature" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract -msgid "Contract" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice.line:0 -msgid "Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "To," -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,driver_salay:0 -msgid "Check this box if you provide allowance for driver" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice.line,advice_id:0 -#: field:hr.payslip,advice_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice -msgid "Bank Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Other No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Draft Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,neft:0 -msgid "Check this box if your company use online transfer for salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,number:0 -msgid "Number" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "September" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Cancel" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Day of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.employee:0 -msgid "Error! You cannot create recursive hierarchy of Employee(s)." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "This wizard will print report which display a pay head employee breakup for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Payslip Batches ready to be Adviced" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Pay Slip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Total Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,employee_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee -#: view:payment.advice.report:0 -#: field:payment.advice.report,employee_id:0 -msgid "Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Compute Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,join_date:0 -msgid "Joining date of employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Dear Sir/Madam," -msgstr "Dear Sir/Madam," - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,note:0 -msgid "Description" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid ")" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Payroll" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "NEFT" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Address" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,bank_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,bank_id:0 -#: report:payroll.advice:0 -#: report:salary.detail.byyear:0 -msgid "Bank" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,end_date:0 -#: field:yearly.salary.detail,date_to:0 -msgid "End Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "February" -msgstr "" - -#. module: l10n_in_hr_payroll -#: sql_constraint:res.company:0 -msgid "The company name must be unique !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,name:0 -#: report:paylip.details.in:0 -#: field:payment.advice.report,name:0 -#: report:salary.employee.bymonth:0 -msgid "Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: field:hr.salary.employee.month,employee_ids:0 -#: view:yearly.salary.detail:0 -#: field:yearly.salary.detail,employee_ids:0 -msgid "Employees" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "April" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Name of the Employe" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Please define bank account for the %s employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,start_date:0 -#: field:yearly.salary.detail,date_from:0 -msgid "Start Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,bank_id:0 -msgid "Select the Bank from which the salary is going to be paid" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "Employee Pay Head Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Phone No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Credit" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,name:0 -#: report:payroll.advice:0 -msgid "Bank Account No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,date:0 -msgid "Advice Date is used to search Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "May" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Create Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,year:0 -msgid "Year" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,neft:0 -#: field:payment.advice.report,neft:0 -msgid "NEFT Transaction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "Total" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "form period" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Year of Payment Advices" -msgstr "" \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/ta.po b/addons/l10n_in_hr_payroll/i18n/ta.po index 98d00c76a33..dbe0e244bee 100644 --- a/addons/l10n_in_hr_payroll/i18n/ta.po +++ b/addons/l10n_in_hr_payroll/i18n/ta.po @@ -6,928 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: OpenERP Server 7.0alpha\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 06:46+0000\n" -"PO-Revision-Date: 2012-08-17 06:46+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2012-09-10 09:14+0000\n" +"PO-Revision-Date: 2012-10-10 12:31+0000\n" +"Last-Translator: Fabien (Open ERP) \n" "Language-Team: Tamil \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-10-11 05:00+0000\n" +"X-Generator: Launchpad (build 16118)\n" -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "E-mail Address" -msgstr "" -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,employee_bank_no:0 -msgid "Employee Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in draft state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Title" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Payment Advice from" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail -msgid "Hr Salary Employee By Category Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employees Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Allowances with Basic:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Department" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Deductions:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "A/C no." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,driver_salay:0 -msgid "Driver Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail -msgid "Yearly Salary by Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list -msgid "Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "March" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "(" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,company_id:0 -#: field:hr.payroll.advice.line,company_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,company_id:0 -msgid "Company" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "The Manager" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Letter Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "," -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Set to Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,number_of_year:0 -msgid "Total years of work experience" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "to" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Total :" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payslip.run,available_advice:0 -msgid "Made Payment Advice?" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Advices which are paid using NEFT transfer" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,tds:0 -msgid "Amount for Tax Deduction at Source" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip -msgid "Pay Slip" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,day:0 -msgid "Day" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Month of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.payslip:0 -msgid "Payslip 'Date From' must be before 'Date To'." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,batch_id:0 -msgid "Batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Other Information" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Cancelled" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "For" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Details by Salary Rule Category:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,voluntary_provident_fund:0 -msgid "VPF computed as percentage(%)" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,number:0 -#: report:paylip.details.in:0 -msgid "Reference" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Group By..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,medical_insurance:0 -msgid "Medical Insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Identification No" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Confirmed" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,bysal:0 -#: field:payment.advice.report,bysal:0 -#: report:payroll.advice:0 -msgid "By Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Confirm" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,chaque_nos:0 -#: field:payment.advice.report,cheque_nos:0 -msgid "Cheque Numbers" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:res.company:0 -msgid "Error! You can not create recursive companies." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month -msgid "Yearly Salary by Head" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#, python-format -msgid "You can not confirm Payment advice without advice lines." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,state:0 -#: field:payment.advice.report,state:0 -msgid "State" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Yours Sincerely" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,medical_insurance:0 -msgid "Deduction towards company provided medical insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line -msgid "Bank Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Email" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payslip.run,available_advice:0 -msgid "If this box is checked which means that Payment Advice exists for current batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Error !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Print" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run -msgid "Payslip Batches" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,debit_credit:0 -#: report:payroll.advice:0 -msgid "C/D" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.employee.bymonth:0 -msgid "Yearly Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice -msgid "Print Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,line_ids:0 -msgid "Employee Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "July" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Configuration" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree -#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice -msgid "Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice -#: view:payment.advice.report:0 -msgid "Advices Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc:0 -msgid "IFSC" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,tds:0 -msgid "TDS" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,join_date:0 -msgid "Join Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.contract:0 -msgid "Error! Contract start-date must be less than contract end-date." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:res.company,dearness_allowance:0 -msgid "Dearness Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "August" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Deduction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Search Payment advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "SI. No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in confirm state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "December" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Sheet" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,month:0 -msgid "Month" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Non Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "or" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month -msgid "Hr Salary Employee By Month Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,category_id:0 -msgid "Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#, python-format -msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "To Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,number_of_year:0 -msgid "No. of Years of Service" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Note" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Salary Rule Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: selection:hr.payroll.advice,state:0 -#: view:payment.advice.report:0 -#: selection:payment.advice.report,state:0 -msgid "Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,voluntary_provident_fund:0 -msgid "Voluntary Provident Fund" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report -msgid "Payment Advice Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Status" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,city_type:0 -msgid "Type of City" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:res.company,dearness_allowance:0 -msgid "Check this box if your company provide Dearness Allowance to employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc_code:0 -#: field:payment.advice.report,ifsc_code:0 -#: report:payroll.advice:0 -msgid "IFSC Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "June" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,nbr:0 -msgid "# Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report -msgid "PaySlip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,date:0 -#: field:payment.advice.report,date:0 -msgid "Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "November" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Extended Filters..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all -msgid "This report performs analysis on Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "October" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Designation" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "January" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "Pay Head Employee Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_res_company -msgid "Companies" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:payroll.advice:0 -msgid "Authorized Signature" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract -msgid "Contract" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice.line:0 -msgid "Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "To," -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,driver_salay:0 -msgid "Check this box if you provide allowance for driver" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice.line,advice_id:0 -#: field:hr.payslip,advice_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice -msgid "Bank Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Other No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Draft Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,neft:0 -msgid "Check this box if your company use online transfer for salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,number:0 -msgid "Number" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "September" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Cancel" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Day of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.employee:0 -msgid "Error! You cannot create recursive hierarchy of Employee(s)." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "This wizard will print report which display a pay head employee breakup for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Payslip Batches ready to be Adviced" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Pay Slip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Total Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,employee_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee -#: view:payment.advice.report:0 -#: field:payment.advice.report,employee_id:0 -msgid "Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Compute Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,join_date:0 -msgid "Joining date of employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Dear Sir/Madam," -msgstr "Dear Sir/Madam," - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,note:0 -msgid "Description" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid ")" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Payroll" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "NEFT" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Address" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,bank_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,bank_id:0 -#: report:payroll.advice:0 -#: report:salary.detail.byyear:0 -msgid "Bank" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,end_date:0 -#: field:yearly.salary.detail,date_to:0 -msgid "End Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "February" -msgstr "" - -#. module: l10n_in_hr_payroll -#: sql_constraint:res.company:0 -msgid "The company name must be unique !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,name:0 -#: report:paylip.details.in:0 -#: field:payment.advice.report,name:0 -#: report:salary.employee.bymonth:0 -msgid "Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: field:hr.salary.employee.month,employee_ids:0 -#: view:yearly.salary.detail:0 -#: field:yearly.salary.detail,employee_ids:0 -msgid "Employees" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "April" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Name of the Employe" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Please define bank account for the %s employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,start_date:0 -#: field:yearly.salary.detail,date_from:0 -msgid "Start Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,bank_id:0 -msgid "Select the Bank from which the salary is going to be paid" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "Employee Pay Head Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Phone No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Credit" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,name:0 -#: report:payroll.advice:0 -msgid "Bank Account No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,date:0 -msgid "Advice Date is used to search Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "May" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Create Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,year:0 -msgid "Year" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,neft:0 -#: field:payment.advice.report,neft:0 -msgid "NEFT Transaction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "Total" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "form period" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Year of Payment Advices" -msgstr "" \ No newline at end of file diff --git a/addons/l10n_in_hr_payroll/i18n/te.po b/addons/l10n_in_hr_payroll/i18n/te.po index 0c6f6127c4d..47ea35552f8 100644 --- a/addons/l10n_in_hr_payroll/i18n/te.po +++ b/addons/l10n_in_hr_payroll/i18n/te.po @@ -6,928 +6,14 @@ msgid "" msgstr "" "Project-Id-Version: OpenERP Server 7.0alpha\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2012-08-17 06:46+0000\n" -"PO-Revision-Date: 2012-08-17 06:46+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2012-09-10 09:14+0000\n" +"PO-Revision-Date: 2012-10-10 12:31+0000\n" +"Last-Translator: Fabien (Open ERP) \n" "Language-Team: Telugu \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-10-11 05:00+0000\n" +"X-Generator: Launchpad (build 16118)\n" -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "E-mail Address" -msgstr "" -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,employee_bank_no:0 -msgid "Employee Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in draft state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Title" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Payment Advice from" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_yearly_salary_detail -msgid "Hr Salary Employee By Category Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employees Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Allowances with Basic:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Department" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Deductions:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "A/C no." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,driver_salay:0 -msgid "Driver Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_yearly_salary_detail -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.yearly_salary -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_yearly_salary_detail -msgid "Yearly Salary by Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.act_hr_emp_payslip_list -msgid "Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "March" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "(" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,company_id:0 -#: field:hr.payroll.advice.line,company_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,company_id:0 -msgid "Company" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "The Manager" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Letter Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "," -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Set to Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,number_of_year:0 -msgid "Total years of work experience" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "to" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Total :" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payslip.run,available_advice:0 -msgid "Made Payment Advice?" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Advices which are paid using NEFT transfer" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,tds:0 -msgid "Amount for Tax Deduction at Source" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip -msgid "Pay Slip" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,day:0 -msgid "Day" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Month of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.payslip:0 -msgid "Payslip 'Date From' must be before 'Date To'." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,batch_id:0 -msgid "Batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Other Information" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Cancelled" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "For" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Details by Salary Rule Category:" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,voluntary_provident_fund:0 -msgid "VPF computed as percentage(%)" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,number:0 -#: report:paylip.details.in:0 -msgid "Reference" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Group By..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,medical_insurance:0 -msgid "Medical Insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Identification No" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.payroll.advice,state:0 -#: selection:payment.advice.report,state:0 -msgid "Confirmed" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,bysal:0 -#: field:payment.advice.report,bysal:0 -#: report:payroll.advice:0 -msgid "By Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Confirm" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,chaque_nos:0 -#: field:payment.advice.report,cheque_nos:0 -msgid "Cheque Numbers" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:res.company:0 -msgid "Error! You can not create recursive companies." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_salary_employee_month -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.hr_salary_employee_bymonth -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_salary_employee_month -msgid "Yearly Salary by Head" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#, python-format -msgid "You can not confirm Payment advice without advice lines." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,state:0 -#: field:payment.advice.report,state:0 -msgid "State" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Yours Sincerely" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,medical_insurance:0 -msgid "Deduction towards company provided medical insurance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice_line -msgid "Bank Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Email" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payslip.run,available_advice:0 -msgid "If this box is checked which means that Payment Advice exists for current batch" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:184 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Error !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Print" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payslip_run -msgid "Payslip Batches" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,debit_credit:0 -#: report:payroll.advice:0 -msgid "C/D" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.employee.bymonth:0 -msgid "Yearly Salary Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payroll_advice -msgid "Print Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,line_ids:0 -msgid "Employee Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "July" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Configuration" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_view_hr_bank_advice_tree -#: model:ir.ui.menu,name:l10n_in_hr_payroll.hr_menu_payment_advice -msgid "Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,name:l10n_in_hr_payroll.action_payment_advice_report_all -#: model:ir.ui.menu,name:l10n_in_hr_payroll.menu_reporting_payment_advice -#: view:payment.advice.report:0 -msgid "Advices Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "This wizard will print report which displays employees break-up of Net Head for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc:0 -msgid "IFSC" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,tds:0 -msgid "TDS" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,join_date:0 -msgid "Join Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.contract:0 -msgid "Error! Contract start-date must be less than contract end-date." -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:res.company,dearness_allowance:0 -msgid "Dearness Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "August" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Deduction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Search Payment advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "SI. No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Payment Advices which are in confirm state" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "December" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Confirm Sheet" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,month:0 -msgid "Month" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Non Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "or" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_salary_employee_month -msgid "Hr Salary Employee By Month Report" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,category_id:0 -msgid "Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:240 -#, python-format -msgid "Payment advice already exists for %s, 'Set to Draft' to create a new advice." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "To Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.employee,number_of_year:0 -msgid "No. of Years of Service" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Note" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Salary Rule Category" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: selection:hr.payroll.advice,state:0 -#: view:payment.advice.report:0 -#: selection:payment.advice.report,state:0 -msgid "Draft" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Date From" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,voluntary_provident_fund:0 -msgid "Voluntary Provident Fund" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Employee Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_payment_advice_report -msgid "Payment Advice Analysis" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:payment.advice.report:0 -msgid "Status" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.contract,city_type:0 -msgid "Type of City" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:res.company,dearness_allowance:0 -msgid "Check this box if your company provide Dearness Allowance to employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,ifsc_code:0 -#: field:payment.advice.report,ifsc_code:0 -#: report:payroll.advice:0 -msgid "IFSC Code" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "June" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,nbr:0 -msgid "# Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.report.xml,name:l10n_in_hr_payroll.payslip_details_report -msgid "PaySlip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Payment Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,date:0 -#: field:payment.advice.report,date:0 -msgid "Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "November" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Extended Filters..." -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.actions.act_window,help:l10n_in_hr_payroll.action_payment_advice_report_all -msgid "This report performs analysis on Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "October" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Designation" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "January" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "Pay Head Employee Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_res_company -msgid "Companies" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:payroll.advice:0 -msgid "Authorized Signature" -msgstr "" - -#. module: l10n_in_hr_payroll -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_contract -msgid "Contract" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice.line:0 -msgid "Advice Lines" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "To," -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.contract,driver_salay:0 -msgid "Check this box if you provide allowance for driver" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice.line,advice_id:0 -#: field:hr.payslip,advice_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_payroll_advice -msgid "Bank Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Other No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Draft Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,neft:0 -msgid "Check this box if your company use online transfer for salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "To" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:payment.advice.report,number:0 -msgid "Number" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "September" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: view:hr.salary.employee.month:0 -#: view:yearly.salary.detail:0 -msgid "Cancel" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Day of Payment Advices" -msgstr "" - -#. module: l10n_in_hr_payroll -#: constraint:hr.employee:0 -msgid "Error! You cannot create recursive hierarchy of Employee(s)." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:yearly.salary.detail:0 -msgid "This wizard will print report which display a pay head employee breakup for a specified dates." -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Payslip Batches ready to be Adviced" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Pay Slip Details" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Total Salary" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,employee_id:0 -#: model:ir.model,name:l10n_in_hr_payroll.model_hr_employee -#: view:payment.advice.report:0 -#: field:payment.advice.report,employee_id:0 -msgid "Employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -msgid "Compute Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.employee,join_date:0 -msgid "Joining date of employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Dear Sir/Madam," -msgstr "Dear Sir/Madam," - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,note:0 -msgid "Description" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid ")" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:res.company:0 -msgid "Payroll" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "NEFT" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -msgid "Address" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,bank_id:0 -#: view:payment.advice.report:0 -#: field:payment.advice.report,bank_id:0 -#: report:payroll.advice:0 -#: report:salary.detail.byyear:0 -msgid "Bank" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,end_date:0 -#: field:yearly.salary.detail,date_to:0 -msgid "End Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "February" -msgstr "" - -#. module: l10n_in_hr_payroll -#: sql_constraint:res.company:0 -msgid "The company name must be unique !" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payroll.advice:0 -#: field:hr.payroll.advice,name:0 -#: report:paylip.details.in:0 -#: field:payment.advice.report,name:0 -#: report:salary.employee.bymonth:0 -msgid "Name" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:hr.contract,city_type:0 -msgid "Metro" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -#: field:hr.salary.employee.month,employee_ids:0 -#: view:yearly.salary.detail:0 -#: field:yearly.salary.detail,employee_ids:0 -msgid "Employees" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Bank Account" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "April" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "Name of the Employe" -msgstr "" - -#. module: l10n_in_hr_payroll -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:158 -#: code:addons/l10n_in_hr_payroll/l10n_in_hr_payroll.py:257 -#, python-format -msgid "Please define bank account for the %s employee" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.salary.employee.month,start_date:0 -#: field:yearly.salary.detail,date_from:0 -msgid "Start Date" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.contract:0 -msgid "Allowance" -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,bank_id:0 -msgid "Select the Bank from which the salary is going to be paid" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.salary.employee.month:0 -msgid "Employee Pay Head Breakup" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:salary.detail.byyear:0 -msgid "Phone No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -msgid "Credit" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice.line,name:0 -#: report:payroll.advice:0 -msgid "Bank Account No." -msgstr "" - -#. module: l10n_in_hr_payroll -#: help:hr.payroll.advice,date:0 -msgid "Advice Date is used to search Payslips" -msgstr "" - -#. module: l10n_in_hr_payroll -#: selection:payment.advice.report,month:0 -msgid "May" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:hr.payslip.run:0 -msgid "Create Advice" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -#: field:payment.advice.report,year:0 -msgid "Year" -msgstr "" - -#. module: l10n_in_hr_payroll -#: field:hr.payroll.advice,neft:0 -#: field:payment.advice.report,neft:0 -msgid "NEFT Transaction" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:paylip.details.in:0 -#: report:salary.detail.byyear:0 -#: report:salary.employee.bymonth:0 -msgid "Total" -msgstr "" - -#. module: l10n_in_hr_payroll -#: report:payroll.advice:0 -msgid "form period" -msgstr "" - -#. module: l10n_in_hr_payroll -#: view:payment.advice.report:0 -msgid "Year of Payment Advices" -msgstr "" \ No newline at end of file