From 7e5feece185205a187d57539d97d5b0279cf4c68 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 1 Mar 2012 14:00:08 +0100 Subject: [PATCH 01/12] [ADD] eval tests for eval_domain_and_context bzr revid: xmo@openerp.com-20120301130008-dijd6r49js1227by --- addons/web/static/src/js/core.js | 105 +++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index fe2ca1beb4f..679b193e121 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -321,6 +321,108 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. this.active_id = null; return this.session_init(); }, + test_eval: function (source, expected) { + try { + var ctx = this.test_eval_contexts(source.contexts); + if (!_.isEqual(ctx, expected.context)) { + console.group('Local context does not match remote'); + console.warn('source', source.contexts); + console.warn('local', ctx); + console.warn('remote', expected.context); + console.groupEnd(); + } + } catch (e) { + console.error( + 'Failed to evaluate contexts', source.contexts, ':', e); + } + + try { + var dom = this.test_eval_domains(source.domains); + if (!_.isEqual(dom, expected.domain)) { + console.group('Local domain does not match remote'); + console.warn('source', source.domains); + console.warn('local', dom); + console.warn('remote', expected.domain); + console.groupEnd(); + } + } catch (e) { + console.error( + 'Failed to evaluate domains', source.domains, ':', e); + } + + try { + var groups = this.test_eval_groupby(source.group_by_seq); + if (!_.isEqual(groups, expected.group_by)) { + console.group('Local groupby does not match remote'); + console.warn('source', source.group_by_seq); + console.warn('local', groups); + console.warn('remote', expected.group_by); + console.groupEnd(); + } + } catch (e) {} + }, + test_eval_contexts: function (contexts) { + var result_context = _.extend({}, this.user_context), + self = this; + _(contexts).each(function (ctx) { + switch(ctx.__ref) { + case 'context': + _.extend(py.eval(ctx.__debug)); + break; + case 'compound_context': + _.extend(self.test_eval_contexts(ctx.__contexts)); + break; + default: + _.extend(result_context, ctx); + } + }); + return result_context; + }, + test_eval_domains: function (domains) { + var result_domain = [], self = this; + _(domains).each(function (dom) { + switch(dom.__ref) { + case 'domain': + result_domain.push.apply( + result_domain, py.eval(dom.__debug)); + break; + case 'compound_domain': + result_domain.push.apply( + result_domain, self.test_eval_contexts(dom.__contexts)); + break; + default: + result_domain.push.apply( + result_domain, dom); + } + }); + return result_domain; + }, + test_eval_groupby: function (contexts) { + var result_group = [], self = this; + _(contexts).each(function (ctx) { + var group; + switch(ctx.__ref) { + case 'context': + group = py.eval(ctx.__debug).group_by; + break; + case 'compound_context': + group = self.test_eval_contexts(ctx.__contexts).group_by; + break; + default: + group = ctx.group_by + } + if (!group) { return; } + if (typeof group === 'string') { + result_group.push(group); + } else if (group instanceof Array) { + result_group.push.apply(result_group, group); + } else { + throw new Error('Got invalid groupby {{' + + JSON.stringify(group) + '}}'); + } + }); + return result_group; + }, /** * Executes an RPC call, registering the provided callbacks. * @@ -358,6 +460,9 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. function (response, textStatus, jqXHR) { self.on_rpc_response(); if (!response.error) { + if (url.url === '/web/session/eval_domain_and_context') { + self.test_eval(params, response.result); + } deferred.resolve(response["result"], textStatus, jqXHR); } else if (response.error.data.type === "session_invalid") { self.uid = false; From 12358af73bc0df054881258d690c91536e92b368 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 1 Mar 2012 14:59:00 +0100 Subject: [PATCH 02/12] [IMP] add basic evaluation context to asserter (for domains, so time and datetime are available). Doesn't really work because kwargs not supported by py.js (see bug #3), so Sales > Reporting > Opportunities analysis does not work Also current implementation of datetime and time.strftime very incomplete bzr revid: xmo@openerp.com-20120301135900-w4aucd5boi9yzmod --- addons/web/static/src/js/core.js | 50 +++++++++++++++++++++++++++++--- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 679b193e121..ca53bce9c59 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -321,6 +321,47 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. this.active_id = null; return this.session_init(); }, + test_eval_get_context: function () { + var datetime = new py.object(); + var date = datetime.date = new py.type(function date(y, m, d) { + this._year = y; + this._month = m; + this._day = d; + }, py.object, { + strftime: function (format) { + var f = format.toJSON(), self = this; + return new py.str(f.replace(/%([A-Za-z])/g, function (m, c) { + switch (c) { + case 'Y': return self._year; + case 'm': return _.str.sprintf('%02d', self._month); + case 'd': return _.str.sprintf('%02d', self._day); + } + throw new Error('ValueError: No known conversion for ' + m); + })); + } + }); + date.__getattribute__ = function (name) { + if (name === 'today') { + return date.today; + } + throw new Error("AttributeError: object 'date' has no attribute '" + name +"'"); + }; + date.today = new py.def(function () { + var d = new Date(); + return new date(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate()); + }); + + var time = new py.object(); + time.strftime = new py.def(function (format) { + return date.today.__call__().strftime(format); + }); + + return { + uid: new py.float(this.uid), + datetime: datetime, + time: time + }; + }, test_eval: function (source, expected) { try { var ctx = this.test_eval_contexts(source.contexts); @@ -337,7 +378,7 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. } try { - var dom = this.test_eval_domains(source.domains); + var dom = this.test_eval_domains(source.domains, this.test_eval_get_context()); if (!_.isEqual(dom, expected.domain)) { console.group('Local domain does not match remote'); console.warn('source', source.domains); @@ -378,17 +419,18 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. }); return result_context; }, - test_eval_domains: function (domains) { + test_eval_domains: function (domains, eval_context) { var result_domain = [], self = this; _(domains).each(function (dom) { switch(dom.__ref) { case 'domain': result_domain.push.apply( - result_domain, py.eval(dom.__debug)); + result_domain, py.eval(dom.__debug, eval_context)); break; case 'compound_domain': result_domain.push.apply( - result_domain, self.test_eval_contexts(dom.__contexts)); + result_domain, self.test_eval_domains( + dom.__domains, eval_context)); break; default: result_domain.push.apply( From f8a93bc2848eba516213142f4f730a1feda5a39e Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Mon, 5 Mar 2012 09:55:32 +0100 Subject: [PATCH 03/12] [FIX] update py.js for operators &al, implement basic crummy version of relativedelta bzr revid: xmo@openerp.com-20120305085532-0vcz6j0m985gjuz1 --- addons/web/static/lib/py.js/.hg_archival.txt | 6 +- addons/web/static/lib/py.js/README.rst | 18 +- addons/web/static/lib/py.js/TODO.rst | 19 +- addons/web/static/lib/py.js/lib/py.js | 196 +++++++++++++++---- addons/web/static/lib/py.js/test/parser.js | 31 +++ addons/web/static/lib/py.js/test/test.js | 67 ++++++- addons/web/static/src/js/core.js | 128 ++++++++++-- 7 files changed, 401 insertions(+), 64 deletions(-) diff --git a/addons/web/static/lib/py.js/.hg_archival.txt b/addons/web/static/lib/py.js/.hg_archival.txt index d10d8a6b1cc..f5355e28a12 100644 --- a/addons/web/static/lib/py.js/.hg_archival.txt +++ b/addons/web/static/lib/py.js/.hg_archival.txt @@ -1,5 +1,5 @@ repo: 076b192d0d8ab2b92d1dbcfa3da055382f30eaea -node: 7be96c381d9302b4aafaf5eb5381083b0731a9aa +node: 5adb2d9c89e53a6445e3799f9c4dc9110458c149 branch: default -latesttag: 0.4 -latesttagdistance: 7 +latesttag: 0.5 +latesttagdistance: 9 diff --git a/addons/web/static/lib/py.js/README.rst b/addons/web/static/lib/py.js/README.rst index a5aaa289a46..046db7153d1 100644 --- a/addons/web/static/lib/py.js/README.rst +++ b/addons/web/static/lib/py.js/README.rst @@ -10,6 +10,17 @@ specification document is the `Python 2.7 Expressions spec `_ (along with the lexical analysis part). +Syntax +------ + +* Lambdas and ternaries should be parsed but are not implemented (in + the evaluator) +* Only floats are implemented, ``int`` literals are parsed as floats. +* Octal and hexadecimal literals are not implemented +* Srings are backed by JavaScript strings and probably behave like + ``unicode`` more than like ``str`` +* Slices don't work + Builtins -------- @@ -89,9 +100,10 @@ Collections Abstract Base Classes Hashable are kind-of implemented as well) Numeric type emulation - Basically not implemented, the only part of it which is - implemented is the unary ``-`` (because it's used to create - negative floats, they're parsed as a negated positive number) + Operators are implemented (but not tested), ``abs``, ``divmod`` + and ``pow`` builtins are not implemented yet. Neither are ``oct`` + and ``hex`` but I'm not sure we care (I'm not sure we care about + ``pow`` or even ``divmod`` either, for that matter) Utilities --------- diff --git a/addons/web/static/lib/py.js/TODO.rst b/addons/web/static/lib/py.js/TODO.rst index f80520944d5..a1309bd3fc5 100644 --- a/addons/web/static/lib/py.js/TODO.rst +++ b/addons/web/static/lib/py.js/TODO.rst @@ -34,15 +34,14 @@ Base methods requirement ************************ * ``__getattr__`` -* ? ``__getitem`` -* ``__call__`` -* ``or`` -* ``toJS`` / ``toJSON`` * ``dict.get`` -* ``datetime.time.today`` -* ``datetime.time.strftime`` -* ``time.strftime`` -* ``__add__`` / ``__radd__`` -* ``__sub__`` / ``__rsub__`` * ``__len__`` -* ``__nonzero__`` + +In datamodel, not implemented in any type, untested +*************************************************** + +* a[b] + +* a + b, a - b, a * b, ... + +* +a, ~a diff --git a/addons/web/static/lib/py.js/lib/py.js b/addons/web/static/lib/py.js/lib/py.js index ec6b9182543..3c0b13d0717 100644 --- a/addons/web/static/lib/py.js/lib/py.js +++ b/addons/web/static/lib/py.js/lib/py.js @@ -104,6 +104,15 @@ var py = {}; symbol(':'); symbol(')'); symbol(']'); symbol('}'); symbol(','); symbol('else'); + infix('=', 10, function (left) { + if (left.id !== '(name)') { + throw new Error("Expected keyword argument name, got " + token.id); + } + this.first = left; + this.second = expression(); + return this; + }); + symbol('lambda', 20).nud = function () { this.first = []; if (token.id !== ':') { @@ -362,7 +371,7 @@ var py = {}; if (val instanceof py.object || val === py.object - || py.issubclass.__call__(val, py.object) === py.True) { + || py.issubclass.__call__([val, py.object]) === py.True) { return val; } @@ -443,7 +452,7 @@ var py = {}; // TODO: second argument should be class return val.__get__(this); } - if (typeof val === 'function' && !this.hasOwnProperty(val)) { + if (typeof val === 'function' && !this.hasOwnProperty(name)) { // val is a method from the class return new PY_instancemethod(val, this); } @@ -476,6 +485,7 @@ var py = {}; py.NotImplemented = new NotImplementedType(); var booleans_initialized = false; py.bool = py.type(function bool(value) { + value = (value instanceof Array) ? value[0] : value; // The only actual instance of py.bool should be py.True // and py.False. Return the new instance of py.bool if we // are initializing py.True and py.False, otherwise always @@ -493,7 +503,26 @@ var py = {}; py.False = new py.bool(); booleans_initialized = true; py.float = py.type(function float(value) { - this._value = value; + value = (value instanceof Array) ? value[0] : value; + if (value === undefined) { this._value = 0; return; } + if (value instanceof py.float) { return value; } + if (typeof value === 'number' || value instanceof Number) { + this._value = value; + return; + } + if (typeof value === 'string' || value instanceof String) { + this._value = parseFloat(value); + return; + } + if (value instanceof py.object && '__float__' in value) { + var res = value.__float__(); + if (res instanceof py.float) { + return res; + } + throw new Error('TypeError: __float__ returned non-float (type ' + + res.constructor.name + ')'); + } + throw new Error('TypeError: float() argument must be a string or a number'); }, py.object, { __eq__: function (other) { return this._value === other._value ? py.True : py.False; @@ -514,9 +543,25 @@ var py = {}; if (!(other instanceof py.float)) { return py.NotImplemented; } return this._value >= other._value ? py.True : py.False; }, + __add__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } + return new py.float(this._value + other._value); + }, __neg__: function () { return new py.float(-this._value); }, + __sub__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } + return new py.float(this._value - other._value); + }, + __mul__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } + return new py.float(this._value * other._value); + }, + __div__: function (other) { + if (!(other instanceof py.float)) { return py.NotImplemented; } + return new py.float(this._value / other._value); + }, __nonzero__: function () { return this._value ? py.True : py.False; }, @@ -525,7 +570,17 @@ var py = {}; } }); py.str = py.type(function str(s) { - this._value = s; + s = (s instanceof Array) ? s[0] : s; + if (s === undefined) { this._value = ''; return; } + if (s instanceof py.str) { return s; } + if (typeof s === 'string' || s instanceof String) { + this._value = s; + return; + } + var v = s.__str__(); + if (v instanceof py.str) { return v; } + throw new Error('TypeError: __str__ returned non-string (type ' + + v.constructor.name + ')'); }, py.object, { __eq__: function (other) { if (other instanceof py.str && this._value === other._value) { @@ -549,6 +604,10 @@ var py = {}; if (!(other instanceof py.str)) { return py.NotImplemented; } return this._value >= other._value ? py.True : py.False; }, + __add__: function (other) { + if (!(other instanceof py.str)) { return py.NotImplemented; } + return new py.str(this._value + other._value); + }, __nonzero__: function () { return this._value.length ? py.True : py.False; }, @@ -596,9 +655,9 @@ var py = {}; this._inst = null; this._func = nativefunc; }, py.object, { - __call__: function () { + __call__: function (args, kwargs) { // don't want to rewrite __call__ for instancemethod - return this._func.apply(this._inst, arguments); + return this._func.call(this._inst, args, kwargs); }, toJSON: function () { return this._func; @@ -610,13 +669,69 @@ var py = {}; this._func = nativefunc; }, py.def, {}); - py.issubclass = new py.def(function issubclass(derived, parent) { + py.issubclass = new py.def(function issubclass(args) { + var derived = args[0], parent = args[1]; // still hurts my brain that this can work return derived.prototype instanceof py.object ? py.True : py.False; }); + + // All binary operators with fallbacks, so they can be applied generically + var PY_operators = { + '==': ['eq', 'eq', function (a, b) { return a === b; }], + '!=': ['ne', 'ne', function (a, b) { return a !== b; }], + '<': ['lt', 'gt', function (a, b) {return a.constructor.name < b.constructor.name;}], + '<=': ['le', 'ge', function (a, b) {return a.constructor.name <= b.constructor.name;}], + '>': ['gt', 'lt', function (a, b) {return a.constructor.name > b.constructor.name;}], + '>=': ['ge', 'le', function (a, b) {return a.constructor.name >= b.constructor.name;}], + + '+': ['add', 'radd'], + '-': ['sub', 'rsub'], + '*': ['mul', 'rmul'], + '/': ['div', 'rdiv'], + '//': ['floordiv', 'rfloordiv'], + '%': ['mod', 'rmod'], + '**': ['pow', 'rpow'], + '<<': ['lshift', 'rlshift'], + '>>': ['rshift', 'rrshift'], + '&': ['and', 'rand'], + '^': ['xor', 'rxor'], + '|': ['or', 'ror'] + }; + /** + * Implements operator fallback/reflection. + * + * First two arguments are the objects to apply the operator on, + * in their actual order (ltr). + * + * Third argument is the actual operator. + * + * If the operator methods raise exceptions, those exceptions are + * not intercepted. + */ + var PY_op = function (o1, o2, op) { + var r; + var methods = PY_operators[op]; + var forward = '__' + methods[0] + '__', reverse = '__' + methods[1] + '__'; + var otherwise = methods[2]; + + if (forward in o1 && (r = o1[forward](o2)) !== py.NotImplemented) { + return r; + } + if (reverse in o2 && (r = o2[reverse](o1)) !== py.NotImplemented) { + return r; + } + if (otherwise) { + return PY_ensurepy(otherwise(o1, o2)); + } + throw new Error( + "TypeError: unsupported operand type(s) for " + op + ": '" + + o1.constructor.name + "' and '" + + o2.constructor.name + "'"); + }; + var PY_builtins = { type: py.type, @@ -643,30 +758,16 @@ var py = {}; var evaluate_operator = function (operator, a, b) { var v; switch (operator) { - case '==': return a.__eq__(b); case 'is': return a === b ? py.True : py.False; - case '!=': return a.__ne__(b); case 'is not': return a !== b ? py.True : py.False; - case '<': - v = a.__lt__(b); - if (v !== py.NotImplemented) { return v; } - return PY_ensurepy(a.constructor.name < b.constructor.name); - case '<=': - v = a.__le__(b); - if (v !== py.NotImplemented) { return v; } - return PY_ensurepy(a.constructor.name <= b.constructor.name); - case '>': - v = a.__gt__(b); - if (v !== py.NotImplemented) { return v; } - return PY_ensurepy(a.constructor.name > b.constructor.name); - case '>=': - v = a.__ge__(b); - if (v !== py.NotImplemented) { return v; } - return PY_ensurepy(a.constructor.name >= b.constructor.name); case 'in': return b.__contains__(a); case 'not in': return b.__contains__(a) === py.True ? py.False : py.True; + case '==': case '!=': + case '<': case '<=': + case '>': case '>=': + return PY_op(a, b, operator); } throw new Error('SyntaxError: unknown comparator [[' + operator + ']]'); }; @@ -700,11 +801,6 @@ var py = {}; if (result === py.False) { return py.False; } } return py.True; - case '-': - if (expr.second) { - throw new Error('SyntaxError: binary [-] not implemented yet'); - } - return (py.evaluate(expr.first, context)).__neg__(); case 'not': return py.evaluate(expr.first, context).__nonzero__() === py.True ? py.False @@ -723,12 +819,20 @@ var py = {}; return py.evaluate(expr.second, context); case '(': if (expr.second) { - var callable = py.evaluate(expr.first, context), args=[]; + var callable = py.evaluate(expr.first, context); + var args = [], kwargs = {}; for (var jj=0; jj>': + case '&': case '^': case '|': + return PY_op( + py.evaluate(expr.first, context), + py.evaluate(expr.second, context), + expr.id); + default: throw new Error('SyntaxError: Unknown node [[' + expr.id + ']]'); } diff --git a/addons/web/static/lib/py.js/test/parser.js b/addons/web/static/lib/py.js/test/parser.js index ca74d2bebc8..bbbd5d7663d 100644 --- a/addons/web/static/lib/py.js/test/parser.js +++ b/addons/web/static/lib/py.js/test/parser.js @@ -11,6 +11,14 @@ expect.Assertion.prototype.tokens = function (n) { 'expected ' + this.obj + ' to not have an end token'); }; +expect.Assertion.prototype.named = function (value) { + this.assert(this.obj.id === '(name)', + 'expected ' + this.obj + ' to be a name token', + 'expected ' + this.obj + ' not to be a name token'); + this.assert(this.obj.value === value, + 'expected ' + this.obj + ' to have tokenized ' + value, + 'expected ' + this.obj + ' not to have tokenized ' + value); +}; expect.Assertion.prototype.constant = function (value) { this.assert(this.obj.id === '(constant)', 'expected ' + this.obj + ' to be a constant token', @@ -98,4 +106,27 @@ describe('Tokenizer', function () { expect(toks[1].id).to.be(')'); }); }); + describe('functions', function () { + it('tokenizes kwargs', function () { + var toks = py.tokenize('foo(bar=3, qux=4)'); + expect(toks).to.have.tokens(10); + }); + }); +}); + +describe('Parser', function () { + describe('functions', function () { + var ast = py.parse(py.tokenize('foo(bar=3, qux=4)')); + expect(ast.id).to.be('('); + expect(ast.first).to.be.named('foo'); + + args = ast.second; + expect(args[0].id).to.be('='); + expect(args[0].first).to.be.named('bar'); + expect(args[0].second).to.be.number(3); + + expect(args[1].id).to.be('='); + expect(args[1].first).to.be.named('qux'); + expect(args[1].second).to.be.number(4); + }); }); \ No newline at end of file diff --git a/addons/web/static/lib/py.js/test/test.js b/addons/web/static/lib/py.js/test/test.js index 6c53bcfad85..31fccc23240 100644 --- a/addons/web/static/lib/py.js/test/test.js +++ b/addons/web/static/lib/py.js/test/test.js @@ -262,6 +262,15 @@ describe('Attribute access', function () { }); expect(py.eval('foo.bar()', {foo: o})).to.be('ok'); }); + it('should not convert function attributes into methods', function () { + var o = new py.object(); + o.bar = new py.type(function bar() {}); + o.bar.__getattribute__ = function () { + return o.bar.baz; + } + o.bar.baz = py.True; + expect(py.eval('foo.bar.baz', {foo: o})).to.be(true); + }); it('should work on instance attributes', function () { var typ = py.type(function MyType() { this.attr = new py.float(3); @@ -296,16 +305,37 @@ describe('Callables', function () { }); expect(py.eval('MyType()', {MyType: typ})).to.be(true); }); + it('should accept kwargs', function () { + expect(py.eval('foo(ok=True)', { + foo: function foo() { return py.True; } + })).to.be(true); + }); + it('should be able to get its kwargs', function () { + expect(py.eval('foo(ok=True)', { + foo: function foo(args, kwargs) { return kwargs.ok; } + })).to.be(true); + }); + it('should be able to have both args and kwargs', function () { + expect(py.eval('foo(1, 2, 3, ok=True, nok=False)', { + foo: function (args, kwargs) { + expect(args).to.have.length(3); + expect(args[0].toJSON()).to.be(1); + expect(kwargs).to.only.have.keys('ok', 'nok') + expect(kwargs.nok.toJSON()).to.be(false); + return kwargs.ok; + } + })).to.be(true); + }); }); describe('issubclass', function () { it('should say a type is its own subclass', function () { - expect(py.issubclass.__call__(py.dict, py.dict).toJSON()) + expect(py.issubclass.__call__([py.dict, py.dict]).toJSON()) .to.be(true); expect(py.eval('issubclass(dict, dict)')) .to.be(true); }); it('should work with subtypes', function () { - expect(py.issubclass.__call__(py.bool, py.object).toJSON()) + expect(py.issubclass.__call__([py.bool, py.object]).toJSON()) .to.be(true); }); }); @@ -313,4 +343,37 @@ describe('builtins', function () { it('should aways be available', function () { expect(py.eval('bool("foo")')).to.be(true); }); +}); + +describe('numerical protocols', function () { + describe('True numbers (float)', function () { + describe('Basic arithmetic', function () { + it('can be added', function () { + expect(py.eval('1 + 1')).to.be(2); + expect(py.eval('1.5 + 2')).to.be(3.5); + expect(py.eval('1 + -1')).to.be(0); + }); + it('can be subtracted', function () { + expect(py.eval('1 - 1')).to.be(0); + expect(py.eval('1.5 - 2')).to.be(-0.5); + expect(py.eval('2 - 1.5')).to.be(0.5); + }); + it('can be multiplied', function () { + expect(py.eval('1 * 3')).to.be(3); + expect(py.eval('0 * 5')).to.be(0); + expect(py.eval('42 * -2')).to.be(-84); + }); + it('can be divided', function () { + expect(py.eval('1 / 2')).to.be(0.5); + expect(py.eval('2 / 1')).to.be(2); + }); + }); + }); + describe('Strings', function () { + describe('Basic arithmetics operators', function () { + it('can be added (concatenation)', function () { + expect(py.eval('"foo" + "bar"')).to.be('foobar'); + }); + }); + }); }); \ No newline at end of file diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index ca53bce9c59..ead33822926 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -322,19 +322,34 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. return this.session_init(); }, test_eval_get_context: function () { + var asJS = function (arg) { + if (arg instanceof py.object) { + return arg.toJSON(); + } + return arg; + }; + var datetime = new py.object(); + datetime.datetime = new py.type(function datetime() { + throw new Error('datetime.datetime not implemented'); + }); var date = datetime.date = new py.type(function date(y, m, d) { - this._year = y; - this._month = m; - this._day = d; + if (y instanceof Array) { + d = y[2]; + m = y[1]; + y = y[0]; + } + this.year = asJS(y); + this.month = asJS(m); + this.day = asJS(d); }, py.object, { - strftime: function (format) { - var f = format.toJSON(), self = this; + strftime: function (args) { + var f = asJS(args[0]), self = this; return new py.str(f.replace(/%([A-Za-z])/g, function (m, c) { switch (c) { - case 'Y': return self._year; - case 'm': return _.str.sprintf('%02d', self._month); - case 'd': return _.str.sprintf('%02d', self._day); + case 'Y': return self.year; + case 'm': return _.str.sprintf('%02d', self.month); + case 'd': return _.str.sprintf('%02d', self.day); } throw new Error('ValueError: No known conversion for ' + m); })); @@ -350,16 +365,107 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. var d = new Date(); return new date(d.getUTCFullYear(), d.getUTCMonth() + 1, d.getUTCDate()); }); + datetime.time = new py.type(function time() { + throw new Error('datetime.time not implemented'); + }); var time = new py.object(); - time.strftime = new py.def(function (format) { - return date.today.__call__().strftime(format); + time.strftime = new py.def(function (args) { + return date.today.__call__().strftime(args); + }); + + var relativedelta = new py.type(function relativedelta(args, kwargs) { + if (!_.isEmpty(args)) { + throw new Error('Extraction of relative deltas from existing datetimes not supported'); + } + this.ops = kwargs; + }, py.object, { + __add__: function (other) { + if (!(other instanceof datetime.date)) { + return py.NotImplemented; + } + // TODO: test this whole mess + var year = asJS(this.ops.year) || asJS(other.year); + if (asJS(this.ops.years)) { + year += asJS(this.ops.years); + } + + var month = asJS(this.ops.month) || asJS(other.month); + if (asJS(this.ops.months)) { + month += asJS(this.ops.months); + // FIXME: no divmod in JS? + while (month < 1) { + year -= 1; + month += 12; + } + while (month > 12) { + year += 1; + month -= 12; + } + } + + var lastMonthDay = new Date(year, month, 0).getDate(); + var day = asJS(this.ops.day) || asJS(other.day); + if (day > lastMonthDay) { day = lastMonthDay; } + var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0); + if (days_offset) { + day = new Date(year, month-1, day + days_offset).getDate(); + } + // TODO: leapdays? + // TODO: hours, minutes, seconds? Not used in XML domains + // TODO: weekday? + return new datetime.date(year, month, day); + }, + __radd__: function (other) { + return this.__add__(other); + }, + + __sub__: function (other) { + if (!(other instanceof datetime.date)) { + return py.NotImplemented; + } + // TODO: test this whole mess + var year = asJS(this.ops.year) || asJS(other.year); + if (asJS(this.ops.years)) { + year -= asJS(this.ops.years); + } + + var month = asJS(this.ops.month) || asJS(other.month); + if (asJS(this.ops.months)) { + month -= asJS(this.ops.months); + // FIXME: no divmod in JS? + while (month < 1) { + year -= 1; + month += 12; + } + while (month > 12) { + year += 1; + month -= 12; + } + } + + var lastMonthDay = new Date(year, month, 0).getDate(); + var day = asJS(this.ops.day) || asJS(other.day); + if (day > lastMonthDay) { day = lastMonthDay; } + var days_offset = ((asJS(this.ops.weeks) || 0) * 7) + (asJS(this.ops.days) || 0); + if (days_offset) { + day = new Date(year, month-1, day - days_offset).getDate(); + } + // TODO: leapdays? + // TODO: hours, minutes, seconds? Not used in XML domains + // TODO: weekday? + return new datetime.date(year, month, day); + }, + __rsub__: function (other) { + return this.__sub__(other); + } }); return { uid: new py.float(this.uid), datetime: datetime, - time: time + time: time, + relativedelta: relativedelta }; }, test_eval: function (source, expected) { From 98341230a78fe9798b55d3a4173b17a00d8d9a0f Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 7 Mar 2012 08:46:41 +0100 Subject: [PATCH 04/12] [FIX] context evaluation: accumulation of literal and compound context data into evaluated context bzr revid: xmo@openerp.com-20120307074641-pm5wl9t3cb4l9s64 --- addons/web/static/src/js/core.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 6e44394c8a2..6f9773a676e 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -514,10 +514,11 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. _(contexts).each(function (ctx) { switch(ctx.__ref) { case 'context': - _.extend(py.eval(ctx.__debug)); + _.extend(result_context, py.eval(ctx.__debug)); break; case 'compound_context': - _.extend(self.test_eval_contexts(ctx.__contexts)); + _.extend( + result_context, self.test_eval_contexts(ctx.__contexts)); break; default: _.extend(result_context, ctx); From dbcac346d66012b9615ff2697ebb60dcea245d02 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 7 Mar 2012 10:12:54 +0100 Subject: [PATCH 05/12] [IMP] simplify opacity mixin, add missing variants to vertical and radial gradients bzr revid: xmo@openerp.com-20120307091254-x9kg0ua1twbjmxp9 --- addons/web/static/src/css/base.css | 26 ++++++++++++++------------ addons/web/static/src/css/base.sass | 19 +++++++++++-------- 2 files changed, 25 insertions(+), 20 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index b81dea89050..52dcc46ec15 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -29,10 +29,13 @@ height: 31px; border-top: solid 1px #d3d3d3; border-bottom: solid 1px black; - background: #646060; - background: -moz-linear-gradient(#646060, #262626); - background: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626)); - background: -webkit-linear-gradient(#646060, #262626); + background-color: #646060; + background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626)); + background-image: -webkit-linear-gradient(top, #646060, #262626); + background-image: -moz-linear-gradient(top, #646060, #262626); + background-image: -ms-linear-gradient(top, #646060, #262626); + background-image: -o-linear-gradient(top, #646060, #262626); + background-image: linear-gradient(to bottom, #646060, #262626); } .openerp2 .oe_topbar .oe_systray { float: right; @@ -226,8 +229,6 @@ border-bottom: 4px solid transparent; border-left: 4px solid #4c4c4c; filter: alpha(opacity=50); - -khtml-opacity: 0.5; - -moz-opacity: 0.5; opacity: 0.5; } .openerp2 .oe_secondary_submenu .oe_menu_opened:before { @@ -263,8 +264,6 @@ border-right: 4px solid transparent; border-top: 4px solid white; filter: alpha(opacity=50); - -khtml-opacity: 0.5; - -moz-opacity: 0.5; opacity: 0.5; } .openerp2 .oe_user_menu .oe_dropdown_options { @@ -309,10 +308,13 @@ color: #eeeeee; } .openerp2 .oe_user_menu .oe_dropdown_options li a:hover { - background: #292929; - background: -moz-linear-gradient(#292929, #191919); - background: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919)); - background: -webkit-linear-gradient(#292929, #191919); + background-color: #292929; + background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919)); + background-image: -webkit-linear-gradient(top, #292929, #191919); + background-image: -moz-linear-gradient(top, #292929, #191919); + background-image: -ms-linear-gradient(top, #292929, #191919); + background-image: -o-linear-gradient(top, #292929, #191919); + background-image: linear-gradient(to bottom, #292929, #191919); -moz-box-shadow: none; -webkit-box-shadow: none; -box-shadow: none; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index cfb8178de12..92b8101f2b7 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -5,16 +5,21 @@ $colour4: #8a89ba // Mixins {{{ @mixin vertical-gradient($startColor: #555, $endColor: #333) - background: $startColor - background: -moz-linear-gradient($startColor, $endColor) - background: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) - background: -webkit-linear-gradient($startColor, $endColor) + background-color: $startColor + background-image: -webkit-gradient(linear, left top, left bottom, from($startColor), to($endColor)) /* Saf4+, Chrome */ + background-image: -webkit-linear-gradient(top, $startColor, $endColor) /* Chrome 10+, Saf5.1+, iOS 5+ */ + background-image: -moz-linear-gradient(top, $startColor, $endColor) /* FF3.6 */ + background-image: -ms-linear-gradient(top, $startColor, $endColor) /* IE10 */ + background-image: -o-linear-gradient(top, $startColor, $endColor) /* Opera 11.10+ */ + background-image: linear-gradient(to bottom, $startColor, $endColor) + @mixin radial-gradient($gradient) background-position: center center - background-image: radial-gradient($gradient) - background-image: -moz-radial-gradient($gradient) background-image: -webkit-radial-gradient(circle, $gradient) + background-image: -moz-radial-gradient($gradient) + background-image: -ms-radial-gradient($gradient) + background-image: radial-gradient($gradient) @mixin radius($radius: 5px) -moz-border-radius: $radius @@ -35,8 +40,6 @@ $colour4: #8a89ba @mixin opacity($opacity: .5) filter: alpha(opacity=$opacity * 100) - -khtml-opacity: $opacity - -moz-opacity: $opacity opacity: $opacity @mixin background-clip($clip: padding-box) From 80ebf90a7c411342cc280cb2824f117fad15bb55 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Wed, 7 Mar 2012 10:57:32 +0100 Subject: [PATCH 06/12] [IMP] debug messages of test evaluator bzr revid: xmo@openerp.com-20120307095732-ckfuowa43i7dkapg --- addons/web/static/src/js/core.js | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/addons/web/static/src/js/core.js b/addons/web/static/src/js/core.js index 6f9773a676e..f464bded13b 100644 --- a/addons/web/static/src/js/core.js +++ b/addons/web/static/src/js/core.js @@ -468,45 +468,57 @@ openerp.web.Connection = openerp.web.CallbackEnabled.extend( /** @lends openerp. relativedelta: relativedelta }; }, + /** + * FIXME: Huge testing hack, especially the evaluation context, rewrite + test for real before switching + */ test_eval: function (source, expected) { try { var ctx = this.test_eval_contexts(source.contexts); if (!_.isEqual(ctx, expected.context)) { - console.group('Local context does not match remote'); + console.group('Local context does not match remote, nothing is broken but please report to R&D (xmo)'); console.warn('source', source.contexts); console.warn('local', ctx); console.warn('remote', expected.context); console.groupEnd(); } } catch (e) { - console.error( - 'Failed to evaluate contexts', source.contexts, ':', e); + console.group('Failed to evaluate contexts, nothing is broken but please report to R&D (xmo)'); + console.error(e); + console.log('source', source.contexts); + console.groupEnd(); } try { var dom = this.test_eval_domains(source.domains, this.test_eval_get_context()); if (!_.isEqual(dom, expected.domain)) { - console.group('Local domain does not match remote'); + console.group('Local domain does not match remote, nothing is broken but please report to R&D (xmo)'); console.warn('source', source.domains); console.warn('local', dom); console.warn('remote', expected.domain); console.groupEnd(); } } catch (e) { - console.error( - 'Failed to evaluate domains', source.domains, ':', e); + console.group('Failed to evaluate domains, nothing is broken but please report to R&D (xmo)'); + console.error(e); + console.log('source', source.domains); + console.groupEnd(); } try { var groups = this.test_eval_groupby(source.group_by_seq); if (!_.isEqual(groups, expected.group_by)) { - console.group('Local groupby does not match remote'); + console.group('Local groupby does not match remote, nothing is broken but please report to R&D (xmo)'); console.warn('source', source.group_by_seq); console.warn('local', groups); console.warn('remote', expected.group_by); console.groupEnd(); } - } catch (e) {} + } catch (e) { + console.group('Failed to evaluate groupby, nothing is broken but please report to R&D (xmo)'); + console.error(e); + console.log('source', source.group_by_seq); + console.groupEnd(); + } }, test_eval_contexts: function (contexts) { var result_context = _.extend({}, this.user_context), From 8c65ef53b93e6e1a8bdc8082b6d3706443875fc5 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Wed, 7 Mar 2012 16:05:00 +0100 Subject: [PATCH 07/12] [imp] minor feature in the web client class to set the content to full screen bzr revid: nicolas.vanhoren@openerp.com-20120307150500-o8kkxp4ohrtzjq69 --- addons/web/static/src/js/chrome.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index 20750f07a4e..cd3ff72ff6a 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1127,6 +1127,12 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie self.menu.on_menu_click(null, action.menu_id); }); } + }, + set_content_full_screen: function(fullscreen) { + if (fullscreen) + this.$element.addClass("oe_content_full_screen"); + else + this.$element.removeClass("oe_content_full_screen"); } }); From df1cfd1e8f1b8f65bc5e28fecf7c9b79aefd4aa7 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Wed, 7 Mar 2012 16:05:49 +0100 Subject: [PATCH 08/12] [imp] changed the way the point of sale switch to fullscreen bzr revid: nicolas.vanhoren@openerp.com-20120307150549-rzkjz0j79l6mpjqs --- addons/point_of_sale/static/src/js/pos.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/addons/point_of_sale/static/src/js/pos.js b/addons/point_of_sale/static/src/js/pos.js index 1885604d42c..8554877d506 100644 --- a/addons/point_of_sale/static/src/js/pos.js +++ b/addons/point_of_sale/static/src/js/pos.js @@ -1325,8 +1325,7 @@ openerp.point_of_sale = function(db) { }); pos.app = new App(self.$element); - $('.oe_toggle_secondary_menu').hide(); - $('.oe_footer').hide(); + db.webclient.set_content_full_screen(true); if (pos.store.get('account.bank.statement').length === 0) return new db.web.Model("ir.model.data").get_func("search_read")([['name', '=', 'action_pos_open_statement']], ['res_id']).pipe( @@ -1384,8 +1383,7 @@ openerp.point_of_sale = function(db) { }, this)); }, destroy: function() { - $('.oe_footer').show(); - $('.oe_toggle_secondary_menu').show(); + db.webclient.set_content_full_screen(false); pos = undefined; this._super(); } From 659ab1ff3b85c0035e8c4b23852973a0ee331888 Mon Sep 17 00:00:00 2001 From: niv-openerp Date: Wed, 7 Mar 2012 16:55:29 +0100 Subject: [PATCH 09/12] [imp] changed detail in full screen feature bzr revid: nicolas.vanhoren@openerp.com-20120307155529-t83uabjx0ocm8mjq --- addons/web/static/src/js/chrome.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/chrome.js b/addons/web/static/src/js/chrome.js index cd3ff72ff6a..829610f2988 100644 --- a/addons/web/static/src/js/chrome.js +++ b/addons/web/static/src/js/chrome.js @@ -1130,9 +1130,9 @@ openerp.web.WebClient = openerp.web.Widget.extend(/** @lends openerp.web.WebClie }, set_content_full_screen: function(fullscreen) { if (fullscreen) - this.$element.addClass("oe_content_full_screen"); + $(".oe_webclient", this.$element).addClass("oe_content_full_screen"); else - this.$element.removeClass("oe_content_full_screen"); + $(".oe_webclient", this.$element).removeClass("oe_content_full_screen"); } }); From b06d76c51f69c0c3352de53bf78390027321bcaa Mon Sep 17 00:00:00 2001 From: Minh Tran Date: Wed, 7 Mar 2012 17:05:12 +0100 Subject: [PATCH 10/12] Added CSS for fullscreen mode (eg. for the point of sale) bzr revid: mit@openerp.com-20120307160512-fziwyn40i45dgxap --- addons/web/static/src/css/base.css | 590 +++++++++++++--------------- addons/web/static/src/css/base.sass | 9 + 2 files changed, 285 insertions(+), 314 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 52dcc46ec15..dab8e6070af 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -5,317 +5,279 @@ color: #4c4c4c; font-size: 13px; background: white; - position: relative; -} -.openerp2 a { - text-decoration: none; -} -.openerp2 .oe_webclient { - position: absolute; - top: 0; - bottom: 0; - left: 0; - right: 0; -} -.openerp2 .oe_webclient .oe_application { - position: absolute; - top: 32px; - bottom: 0; - left: 206px; - right: 0; -} -.openerp2 .oe_topbar { - width: 100%; - height: 31px; - border-top: solid 1px #d3d3d3; - border-bottom: solid 1px black; - background-color: #646060; - background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626)); - background-image: -webkit-linear-gradient(top, #646060, #262626); - background-image: -moz-linear-gradient(top, #646060, #262626); - background-image: -ms-linear-gradient(top, #646060, #262626); - background-image: -o-linear-gradient(top, #646060, #262626); - background-image: linear-gradient(to bottom, #646060, #262626); -} -.openerp2 .oe_topbar .oe_systray { - float: right; -} -.openerp2 .oe_topbar .oe_systray > div { - float: left; - padding: 0 4px 0 4px; -} -.openerp2 .oe_topbar .oe_topbar_item li { - float: left; -} -.openerp2 .oe_topbar .oe_topbar_item li a { - display: block; - padding: 5px 10px 7px; - line-height: 20px; - height: 20px; - color: #eeeeee; - vertical-align: top; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -} -.openerp2 .oe_topbar .oe_topbar_item li a:hover { - background: #303030; - color: white; - -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -} -.openerp2 .oe_topbar .oe_topbar_item .oe_active { - background: #303030; - font-weight: bold; - color: white; - -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -} -.openerp2 .oe_topbar .oe_topbar_avatar { - width: 24px; - height: 24px; - margin: -2px 2px 0 0; - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; -} -.openerp2 .oe_topbar .oe_topbar_avatar { - vertical-align: top; -} -.openerp2 .oe_leftbar { - width: 205px; - height: 100%; - background: #f0eeee; - border-right: 1px solid #afafb6; - overflow: auto; - text-shadow: 0 1px 1px white; -} -.openerp2 .oe_leftbar .oe_footer { - position: absolute; - width: 205px; - text-align: center; - bottom: 8px; -} -.openerp2 a.oe_logo { - display: block; - text-align: center; - height: 70px; - line-height: 70px; -} -.openerp2 a.oe_logo img { - height: 40px; - width: 157px; - margin: 14px 0; -} -.openerp2 .oe_footer { - position: absolute; - width: 205px; - text-align: center; - bottom: 8px; -} -.openerp2 .oe_footer a { - font-weight: 800; - font-family: serif; - font-size: 16px; - color: black; -} -.openerp2 .oe_footer a span { - color: #c81010; - font-style: italic; -} -.openerp2 .oe_menu { - float: left; - padding: 0; - margin: 0; -} -.openerp2 .oe_menu li { - list-style-type: none; - float: left; -} -.openerp2 .oe_menu a { - display: block; - padding: 5px 10px 7px; - line-height: 20px; - height: 20px; - color: #eeeeee; - vertical-align: top; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -} -.openerp2 .oe_menu a:hover { - background: #303030; - color: white; - -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -} -.openerp2 .oe_menu .oe_active { - background: #303030; - font-weight: bold; - color: white; - -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; - -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; -} -.openerp2 .oe_secondary_menu_section { - font-weight: bold; - margin-left: 8px; - color: #8a89ba; -} -.openerp2 .oe_secondary_submenu { - padding: 2px 0 8px 0; - margin: 0; - width: 100%; - display: inline-block; -} -.openerp2 .oe_secondary_submenu li { - position: relative; - padding: 1px 0 1px 16px; - list-style-type: none; -} -.openerp2 .oe_secondary_submenu li a { - display: block; - color: #4c4c4c; - padding: 2px 4px 2px 0; -} -.openerp2 .oe_secondary_submenu li .oe_menu_label { - position: absolute; - top: 1px; - right: 1px; - font-size: 10px; - background: #8a89ba; - color: white; - padding: 2px 4px; - margin: 1px 6px 0 0; - border: 1px solid lightGray; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); - -moz-border-radius: 4px; - -webkit-border-radius: 4px; - border-radius: 4px; - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); - -box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); -} -.openerp2 .oe_secondary_submenu .oe_active { - background: #8a89ba; - border-top: 1px solid lightGray; - border-bottom: 1px solid lightGray; - text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); - -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); - -box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); -} -.openerp2 .oe_secondary_submenu .oe_active a { - color: white; -} -.openerp2 .oe_secondary_submenu .oe_active .oe_menu_label { - background: #eeeeee; - color: #8a89ba; - text-shadow: 0 1px 1px white; - -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); - -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); - -box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -} -.openerp2 .oe_secondary_submenu .oe_menu_toggler:before { - width: 0; - height: 0; - display: inline-block; - content: "&darr"; - text-indent: -99999px; - vertical-align: top; - margin-left: -8px; - margin-top: 4px; - margin-right: 4px; - border-top: 4px solid transparent; - border-bottom: 4px solid transparent; - border-left: 4px solid #4c4c4c; - filter: alpha(opacity=50); - opacity: 0.5; -} -.openerp2 .oe_secondary_submenu .oe_menu_opened:before { - margin-top: 6px; - margin-left: -12px; - margin-right: 4px; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid #4c4c4c; -} -.openerp2 .oe_user_menu { - float: right; - padding: 0; - margin: 0; -} -.openerp2 .oe_user_menu li { - list-style-type: none; - float: left; -} -.openerp2 .oe_user_menu .oe_dropdown { - position: relative; -} -.openerp2 .oe_user_menu .oe_dropdown_toggle:after { - width: 0; - height: 0; - display: inline-block; - content: "&darr"; - text-indent: -99999px; - vertical-align: top; - margin-top: 8px; - margin-left: 4px; - border-left: 4px solid transparent; - border-right: 4px solid transparent; - border-top: 4px solid white; - filter: alpha(opacity=50); - opacity: 0.5; -} -.openerp2 .oe_user_menu .oe_dropdown_options { - float: left; - background: #333333; - background: rgba(37, 37, 37, 0.9); - display: none; - position: absolute; - top: 32px; - right: -1px; - border: 0; - z-index: 900; - margin-left: 0; - margin-right: 0; - padding: 6px 0; - zoom: 1; - border-color: #999999; - border-color: rgba(0, 0, 0, 0.2); - border-style: solid; - border-width: 0 1px 1px; - -moz-border-radius: 0 0 6px 6px; - -webkit-border-radius: 0 0 6px 6px; - border-radius: 0 0 6px 6px; - -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); - -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); - -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); - -webkit-background-clip: padding-box; - -moz-background-clip: padding-box; - background-clip: padding-box; -} -.openerp2 .oe_user_menu .oe_dropdown_options li { - float: none; - display: block; - background-color: none; -} -.openerp2 .oe_user_menu .oe_dropdown_options li a { - display: block; - padding: 4px 15px; - clear: both; - font-weight: normal; - line-height: 18px; - color: #eeeeee; -} -.openerp2 .oe_user_menu .oe_dropdown_options li a:hover { - background-color: #292929; - background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919)); - background-image: -webkit-linear-gradient(top, #292929, #191919); - background-image: -moz-linear-gradient(top, #292929, #191919); - background-image: -ms-linear-gradient(top, #292929, #191919); - background-image: -o-linear-gradient(top, #292929, #191919); - background-image: linear-gradient(to bottom, #292929, #191919); - -moz-box-shadow: none; - -webkit-box-shadow: none; - -box-shadow: none; -} + position: relative; } + .openerp2 a { + text-decoration: none; } + .openerp2 .oe_webclient { + position: absolute; + top: 0; + bottom: 0; + left: 0; + right: 0; } + .openerp2 .oe_webclient .oe_application { + position: absolute; + top: 32px; + bottom: 0; + left: 206px; + right: 0; } + .openerp2 .oe_content_full_screen .oe_application { + top: 0; + left: 0; } + .openerp2 .oe_content_full_screen .topbar, .openerp2 .oe_content_full_screen .leftbar { + display: none; } + .openerp2 .oe_topbar { + width: 100%; + height: 31px; + border-top: solid 1px #d3d3d3; + border-bottom: solid 1px black; + background-color: #646060; + background-image: -webkit-gradient(linear, left top, left bottom, from(#646060), to(#262626)); + background-image: -webkit-linear-gradient(top, #646060, #262626); + background-image: -moz-linear-gradient(top, #646060, #262626); + background-image: -ms-linear-gradient(top, #646060, #262626); + background-image: -o-linear-gradient(top, #646060, #262626); + background-image: linear-gradient(to bottom, #646060, #262626); } + .openerp2 .oe_topbar .oe_systray { + float: right; } + .openerp2 .oe_topbar .oe_systray > div { + float: left; + padding: 0 4px 0 4px; } + .openerp2 .oe_topbar .oe_topbar_item li { + float: left; } + .openerp2 .oe_topbar .oe_topbar_item li a { + display: block; + padding: 5px 10px 7px; + line-height: 20px; + height: 20px; + color: #eeeeee; + vertical-align: top; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } + .openerp2 .oe_topbar .oe_topbar_item li a:hover { + background: #303030; + color: white; + -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } + .openerp2 .oe_topbar .oe_topbar_item .oe_active { + background: #303030; + font-weight: bold; + color: white; + -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } + .openerp2 .oe_topbar .oe_topbar_avatar { + width: 24px; + height: 24px; + margin: -2px 2px 0 0; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; } + .openerp2 .oe_topbar .oe_topbar_avatar { + vertical-align: top; } + .openerp2 .oe_leftbar { + width: 205px; + height: 100%; + background: #f0eeee; + border-right: 1px solid #afafb6; + overflow: auto; + text-shadow: 0 1px 1px white; } + .openerp2 .oe_leftbar .oe_footer { + position: absolute; + width: 205px; + text-align: center; + bottom: 8px; } + .openerp2 a.oe_logo { + display: block; + text-align: center; + height: 70px; + line-height: 70px; } + .openerp2 a.oe_logo img { + height: 40px; + width: 157px; + margin: 14px 0; } + .openerp2 .oe_footer { + position: absolute; + width: 205px; + text-align: center; + bottom: 8px; } + .openerp2 .oe_footer a { + font-weight: 800; + font-family: serif; + font-size: 16px; + color: black; } + .openerp2 .oe_footer a span { + color: #c81010; + font-style: italic; } + .openerp2 .oe_menu { + float: left; + padding: 0; + margin: 0; } + .openerp2 .oe_menu li { + list-style-type: none; + float: left; } + .openerp2 .oe_menu a { + display: block; + padding: 5px 10px 7px; + line-height: 20px; + height: 20px; + color: #eeeeee; + vertical-align: top; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } + .openerp2 .oe_menu a:hover { + background: #303030; + color: white; + -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } + .openerp2 .oe_menu .oe_active { + background: #303030; + font-weight: bold; + color: white; + -moz-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -webkit-box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; + -box-shadow: 0 1px 2px rgba(255, 255, 255, 0.3) inset; } + .openerp2 .oe_secondary_menu_section { + font-weight: bold; + margin-left: 8px; + color: #8a89ba; } + .openerp2 .oe_secondary_submenu { + padding: 2px 0 8px 0; + margin: 0; + width: 100%; + display: inline-block; } + .openerp2 .oe_secondary_submenu li { + position: relative; + padding: 1px 0 1px 16px; + list-style-type: none; } + .openerp2 .oe_secondary_submenu li a { + display: block; + color: #4c4c4c; + padding: 2px 4px 2px 0; } + .openerp2 .oe_secondary_submenu li .oe_menu_label { + position: absolute; + top: 1px; + right: 1px; + font-size: 10px; + background: #8a89ba; + color: white; + padding: 2px 4px; + margin: 1px 6px 0 0; + border: 1px solid lightGray; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); + -box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); } + .openerp2 .oe_secondary_submenu .oe_active { + background: #8a89ba; + border-top: 1px solid lightGray; + border-bottom: 1px solid lightGray; + text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + -moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); + -box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.2); } + .openerp2 .oe_secondary_submenu .oe_active a { + color: white; } + .openerp2 .oe_secondary_submenu .oe_active .oe_menu_label { + background: #eeeeee; + color: #8a89ba; + text-shadow: 0 1px 1px white; + -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); + -box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); } + .openerp2 .oe_secondary_submenu .oe_menu_toggler:before { + width: 0; + height: 0; + display: inline-block; + content: "&darr"; + text-indent: -99999px; + vertical-align: top; + margin-left: -8px; + margin-top: 4px; + margin-right: 4px; + border-top: 4px solid transparent; + border-bottom: 4px solid transparent; + border-left: 4px solid #4c4c4c; + filter: alpha(opacity=50); + opacity: 0.5; } + .openerp2 .oe_secondary_submenu .oe_menu_opened:before { + margin-top: 6px; + margin-left: -12px; + margin-right: 4px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid #4c4c4c; } + .openerp2 .oe_user_menu { + float: right; + padding: 0; + margin: 0; } + .openerp2 .oe_user_menu li { + list-style-type: none; + float: left; } + .openerp2 .oe_user_menu .oe_dropdown { + position: relative; } + .openerp2 .oe_user_menu .oe_dropdown_toggle:after { + width: 0; + height: 0; + display: inline-block; + content: "&darr"; + text-indent: -99999px; + vertical-align: top; + margin-top: 8px; + margin-left: 4px; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-top: 4px solid white; + filter: alpha(opacity=50); + opacity: 0.5; } + .openerp2 .oe_user_menu .oe_dropdown_options { + float: left; + background: #333333; + background: rgba(37, 37, 37, 0.9); + display: none; + position: absolute; + top: 32px; + right: -1px; + border: 0; + z-index: 900; + margin-left: 0; + margin-right: 0; + padding: 6px 0; + zoom: 1; + border-color: #999999; + border-color: rgba(0, 0, 0, 0.2); + border-style: solid; + border-width: 0 1px 1px; + -moz-border-radius: 0 0 6px 6px; + -webkit-border-radius: 0 0 6px 6px; + border-radius: 0 0 6px 6px; + -moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + -box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3); + -webkit-background-clip: padding-box; + -moz-background-clip: padding-box; + background-clip: padding-box; } + .openerp2 .oe_user_menu .oe_dropdown_options li { + float: none; + display: block; + background-color: none; } + .openerp2 .oe_user_menu .oe_dropdown_options li a { + display: block; + padding: 4px 15px; + clear: both; + font-weight: normal; + line-height: 18px; + color: #eeeeee; } + .openerp2 .oe_user_menu .oe_dropdown_options li a:hover { + background-color: #292929; + background-image: -webkit-gradient(linear, left top, left bottom, from(#292929), to(#191919)); + background-image: -webkit-linear-gradient(top, #292929, #191919); + background-image: -moz-linear-gradient(top, #292929, #191919); + background-image: -ms-linear-gradient(top, #292929, #191919); + background-image: -o-linear-gradient(top, #292929, #191919); + background-image: linear-gradient(to bottom, #292929, #191919); + -moz-box-shadow: none; + -webkit-box-shadow: none; + -box-shadow: none; } diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 92b8101f2b7..56a735c98fd 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -78,6 +78,15 @@ $colour4: #8a89ba right: 0 // }}} + // Fullscreen mode {{{ + .oe_content_full_screen + .oe_application + top: 0 + left: 0 + .topbar, .leftbar + display: none + // }} + // Topbar {{{ .oe_topbar width: 100% From dabd80eaafdedc7e6ee1f280a7fa2796863c3b7f Mon Sep 17 00:00:00 2001 From: Launchpad Translations on behalf of openerp <> Date: Thu, 8 Mar 2012 04:43:45 +0000 Subject: [PATCH 11/12] Launchpad automatic translations update. bzr revid: launchpad_translations_on_behalf_of_openerp-20120308044345-k1iwqynbbewkyyoi --- .../i18n/sr@latin.po | 381 ++++++++++++++++ addons/account_budget/i18n/gu.po | 417 ++++++++++++++++++ 2 files changed, 798 insertions(+) create mode 100644 addons/account_bank_statement_extensions/i18n/sr@latin.po create mode 100644 addons/account_budget/i18n/gu.po diff --git a/addons/account_bank_statement_extensions/i18n/sr@latin.po b/addons/account_bank_statement_extensions/i18n/sr@latin.po new file mode 100644 index 00000000000..b8551612a9e --- /dev/null +++ b/addons/account_bank_statement_extensions/i18n/sr@latin.po @@ -0,0 +1,381 @@ +# Serbian Latin 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 , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-02-08 00:35+0000\n" +"PO-Revision-Date: 2012-03-07 18:50+0000\n" +"Last-Translator: Milan Milosevic \n" +"Language-Team: Serbian Latin \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-03-08 04:43+0000\n" +"X-Generator: Launchpad (build 14914)\n" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Search Bank Transactions" +msgstr "Pretraži bankovne transakcije" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: selection:account.bank.statement.line,state:0 +msgid "Confirmed" +msgstr "Potvrđeno" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement:0 +#: view:account.bank.statement.line:0 +msgid "Glob. Id" +msgstr "Glob. Id" + +#. module: account_bank_statement_extensions +#: selection:account.bank.statement.line.global,type:0 +msgid "CODA" +msgstr "CODA" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,parent_id:0 +msgid "Parent Code" +msgstr "Šifra nadređenog" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Debit" +msgstr "Duguje" + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_cancel_statement_line +#: model:ir.model,name:account_bank_statement_extensions.model_cancel_statement_line +msgid "Cancel selected statement lines" +msgstr "Otkaži izabrane redove izvoda" + +#. module: account_bank_statement_extensions +#: constraint:res.partner.bank:0 +msgid "The RIB and/or IBAN is not valid" +msgstr "RIB i / ili IBAN nije validan" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Group By..." +msgstr "Grupiši po..." + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,state:0 +msgid "State" +msgstr "Stanje" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: selection:account.bank.statement.line,state:0 +msgid "Draft" +msgstr "Nacrt" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Statement" +msgstr "Izvod" + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_confirm_statement_line +#: model:ir.model,name:account_bank_statement_extensions.model_confirm_statement_line +msgid "Confirm selected statement lines" +msgstr "Potvrdi izabrane redove izvoda" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +#: model:ir.actions.report.xml,name:account_bank_statement_extensions.bank_statement_balance_report +msgid "Bank Statement Balances Report" +msgstr "Izvod banke o izveštaju balansa" + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +msgid "Cancel Lines" +msgstr "Otkaži linije" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line.global:0 +#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line_global +msgid "Batch Payment Info" +msgstr "Informacije o grupnom plaćanju" + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +msgid "Confirm Lines" +msgstr "Potvrdi linije" + +#. module: account_bank_statement_extensions +#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130 +#, python-format +msgid "" +"Delete operation not allowed ! Please go to the associated bank " +"statement in order to delete and/or modify this bank statement line" +msgstr "" +"Postupak brisanja nije dozvoljen! Molimo idite do povezanog izvoda banke da " +"biste izbrisali i/ili izmenili taj red izvoda banke" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,type:0 +msgid "Type" +msgstr "Tip" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: field:account.bank.statement.line,journal_id:0 +#: report:bank.statement.balance.report:0 +msgid "Journal" +msgstr "Dnevnik" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Confirmed Statement Lines." +msgstr "Potvrđeni redovi izvoda" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Credit Transactions." +msgstr "Kreditne transakcije" + +#. module: account_bank_statement_extensions +#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_cancel_statement_line +msgid "cancel selected statement lines." +msgstr "otkaži izabrane redoive izvode" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_number:0 +msgid "Counterparty Number" +msgstr "Broj druge strane" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line.global:0 +msgid "Transactions" +msgstr "Transakcije" + +#. module: account_bank_statement_extensions +#: code:addons/account_bank_statement_extensions/account_bank_statement.py:130 +#, python-format +msgid "Warning" +msgstr "Upozorenje" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +msgid "Closing Balance" +msgstr "Zatvaranje stanja" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +msgid "Date" +msgstr "Datum" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +#: field:account.bank.statement.line,globalisation_amount:0 +msgid "Glob. Amount" +msgstr "Uk.iznos" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Debit Transactions." +msgstr "Transakcije dugovanja." + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Extended Filters..." +msgstr "Prošireni filteri..." + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +msgid "Confirmed lines cannot be changed anymore." +msgstr "Potvrđeni redovi se ne mogu više izmeniti." + +#. module: account_bank_statement_extensions +#: constraint:res.partner.bank:0 +msgid "" +"\n" +"Please define BIC/Swift code on bank for bank type IBAN Account to make " +"valid payments" +msgstr "" +"\n" +"Molimo definišite BIC/Swift kod banke za tip IBAN račun za izvršenje " +"validnih uplata/isplata." + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,val_date:0 +msgid "Valuta Date" +msgstr "Datum valute" + +#. module: account_bank_statement_extensions +#: model:ir.actions.act_window,help:account_bank_statement_extensions.action_confirm_statement_line +msgid "Confirm selected statement lines." +msgstr "Potvrdi izabrane redove izvoda" + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +msgid "Are you sure you want to cancel the selected Bank Statement lines ?" +msgstr "Da li zaista želite da otkažete izabrane redove izvoda banke?" + +#. module: account_bank_statement_extensions +#: report:bank.statement.balance.report:0 +msgid "Name" +msgstr "Naziv" + +#. module: account_bank_statement_extensions +#: selection:account.bank.statement.line.global,type:0 +msgid "ISO 20022" +msgstr "ISO 20022" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Notes" +msgstr "Beleške" + +#. module: account_bank_statement_extensions +#: selection:account.bank.statement.line.global,type:0 +msgid "Manual" +msgstr "Uputstvo" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Credit" +msgstr "Kredit" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,amount:0 +msgid "Amount" +msgstr "Iznos" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Fin.Account" +msgstr "Kon.račun" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_currency:0 +msgid "Counterparty Currency" +msgstr "Valuta druge strane" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_bic:0 +msgid "Counterparty BIC" +msgstr "BIC druge strane" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,child_ids:0 +msgid "Child Codes" +msgstr "Podređeni kodovi" + +#. module: account_bank_statement_extensions +#: view:confirm.statement.line:0 +msgid "Are you sure you want to confirm the selected Bank Statement lines ?" +msgstr "Da li zaista želite da izabrane redove izvoda banke?" + +#. module: account_bank_statement_extensions +#: constraint:account.bank.statement.line:0 +msgid "" +"The amount of the voucher must be the same amount as the one on the " +"statement line" +msgstr "Iznos vaučera mora biti isti kao onaj na izvodu računa" + +#. module: account_bank_statement_extensions +#: help:account.bank.statement.line,globalisation_id:0 +msgid "" +"Code to identify transactions belonging to the same globalisation level " +"within a batch payment" +msgstr "" +"Kod za identifikaciju transakcija koje pripadaju istom nivou globalizacije " +"unutar grupnog plaćanja" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Draft Statement Lines." +msgstr "Nacrt redova izvoda." + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Glob. Am." +msgstr "" + +#. module: account_bank_statement_extensions +#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,code:0 +msgid "Code" +msgstr "" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,counterparty_name:0 +msgid "Counterparty Name" +msgstr "" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,name:0 +msgid "Communication" +msgstr "" + +#. module: account_bank_statement_extensions +#: model:ir.model,name:account_bank_statement_extensions.model_res_partner_bank +msgid "Bank Accounts" +msgstr "" + +#. module: account_bank_statement_extensions +#: constraint:account.bank.statement:0 +msgid "The journal and period chosen have to belong to the same company." +msgstr "" + +#. module: account_bank_statement_extensions +#: model:ir.model,name:account_bank_statement_extensions.model_account_bank_statement +msgid "Bank Statement" +msgstr "" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Statement Line" +msgstr "" + +#. module: account_bank_statement_extensions +#: sql_constraint:account.bank.statement.line.global:0 +msgid "The code must be unique !" +msgstr "" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line.global,bank_statement_line_ids:0 +#: model:ir.actions.act_window,name:account_bank_statement_extensions.action_bank_statement_line +#: model:ir.ui.menu,name:account_bank_statement_extensions.bank_statement_line +msgid "Bank Statement Lines" +msgstr "" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line.global:0 +msgid "Child Batch Payments" +msgstr "" + +#. module: account_bank_statement_extensions +#: view:cancel.statement.line:0 +#: view:confirm.statement.line:0 +msgid "Cancel" +msgstr "" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Statement Lines" +msgstr "" + +#. module: account_bank_statement_extensions +#: view:account.bank.statement.line:0 +msgid "Total Amount" +msgstr "" + +#. module: account_bank_statement_extensions +#: field:account.bank.statement.line,globalisation_id:0 +msgid "Globalisation ID" +msgstr "" diff --git a/addons/account_budget/i18n/gu.po b/addons/account_budget/i18n/gu.po new file mode 100644 index 00000000000..2b259a56c8b --- /dev/null +++ b/addons/account_budget/i18n/gu.po @@ -0,0 +1,417 @@ +# Gujarati 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 , 2012. +# +msgid "" +msgstr "" +"Project-Id-Version: openobject-addons\n" +"Report-Msgid-Bugs-To: FULL NAME \n" +"POT-Creation-Date: 2012-02-08 00:35+0000\n" +"PO-Revision-Date: 2012-03-07 10:28+0000\n" +"Last-Translator: FULL NAME \n" +"Language-Team: Gujarati \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"X-Launchpad-Export-Date: 2012-03-08 04:43+0000\n" +"X-Generator: Launchpad (build 14914)\n" + +#. module: account_budget +#: field:crossovered.budget,creating_user_id:0 +msgid "Responsible User" +msgstr "" + +#. module: account_budget +#: selection:crossovered.budget,state:0 +msgid "Confirmed" +msgstr "" + +#. module: account_budget +#: model:ir.actions.act_window,name:account_budget.open_budget_post_form +#: model:ir.ui.menu,name:account_budget.menu_budget_post_form +msgid "Budgetary Positions" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +msgid "Printed at:" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +msgid "Confirm" +msgstr "ખાતરી કરો" + +#. module: account_budget +#: field:crossovered.budget,validating_user_id:0 +msgid "Validate User" +msgstr "" + +#. module: account_budget +#: model:ir.actions.act_window,name:account_budget.action_account_budget_crossvered_summary_report +msgid "Print Summary" +msgstr "" + +#. module: account_budget +#: field:crossovered.budget.lines,paid_date:0 +msgid "Paid Date" +msgstr "" + +#. module: account_budget +#: field:account.budget.analytic,date_to:0 +#: field:account.budget.crossvered.report,date_to:0 +#: field:account.budget.crossvered.summary.report,date_to:0 +#: field:account.budget.report,date_to:0 +msgid "End of period" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +#: selection:crossovered.budget,state:0 +msgid "Draft" +msgstr "ડ્રાફ્ટ" + +#. module: account_budget +#: report:account.budget:0 +msgid "at" +msgstr "એટ" + +#. module: account_budget +#: view:account.budget.report:0 +#: model:ir.actions.act_window,name:account_budget.action_account_budget_analytic +#: model:ir.actions.act_window,name:account_budget.action_account_budget_crossvered_report +msgid "Print Budgets" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +msgid "Currency:" +msgstr "" + +#. module: account_budget +#: model:ir.model,name:account_budget.model_account_budget_crossvered_report +msgid "Account Budget crossvered report" +msgstr "" + +#. module: account_budget +#: selection:crossovered.budget,state:0 +msgid "Validated" +msgstr "" + +#. module: account_budget +#: field:crossovered.budget.lines,percentage:0 +msgid "Percentage" +msgstr "ટકાવારી" + +#. module: account_budget +#: report:crossovered.budget.report:0 +msgid "to" +msgstr "થી" + +#. module: account_budget +#: field:crossovered.budget,state:0 +msgid "Status" +msgstr "સ્થિતિ" + +#. module: account_budget +#: model:ir.actions.act_window,help:account_budget.act_crossovered_budget_view +msgid "" +"A budget is a forecast of your company's income and expenses expected for a " +"period in the future. With a budget, a company is able to carefully look at " +"how much money they are taking in during a given period, and figure out the " +"best way to divide it among various categories. By keeping track of where " +"your money goes, you may be less likely to overspend, and more likely to " +"meet your financial goals. Forecast a budget by detailing the expected " +"revenue per analytic account and monitor its evolution based on the actuals " +"realised during that period." +msgstr "" + +#. module: account_budget +#: code:addons/account_budget/account_budget.py:119 +#, python-format +msgid "The Budget '%s' has no accounts!" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: report:crossovered.budget.report:0 +msgid "%" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: report:crossovered.budget.report:0 +msgid "Description" +msgstr "" + +#. module: account_budget +#: report:crossovered.budget.report:0 +msgid "Currency" +msgstr "" + +#. module: account_budget +#: report:crossovered.budget.report:0 +msgid "Total :" +msgstr "" + +#. module: account_budget +#: field:account.budget.post,company_id:0 +#: field:crossovered.budget,company_id:0 +#: field:crossovered.budget.lines,company_id:0 +msgid "Company" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +msgid "To Approve" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +msgid "Reset to Draft" +msgstr "" + +#. module: account_budget +#: view:account.budget.post:0 +#: view:crossovered.budget:0 +#: field:crossovered.budget.lines,planned_amount:0 +msgid "Planned Amount" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: report:crossovered.budget.report:0 +msgid "Perc(%)" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +#: selection:crossovered.budget,state:0 +msgid "Done" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: report:crossovered.budget.report:0 +msgid "Practical Amt" +msgstr "" + +#. module: account_budget +#: view:account.analytic.account:0 +#: view:account.budget.post:0 +#: view:crossovered.budget:0 +#: field:crossovered.budget.lines,practical_amount:0 +msgid "Practical Amount" +msgstr "" + +#. module: account_budget +#: field:crossovered.budget,date_to:0 +#: field:crossovered.budget.lines,date_to:0 +msgid "End Date" +msgstr "" + +#. module: account_budget +#: model:ir.model,name:account_budget.model_account_budget_analytic +#: model:ir.model,name:account_budget.model_account_budget_report +msgid "Account Budget report for analytic account" +msgstr "" + +#. module: account_budget +#: view:account.analytic.account:0 +msgid "Theoritical Amount" +msgstr "" + +#. module: account_budget +#: field:account.budget.post,name:0 +#: field:crossovered.budget,name:0 +msgid "Name" +msgstr "" + +#. module: account_budget +#: model:ir.model,name:account_budget.model_crossovered_budget_lines +msgid "Budget Line" +msgstr "" + +#. module: account_budget +#: view:account.analytic.account:0 +#: view:account.budget.post:0 +msgid "Lines" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: view:crossovered.budget:0 +#: field:crossovered.budget.lines,crossovered_budget_id:0 +#: report:crossovered.budget.report:0 +#: model:ir.actions.report.xml,name:account_budget.account_budget +#: model:ir.model,name:account_budget.model_crossovered_budget +msgid "Budget" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +msgid "To Approve Budgets" +msgstr "" + +#. module: account_budget +#: code:addons/account_budget/account_budget.py:119 +#, python-format +msgid "Error!" +msgstr "" + +#. module: account_budget +#: field:account.budget.post,code:0 +#: field:crossovered.budget,code:0 +msgid "Code" +msgstr "" + +#. module: account_budget +#: view:account.budget.analytic:0 +#: view:account.budget.crossvered.report:0 +msgid "This wizard is used to print budget" +msgstr "" + +#. module: account_budget +#: model:ir.actions.act_window,name:account_budget.act_crossovered_budget_view +#: model:ir.actions.act_window,name:account_budget.action_account_budget_post_tree +#: model:ir.actions.act_window,name:account_budget.action_account_budget_report +#: model:ir.actions.report.xml,name:account_budget.report_crossovered_budget +#: model:ir.ui.menu,name:account_budget.menu_act_crossovered_budget_view +#: model:ir.ui.menu,name:account_budget.menu_action_account_budget_post_tree +#: model:ir.ui.menu,name:account_budget.next_id_31 +#: model:ir.ui.menu,name:account_budget.next_id_pos +msgid "Budgets" +msgstr "" + +#. module: account_budget +#: view:account.budget.crossvered.summary.report:0 +msgid "This wizard is used to print summary of budgets" +msgstr "" + +#. module: account_budget +#: selection:crossovered.budget,state:0 +msgid "Cancelled" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +msgid "Approve" +msgstr "" + +#. module: account_budget +#: field:crossovered.budget,date_from:0 +#: field:crossovered.budget.lines,date_from:0 +msgid "Start Date" +msgstr "" + +#. module: account_budget +#: view:account.budget.post:0 +#: field:crossovered.budget.lines,general_budget_id:0 +#: model:ir.model,name:account_budget.model_account_budget_post +msgid "Budgetary Position" +msgstr "" + +#. module: account_budget +#: field:account.budget.analytic,date_from:0 +#: field:account.budget.crossvered.report,date_from:0 +#: field:account.budget.crossvered.summary.report,date_from:0 +#: field:account.budget.report,date_from:0 +msgid "Start of period" +msgstr "" + +#. module: account_budget +#: model:ir.model,name:account_budget.model_account_budget_crossvered_summary_report +msgid "Account Budget crossvered summary report" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: report:crossovered.budget.report:0 +msgid "Theoretical Amt" +msgstr "" + +#. module: account_budget +#: view:account.budget.analytic:0 +#: view:account.budget.crossvered.report:0 +#: view:account.budget.crossvered.summary.report:0 +#: view:account.budget.report:0 +msgid "Select Dates Period" +msgstr "" + +#. module: account_budget +#: view:account.budget.analytic:0 +#: view:account.budget.crossvered.report:0 +#: view:account.budget.crossvered.summary.report:0 +#: view:account.budget.report:0 +msgid "Print" +msgstr "" + +#. module: account_budget +#: view:account.budget.post:0 +#: view:crossovered.budget:0 +#: field:crossovered.budget.lines,theoritical_amount:0 +msgid "Theoretical Amount" +msgstr "" + +#. module: account_budget +#: field:crossovered.budget.lines,analytic_account_id:0 +#: model:ir.model,name:account_budget.model_account_analytic_account +msgid "Analytic Account" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +msgid "Budget :" +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: report:crossovered.budget.report:0 +msgid "Planned Amt" +msgstr "" + +#. module: account_budget +#: view:account.budget.post:0 +#: field:account.budget.post,account_ids:0 +msgid "Accounts" +msgstr "" + +#. module: account_budget +#: view:account.analytic.account:0 +#: field:account.analytic.account,crossovered_budget_line:0 +#: view:account.budget.post:0 +#: field:account.budget.post,crossovered_budget_line:0 +#: view:crossovered.budget:0 +#: field:crossovered.budget,crossovered_budget_line:0 +#: view:crossovered.budget.lines:0 +#: model:ir.actions.act_window,name:account_budget.act_account_analytic_account_cb_lines +#: model:ir.actions.act_window,name:account_budget.act_crossovered_budget_lines_view +#: model:ir.ui.menu,name:account_budget.menu_act_crossovered_budget_lines_view +msgid "Budget Lines" +msgstr "" + +#. module: account_budget +#: view:account.budget.analytic:0 +#: view:account.budget.crossvered.report:0 +#: view:account.budget.crossvered.summary.report:0 +#: view:account.budget.report:0 +#: view:crossovered.budget:0 +msgid "Cancel" +msgstr "" + +#. module: account_budget +#: constraint:account.analytic.account:0 +msgid "Error! You can not create recursive analytic accounts." +msgstr "" + +#. module: account_budget +#: report:account.budget:0 +#: report:crossovered.budget.report:0 +msgid "Analysis from" +msgstr "" + +#. module: account_budget +#: view:crossovered.budget:0 +msgid "Draft Budgets" +msgstr "" From b73b0918585bdea449afe5c6ed083ff631e84411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 8 Mar 2012 10:45:03 +0100 Subject: [PATCH 12/12] [ADD] project: task kanban view: user avatar is now used instead of gravatar. It now takes all height in the right-side of the context box, instead of only the right-side of title box. Added a css file for new style definitions. bzr revid: tde@openerp.com-20120308094503-cv1xt4as5lymxe95 --- addons/hr/hr_view.xml | 1 - addons/project/__openerp__.py | 3 + addons/project/project_view.xml | 76 ++++++++++--------- .../project/static/src/css/project_task.css | 21 +++++ 4 files changed, 63 insertions(+), 38 deletions(-) create mode 100644 addons/project/static/src/css/project_task.css diff --git a/addons/hr/hr_view.xml b/addons/hr/hr_view.xml index 045bdd90f99..81f1c655d33 100644 --- a/addons/hr/hr_view.xml +++ b/addons/hr/hr_view.xml @@ -130,7 +130,6 @@ hr.employee kanban - diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py index 96a7dc4f28f..fc196890f78 100644 --- a/addons/project/__openerp__.py +++ b/addons/project/__openerp__.py @@ -67,6 +67,9 @@ Dashboard for project members that includes: 'test/project_process.yml', 'test/task_process.yml', ], + 'css': [ + 'static/src/css/project_task.css', + ], 'installable': True, 'auto_install': False, 'application': True, diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 26abe86fe99..9f791f81053 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -344,44 +344,46 @@ oe_kanban_color_green
- - - - - - -
- - - - - - -
-
-
- - - , - - - - - - - ! - +
+ +
+
+ + + + + +
+ + + + +
+
+
+ + + , + + + + + + + ! + +
+
-
diff --git a/addons/project/static/src/css/project_task.css b/addons/project/static/src/css/project_task.css new file mode 100644 index 00000000000..c029df7b2ba --- /dev/null +++ b/addons/project/static/src/css/project_task.css @@ -0,0 +1,21 @@ +.openerp .oe_proj_task_content_box { + margin-right: 41px; +} + +.openerp .oe_proj_task_avatar_box { + float: right; + width: 40px; +} + +.openerp .oe_kanban_avatar { + display: block; + width: 40px; + height: auto; + clip: rect(5px, 40px, 45px, 0px); +} + +.openerp .oe_kanban_avatar_wide { + height: 40px; + width: auto; + clip: rect(0px, 45px, 40px, 05px); +}