diff --git a/addons/account/account.py b/addons/account/account.py
index e598d9fce4b..bd3e015f2c7 100644
--- a/addons/account/account.py
+++ b/addons/account/account.py
@@ -2163,6 +2163,13 @@ class account_tax(osv.osv):
total += r['amount']
return res
+ def _fix_tax_included_price(self, cr, uid, price, prod_taxes, line_taxes):
+ """Subtract tax amount from price when corresponding "price included" taxes do not apply"""
+ incl_tax = [tax for tax in prod_taxes if tax.id not in line_taxes and tax.price_include]
+ if incl_tax:
+ return self._unit_compute_inv(cr, uid, incl_tax, price)[0]['price_unit']
+ return price
+
def _unit_compute_inv(self, cr, uid, taxes, price_unit, product=None, partner=None):
taxes = self._applicable(cr, uid, taxes, price_unit, product, partner)
res = []
diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py
index 355d1235290..51c815b0998 100644
--- a/addons/account/account_invoice.py
+++ b/addons/account/account_invoice.py
@@ -737,7 +737,7 @@ class account_invoice(models.Model):
if self.currency_id != company_currency:
currency = self.currency_id.with_context(date=self.date_invoice or fields.Date.context_today(self))
line['currency_id'] = currency.id
- line['amount_currency'] = line['price']
+ line['amount_currency'] = currency.round(line['price'])
line['price'] = currency.compute(line['price'], company_currency)
else:
line['currency_id'] = False
diff --git a/addons/account/partner_view.xml b/addons/account/partner_view.xml
index dd99a854135..80e6e7a2249 100644
--- a/addons/account/partner_view.xml
+++ b/addons/account/partner_view.xml
@@ -23,11 +23,11 @@
-
+
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py
index 9cac67f0634..1ac80be885a 100644
--- a/addons/point_of_sale/point_of_sale.py
+++ b/addons/point_of_sale/point_of_sale.py
@@ -449,7 +449,7 @@ class pos_session(osv.osv):
bank_statement_ids.append(statement_id)
values.update({
- 'name': self.pool['ir.sequence'].next_by_code(cr, uid, 'pos.session'),
+ 'name': self.pool['ir.sequence'].next_by_code(cr, uid, 'pos.session', context=context),
'statement_ids' : [(6, 0, bank_statement_ids)],
'config_id': config_id
})
diff --git a/addons/point_of_sale/report/pos_details.py b/addons/point_of_sale/report/pos_details.py
index bb784b98a45..005c2cc8d83 100644
--- a/addons/point_of_sale/report/pos_details.py
+++ b/addons/point_of_sale/report/pos_details.py
@@ -47,7 +47,7 @@ class pos_details(report_sxw.rml_parse):
user_ids = form['user_ids'] or self._get_all_users()
company_id = user_obj.browse(self.cr, self.uid, self.uid).company_id.id
pos_ids = pos_obj.search(self.cr, self.uid, [('date_order','>=',form['date_start'] + ' 00:00:00'),('date_order','<=',form['date_end'] + ' 23:59:59'),('user_id','in',user_ids),('state','in',['done','paid','invoiced']),('company_id','=',company_id)])
- for pos in pos_obj.browse(self.cr, self.uid, pos_ids):
+ for pos in pos_obj.browse(self.cr, self.uid, pos_ids, context=self.localcontext):
for pol in pos.lines:
result = {
'code': pol.product_id.default_code,
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index fccebe9e9df..3a977732467 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -1217,6 +1217,7 @@ class purchase_order_line(osv.osv):
taxes = account_tax.browse(cr, uid, map(lambda x: x.id, product.supplier_taxes_id))
fpos = fiscal_position_id and account_fiscal_position.browse(cr, uid, fiscal_position_id, context=context) or False
taxes_ids = account_fiscal_position.map_tax(cr, uid, fpos, taxes)
+ price = self.pool['account.tax']._fix_tax_included_price(cr, uid, price, product.supplier_taxes_id, taxes_ids)
res['value'].update({'price_unit': price, 'taxes_id': taxes_ids})
return res
diff --git a/addons/purchase/tests/__init__.py b/addons/purchase/tests/__init__.py
index ce452e2c94e..267ff6a4070 100644
--- a/addons/purchase/tests/__init__.py
+++ b/addons/purchase/tests/__init__.py
@@ -20,3 +20,4 @@
##############################################################################
from . import test_purchase_to_invoice
+from . import test_onchange_product_id
diff --git a/addons/purchase/tests/test_onchange_product_id.py b/addons/purchase/tests/test_onchange_product_id.py
new file mode 100644
index 00000000000..9abf7f1cb68
--- /dev/null
+++ b/addons/purchase/tests/test_onchange_product_id.py
@@ -0,0 +1,44 @@
+from openerp.tests.common import TransactionCase
+
+class TestOnchangeProductId(TransactionCase):
+ """Test that when an included tax is mapped by a fiscal position, the included tax must be
+ subtracted to the price of the product.
+ """
+
+ def setUp(self):
+ super(TestOnchangeProductId, self).setUp()
+ self.fiscal_position_model = self.registry('account.fiscal.position')
+ self.fiscal_position_tax_model = self.registry('account.fiscal.position.tax')
+ self.tax_model = self.registry('account.tax')
+ self.pricelist_model = self.registry('product.pricelist')
+ self.res_partner_model = self.registry('res.partner')
+ self.product_tmpl_model = self.registry('product.template')
+ self.product_model = self.registry('product.product')
+ self.product_uom_model = self.registry('product.uom')
+ self.so_line_model = self.registry('purchase.order.line')
+
+ def test_onchange_product_id(self):
+ cr, uid = self.cr, self.uid
+ uom_id = self.product_uom_model.search(cr, uid, [('name', '=', 'Unit(s)')])[0]
+ pricelist = self.pricelist_model.search(cr, uid, [('name', '=', 'Public Pricelist')])[0]
+ partner_id = self.res_partner_model.create(cr, uid, dict(name="George"))
+ tax_include_id = self.tax_model.create(cr, uid, dict(name="Include tax",
+ type='percent',
+ amount='0.21',
+ price_include=True))
+ tax_exclude_id = self.tax_model.create(cr, uid, dict(name="Exclude tax",
+ type='percent',
+ amount='0.00'))
+ product_tmpl_id = self.product_tmpl_model.create(cr, uid, dict(name="Voiture",
+ list_price='121',
+ supplier_taxes_id=[(6, 0, [tax_include_id])]))
+ product_id = self.product_model.create(cr, uid, dict(product_tmpl_id=product_tmpl_id))
+ fp_id = self.fiscal_position_model.create(cr, uid, dict(name="fiscal position",
+ sequence=1))
+ fp_tax_id = self.fiscal_position_tax_model.create(cr, uid, dict(position_id=fp_id,
+ tax_src_id=tax_include_id,
+ tax_dest_id=tax_exclude_id))
+ res = self.so_line_model.onchange_product_id(cr, uid, [], pricelist, product_id, 1.0, uom_id, partner_id,
+ fiscal_position_id=fp_id)
+
+ self.assertEquals(100, res['value']['price_unit'], "The included tax must be subtracted to the price")
diff --git a/addons/sale/sale.py b/addons/sale/sale.py
index 762fe0a70cb..cb1597d5e34 100644
--- a/addons/sale/sale.py
+++ b/addons/sale/sale.py
@@ -1052,7 +1052,7 @@ class sale_order_line(osv.osv):
date_order=order['date_order'],
fiscal_position=order['fiscal_position'][0] if order['fiscal_position'] else False,
flag=False, # Force name update
- context=context
+ context=dict(context or {}, company_id=values.get('company_id'))
)['value']
if defaults.get('tax_id'):
defaults['tax_id'] = [[6, 0, defaults['tax_id']]]
@@ -1108,8 +1108,8 @@ class sale_order_line(osv.osv):
# The superuser is used by website_sale in order to create a sale order. We need to make
# sure we only select the taxes related to the company of the partner. This should only
# apply if the partner is linked to a company.
- if uid == SUPERUSER_ID and partner.company_id:
- taxes = product_obj.taxes_id.filtered(lambda r: r.company_id == partner.company_id)
+ if uid == SUPERUSER_ID and context.get('company_id'):
+ taxes = product_obj.taxes_id.filtered(lambda r: r.company_id.id == context['company_id'])
else:
taxes = product_obj.taxes_id
result['tax_id'] = self.pool['account.fiscal.position'].map_tax(cr, uid, fpos, taxes)
@@ -1162,6 +1162,8 @@ class sale_order_line(osv.osv):
product, qty or 1.0, partner_id, ctx)[pricelist]
else:
price = Product.price_get(cr, uid, [product], ptype='list_price', context=ctx_product)[product] or False
+ if update_tax:
+ price = self.pool['account.tax']._fix_tax_included_price(cr, uid, price, product_obj.taxes_id, result['tax_id'])
if context.get('uom_qty_change', False):
result, domain = {}, {}
result.update({'price_unit': price})
diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml
index 4e054ad1bdc..900dadf999a 100644
--- a/addons/sale/sale_view.xml
+++ b/addons/sale/sale_view.xml
@@ -141,17 +141,17 @@
@@ -184,14 +184,15 @@
diff --git a/addons/sale/tests/__init__.py b/addons/sale/tests/__init__.py
index 303cdd6bc6e..d94274c6fca 100644
--- a/addons/sale/tests/__init__.py
+++ b/addons/sale/tests/__init__.py
@@ -20,3 +20,4 @@
##############################################################################
from . import test_sale_to_invoice
+from . import test_product_id_change
diff --git a/addons/sale/tests/test_product_id_change.py b/addons/sale/tests/test_product_id_change.py
new file mode 100644
index 00000000000..2f2957d3b4a
--- /dev/null
+++ b/addons/sale/tests/test_product_id_change.py
@@ -0,0 +1,41 @@
+from openerp.tests.common import TransactionCase
+
+class TestProductIdChange(TransactionCase):
+ """Test that when an included tax is mapped by a fiscal position, the included tax must be
+ subtracted to the price of the product.
+ """
+
+ def setUp(self):
+ super(TestProductIdChange, self).setUp()
+ self.fiscal_position_model = self.registry('account.fiscal.position')
+ self.fiscal_position_tax_model = self.registry('account.fiscal.position.tax')
+ self.tax_model = self.registry('account.tax')
+ self.pricelist_model = self.registry('product.pricelist')
+ self.res_partner_model = self.registry('res.partner')
+ self.product_tmpl_model = self.registry('product.template')
+ self.product_model = self.registry('product.product')
+ self.so_line_model = self.registry('sale.order.line')
+
+ def test_product_id_change(self):
+ cr, uid = self.cr, self.uid
+ pricelist = self.pricelist_model.search(cr, uid, [('name', '=', 'Public Pricelist')])[0]
+ partner_id = self.res_partner_model.create(cr, uid, dict(name="George"))
+ tax_include_id = self.tax_model.create(cr, uid, dict(name="Include tax",
+ type='percent',
+ amount='0.21',
+ price_include=True))
+ tax_exclude_id = self.tax_model.create(cr, uid, dict(name="Exclude tax",
+ type='percent',
+ amount='0.00'))
+ product_tmpl_id = self.product_tmpl_model.create(cr, uid, dict(name="Voiture",
+ list_price='121',
+ taxes_id=[(6, 0, [tax_include_id])]))
+ product_id = self.product_model.create(cr, uid, dict(product_tmpl_id=product_tmpl_id))
+ fp_id = self.fiscal_position_model.create(cr, uid, dict(name="fiscal position",
+ sequence=1))
+ fp_tax_id = self.fiscal_position_tax_model.create(cr, uid, dict(position_id=fp_id,
+ tax_src_id=tax_include_id,
+ tax_dest_id=tax_exclude_id))
+ res = self.so_line_model.product_id_change(cr, uid, [], pricelist, product_id, partner_id=partner_id,
+ fiscal_position=fp_id)
+ self.assertEquals(100, res['value']['price_unit'], "The included tax must be subtracted to the price")
diff --git a/addons/sale_stock/sale_stock_view.xml b/addons/sale_stock/sale_stock_view.xml
index 6171304e589..473ad7c3cce 100644
--- a/addons/sale_stock/sale_stock_view.xml
+++ b/addons/sale_stock/sale_stock_view.xml
@@ -40,7 +40,7 @@
parent.partner_id, False, True, parent.date_order, product_packaging, parent.fiscal_position,
False, parent.warehouse_id, context)
- {'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':False}
+ {'partner_id':parent.partner_id, 'quantity':product_uom_qty, 'pricelist':parent.pricelist_id, 'uom':False, 'company_id': parent.company_id}
product_id_change_with_wh(
@@ -76,7 +76,7 @@
-
+
diff --git a/addons/survey/survey.py b/addons/survey/survey.py
index 7a4617c328e..793715e26e8 100644
--- a/addons/survey/survey.py
+++ b/addons/survey/survey.py
@@ -848,7 +848,7 @@ class survey_user_input(osv.Model):
'survey_id': fields.many2one('survey.survey', 'Survey', required=True,
readonly=1, ondelete='restrict'),
'date_create': fields.datetime('Creation Date', required=True,
- readonly=1),
+ readonly=1, copy=False),
'deadline': fields.datetime("Deadline",
help="Date by which the person can open the survey and submit answers",
oldname="date_deadline"),
@@ -861,7 +861,7 @@ class survey_user_input(osv.Model):
'Status',
readonly=True),
'test_entry': fields.boolean('Test entry', readonly=1),
- 'token': fields.char("Identification token", readonly=1, required=1),
+ 'token': fields.char("Identification token", readonly=1, required=1, copy=False),
# Optional Identification data
'partner_id': fields.many2one('res.partner', 'Partner', readonly=1),
@@ -872,7 +872,7 @@ class survey_user_input(osv.Model):
'Last displayed page'),
# The answers !
'user_input_line_ids': fields.one2many('survey.user_input_line',
- 'user_input_id', 'Answers'),
+ 'user_input_id', 'Answers', copy=True),
# URLs used to display the answers
'result_url': fields.related('survey_id', 'result_url', type='char',
@@ -895,9 +895,6 @@ class survey_user_input(osv.Model):
('deadline_in_the_past', 'CHECK (deadline >= date_create)', 'The deadline cannot be in the past')
]
- def copy_data(self, cr, uid, id, default=None, context=None):
- raise UserError(_('You cannot duplicate this element!'))
-
def do_clean_emptys(self, cr, uid, automatic=False, context=None):
''' Remove empty user inputs that have been created manually
(used as a cronjob declared in data/survey_cron.xml) '''
@@ -1026,9 +1023,6 @@ class survey_user_input_line(osv.Model):
vals.update({'quizz_mark': self.__get_mark(cr, uid, value_suggested)})
return super(survey_user_input_line, self).write(cr, uid, ids, vals, context=context)
- def copy_data(self, cr, uid, id, default=None, context=None):
- raise UserError(_('You cannot duplicate this element!'))
-
def save_lines(self, cr, uid, user_input_id, question, post, answer_tag,
context=None):
''' Save answers to questions, depending on question type
diff --git a/addons/website_mail/controllers/main.py b/addons/website_mail/controllers/main.py
index 18bddd09a58..092465850f0 100644
--- a/addons/website_mail/controllers/main.py
+++ b/addons/website_mail/controllers/main.py
@@ -46,7 +46,8 @@ class WebsiteMail(http.Controller):
partner_ids = _object._find_partner_from_emails(
cr, SUPERUSER_ID, _id, [email], context=context, check_followers=True)
if not partner_ids or not partner_ids[0]:
- partner_ids = [partner_obj.create(cr, SUPERUSER_ID, {'name': email, 'email': email}, context=context)]
+ name = email.split('@')[0]
+ partner_ids = [partner_obj.create(cr, SUPERUSER_ID, {'name': name, 'email': email}, context=context)]
# add or remove follower
if _message_is_follower:
diff --git a/addons/website_quote/controllers/main.py b/addons/website_quote/controllers/main.py
index 6af6dbfc5bf..ace3bbb9f2b 100644
--- a/addons/website_quote/controllers/main.py
+++ b/addons/website_quote/controllers/main.py
@@ -184,7 +184,7 @@ class sale_quote(http.Controller):
res = request.registry.get('sale.order.line').product_id_change(request.cr, SUPERUSER_ID, order_id,
False, option.product_id.id, option.quantity, option.uom_id.id, option.quantity, option.uom_id.id,
option.name, order.partner_id.id, False, True, time.strftime('%Y-%m-%d'),
- False, order.fiscal_position.id, True, request.context)
+ False, order.fiscal_position.id, True, dict(request.context or {}, company_id=order.company_id.id))
vals = res.get('value', {})
if 'tax_id' in vals:
vals['tax_id'] = [(6, 0, vals['tax_id'])]
diff --git a/addons/website_quote/views/website_quotation_backend.xml b/addons/website_quote/views/website_quotation_backend.xml
index d8c12f51035..c2ff76c36de 100644
--- a/addons/website_quote/views/website_quotation_backend.xml
+++ b/addons/website_quote/views/website_quotation_backend.xml
@@ -43,7 +43,7 @@
-
+
diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py
index efa32ae5e71..b7054e527ca 100644
--- a/addons/website_sale/controllers/main.py
+++ b/addons/website_sale/controllers/main.py
@@ -245,6 +245,7 @@ class website_sale(http.Controller):
if category:
category = category_obj.browse(cr, uid, int(category), context=context)
+ category = category if category.exists() else False
attrib_list = request.httprequest.args.getlist('attrib')
attrib_values = [map(int,v.split("-")) for v in attrib_list if v]
diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py
index 608e50a787b..2867585fc36 100644
--- a/addons/website_sale/models/sale_order.py
+++ b/addons/website_sale/models/sale_order.py
@@ -57,7 +57,7 @@ class sale_order(osv.Model):
partner_id=so.partner_id.id,
fiscal_position=so.fiscal_position.id,
qty=qty,
- context=context
+ context=dict(context or {}, company_id=so.company_id.id)
)['value']
if line_id:
diff --git a/doc/howtos/backend.rst b/doc/howtos/backend.rst
index 45b02c60e80..a47bfc93a46 100644
--- a/doc/howtos/backend.rst
+++ b/doc/howtos/backend.rst
@@ -626,9 +626,9 @@ instead of a single view its ``arch`` field is composed of any number of
idea.category
-
-
+
@@ -652,6 +652,22 @@ instead of a single view its ``arch`` field is composed of any number of
alters the attributes of the matched element using special
``attribute`` elements in the ``xpath``'s body
+.. tip::
+
+ When matching a single element, the ``position`` attribute can be set directly
+ on the element to be found. Both inheritances below will give the same result.
+
+ .. code-block:: xml
+
+
+
+
+
+
+
+
+
+
.. exercise:: Alter existing content
* Using model inheritance, modify the existing *Partner* model to add an
diff --git a/openerp/addons/base/ir/ir_sequence.py b/openerp/addons/base/ir/ir_sequence.py
index e4db58481a6..c5fa55ac465 100644
--- a/openerp/addons/base/ir/ir_sequence.py
+++ b/openerp/addons/base/ir/ir_sequence.py
@@ -19,6 +19,7 @@
#
##############################################################################
import logging
+import pytz
import time
from datetime import datetime, timedelta
@@ -212,22 +213,14 @@ class ir_sequence(models.Model):
def _interpolation_dict():
if self.env.context.get('ir_sequence_date'):
- t = time.strptime(self.env.context.get('ir_sequence_date'), '%Y-%m-%d')
+ t = datetime.strptime(self.env.context.get('ir_sequence_date'), '%Y-%m-%d')
else:
- t = time.localtime() # Actually, the server is always in UTC.
- return {
- 'year': time.strftime('%Y', t),
- 'month': time.strftime('%m', t),
- 'day': time.strftime('%d', t),
- 'y': time.strftime('%y', t),
- 'doy': time.strftime('%j', t),
- 'woy': time.strftime('%W', t),
- 'weekday': time.strftime('%w', t),
- 'h24': time.strftime('%H', t),
- 'h12': time.strftime('%I', t),
- 'min': time.strftime('%M', t),
- 'sec': time.strftime('%S', t),
+ t = datetime.now(pytz.timezone(self.env.context.get('tz') or 'UTC'))
+ sequences = {
+ 'year': '%Y', 'month': '%m', 'day': '%d', 'y': '%y', 'doy': '%j', 'woy': '%W',
+ 'weekday': '%w', 'h24': '%H', 'h12': '%I', 'min': '%M', 'sec': '%S'
}
+ return {key: t.strftime(sequence) for key, sequence in sequences.iteritems()}
d = _interpolation_dict()
try: