diff --git a/CHANGELOG b/CHANGELOG index b6216fed12..632e5a0472 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,4 @@ +* Add One2One field * Add AUTOINCREMENT to sqlite primary key Version 1.8.0 - 2010-11-01 diff --git a/doc/ref/models/fields.rst b/doc/ref/models/fields.rst index 49fb4398d0..5171e192ff 100644 --- a/doc/ref/models/fields.rst +++ b/doc/ref/models/fields.rst @@ -497,6 +497,28 @@ Instance methods: Return the target :class:`~trytond.model.Model`. +One2One +------- + +.. class:: One2One(relation_name, origin, target, string[, datetime_field[, \**options]]) + +A one-to-one relation field. + +.. warning:: + It is on the relation_name :class:`~trytond.model.Model` that the + unicity of the couple (origin, target) must be checked. +.. + +.. attribute:: One2One.datetime_field + + Same as :attr:`Many2One.datetime_field` + +Instance methods: + +.. method:: One2One.get_target(pool) + + Return the target :class:`~trytond.model.Model`. + Function -------- diff --git a/doc/topics/pyson.rst b/doc/topics/pyson.rst index 1d5d5e897b..120b441cfa 100644 --- a/doc/topics/pyson.rst +++ b/doc/topics/pyson.rst @@ -98,7 +98,7 @@ dictionary ``{}``. ``Get()`` checks the former resulting dictionary and returns the value of the key ``'company'``, otherwise it returns the number ``0``. A similar expression in Python looks like this:: - 'context' in locals() and context.get('company', 0) + 'context' in locals() and context.get('company', 0) or 0 diff --git a/trytond/backend/sqlite/database.py b/trytond/backend/sqlite/database.py index 460f9e7ec8..aacd47fdc9 100644 --- a/trytond/backend/sqlite/database.py +++ b/trytond/backend/sqlite/database.py @@ -150,7 +150,7 @@ class Database(DatabaseInterface): conn.close() def drop(self, cursor, database_name): - if database_name == ':memory': + if database_name == ':memory:': self._conn = None return if os.sep in database_name: diff --git a/trytond/ir/__tryton__.py b/trytond/ir/__tryton__.py index c491d51efc..45f0a7e8d8 100644 --- a/trytond/ir/__tryton__.py +++ b/trytond/ir/__tryton__.py @@ -2,12 +2,17 @@ #this repository contains the full copyright notices and license terms. { 'name': 'IR', + 'name_bg_BG': 'Вътрешна администрация', 'name_de_DE': 'Interne Administration', 'name_es_ES': 'Administración interna', 'name_fr_FR': 'Administration Interne', 'name_ru_RU': 'Внутреннее управление', 'description': '''Basic module handling internal tasks of the application. Provides concepts and administration of models, actions, sequences, localizations, cron jobs etc. +''', + 'description_bg_BG': '''Общ модул за вътрешните задачи на приложението + + - Предлага предсатва и адмнинистрация на модули, десйтвия, последователности, преводи, планировщици и тн. ''', 'description_de_DE': '''Basismodul für interne Aufgaben der Anwendung @@ -43,6 +48,7 @@ Fournit les concepts et l'administration des modèles, actions, séquences, loca 'trigger.xml', ], 'translation': [ + 'bg_BG.csv', 'fr_FR.csv', 'de_DE.csv', 'es_ES.csv', diff --git a/trytond/ir/cron.py b/trytond/ir/cron.py index 589f5f66d0..1098e23781 100644 --- a/trytond/ir/cron.py +++ b/trytond/ir/cron.py @@ -111,16 +111,16 @@ class Cron(ModelSQL, ModelView): continue tb_s += line try: - tb_s += error.message.decode('utf-8', 'ignore') + tb_s += str(error).decode('utf-8', 'ignore') except Exception: pass request_obj = self.pool.get('res.request') try: user_obj = self.pool.get('res.user') + req_user = user_obj.browse(cron['request_user']) with contextlib.nested(Transaction().set_user(cron['user']), Transaction().set_context(language=req_user.language)): - req_user = user_obj.browse(cron['request_user']) rid = request_obj.create({ 'name': self.raise_user_error('request_title', raise_exception=False), diff --git a/trytond/ir/lang.xml b/trytond/ir/lang.xml index 48cc8ec1a0..253175aa0d 100644 --- a/trytond/ir/lang.xml +++ b/trytond/ir/lang.xml @@ -5,7 +5,14 @@ this repository contains the full copyright notices and license terms. --> - + + bg_BG + Български + %d.%m.%Y + [3, 3, 0] + , + + cs_CZ Czech diff --git a/trytond/ir/time_locale.py b/trytond/ir/time_locale.py index 5b52d13eec..94617fba5f 100644 --- a/trytond/ir/time_locale.py +++ b/trytond/ir/time_locale.py @@ -2,7 +2,48 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of') #this repository contains the full copyright notices and license terms.') TIME_LOCALE = \ -{'cs_CZ': {'%A': [u'Pond\u011bl\xed', +{'bg_BG': {'%A': [u'\u043f\u043e\u043d\u0435\u0434\u0435\u043b\u043d\u0438\u043a', + u'\u0432\u0442\u043e\u0440\u043d\u0438\u043a', + u'\u0441\u0440\u044f\u0434\u0430', + u'\u0447\u0435\u0442\u0432\u044a\u0440\u0442\u044a\u043a', + u'\u043f\u0435\u0442\u044a\u043a', + u'\u0441\u044a\u0431\u043e\u0442\u0430', + u'\u043d\u0435\u0434\u0435\u043b\u044f'], + '%B': [None, + u'\u044f\u043d\u0443\u0430\u0440\u0438', + u'\u0444\u0435\u0432\u0440\u0443\u0430\u0440\u0438', + u'\u043c\u0430\u0440\u0442', + u'\u0430\u043f\u0440\u0438\u043b', + u'\u043c\u0430\u0439', + u'\u044e\u043d\u0438', + u'\u044e\u043b\u0438', + u'\u0430\u0432\u0433\u0443\u0441\u0442', + u'\u0441\u0435\u043f\u0442\u0435\u043c\u0432\u0440\u0438', + u'\u043e\u043a\u0442\u043e\u043c\u0432\u0440\u0438', + u'\u043d\u043e\u0435\u043c\u0432\u0440\u0438', + u'\u0434\u0435\u043a\u0435\u043c\u0432\u0440\u0438'], + '%a': [u'\u043f\u043d', + u'\u0432\u0442', + u'\u0441\u0440', + u'\u0447\u0442', + u'\u043f\u0442', + u'\u0441\u0431', + u'\u043d\u0434'], + '%b': [None, + u'\u044f\u043d\u0443', + u'\u0444\u0435\u0432', + u'\u043c\u0430\u0440', + u'\u0430\u043f\u0440', + u'\u043c\u0430\u0439', + u'\u044e\u043d\u0438', + u'\u044e\u043b\u0438', + u'\u0430\u0432\u0433', + u'\u0441\u0435\u043f', + u'\u043e\u043a\u0442', + u'\u043d\u043e\u0435', + u'\u0434\u0435\u043a'], + '%p': [u'', u'']}, +'cs_CZ': {'%A': [u'Pond\u011bl\xed', u'\xdater\xfd', u'St\u0159eda', u'\u010ctvrtek', diff --git a/trytond/model/browse.py b/trytond/model/browse.py index bc31f2718a..e3ffac4d65 100644 --- a/trytond/model/browse.py +++ b/trytond/model/browse.py @@ -15,7 +15,7 @@ class BrowseRecordList(list): super(BrowseRecordList, self).__init__(lst) def get_eval(self): - return [record.get_eval() for record in self] + return [record.id for record in self] class BrowseRecordNull(object): @@ -145,7 +145,7 @@ class BrowseRecord(object): model = self._model.pool.get(j.model_name) elif hasattr(j, 'get_target'): model = j.get_target(self._model.pool) - if model and j._type in ('many2one',): + if model and j._type in ('many2one', 'one2one'): if (not data[i] and not (isinstance(data[i], (int, long)) and not isinstance(data[i], diff --git a/trytond/model/fields/__init__.py b/trytond/model/fields/__init__.py index b3cc710f40..eb8e16d1e9 100644 --- a/trytond/model/fields/__init__.py +++ b/trytond/model/fields/__init__.py @@ -18,3 +18,4 @@ from one2many import * from many2many import * from function import * from property import * +from one2one import * diff --git a/trytond/model/fields/one2one.py b/trytond/model/fields/one2one.py new file mode 100644 index 0000000000..d3ad640e68 --- /dev/null +++ b/trytond/model/fields/one2one.py @@ -0,0 +1,48 @@ +#This file is part of Tryton. The COPYRIGHT file at the top level of +#this repository contains the full copyright notices and license terms. + +from trytond.model.fields.many2many import Many2Many +from trytond.transaction import Transaction + + +class One2One(Many2Many): + ''' + Define one2one field (``int``). + ''' + _type = 'one2one' + + def get(self, ids, model, name, values=None): + ''' + Return target record. + + :param ids: a list of ids + :param model: a string with the name of the model + :param name: a string with the name of the field + :param values: a dictionary with the read values + :return: a dictionary with ids as key and target id as value + ''' + res = super(One2One, self).get(ids, model, name, values=values) + for i, vals in res.iteritems(): + res[i] = vals[0] if vals else False + return res + + def set(self, ids, model, name, value): + ''' + Set the values. + + :param ids: A list of ids + :param model: A string with the name of the model + :param name: A string with the name of the field + :param value: The id to link + ''' + relation_obj = model.pool.get(self.relation_name) + relation_ids = relation_obj.search([ + (self.origin, 'in', ids), + ]) + relation_obj.delete(relation_ids) + if value: + for record_id in ids: + relation_obj.create({ + self.origin: record_id, + self.target: value, + }) diff --git a/trytond/model/model.py b/trytond/model/model.py index bc1d952061..7eb092d209 100644 --- a/trytond/model/model.py +++ b/trytond/model/model.py @@ -534,7 +534,7 @@ class Model(object): field = self._columns[field_name] else: field = self._inherit_fields[field_name][2] - if field._type in ('many2one',): + if field._type in ('many2one', 'one2one'): if not isinstance(field_value, (int, long)): continue obj = self.pool.get(field.model_name) @@ -740,6 +740,7 @@ class Model(object): 'one2many', 'many2many', 'many2one', + 'one2one', ): if hasattr(self._columns[field], 'model_name'): relation = copy.copy(self._columns[field].model_name) diff --git a/trytond/model/modelsingleton.py b/trytond/model/modelsingleton.py index 1dca66420e..b1aa2200a0 100644 --- a/trytond/model/modelsingleton.py +++ b/trytond/model/modelsingleton.py @@ -29,6 +29,12 @@ class ModelSingleton(ModelStorage): singleton_id = self.get_singleton_id() if not singleton_id: res = self.default_get(fields_names, with_rec_name=False) + if not fields_names: + fields_names = (set(self._columns.keys() + + self._inherit_fields.keys())) + for field_name in fields_names: + if field_name not in res: + res[field_name] = False if not isinstance(ids, (int, long)): res['id'] = ids[0] res = [res] diff --git a/trytond/model/modelsql.py b/trytond/model/modelsql.py index 52aa291676..a3f14bbb4d 100644 --- a/trytond/model/modelsql.py +++ b/trytond/model/modelsql.py @@ -632,8 +632,11 @@ class ModelSQL(ModelStorage): if field not in fields_related.keys(): continue fields_related2values.setdefault(field, {}) - if self._columns[field]._type == 'many2one': - obj = self.pool.get(self._columns[field].model_name) + if self._columns[field]._type in ('many2one', 'one2one'): + if hasattr(self._columns[field], 'model_name'): + obj = self.pool.get(self._columns[field].model_name) + else: + obj = self._columns[field].get_target(self.pool) if hasattr(self._columns[field], 'datetime_field') \ and self._columns[field].datetime_field: for record in res: @@ -678,7 +681,7 @@ class ModelSQL(ModelStorage): if field not in self._columns: continue for related in fields_related[field]: - if self._columns[field]._type == 'many2one': + if self._columns[field]._type in ('many2one', 'one2one'): if record[field]: record[field + '.' + related] = \ fields_related2values[field]\ @@ -1358,18 +1361,37 @@ class ModelSQL(ModelStorage): field = table._columns.get(fargs[0], False) if len(fargs) > 1: if field._type == 'many2one': + target_obj = self.pool.get(field.model_name) if hasattr(field, 'search'): - domain.extend([(fargs[0], 'in', - self.pool.get(field.model_name).search([ - (fargs[1], domain[i][1], domain[i][2]), - ], order=[]))]) + domain.extend([(fargs[0], 'in', target_obj.search([ + (fargs[1], domain[i][1], domain[i][2]), + ], order=[]))]) domain.pop(i) else: - domain[i] = (fargs[0], 'inselect', - self.pool.get(field.model_name).search([ - (fargs[1], domain[i][1], domain[i][2]), - ], order=[], query_string=True), - table) + domain[i] = (fargs[0], 'inselect', target_obj.search([ + (fargs[1], domain[i][1], domain[i][2]), + ], order=[], query_string=True), table) + i += 1 + continue + elif field._type in ('one2one', 'many2many'): + if hasattr(field, 'model_name'): + target_obj = self.pool.get(field.model_name) + else: + target_obj = field.get_target(self.pool) + relation_obj = self.pool.get(field.relation_name) + if hasattr(field, 'search'): + domain.extend([(fargs[0], 'in', target_obj.search([ + (fargs[1], domain[i][1], domain[i][2]), + ], order=[]))]) + domain.pop(i) + else: + query1, query2 = target_obj.search([ + (fargs[1], domain[i][1], domain[i][2]), + ], order=[], query_string=True) + query1 = ('SELECT "%s" FROM "%s" WHERE "%s" IN (%s)' % + (field.origin, relation_obj._table, + field.target, query1)) + domain[i] = ('id', 'inselect', (query1, query2)) i += 1 continue else: @@ -1433,7 +1455,7 @@ class ModelSQL(ModelStorage): domain[i] = ('id', 'in', ids3) i += 1 - elif field._type == 'many2many': + elif field._type in ('many2many', 'one2one'): # XXX must find a solution for long id list if hasattr(field, 'model_name'): target_obj = self.pool.get(field.model_name) diff --git a/trytond/model/modelstorage.py b/trytond/model/modelstorage.py index 6af77286c2..2d52984b11 100644 --- a/trytond/model/modelstorage.py +++ b/trytond/model/modelstorage.py @@ -278,7 +278,7 @@ class ModelStorage(Model): data[field_name] = default[field_name] elif ftype == 'function': del data[field_name] - elif ftype == 'many2one': + elif ftype in ('many2one', 'one2one'): try: data[field_name] = data[field_name] and \ data[field_name][0] @@ -648,6 +648,9 @@ class ModelStorage(Model): res = [('set', res)] return res + def get_one2one(relation, value): + return ('set', get_many2one(relation, value)) + def get_reference(value): if not value: return False @@ -749,6 +752,8 @@ class ModelStorage(Model): res = get_many2one(this_field_def['relation'], value) elif field_type == 'many2many': res = get_many2many(this_field_def['relation'], value) + elif field_type == 'one2one': + res = get_one2one(this_field_def['relation'], value) elif field_type == 'reference': res = get_reference(value) else: @@ -969,8 +974,9 @@ class ModelStorage(Model): not field.setter: continue # validate domain - if field._type in ('many2one', 'many2many', 'one2many') \ - and field.domain: + if (field._type in + ('many2one', 'many2many', 'one2many', 'one2one') + and field.domain): if field._type in ('many2one', 'one2many'): relation_obj = self.pool.get(field.model_name) else: @@ -1095,7 +1101,7 @@ class ModelStorage(Model): for field in defaults.keys(): fld_def = (field in self._columns) and self._columns[field] \ or self._inherit_fields[field][2] - if fld_def._type in ('many2one',): + if fld_def._type in ('many2one', 'one2one'): if isinstance(defaults[field], (list, tuple)): vals[field] = defaults[field][0] else: diff --git a/trytond/modules/__init__.py b/trytond/modules/__init__.py index 82429c5bcd..428d51b12a 100644 --- a/trytond/modules/__init__.py +++ b/trytond/modules/__init__.py @@ -133,7 +133,6 @@ def create_graph(module_list, force=None): force = [] graph = Graph() packages = [] - logger = logging.getLogger('modules') for module in module_list: tryton_file = OPJ(MODULES_PATH, module, '__tryton__.py') @@ -154,15 +153,11 @@ def create_graph(module_list, force=None): tryton_file = OPJ(ep.dist.location, '__tryton__.py') mod_path = os.path.dirname(ep.dist.location) if os.path.isfile(tryton_file) or zipfile.is_zipfile(mod_path+'.zip'): - try: - info = tools.safe_eval(tools.file_open(tryton_file, - subdir='').read()) - except Exception: - logger.error('%s:eval file %s' % (module, tryton_file)) - raise + info = tools.safe_eval(tools.file_open(tryton_file, + subdir='').read()) packages.append((module, info.get('depends', []), info)) elif module != 'all': - logger.error('%s:Module not found!' % (module,)) + raise Exception('Module %s not found' % module) current, later = set([x[0] for x in packages]), set() while packages and current > later: @@ -194,7 +189,7 @@ def create_graph(module_list, force=None): if package not in later: continue missings = [x for x in deps if x not in graph] - logger.error('%s:Unmet dependency %s' % (package, missings)) + raise Exception('%s unmet dependencies: %s' % (package, missings)) return graph, packages, later def load_module_graph(graph, pool, lang=None): diff --git a/trytond/protocols/dispatcher.py b/trytond/protocols/dispatcher.py index 2c9e19214d..d78cadfda8 100644 --- a/trytond/protocols/dispatcher.py +++ b/trytond/protocols/dispatcher.py @@ -51,6 +51,7 @@ def dispatch(host, port, protocol, database_name, user, session, object_type, return time.tzname[0] elif method == 'list_lang': return [ + ('bg_BG', 'Български'), ('cs_CZ', 'Čeština'), ('de_DE', 'Deutsch'), ('en_US', 'English'), diff --git a/trytond/res/__tryton__.py b/trytond/res/__tryton__.py index 61c69fd9f4..da1bbbfc98 100644 --- a/trytond/res/__tryton__.py +++ b/trytond/res/__tryton__.py @@ -2,12 +2,17 @@ #this repository contains the full copyright notices and license terms. { 'name': 'Res', + 'name_bg_BG': 'Ресурс', 'name_de_DE': 'Benutzerverwaltung', 'name_es_ES': 'Gestión de usuarios', 'name_fr_FR': 'Ressource', 'name_ru_RU': 'Базовые ресурсы сервера', 'description': '''Basic module handling internal tasks of the application. Provides concepts and administration of users and internal communication. +''', + 'description_bg_BG': '''Основен модул управляващ вътрешните задачи на приложението + + - Предоставя идеи и администрация на потребители и вътрешна комуникация ''', 'description_de_DE': '''Basismodul für interne Aufgaben der Anwendung @@ -33,6 +38,7 @@ Fournit les concepts et l'administration des utilisateurs et de la communication 'ir.xml', ], 'translation': [ + 'bg_BG.csv', 'fr_FR.csv', 'de_DE.csv', 'es_ES.csv', diff --git a/trytond/res/bg_BG.csv b/trytond/res/bg_BG.csv new file mode 100644 index 0000000000..3bb0be7ccb --- /dev/null +++ b/trytond/res/bg_BG.csv @@ -0,0 +1,170 @@ +type,name,res_id,src,value,fuzzy +error,res.group,0,The name of the group must be unique!,Името на групата трябва да е уникално!,0 +error,res.user,0,Wrong password!,Грешна парола!,0 +error,res.user,0,You can not have two users with the same login!,Не може да имате двама потребители с еднакво потребителско име,0 +error,res.user,0,"You can not remove the root user +as it is used internally for resources +created by the system (updates, module installation, ...)","Не може да премахнете потребител root +тъй като вътрешно се използва за ресурси +създадени от системата (обновяване, инсталиране на модули, ....)",0 +field,"ir.action-res.group,action_id",0,Action,Действие,0 +field,"ir.action-res.group,gid",0,Group,Група,0 +field,"ir.action-res.group,rec_name",0,Name,Име,0 +field,"ir.model.field-res.group,field_id",0,Model Field,Поле от модел,0 +field,"ir.model.field-res.group,group_id",0,Group,Група,0 +field,"ir.model.field-res.group,rec_name",0,Name,Име,0 +field,"ir.rule.group-res.group,group_id",0,Group,Група,0 +field,"ir.rule.group-res.group,rec_name",0,Name,Име,0 +field,"ir.rule.group-res.group,rule_group_id",0,Rule Group,Правила на група,0 +field,"ir.rule.group-res.user,rec_name",0,Name,Име,0 +field,"ir.rule.group-res.user,rule_group_id",0,Rule Group,Правила на група,0 +field,"ir.rule.group-res.user,user_id",0,User,Потребител,0 +field,"ir.sequence,groups",0,User Groups,Групи потребители,0 +field,"ir.sequence.strict,groups",0,User Groups,Групи потребители,0 +field,"ir.sequence.type,groups",0,User Groups,Групи потребители,0 +field,"ir.sequence.type-res.group,group",0,User Groups,Групи потребители,0 +field,"ir.sequence.type-res.group,rec_name",0,Name,Име,0 +field,"ir.sequence.type-res.group,sequence_type",0,Sequence Type,Вид последователност,0 +field,"ir.ui.menu-res.group,gid",0,Group,Група,0 +field,"ir.ui.menu-res.group,menu_id",0,Menu,Меню,0 +field,"ir.ui.menu-res.group,rec_name",0,Name,Име,0 +field,"res.group,menu_access",0,Access Menu,Достъп до меню,0 +field,"res.group,model_access",0,Access Model,Модел на достъпа,0 +field,"res.group,name",0,Name,Име,0 +field,"res.group,rec_name",0,Name,Име,0 +field,"res.group,rule_groups",0,Rules,Правила,0 +field,"res.group,users",0,Users,Потребители,0 +field,"res.request,act_from",0,From,От,0 +field,"res.request,act_to",0,To,До,0 +field,"res.request,active",0,Active,Активен,0 +field,"res.request,body",0,Body,Тяло,0 +field,"res.request,date_sent",0,Date,Дата,0 +field,"res.request,history",0,History,Изтория,0 +field,"res.request,name",0,Subject,Относно,0 +field,"res.request,number_references",0,Number of References,Брой препратки,0 +field,"res.request,priority",0,Priority,Важност,0 +field,"res.request,rec_name",0,Name,Име,0 +field,"res.request,references",0,References,Отпратки,0 +field,"res.request,state",0,State,Състояние,0 +field,"res.request,trigger_date",0,Trigger Date,Дата на активиране,0 +field,"res.request.history,act_from",0,From,От,0 +field,"res.request.history,act_to",0,To,До,0 +field,"res.request.history,body",0,Body,Тяло,0 +field,"res.request.history,date_sent",0,Date sent,Дата на изпращане,0 +field,"res.request.history,name",0,Summary,Обобщено,0 +field,"res.request.history,number_references",0,References,Отпратки,0 +field,"res.request.history,priority",0,Priority,Важност,0 +field,"res.request.history,rec_name",0,Name,Име,0 +field,"res.request.history,request",0,Request,Заявка,0 +field,"res.request.history,state",0,State,Състояние,0 +field,"res.request.history,subject",0,Subject,Относно,0 +field,"res.request.link,model",0,Model,Модел,0 +field,"res.request.link,name",0,Name,Име,0 +field,"res.request.link,priority",0,Priority,Важност,0 +field,"res.request.link,rec_name",0,Name,Име,0 +field,"res.request.reference,rec_name",0,Name,Име,0 +field,"res.request.reference,reference",0,Reference,Отпратка,0 +field,"res.request.reference,request",0,Request,Заявка,0 +field,"res.user,action",0,Home Action,Начално действие,0 +field,"res.user,active",0,Active,Активен,0 +field,"res.user,connections",0,Connections,Връзки,0 +field,"res.user,email",0,Email,Email,0 +field,"res.user,groups",0,Groups,Групи,0 +field,"res.user,language",0,Language,Език,0 +field,"res.user,language_direction",0,Language Direction,Посока на езика,0 +field,"res.user,login",0,Login,Потребителско име,0 +field,"res.user,menu",0,Menu Action,Действие на меню,0 +field,"res.user,name",0,Name,Име,0 +field,"res.user,password",0,Password,Парола,0 +field,"res.user,rec_name",0,Name,Име,0 +field,"res.user,rule_groups",0,Rules,Правила,0 +field,"res.user,salt",0,Salt,Ключ,0 +field,"res.user,signature",0,Signature,Подпис,0 +field,"res.user,status_bar",0,Status Bar,Лента със статус,0 +field,"res.user,timezone",0,Timezone,Времева зона,0 +field,"res.user,warnings",0,Warnings,Предупреждения,0 +field,"res.user-res.group,gid",0,Group,Група,0 +field,"res.user-res.group,rec_name",0,Name,Име,0 +field,"res.user-res.group,uid",0,User,Потребител,0 +field,"res.user.warning,always",0,Always,Винаги,0 +field,"res.user.warning,name",0,Name,Име,0 +field,"res.user.warning,rec_name",0,Name,Име,0 +field,"res.user.warning,user",0,User,Потребител,0 +help,"ir.sequence.type,groups",0,Groups allowed to edit the sequences of this type,Групите на които е позволено да редактират този вид последователност,0 +model,"ir.action,name",act_group_form,Groups,Групи,0 +model,"ir.action,name",act_request_form,Requests,Заявки,0 +model,"ir.action,name",act_request_link_form,Request Links,Връзки към заявка,0 +model,"ir.action,name",act_user_form,Users,Потребители,0 +model,"ir.action-res.group,name",0,Action - Group,Действие - група,0 +model,"ir.cron,name",cron_trigger_time,Run On Time Triggers,Стартиране при настъпване на време,0 +model,"ir.model.field-res.group,name",0,Model Field Group Rel,Връзка на група за модел на поле,0 +model,"ir.rule.group-res.group,name",0,Rule Group - Group,Група правила - Група,0 +model,"ir.rule.group-res.user,name",0,Rule Group - User,Правило на група - Потребител,0 +model,"ir.sequence.type-res.group,name",0,Sequence Type - Group,Вид последователност - Група,0 +model,"ir.ui.menu,name",menu_group_form,Groups,Групи,0 +model,"ir.ui.menu,name",menu_request_form,Requests,Заявки,0 +model,"ir.ui.menu,name",menu_request_link_form,Request Links,Връзки към заявки,0 +model,"ir.ui.menu,name",menu_res,Users,Потребители,0 +model,"ir.ui.menu,name",menu_user_form,Users,Потребители,0 +model,"ir.ui.menu-res.group,name",0,UI Menu - Group,Потребителско меню - група,0 +model,"res.group,name",0,Group,Група,0 +model,"res.group,name",group_admin,Administration,Администрация,0 +model,"res.request,name",0,Request,Заявка,0 +model,"res.request.history,name",0,Request history,Изтория на заявка,0 +model,"res.request.link,name",0,Request link,Връзка към заявка,0 +model,"res.request.link,name",request_link_cron,Scheduler,Планировщик,0 +model,"res.request.reference,name",0,Request Reference,Отпратка към заявка,0 +model,"res.user,name",0,User,Потребител,0 +model,"res.user,name",user_admin,Administrator,Администратор,0 +model,"res.user,name",user_trigger,Cron Trigger,Превключвател на планировщик,0 +model,"res.user-res.group,name",0,User - Group,Потребител - група,0 +model,"res.user.config.init,name",0,User Config Init,Начално конфигуриране на потребител,0 +model,"res.user.warning,name",0,User Warning,Потребителско предупреждение,0 +selection,"res.request,priority",0,High,Висок,0 +selection,"res.request,priority",0,Low,Ниска,0 +selection,"res.request,priority",0,Normal,Нормален,0 +selection,"res.request,state",0,Chatting,Преговори,0 +selection,"res.request,state",0,Closed,Приключен,0 +selection,"res.request,state",0,Draft,Проект,0 +selection,"res.request,state",0,Waiting,Изчакващ,0 +selection,"res.request.history,priority",0,High,Висок,0 +selection,"res.request.history,priority",0,Low,Ниска,0 +selection,"res.request.history,priority",0,Normal,Нормален,0 +selection,"res.request.history,state",0,Chatting,Преговори,0 +selection,"res.request.history,state",0,Closed,Приключен,0 +selection,"res.request.history,state",0,Draft,Проект,0 +selection,"res.request.history,state",0,Waiting,Изчакващ,0 +view,ir.module.module,0,Cancel Installation,Отказ от инсталалиране,0 +view,ir.module.module,0,Cancel Uninstallation,Отказ от деинсталиране,0 +view,ir.module.module,0,Cancel Upgrade,Отказ от обновяване,0 +view,ir.module.module,0,Mark for Installation,Отбелязване за инсталиране,0 +view,ir.module.module,0,Mark for Uninstallation (beta),Маркиране за деинсталиране (beta),0 +view,ir.module.module,0,Mark for Upgrade,Отбелязване за обновяване,0 +view,res.group,0,Access Permissions,Фактура,0 +view,res.group,0,Group,Група,0 +view,res.group,0,Groups,Групи,0 +view,res.group,0,Members,Членове,0 +view,res.request,0,Close,Затваряне,0 +view,res.request,0,References,Отпратки,0 +view,res.request,0,Reply,Отговор,0 +view,res.request,0,Request,Заявка,0 +view,res.request,0,Requests,Заявки,0 +view,res.request,0,Send,Изпратен,0 +view,res.request.history,0,Request History,Истоярия на заявка,0 +view,res.request.link,0,Request Link,Връзка към заявка,0 +view,res.request.reference,0,Request Reference,Отпратка към заявка,0 +view,res.request.reference,0,Request References,Отпратки към заявка,0 +view,res.user,0,Access Permissions,Права за достъп,0 +view,res.user,0,Group Membership,Членство към група,0 +view,res.user,0,Preferences,Предпочитания,0 +view,res.user,0,User,Потребител,0 +view,res.user,0,Users,Потребители,0 +view,res.user.config.init,0,Add Users,Добавяне на потребители,0 +view,res.user.config.init,0,Be careful that the login must be unique!,Вмимавайте потребителско има да е уникално,0 +view,res.user.config.init,0,You can now add some users into the system.,Сега може да добавите потребители към системата.,0 +view,res.user.warning,0,Warning,Предупреждение,0 +view,res.user.warning,0,Warnings,Предупреждения,0 +wizard_button,"res.user.config,init,end",0,Cancel,Отказ,0 +wizard_button,"res.user.config,init,user",0,Ok,Добре,0 +wizard_button,"res.user.config,user,add",0,Add,Добавяне,0 +wizard_button,"res.user.config,user,end",0,End,Край,0 diff --git a/trytond/test/test.py b/trytond/test/test.py index be7f8c82be..b4577b431a 100644 --- a/trytond/test/test.py +++ b/trytond/test/test.py @@ -354,3 +354,72 @@ class DateTimeRequired(ModelSQL): required=True) DateTimeRequired() + + +class One2One(ModelSQL): + 'One2One' + _name = 'test.one2one' + _description = __doc__ + name = fields.Char('Name', required=True) + one2one = fields.One2One('test.one2one.relation', 'origin', 'target', + string='One2One', help='Test one2one', required=False) + +One2One() + + +class One2OneTarget(ModelSQL): + 'One2One Target' + _name = 'test.one2one.target' + name = fields.Char('Name', required=True) + +One2OneTarget() + + +class One2OneRelation(ModelSQL): + 'One2One Relation' + _name = 'test.one2one.relation' + _description = __doc__ + origin = fields.Many2One('test.one2one', 'Origin') + target = fields.Many2One('test.one2one.target', 'Target') + + def __init__(self): + super(One2OneRelation, self).__init__() + self._sql_constraints += [ + ('origin_unique', 'UNIQUE(origin)', + 'Origin must be unique'), + ('target_unique', 'UNIQUE(target)', + 'Target must be unique'), + ] + +One2OneRelation() + + +class One2OneRequired(ModelSQL): + 'One2One' + _name = 'test.one2one_required' + _description = __doc__ + name = fields.Char('Name', required=True) + one2one = fields.One2One('test.one2one_required.relation', 'origin', 'target', + string='One2One', help='Test one2one', required=True) + +One2OneRequired() + + +class One2OneRequiredRelation(ModelSQL): + 'One2One Relation' + _name = 'test.one2one_required.relation' + _description = __doc__ + origin = fields.Many2One('test.one2one_required', 'Origin') + target = fields.Many2One('test.one2one.target', 'Target') + + def __init__(self): + super(One2OneRequiredRelation, self).__init__() + self._sql_constraints += [ + ('origin_unique', 'UNIQUE(origin)', + 'Origin must be unique'), + ('target_unique', 'UNIQUE(target)', + 'Target must be unique'), + ] + +One2OneRequiredRelation() + diff --git a/trytond/tests/test_fields.py b/trytond/tests/test_fields.py index 0976295f0c..a5724bd936 100644 --- a/trytond/tests/test_fields.py +++ b/trytond/tests/test_fields.py @@ -60,6 +60,10 @@ class FieldsTestCase(unittest.TestCase): self.datetime_default = POOL.get('test.datetime_default') self.datetime_required = POOL.get('test.datetime_required') + self.one2one = POOL.get('test.one2one') + self.one2one_target = POOL.get('test.one2one.target') + self.one2one_required = POOL.get('test.one2one_required') + def test0010boolean(self): ''' Test Boolean. @@ -2072,6 +2076,130 @@ class FieldsTestCase(unittest.TestCase): transaction.cursor.rollback() + def test0100one2one(self): + ''' + Test One2One. + ''' + with Transaction().start(DB_NAME, USER, CONTEXT) as transaction: + target1_id = self.one2one_target.create({ + 'name': 'target1', + }) + one2one1_id = self.one2one.create({ + 'name': 'origin1', + 'one2one': target1_id, + }) + self.assert_(one2one1_id) + + one2one1 = self.one2one.read(one2one1_id, ['one2one', + 'one2one.name']) + self.assert_(one2one1['one2one'] == target1_id) + self.assert_(one2one1['one2one.name'] == 'target1') + + one2one_ids = self.one2one.search([ + ('one2one', '=', 'target1'), + ]) + self.assert_(one2one_ids == [one2one1_id]) + + one2one_ids = self.one2one.search([ + ('one2one', '!=', 'target1'), + ]) + self.assert_(one2one_ids == []) + + one2one_ids = self.one2one.search([ + ('one2one', 'in', [target1_id]), + ]) + self.assert_(one2one_ids == [one2one1_id]) + + one2one_ids = self.one2one.search([ + ('one2one', 'in', [0]), + ]) + self.assert_(one2one_ids == []) + + one2one_ids = self.one2one.search([ + ('one2one', 'not in', [target1_id]), + ]) + self.assert_(one2one_ids == []) + + one2one_ids = self.one2one.search([ + ('one2one', 'not in', [0]), + ]) + self.assert_(one2one_ids == [one2one1_id]) + + one2one_ids = self.one2one.search([ + ('one2one.name', '=', 'target1'), + ]) + self.assert_(one2one_ids == [one2one1_id]) + + one2one_ids = self.one2one.search([ + ('one2one.name', '!=', 'target1'), + ]) + self.assert_(one2one_ids == []) + + one2one = self.one2one.browse(one2one1_id) + self.assert_(one2one.one2one.name == 'target1') + + one2one2_id = self.one2one.create({ + 'name': 'origin2', + }) + self.assert_(one2one2_id) + + one2one2 = self.one2one.read(one2one2_id, ['one2one']) + self.assert_(one2one2['one2one'] == False) + + one2one_ids = self.one2one.search([ + ('one2one', '=', False), + ]) + self.assert_(one2one_ids == [one2one2_id]) + + target2_id = self.one2one_target.create({ + 'name': 'target2', + }) + self.one2one.write(one2one2_id, { + 'one2one': target2_id, + }) + target2_id = self.one2one_target.search([ + ('name', '=', 'target2'), + ])[0] + one2one2 = self.one2one.read(one2one2_id, ['one2one']) + self.assert_(one2one2['one2one'] == target2_id) + + self.one2one.write(one2one2_id, { + 'one2one': False, + }) + one2one2 = self.one2one.read(one2one2_id, ['one2one']) + self.assert_(one2one2['one2one'] == False) + + one2one2 = self.one2one.browse(one2one2_id) + self.assert_(not one2one2.one2one) + + self.failUnlessRaises(Exception, self.one2one.create, { + 'name': 'one2one3', + 'one2one': target1_id, + }) + transaction.cursor.rollback() + + self.failUnlessRaises(Exception, self.one2one.write, one2one2_id, { + 'one2one': target1_id, + }) + transaction.cursor.rollback() + + self.failUnlessRaises(Exception, self.one2one_required.create, { + 'name': 'one2one3', + }) + transaction.cursor.rollback() + + target3_id = self.one2one_target.create({ + 'name': 'target3_id', + }) + + one2one3_id = self.one2one_required.create({ + 'name': 'one2one3', + 'one2one': target3_id, + }) + self.assert_(one2one3_id) + + transaction.cursor.rollback() + def suite(): return unittest.TestLoader().loadTestsFromTestCase(FieldsTestCase) diff --git a/trytond/tests/test_modelsingleton.py b/trytond/tests/test_modelsingleton.py index 1417739a1b..b94a8f19d6 100644 --- a/trytond/tests/test_modelsingleton.py +++ b/trytond/tests/test_modelsingleton.py @@ -4,6 +4,7 @@ #this repository contains the full copyright notices and license terms. from __future__ import with_statement import unittest +from datetime import datetime from trytond.tests.test_tryton import POOL, DB_NAME, USER, CONTEXT, \ install_module from trytond.transaction import Transaction @@ -27,10 +28,21 @@ class ModelSingletonTestCase(unittest.TestCase): self.assert_(singleton['name'] == 'test') self.assert_(singleton['id'] == 1) - singleton = self.singleton.read([1], ['name'])[0] + singleton, = self.singleton.read([1], ['name']) self.assert_(singleton['name'] == 'test') self.assert_(singleton['id'] == 1) + singleton = self.singleton.read(1, [ + 'create_uid', + 'create_date', + 'write_uid', + 'write_date', + ]) + self.assertEqual(singleton['create_uid'], USER) + self.assert_(isinstance(singleton['create_date'], datetime)) + self.assertEqual(singleton['write_uid'], False) + self.assertEqual(singleton['write_date'], False) + transaction.cursor.rollback() def test0020create(self): diff --git a/trytond/tests/test_tryton.py b/trytond/tests/test_tryton.py index ea334b4481..c8903cf7a7 100644 --- a/trytond/tests/test_tryton.py +++ b/trytond/tests/test_tryton.py @@ -12,6 +12,7 @@ if os.path.isdir(DIR): sys.path.insert(0, os.path.dirname(DIR)) import unittest +import doctest from lxml import etree import time import imp @@ -227,7 +228,15 @@ def modules_suite(): break if not found: suite_.addTest(test) - return suite_ + tests = [] + doc_tests = [] + for test in suite_: + if isinstance(test, doctest.DocTestCase): + doc_tests.append(test) + else: + tests.append(test) + tests.extend(doc_tests) + return unittest.TestSuite(tests) if __name__ == '__main__': if not _MODULES: diff --git a/trytond/webdav/__tryton__.py b/trytond/webdav/__tryton__.py index a7b99b698b..b9abe4f378 100644 --- a/trytond/webdav/__tryton__.py +++ b/trytond/webdav/__tryton__.py @@ -2,11 +2,14 @@ #this repository contains the full copyright notices and license terms. { 'name': 'WebDAV', + 'name_bg_BG': 'WebDAV', 'name_de_DE': 'WebDAV', 'name_es_ES': 'WebDAV', 'name_fr_FR': 'WebDAV', 'name_ru_RU': 'WebDAV', 'description': '''Basic module providing concept and administration of integrated WebDAV features. +''', + 'description_bg_BG': '''Общ модул предоставящ представа и администрация на интегрирани WebDAV услуги. ''', 'description_de_DE': '''Basismodul für Konzept und Administration der integrierten WebDAV-Komponente ''', @@ -22,6 +25,7 @@ 'webdav.xml', ], 'translation': [ + 'bg_BG.csv', 'fr_FR.csv', 'de_DE.csv', 'es_ES.csv', diff --git a/trytond/webdav/bg_BG.csv b/trytond/webdav/bg_BG.csv new file mode 100644 index 0000000000..08f8a0ea35 --- /dev/null +++ b/trytond/webdav/bg_BG.csv @@ -0,0 +1,28 @@ +type,name,res_id,src,value,fuzzy +error,ir.attachment,0,"You can not create an attachment +in a collection with the name +of an existing child collection!","Не може да създадете +прикачен файл в колекция +с име като на съществуваща +подчинена колекция!",0 +error,webdav.collection,0,The collection name must be unique inside a collection!,Името на колекцията трябва да уникално вътре в колекцията!,0 +error,webdav.collection,0,"You can not create a collection +in a collection with the name of an existing file!","Не може да създадете колекция +в колекция с име на съществуващ файл!",0 +error,webdav.collection,0,You can not create recursive collections!,Не може да създавате взаимно вложени колекции!,0 +field,"webdav.collection,childs",0,Children,Деца,0 +field,"webdav.collection,domain",0,Domain,Домейн,0 +field,"webdav.collection,model",0,Model,Модел,0 +field,"webdav.collection,name",0,Name,Име,0 +field,"webdav.collection,parent",0,Parent,Родител,0 +field,"webdav.collection,rec_name",0,Name,Име,0 +model,"ir.action,name",act_collection_form,Collections,Колекции,0 +model,"ir.action,name",act_collection_form2,Collections,Колекции,0 +model,"ir.action,name",act_collection_tree,Collections,Колекции,0 +model,"ir.ui.menu,name",menu_collection_form,Edit Collections,Редактиране на колекции,0 +model,"ir.ui.menu,name",menu_collection_form2,New Collection,Нова колекция,0 +model,"ir.ui.menu,name",menu_collection_tree,Collections,Колекции,0 +model,"ir.ui.menu,name",menu_webdav,WebDAV,WebDAV,0 +model,"webdav.collection,name",0,Collection,Колекция,0 +view,webdav.collection,0,Collection,Колекция,0 +view,webdav.collection,0,Collections,Колекции,0 diff --git a/trytond/workflow/__tryton__.py b/trytond/workflow/__tryton__.py index fb37597eaf..8420f12ab6 100644 --- a/trytond/workflow/__tryton__.py +++ b/trytond/workflow/__tryton__.py @@ -2,11 +2,14 @@ #this repository contains the full copyright notices and license terms. { 'name': 'Workflow', + 'name_bg_BG': 'Работен процес', 'name_de_DE': 'Workflow', 'name_es_ES': 'Flujo de trabajo', 'name_fr_FR': 'Workflow', 'name_ru_RU': 'Бизнес процессы', 'description': '''Basic module providing concept and administration of workflows. +''', + 'description_bg_BG': '''Основен модул даващ идея и администрация на работни процеси. ''', 'description_de_DE': '''Basismodul für Konzept und Administration von Workflows ''', @@ -22,6 +25,7 @@ 'workflow.xml', ], 'translation': [ + 'bg_BG.csv', 'fr_FR.csv', 'de_DE.csv', 'es_ES.csv', diff --git a/trytond/workflow/bg_BG.csv b/trytond/workflow/bg_BG.csv new file mode 100644 index 0000000000..f6c334fb82 --- /dev/null +++ b/trytond/workflow/bg_BG.csv @@ -0,0 +1,89 @@ +type,name,res_id,src,value,fuzzy +error,workflow,0,No workflow defined!,Не е зададен работен процес!,0 +error,workflow.instance,0,No workflow instance defined!,Не е зададен екземпляр на работен процес!,0 +field,"workflow,activities",0,Activities,Дейности,0 +field,"workflow,model",0,Resource Model,Модел на ресурс,0 +field,"workflow,name",0,Name,Име,0 +field,"workflow,on_create",0,On Create,При създаване,0 +field,"workflow,rec_name",0,Name,Име,0 +field,"workflow.activity,action",0,Action,Действие,0 +field,"workflow.activity,flow_start",0,Flow Start,Стартиране на процес,0 +field,"workflow.activity,flow_stop",0,Flow Stop,Спиране на процес,0 +field,"workflow.activity,in_transitions",0,Incoming transitions,Входящи преходи,0 +field,"workflow.activity,join_mode",0,Join Mode,Режим на свързаване,0 +field,"workflow.activity,kind",0,Kind,Вид,0 +field,"workflow.activity,name",0,Name,Име,0 +field,"workflow.activity,out_transitions",0,Outgoing transitions,Изходящи преходи,0 +field,"workflow.activity,rec_name",0,Name,Име,0 +field,"workflow.activity,signal_send",0,Signal (subflow.*),Сигнал (subflow.*),0 +field,"workflow.activity,split_mode",0,Split Mode,Режим на разделяне,0 +field,"workflow.activity,subflow",0,Subflow,Подпоток,0 +field,"workflow.activity,workflow",0,Workflow,Работен процес,0 +field,"workflow.instance,overflows",0,Overflow,Препълване,0 +field,"workflow.instance,rec_name",0,Name,Име,0 +field,"workflow.instance,res_id",0,Resource ID,ID на ресурс,0 +field,"workflow.instance,res_type",0,Resource Model,Модел на ресурс,0 +field,"workflow.instance,state",0,State,Състояние,0 +field,"workflow.instance,transitions",0,Transitions,Преходи,0 +field,"workflow.instance,uid",0,User ID,Потребителско ID,0 +field,"workflow.instance,workflow",0,Workflow,Работен процес,0 +field,"workflow.instance,workitems",0,Workitems,Задачи,0 +field,"workflow.transition,act_from",0,Source Activity,Източник на действие,0 +field,"workflow.transition,act_to",0,Destination Activity,Активност на местонахождение-цел,0 +field,"workflow.transition,condition",0,Condition,Условие,0 +field,"workflow.transition,group",0,Group Required,Групата е задължителна,0 +field,"workflow.transition,instances",0,Instances,Екземпляри,0 +field,"workflow.transition,rec_name",0,Name,Име,0 +field,"workflow.transition,signal",0,Signal (button Name),Сигнал (име на бутон),0 +field,"workflow.transition,trigger_expr_id",0,Trigger Expr ID,ID на израз на задействащ механизъм,0 +field,"workflow.transition,trigger_model",0,Trigger Type,Вид превключвател,0 +field,"workflow.transition-workflow.instance,inst_id",0,Instance,Екземпляр,0 +field,"workflow.transition-workflow.instance,rec_name",0,Name,Име,0 +field,"workflow.transition-workflow.instance,trans_id",0,Transition,Преход,0 +field,"workflow.trigger,instance",0,Destination Instance,Ексемплят на местонахождение-цел,0 +field,"workflow.trigger,model",0,Model,Модел,0 +field,"workflow.trigger,rec_name",0,Name,Име,0 +field,"workflow.trigger,res_id",0,Resource ID,ID на ресурс,0 +field,"workflow.trigger,workitem",0,Workitem,Задача,0 +field,"workflow.workitem,activity",0,Activity,Дейност,0 +field,"workflow.workitem,instance",0,Instance,Екземпляр,0 +field,"workflow.workitem,rec_name",0,Name,Име,0 +field,"workflow.workitem,state",0,State,Състояние,0 +field,"workflow.workitem,subflow",0,Subflow,Подпоток,0 +model,"ir.action,name",act_activity_form,Activities,Дейности,0 +model,"ir.action,name",act_instance_form,Instances,Екземпляри,0 +model,"ir.action,name",act_transition_form,Transitions,Преходи,0 +model,"ir.action,name",act_workflow_form,Workflows,Работни процеси,0 +model,"ir.action,name",act_workitem_form,Workitems,Задачи,0 +model,"ir.ui.menu,name",menu_activity_form,Activities,Дейности,0 +model,"ir.ui.menu,name",menu_instance_form,Instances,Екземпляри,0 +model,"ir.ui.menu,name",menu_transition_form,Transitions,Преходи,0 +model,"ir.ui.menu,name",menu_workflow,Workflow,Работен процес,0 +model,"ir.ui.menu,name",menu_workflow_form,Workflows,Работни процеси,0 +model,"ir.ui.menu,name",menu_workitem_form,Workitems,Задачи,0 +model,"workflow,name",0,Workflow,Работен процес,0 +model,"workflow.activity,name",0,Workflow activity,Дейности,0 +model,"workflow.instance,name",0,Workflow instance,Екземплят на работен процес,0 +model,"workflow.transition,name",0,Workflow transition,Преход на работен процес,0 +model,"workflow.transition-workflow.instance,name",0,Workflow Transition - Instance,Преход на работен процес - екземпляр,0 +model,"workflow.trigger,name",0,Workflow trigger,Задействане на работен процес,0 +model,"workflow.workitem,name",0,Workflow workitem,Задачи от работен процес,0 +selection,"workflow.activity,join_mode",0,And,и,0 +selection,"workflow.activity,join_mode",0,Xor,Xor,0 +selection,"workflow.activity,kind",0,Dummy,Фиктивен,0 +selection,"workflow.activity,kind",0,Function,Функция,0 +selection,"workflow.activity,kind",0,Stop All,Спиране на всички,0 +selection,"workflow.activity,kind",0,Subflow,Подпоток,0 +selection,"workflow.activity,split_mode",0,And,и,0 +selection,"workflow.activity,split_mode",0,Or,Или,0 +selection,"workflow.activity,split_mode",0,Xor,Xor,0 +view,workflow,0,Workflow,Работен процес,0 +view,workflow.activity,0,Activity,Дейност,0 +view,workflow.activity,0,Transition,Преход,0 +view,workflow.activity,0,Transitions,Преходи,0 +view,workflow.instance,0,Instance,Екземпляр,0 +view,workflow.instance,0,Instances,Екземпляри,0 +view,workflow.transition,0,Transition,Преход,0 +view,workflow.transition,0,Transitions,Преходи,0 +view,workflow.workitem,0,Workitem,Задача,0 +view,workflow.workitem,0,Workitems,Задачи,0