diff --git a/addons/hr_expense/views/report_expense.xml b/addons/hr_expense/views/report_expense.xml
index 42be66c3492..5bb2e1dd8e2 100644
--- a/addons/hr_expense/views/report_expense.xml
+++ b/addons/hr_expense/views/report_expense.xml
@@ -44,7 +44,7 @@
-
+
|
diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py
index c1406a3743a..320d5438a7d 100644
--- a/addons/product/pricelist.py
+++ b/addons/product/pricelist.py
@@ -293,7 +293,7 @@ class product_pricelist(osv.osv):
if rule.base_pricelist_id:
price_tmp = self._price_get_multi(cr, uid,
rule.base_pricelist_id, [(product,
- qty, False)], context=context)[product.id]
+ qty, partner)], context=context)[product.id]
ptype_src = rule.base_pricelist_id.currency_id.id
price_uom_id = qty_uom_id
price = currency_obj.compute(cr, uid,
diff --git a/addons/product/product.py b/addons/product/product.py
index 72a7848fd8c..5e1e0290390 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -664,7 +664,7 @@ class product_template(osv.osv):
temp_variants = []
for variant in all_variants:
for value_id in variant_id.value_ids:
- temp_variants.append(variant + [int(value_id)])
+ temp_variants.append(sorted(variant + [int(value_id)]))
if temp_variants:
all_variants = temp_variants
@@ -682,7 +682,7 @@ class product_template(osv.osv):
variants_active_ids = []
variants_inactive = []
for product_id in tmpl_id.product_variant_ids:
- variants = map(int,product_id.attribute_value_ids)
+ variants = sorted(map(int,product_id.attribute_value_ids))
if variants in all_variants:
variants_active_ids.append(product_id.id)
all_variants.pop(all_variants.index(variants))
diff --git a/addons/purchase/views/report_purchaseorder.xml b/addons/purchase/views/report_purchaseorder.xml
index d1285c1d71b..87c6834d869 100644
--- a/addons/purchase/views/report_purchaseorder.xml
+++ b/addons/purchase/views/report_purchaseorder.xml
@@ -79,7 +79,7 @@
|
+ t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
|
@@ -92,21 +92,21 @@
Total Without Taxes |
+ t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
|
| Taxes |
+ t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
|
| Total |
+ t-field-options='{"widget": "monetary", "display_currency": "o.currency_id"}'/>
|
diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py
index 5a8630c9a90..d6560da4278 100644
--- a/addons/purchase_requisition/purchase_requisition.py
+++ b/addons/purchase_requisition/purchase_requisition.py
@@ -173,7 +173,7 @@ class purchase_requisition(osv.osv):
'order_id': purchase_id,
'product_id': product.id,
'account_analytic_id': requisition_line.account_analytic_id.id,
- 'taxes_id': [(6, 0, vals.get('taxes_ids', []))],
+ 'taxes_id': [(6, 0, vals.get('taxes_id', []))],
})
return vals
diff --git a/addons/report/views/layouts.xml b/addons/report/views/layouts.xml
index d68c3ef0211..917495e7e72 100644
--- a/addons/report/views/layouts.xml
+++ b/addons/report/views/layouts.xml
@@ -32,6 +32,7 @@
body {
color: #000 !important;
+ word-wrap: break-word;
}
.border-black td {
border-top: 1px solid black !important;
diff --git a/addons/stock/stock.py b/addons/stock/stock.py
index 82cdf67d32b..325729cf992 100644
--- a/addons/stock/stock.py
+++ b/addons/stock/stock.py
@@ -2831,7 +2831,8 @@ class stock_inventory_line(osv.osv):
'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"),
'state': fields.related('inventory_id', 'state', type='char', string='Status', readonly=True),
'theoretical_qty': fields.function(_get_theoretical_qty, type='float', digits_compute=dp.get_precision('Product Unit of Measure'),
- store=True, readonly=True, string="Theoretical Quantity"),
+ store={'stock.inventory.line': (lambda self, cr, uid, ids, c={}: ids, ['location_id', 'product_id', 'package_id', 'product_uom_id', 'company_id', 'prod_lot_id', 'partner_id'], 20),},
+ readonly=True, string="Theoretical Quantity"),
'partner_id': fields.many2one('res.partner', 'Owner'),
'product_name': fields.related('product_id', 'name', type='char', string='Product Name', store={
'product.product': (_get_product_name_change, ['name', 'default_code'], 20),
diff --git a/addons/web/static/lib/py.js/lib/py.js b/addons/web/static/lib/py.js/lib/py.js
index 2c6c832f9fe..35bb0b5223e 100644
--- a/addons/web/static/lib/py.js/lib/py.js
+++ b/addons/web/static/lib/py.js/lib/py.js
@@ -547,11 +547,13 @@ var py = {};
};
py.PY_call = function (callable, args, kwargs) {
if (!args) {
- args = []; kwargs = {};
- } else if (typeof args === 'object' && !(args instanceof Array)) {
+ args = [];
+ }
+ if (typeof args === 'object' && !(args instanceof Array)) {
kwargs = args;
args = [];
- } else if (!kwargs) {
+ }
+ if (!kwargs) {
kwargs = {};
}
if (callable.__is_type) {
diff --git a/addons/web/static/src/js/pyeval.js b/addons/web/static/src/js/pyeval.js
index 4cb17e70f29..c132b8365fb 100644
--- a/addons/web/static/src/js/pyeval.js
+++ b/addons/web/static/src/js/pyeval.js
@@ -222,7 +222,6 @@
['milliseconds', zero], ['minutes', zero], ['hours', zero],
['weeks', zero]
]);
-
var d = 0, s = 0, m = 0;
var days = args.days.toJSON() + args.weeks.toJSON() * 7;
var seconds = args.seconds.toJSON()
@@ -496,13 +495,14 @@
if (!py.PY_isInstance(other, datetime.timedelta)) {
return py.NotImplemented;
}
+
var s = tmxxx(this.year, this.month, this.day + other.days);
return datetime.date.fromJSON(s.year, s.month, s.day);
},
__radd__: function (other) { return this.__add__(other); },
__sub__: function (other) {
if (py.PY_isInstance(other, datetime.timedelta)) {
- return this.__add__(other.__neg__());
+ return py.PY_add(this, py.PY_negative(other));
}
if (py.PY_isInstance(other, datetime.date)) {
// FIXME: getattr and sub API methods
@@ -562,103 +562,182 @@
return py.PY_call(py.PY_getAttr(d, 'strftime'), [args.format]);
});
- var args = _.map(('year month day hour minute second microsecond '
- + 'years months weeks days hours minutes secondes microseconds '
- + 'weekday leakdays yearday nlyearday').split(' '), function (arg) {
- return [arg, null];
+ var args = _.map(('year month day '
+ + 'years months weeks days '
+ + 'weekday leapdays yearday nlyearday').split(' '), function (arg) {
+ switch (arg) {
+ case 'years':case 'months':case 'days':case 'leapdays':case 'weeks':
+ return [arg, zero];
+ case 'year':case 'month':case 'day':case 'weekday':
+ case 'yearday':case 'nlyearday':
+ return [arg, null];
+ default:
+ throw new Error("Unknown relativedelta argument " + arg);
+ }
});
args.unshift('*');
+ var utils = {
+ divmod: function (x, y) {
+ var rem = x % y;
+ return {
+ div: (x - rem) / y,
+ mod: rem
+ };
+ },
+ monthrange: function (year, month) {
+ if (month < 1 || month > 12) {
+ throw new Error("Illegal month " + month);
+ }
+
+ var day1 = this.weekday(year, month, 1);
+ var ndays = this.mdays[month] + (month == this.February && this.isleap(year));
+ return [day1, ndays];
+ },
+ weekday: function (year, month, day) {
+ var date = py.PY_call(datetime.date, [year, month, day]);
+ return py.PY_call(py.PY_getAttr(date, 'weekday'));
+ },
+ isleap: function (year) {
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)
+ },
+ mdays: [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+ January: 1,
+ February: 2
+ };
var relativedelta = py.type('relativedelta', null, {
__init__: function () {
this.ops = py.PY_parseArgs(arguments, args);
+ this.ops.days = py.float.fromJSON(
+ asJS(this.ops.days) + asJS(this.ops.weeks) * 7
+ );
+
+ var yday = zero;
+ if (this.ops.nlyearday) {
+ yday = this.ops.nlyearday;
+ } else if (this.ops.yearday) {
+ yday = this.ops.yearday;
+ if (asJS(this.ops.yearday) > 59) {
+ this.ops.leapdays = py.float.fromJS(-1);
+ }
+ }
+ if (py.PY_isTrue(yday)) {
+ var ydayidx = [31, 59, 90, 120, 151, 181, 212,
+ 243, 273, 304, 334, 366];
+ for(var idx=0; idx 11) {
+ var s = months > 0 ? 1 : -1;
+ var r = utils.divmod(months * s, 12);
+ this.ops.months = py.float.fromJSON(r.mod*s);
+ this.ops.years = py.float.fromJSON(
+ asJS(this.ops.years) + r.div*s);
+ }
+ this._has_time = 0;
},
__add__: function (other) {
if (!py.PY_isInstance(other, 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 year = (asJS(this.ops.year) || asJS(other.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;
+ var months;
+ if (months = asJS(this.ops.months)) {
+ if (Math.abs(months) < 1 || Math.abs(months) > 12) {
+ throw new Error("Can only use relative months between -12 and +12");
}
- while (month > 12) {
+ month += months;
+ if (month > 12) {
year += 1;
month -= 12;
}
+ if (month < 1) {
+ 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();
+ var day = Math.min(utils.monthrange(year, month)[1],
+ asJS(this.ops.day) || asJS(other.day));
+
+ var repl = {
+ year: py.float.fromJSON(year),
+ month: py.float.fromJSON(month),
+ day: py.float.fromJSON(day)
+ };
+
+ var days = asJS(this.ops.days);
+ if (py.PY_isTrue(this.ops.leapdays) && month > 2 && utils.isleap(year)) {
+ days += asJS(this.ops.leapdays);
}
- // TODO: leapdays?
- // TODO: hours, minutes, seconds? Not used in XML domains
- // TODO: weekday?
- // FIXME: use date.replace
- return py.PY_call(datetime.date, [
- py.float.fromJSON(year),
- py.float.fromJSON(month),
- py.float.fromJSON(day)
- ]);
+
+ var ret = py.PY_add(
+ py.PY_call(py.PY_getAttr(other, 'replace'), repl),
+ py.PY_call(datetime.timedelta, {
+ days: py.float.fromJSON(days)
+ })
+ );
+
+ if (this.ops.weekday) {
+ // FIXME: only handles numeric weekdays, not decorated
+ var weekday = asJS(this.ops.weekday), nth = 1;
+ var jumpdays = (Math.abs(nth) - 1) * 7;
+
+ var ret_weekday = asJS(py.PY_call(py.PY_getAttr(ret, 'weekday')));
+ if (nth > 0) {
+ jumpdays += (7-ret_weekday+weekday) % 7;
+ } else {
+ jumpdays += (ret_weekday - weekday) % 7;
+ jumpdays *= -1;
+ }
+ ret = py.PY_add(
+ ret,
+ py.PY_call(datetime.timedelta, {
+ days: py.float.fromJSON(jumpdays)
+ })
+ );
+ }
+
+ return ret;
},
__radd__: function (other) {
return this.__add__(other);
},
-
- __sub__: function (other) {
- if (!py.PY_isInstance(other, 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 py.PY_call(datetime.date, [
- py.float.fromJSON(year),
- py.float.fromJSON(month),
- py.float.fromJSON(day)
- ]);
- },
__rsub__: function (other) {
- return this.__sub__(other);
+ return this.__neg__().__radd__(other);
+ },
+ __neg__: function () {
+ return py.PY_call(relativedelta, {
+ years: py.PY_negative(this.ops.years),
+ months: py.PY_negative(this.ops.months),
+ days: py.PY_negative(this.ops.days),
+ leapdays: this.ops.leapdays,
+ year: this.ops.year,
+ month: this.ops.month,
+ day: this.ops.day,
+ weekday: this.ops.weekday
+ });
}
});
diff --git a/addons/web/static/test/evals.js b/addons/web/static/test/evals.js
index 13ac0f3f3b7..a85f973e874 100644
--- a/addons/web/static/test/evals.js
+++ b/addons/web/static/test/evals.js
@@ -261,9 +261,21 @@ openerp.testing.section('eval.types', {
});
test('relastivedelta', function (instance) {
strictEqual(
- py.eval("(datetime.date(2012, 2, 15) + relativedelta(days=-1)).strftime('%Y-%m-%d 23:59:59')",
+ py.eval("(datetime.date(2012, 2, 15) + relativedelta(days=1)).strftime('%Y-%m-%d')",
instance.web.pyeval.context()),
- "2012-02-14 23:59:59");
+ "2012-02-16");
+ strictEqual(
+ py.eval("(datetime.date(2012, 2, 15) + relativedelta(days=-1)).strftime('%Y-%m-%d')",
+ instance.web.pyeval.context()),
+ "2012-02-14");
+ strictEqual(
+ py.eval("(datetime.date(2012, 2, 1) + relativedelta(days=-1)).strftime('%Y-%m-%d')",
+ instance.web.pyeval.context()),
+ '2012-01-31');
+ strictEqual(
+ py.eval("(datetime.date(2015,2,5)+relativedelta(days=-6,weekday=0)).strftime('%Y-%m-%d')",
+ instance.web.pyeval.context()),
+ '2015-02-02');
});
test('datetime.tojson', function (instance) {
var result = py.eval(
diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml
index 0e4bd883aee..ff9d25f06e7 100644
--- a/addons/web/views/webclient_templates.xml
+++ b/addons/web/views/webclient_templates.xml
@@ -327,6 +327,9 @@
+