diff --git a/addons/account/models/account_analytic_line.py b/addons/account/models/account_analytic_line.py
index a2f06c778d7..68ac8cbff8a 100644
--- a/addons/account/models/account_analytic_line.py
+++ b/addons/account/models/account_analytic_line.py
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
from openerp import api, fields, models, _
-from openerp.exceptions import UserError
class AccountAnalyticLine(models.Model):
@@ -21,17 +20,17 @@ class AccountAnalyticLine(models.Model):
partner_id = fields.Many2one('res.partner', related='account_id.partner_id', string='Partner', store=True)
@api.v8
- @api.onchange('product_id', 'product_uom_id')
+ @api.onchange('product_id', 'product_uom_id', 'unit_amount', 'currency_id')
def on_change_unit_amount(self):
- product_price_type_obj = self.env['product.price.type']
+ if not self.product_id:
+ return {}
+
result = 0.0
- unit = False
- if self.product_id:
- unit = self.product_uom_id.id
- if not self.product_uom_id or self.product_id.uom_id.category_id.id != self.product_uom_id.category_id.id:
- unit = self.product_id.uom_id.id
- account = self.product_id.property_account_income_id.id or self.product_id.categ_id.property_account_income_categ_id.id
- if not account: account = False
+ prod_accounts = self.product_id.product_tmpl_id._get_product_accounts()
+ unit = self.product_uom_id.id
+ account = prod_accounts['expense']
+ if not unit or self.product_id.uom_po_id.category_id.id != unit.category_id.id:
+ unit = self.product_id.uom_po_id.id
ctx = dict(self._context or {})
if unit:
@@ -39,16 +38,10 @@ class AccountAnalyticLine(models.Model):
# to return a default price for those units
ctx['uom'] = unit
- pricetype = False
- amount_unit = 0.0
- if self.product_id:
- # Compute based on pricetype
- pricetype = product_price_type_obj.search([('field', '=', 'list_price')], limit=1)
- amount_unit = self.product_id.with_context(ctx).price_get(pricetype.field)[self.product_id.id]
+ # Compute based on pricetype
+ amount_unit = self.product_id.with_context(ctx).price_get('standard_price')[self.product_id.id]
amount = amount_unit * self.unit_amount or 0.0
- result = round(amount, self.currency_id.decimal_places)
- if pricetype and pricetype.field != 'list_price':
- result *= -1
+ result = round(amount, self.currency_id.decimal_places) * -1
self.amount = result
self.general_account_id = account
self.product_uom_id = unit
diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py
index 5501ccf0739..32740b99ddc 100644
--- a/addons/account/models/chart_template.py
+++ b/addons/account/models/chart_template.py
@@ -703,7 +703,7 @@ class WizardMultiChartsAccounts(models.TransientModel):
# When we install the CoA of first company, set the currency to price types and pricelists
if company.id == 1:
- for reference in ['product.list_price', 'product.standard_price', 'product.list0', 'purchase.list0']:
+ for reference in ['product.list_price', 'product.standard_price', 'product.list0']:
try:
tmp2 = self.env.ref(reference).write({'currency_id': self.currency_id.id})
except ValueError:
diff --git a/addons/hr_expense/security/ir.model.access.csv b/addons/hr_expense/security/ir.model.access.csv
index bfc865a5243..3b59343b112 100644
--- a/addons/hr_expense/security/ir.model.access.csv
+++ b/addons/hr_expense/security/ir.model.access.csv
@@ -7,7 +7,6 @@ access_hr_expense_report_manager,hr.expense.report.manager,model_hr_expense_repo
access_product_product_hr_expense_user,product.product.hr.expense.user,product.model_product_product,base.group_hr_user,1,1,1,1
access_product_template_hr_expense_user,product.template.hr.expense.user,product.model_product_template,base.group_hr_user,1,1,1,1
access_product_uom_hr_expense_user,product.uom.hr.expense.user,product.model_product_uom,base.group_hr_user,1,1,1,1
-access_product_price_type_user,product.price.type.user,product.model_product_price_type,base.group_hr_user,1,1,1,1
access_account_journal_user,account.journal.user,account.model_account_journal,base.group_hr_user,1,1,1,1
access_account_journal_employee,account.journal.employee,account.model_account_journal,base.group_user,1,0,0,0
access_account_invoice_user,account.invoice.user,account.model_account_invoice,base.group_hr_user,1,1,1,1
diff --git a/addons/mrp/security/ir.model.access.csv b/addons/mrp/security/ir.model.access.csv
index a270620491a..89b966f4a08 100644
--- a/addons/mrp/security/ir.model.access.csv
+++ b/addons/mrp/security/ir.model.access.csv
@@ -62,13 +62,9 @@ access_product_category_mrp_manager,product.category mrp_manager,product.model_p
access_product_template_mrp_manager,product.template mrp_manager,product.model_product_template,mrp.group_mrp_manager,1,1,1,1
access_product_product_mrp_manager,product.product mrp_manager,product.model_product_product,mrp.group_mrp_manager,1,1,1,1
access_product_packaging_mrp_manager,product.packaging mrp_manager,product.model_product_packaging,mrp.group_mrp_manager,1,1,1,1
-access_pricelist_partnerinfo_mrp_manager,pricelist.partnerinfo mrp_manager,product.model_pricelist_partnerinfo,mrp.group_mrp_manager,1,1,1,1
-access_product_price_type_mrp_manager,product.price.type mrp_manager,product.model_product_price_type,mrp.group_mrp_manager,1,1,1,1
-access_product_pricelist_type_mrp_manager,product.pricelist.type mrp_manager,product.model_product_pricelist_type,mrp.group_mrp_manager,1,1,1,1
access_product_pricelist_mrp_manager,product.pricelist mrp_manager,product.model_product_pricelist,mrp.group_mrp_manager,1,1,1,1
access_ir_property_group_product_mrp_manager,ir_property group_product_mrp_manager,base.model_ir_property,mrp.group_mrp_manager,1,1,1,1
access_product_group_res_partner_mrp_manager,res_partner group_mrp_manager,base.model_res_partner,mrp.group_mrp_manager,1,1,1,0
-access_product_pricelist_version_mrp_manager,product.pricelist.version mrp_manager,product.model_product_pricelist_version,mrp.group_mrp_manager,1,1,1,1
access_product_pricelist_item_mrp_manager,product.pricelist.item mrp_manager,product.model_product_pricelist_item,mrp.group_mrp_manager,1,1,1,1
access_resource_calendar_manufacturinguser,resource.calendar manufacturing.user,resource.model_resource_calendar,mrp.group_mrp_user,1,0,0,0
access_account_journal_mrp_manager,account.journal mrp manager,account.model_account_journal,mrp.group_mrp_manager,1,0,0,0
diff --git a/addons/mrp_repair/mrp_repair.py b/addons/mrp_repair/mrp_repair.py
index 1f871a6a366..9fca802ae88 100644
--- a/addons/mrp_repair/mrp_repair.py
+++ b/addons/mrp_repair/mrp_repair.py
@@ -173,7 +173,7 @@ class mrp_repair(osv.osv):
'name': lambda obj, cr, uid, context: obj.pool.get('ir.sequence').next_by_code(cr, uid, 'mrp.repair'),
'invoice_method': lambda *a: 'none',
'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'mrp.repair', context=context),
- 'pricelist_id': lambda self, cr, uid, context: self.pool.get('product.pricelist').search(cr, uid, [('type', '=', 'sale')])[0],
+ 'pricelist_id': lambda self, cr, uid, context: self.pool['product.pricelist'].search(cr, uid, [], limit=1)[0],
'product_qty': 1.0,
'location_id': _default_stock_location,
}
@@ -229,7 +229,7 @@ class mrp_repair(osv.osv):
return {'value': {
'address_id': False,
'partner_invoice_id': False,
- 'pricelist_id': pricelist_obj.search(cr, uid, [('type', '=', 'sale')])[0]
+ 'pricelist_id': pricelist_obj.search(cr, uid, [], limit=1)[0]
}
}
addr = part_obj.address_get(cr, uid, [part], ['delivery', 'invoice', 'contact'])
diff --git a/addons/mrp_repair/security/ir.model.access.csv b/addons/mrp_repair/security/ir.model.access.csv
index 95f2462909a..89a7d680ae0 100644
--- a/addons/mrp_repair/security/ir.model.access.csv
+++ b/addons/mrp_repair/security/ir.model.access.csv
@@ -13,8 +13,6 @@ access_stock_production_lot_user,stock.production.lot user,stock.model_stock_pro
access_stock_production_lot_manager,stock.production.lot manager,stock.model_stock_production_lot,stock.group_stock_manager,1,0,0,0
access_mrp_repair_line_user,mrp.repair.line user,model_mrp_repair_line,stock.group_stock_user,1,1,1,1
access_mrp_repair_line_manager,mrp.repair.line manager,model_mrp_repair_line,stock.group_stock_manager,1,0,0,0
-access_product_price_type_manager,product.price.type manager,product.model_product_price_type,stock.group_stock_manager,1,0,0,0
-access_product_price_type_user,product.price.type,product.model_product_price_type,stock.group_stock_user,1,1,1,1
access_account_tax_user,account.tax,account.model_account_tax,stock.group_stock_user,1,1,1,1
access_account_tax_manager,account.tax manager,account.model_account_tax,stock.group_stock_manager,1,0,0,0
access_mrp_repair_fee_user_mrp,MRP Repair Fee user mrp,model_mrp_repair_fee,stock.group_stock_user,1,1,1,1
diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py
index 228d136d2c8..bbc5d2497e5 100644
--- a/addons/point_of_sale/point_of_sale.py
+++ b/addons/point_of_sale/point_of_sale.py
@@ -179,7 +179,7 @@ class pos_config(osv.osv):
return res and res[0] or False
def _default_pricelist(self, cr, uid, context=None):
- res = self.pool.get('product.pricelist').search(cr, uid, [('type', '=', 'sale')], limit=1, context=context)
+ res = self.pool.get('product.pricelist').search(cr, uid, [], limit=1, context=context)
return res and res[0] or False
def _get_default_location(self, cr, uid, context=None):
diff --git a/addons/product/partner.py b/addons/product/partner.py
index 08c5354afc3..35db117460c 100644
--- a/addons/product/partner.py
+++ b/addons/product/partner.py
@@ -11,7 +11,6 @@ class res_partner(osv.osv):
'property_product_pricelist': fields.property(
type='many2one',
relation='product.pricelist',
- domain=[('type','=','sale')],
string="Sale Pricelist",
help="This pricelist will be used, instead of the default one, for sales to the current partner"),
}
diff --git a/addons/product/partner_view.xml b/addons/product/partner_view.xml
index 813e8c9a59a..61a4d11186b 100644
--- a/addons/product/partner_view.xml
+++ b/addons/product/partner_view.xml
@@ -5,7 +5,7 @@
res.partner.product.property.form.inheritres.partner
-
+
diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py
index 3f11cde9948..a70d8bc7ea3 100644
--- a/addons/product/pricelist.py
+++ b/addons/product/pricelist.py
@@ -5,79 +5,18 @@ from itertools import chain
import time
from openerp import tools
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from openerp.osv import fields, osv
from openerp.tools.translate import _
import openerp.addons.decimal_precision as dp
from openerp.exceptions import UserError
-
-class price_type(osv.osv):
- """
- The price type is used to points which field in the product form
- is a price and in which currency is this price expressed.
- When a field is a price, you can use it in pricelists to base
- sale and purchase prices based on some fields of the product.
- """
- def _price_field_get(self, cr, uid, context=None):
- mf = self.pool.get('ir.model.fields')
- ids = mf.search(cr, uid, [('model','in', (('product.product'),('product.template'))), ('ttype','=','float')], context=context)
- res = []
- for field in mf.browse(cr, uid, ids, context=context):
- if not (field.name, field.field_description) in res:
- res.append((field.name, field.field_description))
- return res
-
- def _get_field_currency(self, cr, uid, fname, ctx):
- ids = self.search(cr, uid, [('field','=',fname)], context=ctx)
- return self.browse(cr, uid, ids, context=ctx)[0].currency_id
-
- def _get_currency(self, cr, uid, ctx):
- comp = self.pool.get('res.users').browse(cr,uid,uid).company_id
- if not comp:
- comp_id = self.pool.get('res.company').search(cr, uid, [])[0]
- comp = self.pool.get('res.company').browse(cr, uid, comp_id)
- return comp.currency_id.id
-
- _name = "product.price.type"
- _description = "Price Type"
- _columns = {
- "name" : fields.char("Price Name", required=True, translate=True, help="Name of this kind of price."),
- "active" : fields.boolean("Active"),
- "field" : fields.selection(_price_field_get, "Product Field", size=32, required=True, help="Associated field in the product form."),
- "currency_id" : fields.many2one('res.currency', "Currency", required=True, help="The currency the field is expressed in."),
- }
- _defaults = {
- "active": lambda *args: True,
- "currency_id": _get_currency
- }
-
-
#----------------------------------------------------------
# Price lists
#----------------------------------------------------------
-class product_pricelist_type(osv.osv):
- _name = "product.pricelist.type"
- _description = "Pricelist Type"
- _columns = {
- 'name': fields.char('Name', required=True, translate=True),
- 'key': fields.char('Key', required=True, help="Used in the code to select specific prices based on the context. Keep unchanged."),
- }
-
-
class product_pricelist(osv.osv):
- def _pricelist_type_get(self, cr, uid, context=None):
- pricelist_type_obj = self.pool.get('product.pricelist.type')
- pricelist_type_ids = pricelist_type_obj.search(cr, uid, [], order='name')
- pricelist_types = pricelist_type_obj.read(cr, uid, pricelist_type_ids, ['key','name'], context=context)
-
- res = []
-
- for type in pricelist_types:
- res.append((type['key'],type['name']))
-
- return res
_name = "product.pricelist"
_description = "Pricelist"
@@ -85,8 +24,7 @@ class product_pricelist(osv.osv):
_columns = {
'name': fields.char('Pricelist Name', required=True, translate=True),
'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the pricelist without removing it."),
- 'type': fields.selection(_pricelist_type_get, 'Pricelist Type', required=True),
- 'version_id': fields.one2many('product.pricelist.version', 'pricelist_id', 'Pricelist Versions', copy=True),
+ 'item_ids': fields.one2many('product.pricelist.item', 'pricelist_id', 'Pricelist Items', copy=True),
'currency_id': fields.many2one('res.currency', 'Currency', required=True),
'company_id': fields.many2one('res.company', 'Company'),
}
@@ -139,7 +77,6 @@ class product_pricelist(osv.osv):
return super(product_pricelist, self).name_search(
cr, uid, name, args, operator=operator, context=context, limit=limit)
-
def _get_currency(self, cr, uid, ctx):
comp = self.pool.get('res.users').browse(cr, uid, uid).company_id
if not comp:
@@ -152,9 +89,6 @@ class product_pricelist(osv.osv):
"currency_id": _get_currency
}
- def price_get_multi(self, cr, uid, ids, products_by_qty_by_partner, context=None):
- return dict((key, dict((key, price[0]) for key, price in value.items())) for key, value in self.price_rule_get_multi(cr, uid, ids, products_by_qty_by_partner, context=context).items())
-
def price_rule_get_multi(self, cr, uid, ids, products_by_qty_by_partner, context=None):
"""multi products 'price_get'.
@param ids:
@@ -169,7 +103,7 @@ class product_pricelist(osv.osv):
results = {}
for pricelist in self.browse(cr, uid, ids, context=context):
subres = self._price_rule_get_multi(cr, uid, pricelist, products_by_qty_by_partner, context=context)
- for product_id,price in subres.items():
+ for product_id, price in subres.items():
results.setdefault(product_id, {})
results[product_id][pricelist.id] = price
return results
@@ -179,25 +113,13 @@ class product_pricelist(osv.osv):
def _price_rule_get_multi(self, cr, uid, pricelist, products_by_qty_by_partner, context=None):
context = context or {}
- date = context.get('date') or time.strftime('%Y-%m-%d')
- date = date[0:10]
-
+ date = context.get('date') and context['date'][0:10] or time.strftime(DEFAULT_SERVER_DATE_FORMAT)
products = map(lambda x: x[0], products_by_qty_by_partner)
- currency_obj = self.pool.get('res.currency')
- product_obj = self.pool.get('product.template')
product_uom_obj = self.pool.get('product.uom')
- price_type_obj = self.pool.get('product.price.type')
if not products:
return {}
- version = False
- for v in pricelist.version_id:
- if ((v.date_start is False) or (v.date_start <= date)) and ((v.date_end is False) or (v.date_end >= date)):
- version = v
- break
- if not version:
- raise UserError(_("At least one pricelist has no active version !\nPlease create or activate one."))
categ_ids = {}
for p in products:
categ = p.categ_id
@@ -220,22 +142,20 @@ class product_pricelist(osv.osv):
cr.execute(
'SELECT i.id '
'FROM product_pricelist_item AS i '
- 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = any(%s)) '
- 'AND (product_id IS NULL OR (product_id = any(%s))) '
- 'AND ((categ_id IS NULL) OR (categ_id = any(%s))) '
- 'AND (price_version_id = %s) '
- 'ORDER BY sequence, min_quantity desc',
- (prod_tmpl_ids, prod_ids, categ_ids, version.id))
-
+ 'WHERE (product_tmpl_id IS NULL OR product_tmpl_id = any(%s))'
+ 'AND (product_id IS NULL OR product_id = any(%s))'
+ 'AND (categ_id IS NULL OR categ_id = any(%s)) '
+ 'AND (pricelist_id = %s) '
+ 'AND ((i.date_start IS NULL OR i.date_start<=%s) AND (i.date_end IS NULL OR i.date_end>=%s))'
+ 'ORDER BY applied_on, min_quantity desc',
+ (prod_tmpl_ids, prod_ids, categ_ids, pricelist.id, date, date))
+
item_ids = [x[0] for x in cr.fetchall()]
items = self.pool.get('product.pricelist.item').browse(cr, uid, item_ids, context=context)
-
- price_types = {}
-
results = {}
for product, qty, partner in products_by_qty_by_partner:
results[product.id] = 0.0
- rule_id = False
+ suitable_rule = False
# Final unit price is computed according to `qty` in the `qty_uom_id` UoM.
# An intermediary unit price may be computed according to a different UoM, in
@@ -252,9 +172,10 @@ class product_pricelist(osv.osv):
# Ignored - incompatible UoM in context, use default product UoM
pass
- price_type = 'standard_price' if pricelist.type == 'purchase' else 'list_price'
# if Public user try to access standard price from website sale, need to call _price_get.
- price = product_obj._price_get(cr, uid, [product], price_type, context=context)[product.id] or False
+ price = self.pool['product.template']._price_get(cr, uid, [product], 'list_price', context=context)[product.id]
+
+ price_uom_id = qty_uom_id
for rule in items:
if rule.min_quantity and qty_in_product_uom < rule.min_quantity:
continue
@@ -271,68 +192,50 @@ class product_pricelist(osv.osv):
if rule.product_id and product.id != rule.product_id.id:
continue
- if rule.base == -1:
- if rule.base_pricelist_id:
- price_tmp = self._price_get_multi(cr, uid,
- rule.base_pricelist_id, [(product,
- 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,
- ptype_src, pricelist.currency_id.id,
- price_tmp, round=False,
- context=context)
+ if rule.base == 'pricelist' and rule.base_pricelist_id:
+ price_tmp = self._price_get_multi(cr, uid, rule.base_pricelist_id, [(product, qty, partner)], context=context)[product.id]
+ ptype_src = rule.base_pricelist_id.currency_id.id
+ price = self.pool['res.currency'].compute(cr, uid, ptype_src, pricelist.currency_id.id, price_tmp, round=False, context=context)
else:
- if rule.base not in price_types:
- price_types[rule.base] = price_type_obj.browse(cr, uid, int(rule.base))
- price_type = price_types[rule.base]
-
+ # if base option is public price take sale price else cost price of product
# price_get returns the price in the context UoM, i.e. qty_uom_id
- price_uom_id = qty_uom_id
- price = currency_obj.compute(
- cr, uid,
- price_type.currency_id.id, pricelist.currency_id.id,
- product_obj._price_get(cr, uid, [product], price_type.field, context=context)[product.id],
- round=False, context=context)
- for seller in product.seller_ids:
- partner = partner.id if partner and not isinstance(partner, int) else partner
- if seller.name.id == partner:
- qty_in_seller = qty
- seller_uom = seller.product_uom and seller.product_uom.id or False
- if qty_uom_id != seller_uom:
- qty_in_seller = product_uom_obj._compute_qty(cr, uid, qty_uom_id, qty, to_uom_id=seller_uom)
- for line in seller.pricelist_ids:
- if line.min_quantity <= qty_in_seller:
- price = line.price
+ price = self.pool['product.template']._price_get(cr, uid, [product], rule.base, context=context)[product.id]
+
+ convert_to_price_uom = (lambda price: product_uom_obj._compute_price(
+ cr, uid, product.uom_id.id,
+ price, price_uom_id))
if price is not False:
- price_limit = price
- price = price * (1.0+(rule.price_discount or 0.0))
- if rule.price_round:
- price = tools.float_round(price, precision_rounding=rule.price_round)
+ if rule.compute_price == 'fixed':
+ price = convert_to_price_uom(rule.fixed_price)
+ elif rule.compute_price == 'percentage':
+ price = (price - (price * (rule.percent_price / 100))) or 0.0
+ else:
+ #complete formula
+ price_limit = price
+ price = (price - (price * (rule.price_discount / 100))) or 0.0
+ if rule.price_round:
+ price = tools.float_round(price, precision_rounding=rule.price_round)
- convert_to_price_uom = (lambda price: product_uom_obj._compute_price(
- cr, uid, product.uom_id.id,
- price, price_uom_id))
- if rule.price_surcharge:
- price_surcharge = convert_to_price_uom(rule.price_surcharge)
- price += price_surcharge
+ if rule.price_surcharge:
+ price_surcharge = convert_to_price_uom(rule.price_surcharge)
+ price += price_surcharge
- if rule.price_min_margin:
- price_min_margin = convert_to_price_uom(rule.price_min_margin)
- price = max(price, price_limit + price_min_margin)
+ if rule.price_min_margin:
+ price_min_margin = convert_to_price_uom(rule.price_min_margin)
+ price = max(price, price_limit + price_min_margin)
- if rule.price_max_margin:
- price_max_margin = convert_to_price_uom(rule.price_max_margin)
- price = min(price, price_limit + price_max_margin)
-
- rule_id = rule.id
+ if rule.price_max_margin:
+ price_max_margin = convert_to_price_uom(rule.price_max_margin)
+ price = min(price, price_limit + price_max_margin)
+ suitable_rule = rule
break
+ # Final price conversion into pricelist currency
+ if suitable_rule and suitable_rule.compute_price != 'fixed' and suitable_rule.base != 'pricelist':
+ user_company = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id
+ price = self.pool['res.currency'].compute(cr, uid, user_company.currency_id.id, pricelist.currency_id.id, price, context=context)
- # Final price conversion to target UoM
- price = product_uom_obj._compute_price(cr, uid, price_uom_id, price, qty_uom_id)
-
- results[product.id] = (price, rule_id)
+ results[product.id] = (price, suitable_rule and suitable_rule.id or False)
return results
def price_get(self, cr, uid, ids, prod_id, qty, partner=None, context=None):
@@ -344,104 +247,15 @@ class product_pricelist(osv.osv):
res = res_multi[prod_id]
return res
-
-class product_pricelist_version(osv.osv):
- _name = "product.pricelist.version"
- _description = "Pricelist Version"
- _columns = {
- 'pricelist_id': fields.many2one('product.pricelist', 'Price List',
- required=True, select=True, ondelete='cascade'),
- 'name': fields.char('Name', required=True, translate=True),
- 'active': fields.boolean('Active',
- help="When a version is duplicated it is set to non active, so that the " \
- "dates do not overlaps with original version. You should change the dates " \
- "and reactivate the pricelist"),
- 'items_id': fields.one2many('product.pricelist.item',
- 'price_version_id', 'Price List Items', required=True, copy=True),
- 'date_start': fields.date('Start Date', help="First valid date for the version."),
- 'date_end': fields.date('End Date', help="Last valid date for the version."),
- 'company_id': fields.related('pricelist_id','company_id',type='many2one',
- readonly=True, relation='res.company', string='Company', store=True)
- }
- _defaults = {
- 'active': lambda *a: 1,
- }
-
- def _check_date(self, cursor, user, ids, context=None):
- for pricelist_version in self.browse(cursor, user, ids, context=context):
- if not pricelist_version.active:
- continue
- where = []
- if pricelist_version.date_start:
- where.append("((date_end>='%s') or (date_end is null))" % (pricelist_version.date_start,))
- if pricelist_version.date_end:
- where.append("((date_start<='%s') or (date_start is null))" % (pricelist_version.date_end,))
-
- cursor.execute('SELECT id ' \
- 'FROM product_pricelist_version ' \
- 'WHERE '+' and '.join(where) + (where and ' and ' or '')+
- 'pricelist_id = %s ' \
- 'AND active ' \
- 'AND id <> %s', (
- pricelist_version.pricelist_id.id,
- pricelist_version.id))
- if cursor.fetchall():
- return False
- return True
-
- _constraints = [
- (_check_date, 'You cannot have 2 pricelist versions that overlap!',
- ['date_start', 'date_end'])
- ]
-
- def copy(self, cr, uid, id, default=None, context=None):
- # set active False to prevent overlapping active pricelist
- # versions
- if not default:
- default = {}
- default['active'] = False
- return super(product_pricelist_version, self).copy(cr, uid, id, default, context=context)
-
class product_pricelist_item(osv.osv):
- def _price_field_get(self, cr, uid, context=None):
- pt = self.pool.get('product.price.type')
- ids = pt.search(cr, uid, [], context=context)
- result = []
- for line in pt.browse(cr, uid, ids, context=context):
- result.append((line.id, line.name))
-
- result.append((-1, _('Other Pricelist')))
- return result
-
-# Added default function to fetch the Price type Based on Pricelist type.
- def _get_default_base(self, cr, uid, fields, context=None):
- product_price_type_obj = self.pool.get('product.price.type')
- if fields.get('type') == 'purchase':
- product_price_type_ids = product_price_type_obj.search(cr, uid, [('field', '=', 'standard_price')], context=context)
- elif fields.get('type') == 'sale':
- product_price_type_ids = product_price_type_obj.search(cr, uid, [('field','=','list_price')], context=context)
- else:
- return -1
- if not product_price_type_ids:
- return False
- else:
- pricetype = product_price_type_obj.browse(cr, uid, product_price_type_ids, context=context)[0]
- return pricetype.id
-
_name = "product.pricelist.item"
_description = "Pricelist item"
- _order = "sequence, min_quantity desc"
- _defaults = {
- 'base': _get_default_base,
- 'min_quantity': lambda *a: 0,
- 'sequence': lambda *a: 5,
- 'price_discount': lambda *a: 0,
- }
+ _order = "applied_on, min_quantity desc"
def _check_recursion(self, cr, uid, ids, context=None):
for obj_list in self.browse(cr, uid, ids, context=context):
- if obj_list.base == -1:
- main_pricelist = obj_list.price_version_id.pricelist_id.id
+ if obj_list.base == 'pricelist':
+ main_pricelist = obj_list.pricelist_id.id
other_pricelist = obj_list.base_pricelist_id.id
if main_pricelist == other_pricelist:
return False
@@ -453,26 +267,48 @@ class product_pricelist_item(osv.osv):
return False
return True
+ def _get_pricelist_item_name_price(self, cr, uid, ids, fields, args, context=None):
+ """This function is used to set some fields used for usability purposes only (state explicitly what a rule does)
+ """
+ res = {}
+ for item in self.browse(cr, uid, ids, context=context):
+ res[item.id] = {'name': '', 'price': ''}
+ if item.categ_id:
+ res[item.id]['name'] = _("Category: %s") % (item.categ_id.name)
+ elif item.product_tmpl_id:
+ res[item.id]['name'] = item.product_tmpl_id.name
+ elif item.product_id:
+ res[item.id]['name'] = item.product_id.display_name.replace('[%s]' % item.product_id.code, '')
+ else:
+ res[item.id]['name'] = _("All Products")
+
+ if item.compute_price == 'fixed':
+ res[item.id]['price'] = ("%s %s") % (item.fixed_price, item.pricelist_id.currency_id.name)
+ elif item.compute_price == 'percentage':
+ res[item.id]['price'] = _("%s %% discount") % (item.percent_price)
+ else:
+ res[item.id]['price'] = _("%s %% discount and %s surcharge") % (abs(item.price_discount), item.price_surcharge)
+ return res
+
_columns = {
- 'name': fields.char('Rule Name', help="Explicit rule name for this pricelist line."),
- 'price_version_id': fields.many2one('product.pricelist.version', 'Price List Version', required=True, select=True, ondelete='cascade'),
'product_tmpl_id': fields.many2one('product.template', 'Product Template', ondelete='cascade', help="Specify a template if this rule only applies to one product template. Keep empty otherwise."),
'product_id': fields.many2one('product.product', 'Product', ondelete='cascade', help="Specify a product if this rule only applies to one product. Keep empty otherwise."),
'categ_id': fields.many2one('product.category', 'Product Category', ondelete='cascade', help="Specify a product category if this rule only applies to products belonging to this category or its children categories. Keep empty otherwise."),
- 'min_quantity': fields.integer('Min. Quantity', required=True,
+ 'min_quantity': fields.integer('Min. Quantity',
help="For the rule to apply, bought/sold quantity must be greater "
"than or equal to the minimum quantity specified in this field.\n"
"Expressed in the default unit of measure of the product."
),
+ 'applied_on': fields.selection([('3_global', 'Global'),('2_product_category', ' Product Category'), ('1_product', 'Product'), ('0_product_variant', 'Product Variant')], string="Apply On", required=True,
+ help='Pricelist Item applicable on selected option'),
'sequence': fields.integer('Sequence', required=True, help="Gives the order in which the pricelist items will be checked. The evaluation gives highest priority to lowest sequence and stops as soon as a matching item is found."),
- 'base': fields.selection(_price_field_get, 'Based on', required=True,
- size=-1, # here use size=-1 to store the values as integers
- help='Base price for computation. \n Public Price: The base price will be the Sale/public Price. \n Vendor Section on Product or Cost Price : The base price will be the vendor price if it is set, otherwise it will be the cost price. \n Other Pricelist : Computation of the base price based on another Pricelist.'),
+ 'base': fields.selection([('list_price', 'Public Price'), ('standard_price', 'Cost'), ('pricelist', 'Other Pricelist')], string="Based on", required=True,
+ help='Base price for computation. \n Public Price: The base price will be the Sale/public Price. \n Cost Price : The base price will be the cost price. \n Other Pricelist : Computation of the base price based on another Pricelist.'),
'base_pricelist_id': fields.many2one('product.pricelist', 'Other Pricelist'),
-
+ 'pricelist_id': fields.many2one('product.pricelist', 'Pricelist'),
'price_surcharge': fields.float('Price Surcharge',
digits_compute= dp.get_precision('Product Price'), help='Specify the fixed amount to add or substract(if negative) to the amount calculated with the discount.'),
- 'price_discount': fields.float('Price Discount', digits=(16,4)),
+ 'price_discount': fields.float('Price Discount', digits=(16,2)),
'price_round': fields.float('Price Rounding',
digits_compute= dp.get_precision('Product Price'),
help="Sets the price so that it is a multiple of this value.\n" \
@@ -483,19 +319,28 @@ class product_pricelist_item(osv.osv):
digits_compute= dp.get_precision('Product Price'), help='Specify the minimum amount of margin over the base price.'),
'price_max_margin': fields.float('Max. Price Margin',
digits_compute= dp.get_precision('Product Price'), help='Specify the maximum amount of margin over the base price.'),
- 'company_id': fields.related('price_version_id','company_id',type='many2one',
- readonly=True, relation='res.company', string='Company', store=True)
+ 'company_id': fields.related('pricelist_id','company_id',type='many2one',
+ readonly=True, relation='res.company', string='Company', store=True),
+ 'currency_id': fields.related('pricelist_id', 'currency_id', type='many2one',
+ readonly=True, relation='res.currency', string='Currency', store=True),
+ 'date_start': fields.date('Start Date', help="Starting date for the pricelist item validation"),
+ 'date_end': fields.date('End Date', help="Ending valid for the pricelist item validation"),
+ 'compute_price': fields.selection([('fixed', 'Fix Price'), ('percentage', 'Percentage (discount)'), ('formula', 'Formula')], select=True, default='fixed'),
+ 'fixed_price': fields.float('Fixed Price'),
+ 'percent_price': fields.float('Percentage Price'),
+ #functional fields used for usability purposes
+ 'name': fields.function(_get_pricelist_item_name_price, type="char", string='Name', multi='item_name_price', help="Explicit rule name for this pricelist line."),
+ 'price': fields.function(_get_pricelist_item_name_price, type="char", string='Price', multi='item_name_price', help="Explicit rule name for this pricelist line."),
}
+ _defaults = {
+ 'base': 'list_price',
+ 'min_quantity': 1,
+ 'sequence': 5,
+ 'price_discount': 0,
+ 'applied_on': '3_global',
+ }
_constraints = [
(_check_recursion, 'Error! You cannot assign the Main Pricelist as Other Pricelist in PriceList Item!', ['base_pricelist_id']),
(_check_margin, 'Error! The minimum margin should be lower than the maximum margin.', ['price_min_margin', 'price_max_margin'])
]
-
- def product_id_change(self, cr, uid, ids, product_id, context=None):
- if not product_id:
- return {}
- prod = self.pool.get('product.product').read(cr, uid, [product_id], ['code','name'])
- if prod[0]['code']:
- return {'value': {'name': prod[0]['code']}}
- return {}
diff --git a/addons/product/pricelist_view.xml b/addons/product/pricelist_view.xml
index 9608b10c45f..7fd93edce64 100644
--- a/addons/product/pricelist_view.xml
+++ b/addons/product/pricelist_view.xml
@@ -7,77 +7,19 @@
-
- product.pricelist.version.form
- product.pricelist.version
-
-
-
-
-
- product.pricelist.version.tree
- product.pricelist.version
-
-
-
-
-
-
-
-
-
-
-
- Pricelist Versions
- ir.actions.act_window
- product.pricelist.version
- form
-
-
-
- Click to add a pricelist version.
-
- There can be more than one version of a pricelist, each of
- these must be valid during a certain period of time. Some
- examples of versions: Main Prices, 2010, 2011, Summer Sales,
- etc.
-
Click to create a pricelist.
@@ -199,66 +155,15 @@
A price list contains rules to be evaluated in order to compute
the sales price of the products.
- Price lists may have several versions (2010, 2011, Promotion of
- February 2010, etc.) and each version may have several rules.
- (e.g. the customer price of a product category will be based on
- the supplier price multiplied by 1.80).
-
- A price list contains rules to be evaluated in order to compute
- the purchase price. The default price list has only one rule; use
- the cost price defined on the product form, so that you do not have to
- worry about supplier pricelists if you have very simple needs.
-
- But you can also import complex price lists form your supplier
- that may depends on the quantities ordered or the current
- promotions.
+ Each rule include a set of applicability criteria (date range,
+ product category...) and a computation that easily helps to achieve
+ any kind of pricing.
-
-
-
-
- Price Types
- ir.actions.act_window
- product.price.type
- form
- tree,form
-
-
-
-
diff --git a/addons/product/product.py b/addons/product/product.py
index a03a80b6772..649c69f9eef 100644
--- a/addons/product/product.py
+++ b/addons/product/product.py
@@ -7,7 +7,7 @@ import time
from openerp import api, tools, SUPERUSER_ID
from openerp.osv import osv, fields, expression
from openerp.tools.translate import _
-from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
+from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT
import psycopg2
import openerp.addons.decimal_precision as dp
@@ -400,7 +400,7 @@ class product_template(osv.osv):
products = self.browse(cr, uid, ids, context=context)
qtys = map(lambda x: (x, quantity, partner), products)
pl = plobj.browse(cr, uid, pricelist, context=context)
- price = plobj._price_get_multi(cr,uid, pl, qtys, context=context)
+ price = plobj._price_get_multi(cr, uid, pl, qtys, context=context)
for id in ids:
res[id] = price.get(id, 0.0)
for id in ids:
@@ -466,6 +466,28 @@ class product_template(osv.osv):
return variant.write({name: value})
return {}
+ def _select_seller(self, cr, uid, ids, name, arg, context=None):
+ if context is None:
+ context = {}
+ res = {}
+ partner = context.get('partner')
+ minimal_quantity = context.get('quantity', 0.0)
+ date = context.get('date', time.strftime(DEFAULT_SERVER_DATE_FORMAT))
+ for product in self.browse(cr, uid, ids, context=context):
+ res[product.id] = False
+ for seller in product.seller_ids:
+ if seller.date_start and seller.date_start > date:
+ continue
+ if seller.date_end and seller.date_end < date:
+ continue
+ if partner and seller.name.id != partner:
+ continue
+ if minimal_quantity and minimal_quantity < seller.qty:
+ continue
+ res[product.id] = seller
+ break
+ return res
+
def _get_product_template_type(self, cr, uid, context=None):
return [('consu', 'Consumable'), ('service', 'Service')]
_get_product_template_type_wrapper = lambda self, *args, **kwargs: self._get_product_template_type(*args, **kwargs)
@@ -531,38 +553,35 @@ class product_template(osv.osv):
help="Gives the different ways to package the same product. This has no impact on "
"the picking order and is mainly used if you use the EDI module."),
'seller_ids': fields.one2many('product.supplierinfo', 'product_tmpl_id', 'Vendor'),
- 'seller_delay': fields.related('seller_ids','delay', type='integer', string='Vendor Lead Time',
+ 'selected_seller_id': fields.function(_select_seller, type='many2one', relation='product.supplierinfo', string='Selected Seller', help='Technical field that selects a seller based on priority (sequence) and an optional partner and/or a minimal quantity in the context'),
+ 'seller_delay': fields.related('selected_seller_id','delay', type='integer', string='Vendor Lead Time',
help="This is the average delay in days between the purchase order confirmation and the receipts for this product and for the default vendor. It is used by the scheduler to order requests based on reordering delays."),
- 'seller_qty': fields.related('seller_ids','qty', type='float', string='Vendor Quantity',
+ 'seller_qty': fields.related('selected_seller_id','qty', type='float', string='Vendor Quantity',
help="This is minimum quantity to purchase from Main Vendor."),
- 'seller_id': fields.related('seller_ids','name', type='many2one', relation='res.partner', string='Main Vendor',
+ 'seller_id': fields.related('selected_seller_id','name', type='many2one', relation='res.partner', string='Main Vendor',
help="Main vendor who has highest priority in vendor list."),
+ 'seller_price': fields.related('selected_seller_id','price', type='float', string='Vendor Price', help="Purchase price from from Main Vendor."),
'active': fields.boolean('Active', help="If unchecked, it will allow you to hide the product without removing it."),
'color': fields.integer('Color Index'),
- 'is_product_variant': fields.function( _is_product_variant, type='boolean', string='Is a product variant'),
+ 'is_product_variant': fields.function(_is_product_variant, type='boolean', string='Is a product variant'),
'attribute_line_ids': fields.one2many('product.attribute.line', 'product_tmpl_id', 'Product Attributes'),
'product_variant_ids': fields.one2many('product.product', 'product_tmpl_id', 'Products', required=True),
- 'product_variant_count': fields.function( _get_product_variant_count, type='integer', string='# of Product Variants'),
+ 'product_variant_count': fields.function(_get_product_variant_count, type='integer', string='# of Product Variants'),
# related to display product product information if is_product_variant
'barcode': fields.related('product_variant_ids', 'barcode', type='char', string='Barcode', oldname='ean13'),
'default_code': fields.related('product_variant_ids', 'default_code', type='char', string='Internal Reference'),
+ 'item_ids': fields.one2many('product.pricelist.item', 'product_tmpl_id', 'Pricelist Items'),
}
- def _price_get_list_price(self, product):
- return 0.0
-
def _price_get(self, cr, uid, products, ptype='list_price', context=None):
if context is None:
context = {}
if 'currency_id' in context:
- pricetype_obj = self.pool.get('product.price.type')
- price_type_id = pricetype_obj.search(cr, uid, [('field','=',ptype)])[0]
- price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id
-
+ currency_id = self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id
res = {}
product_uom_obj = self.pool.get('product.uom')
for product in products:
@@ -581,13 +600,12 @@ class product_template(osv.osv):
uom = product.uom_id
res[product.id] = product_uom_obj._compute_price(cr, uid,
uom.id, res[product.id], context['uom'])
- # Convert from price_type currency to asked one
+ # Convert from current user company currency to asked one
if 'currency_id' in context:
- # Take the price_type currency from the product field
+ # Take current user company currency.
# This is right cause a field cannot be in more than one currency
- res[product.id] = self.pool.get('res.currency').compute(cr, uid, price_type_currency_id,
- context['currency_id'], res[product.id],context=context)
-
+ res[product.id] = self.pool.get('res.currency').compute(cr, uid, currency_id,
+ context['currency_id'], res[product.id], context=context)
return res
def _get_uom_id(self, cr, uid, *args):
@@ -1173,6 +1191,8 @@ class product_packaging(osv.osv):
class product_supplierinfo(osv.osv):
_name = "product.supplierinfo"
_description = "Information about a product vendor"
+ _order = 'sequence, min_qty desc, price'
+
def _calc_qty(self, cr, uid, ids, fields, arg, context=None):
result = {}
for supplier_info in self.browse(cr, uid, ids, context=context):
@@ -1183,37 +1203,30 @@ class product_supplierinfo(osv.osv):
return result
_columns = {
- 'name' : fields.many2one('res.partner', 'Vendor', required=True,domain = [('supplier','=',True)], ondelete='cascade', help="Vendor of this product"),
+ 'name': fields.many2one('res.partner', 'Vendor', required=True, domain=[('supplier', '=', True)], ondelete='cascade', help="Vendor of this product"),
'product_name': fields.char('Vendor Product Name', help="This vendor's product name will be used when printing a request for quotation. Keep empty to use the internal one."),
'product_code': fields.char('Vendor Product Code', help="This vendor's product code will be used when printing a request for quotation. Keep empty to use the internal one."),
- 'sequence' : fields.integer('Sequence', help="Assigns the priority to the list of product vendor."),
+ 'sequence': fields.integer('Sequence', help="Assigns the priority to the list of product vendor."),
'product_uom': fields.related('product_tmpl_id', 'uom_po_id', type='many2one', relation='product.uom', string="Vendor Unit of Measure", readonly="1", help="This comes from the product form."),
- 'min_qty': fields.float('Minimal Quantity', required=True, help="The minimal quantity to purchase to this vendor, expressed in the vendor Product Unit of Measure if not empty, in the default unit of measure of the product otherwise."),
+ 'min_qty': fields.float('Minimal Quantity', required=True, help="The minimal quantity to purchase from this vendor, expressed in the vendor Product Unit of Measure if not any, in the default unit of measure of the product otherwise."),
'qty': fields.function(_calc_qty, store=True, type='float', string='Quantity', multi="qty", help="This is a quantity which is converted into Default Unit of Measure."),
- 'product_tmpl_id' : fields.many2one('product.template', 'Product Template', required=True, ondelete='cascade', select=True, oldname='product_id'),
- 'delay' : fields.integer('Delivery Lead Time', required=True, help="Lead time in days between the confirmation of the purchase order and the receipt of the products in your warehouse. Used by the scheduler for automatic computation of the purchase order planning."),
- 'pricelist_ids': fields.one2many('pricelist.partnerinfo', 'suppinfo_id', 'Vendor Pricelist', copy=True),
- 'company_id':fields.many2one('res.company', string='Company',select=1),
+ 'price': fields.float('Price', required=True, digits_compute=dp.get_precision('Product Price'), help="The price to purchase a product"),
+ 'currency_id': fields.many2one('res.currency', 'Currency', required=True),
+ 'date_start': fields.date('Start Date', help="Start date for this vendor price"),
+ 'date_end': fields.date('End Date', help="End date for this vendor price"),
+ 'product_tmpl_id': fields.many2one('product.template', 'Product Template', ondelete='cascade', select=True, oldname='product_id'),
+ 'delay': fields.integer('Delivery Lead Time', required=True, help="Lead time in days between the confirmation of the purchase order and the receipt of the products in your warehouse. Used by the scheduler for automatic computation of the purchase order planning."),
+ 'company_id': fields.many2one('res.company', string='Company', select=1),
}
_defaults = {
'min_qty': 0.0,
'sequence': 1,
'delay': 1,
- 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.supplierinfo', context=c),
+ 'price': 0.0,
+ 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'product.supplierinfo', context=c),
+ 'currency_id': lambda self, cr, uid, context: self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id,
}
- _order = 'sequence'
-
-
-class pricelist_partnerinfo(osv.osv):
- _name = 'pricelist.partnerinfo'
- _columns = {
- 'name': fields.char('Description'),
- 'suppinfo_id': fields.many2one('product.supplierinfo', 'Partner Information', required=True, ondelete='cascade'),
- 'min_quantity': fields.float('Quantity', required=True, help="The minimal quantity to trigger this rule, expressed in the vendor Unit of Measure if any or in the default Unit of Measure of the product otherrwise."),
- 'price': fields.float('Unit Price', required=True, digits_compute=dp.get_precision('Product Price'), help="This price will be considered as a price for the vendor Unit of Measure if any or the default Unit of Measure of the product otherwise"),
- }
- _order = 'min_quantity asc'
class res_currency(osv.osv):
_inherit = 'res.currency'
diff --git a/addons/product/product_data.xml b/addons/product/product_data.xml
index e24840ba489..058ec7ce5eb 100644
--- a/addons/product/product_data.xml
+++ b/addons/product/product_data.xml
@@ -182,48 +182,20 @@ parameter) will see those record just disappear.
product.product
-
-
- Public Price
- list_price
-
-
- Vendor Section on Product or Cost Price
- standard_price
-
+
-
-
-
-
-
- Sale Pricelist
- sale
-
-
-
-
Public Pricelist
- sale
-
-
-
- Default Public Pricelist Version
-
-
-
-
- 1000
- Default Public Pricelist Line
+
+
+
+
+ formula
+ list_price
+ 1000
+
diff --git a/addons/product/product_demo.xml b/addons/product/product_demo.xml
index f69817a024f..494666cb7ba 100644
--- a/addons/product/product_demo.xml
+++ b/addons/product/product_demo.xml
@@ -891,6 +891,7 @@ QWERTY keyboard
31
+ 750
@@ -898,6 +899,15 @@ QWERTY keyboard
31
+ 790
+
+
+
+
+
+ 3
+ 3
+ 785
diff --git a/addons/product/product_pricelist_demo.yml b/addons/product/product_pricelist_demo.yml
index d7b3e329489..583eb806887 100644
--- a/addons/product/product_pricelist_demo.yml
+++ b/addons/product/product_pricelist_demo.yml
@@ -5,58 +5,38 @@
-
!record {model: product.pricelist, id: customer_pricelist}:
name: Customer Pricelist
- type: sale
- version_id:
- - name: v1.0
- date_start: 2012-01-01
- items_id:
- - name: Default pricelist
- base: -1
- base_pricelist_id: list0
- - name: 10% Discount on Assemble Computer
- sequence: 1
- product_id: product_product_4
- base: !eval (ref('product.list_price'))
- price_discount: -0.10
- - name: 1 surchange on Laptop
- sequence: 1
- product_id: product_product_25
- base: !eval (ref('product.list_price'))
- price_surcharge: 1
- - name: 5% Discount on all Computer related products
- sequence: 1
- min_quantity: 2
- base: !eval (ref('product.list_price'))
- categ_id: product_category_5
- price_discount: -0.05
- - name: v2.0 (Special Discount on all products during last 5 days in current year)
+ item_ids:
+ - name: Default pricelist
+ compute_price: 'formula'
+ base: 'pricelist'
+ base_pricelist_id: list0
+ - name: 10% Discount on Assemble Computer
+ applied_on : '1_product'
+ sequence: 1
+ product_id: product_product_4
+ compute_price: 'formula'
+ base: 'list_price'
+ price_discount: 10
+ - name: 1 surchange on Laptop
+ applied_on : '1_product'
+ sequence: 1
+ product_id: product_product_25
+ compute_price: 'formula'
+ base: 'list_price'
+ price_surcharge: 1
+ - name: 5% Discount on all Computer related products
+ applied_on : '2_product_category'
+ sequence: 1
+ min_quantity: 2
+ compute_price: 'formula'
+ base: 'list_price'
+ categ_id: product_category_5
+ price_discount: 5
+ - name: 30% Discount on all products
+ applied_on : '0_product_variant'
date_start: 2011-12-27
date_end: 2011-12-31
- items_id:
- - name: 30% Discount on all products
- price_discount: -0.30
- sequence: 1
- base: !eval (ref('product.list_price'))
--
- !record {model: product.pricelist, id: supplier_pricelist}:
- name: Vendor Pricelist
- type: sale
- version_id:
- - name: v1.0
- items_id:
- - name: 20% Discount given by my supplier
- sequence: 1
- price_min_margin: 2
- price_max_margin: 5
- product_id: product_product_6
- base: !eval (ref('product.standard_price'))
--
- !record {model: pricelist.partnerinfo, id: supplier_pricelist0_product_pc2}:
- suppinfo_id: product_supplierinfo_2
- min_quantity: 3
- price: 785
--
- !record {model: pricelist.partnerinfo, id: supplier_pricelist1_product_pc2}:
- suppinfo_id: product_supplierinfo_2
- min_quantity: 1
- price: 790
+ compute_price: 'formula'
+ price_discount: 30
+ sequence: 1
+ base: 'list_price'
diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml
index 9cdaf667fce..e9adcad0d74 100644
--- a/addons/product/product_view.xml
+++ b/addons/product/product_view.xml
@@ -133,6 +133,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -760,30 +773,33 @@
-
+
-
-
-
-
-
+
+
+ days
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
to
+
+
+
-
The prices below will only be taken into account when your pricelist is set as based on vendor prices.
-
-
-
-
-
-
-
-
-
-
-
-
@@ -794,12 +810,23 @@
-
+
-
+
+
+
+
+ Supplier Pricelist
+ ir.actions.act_window
+ product.supplierinfo
+ form
+ tree,form
+ {'visible_product_tmpl_id':False}
+
+
diff --git a/addons/product/security/ir.model.access.csv b/addons/product/security/ir.model.access.csv
index e0f8e74c9dd..1b5efabf9d2 100644
--- a/addons/product/security/ir.model.access.csv
+++ b/addons/product/security/ir.model.access.csv
@@ -5,14 +5,9 @@ access_product_category_user,product.category.user,model_product_category,base.g
access_product_template_user,product.template.user,model_product_template,base.group_user,1,0,0,0
access_product_packaging_user,product.packaging.user,model_product_packaging,base.group_user,1,0,0,0
access_product_supplierinfo_user,product.supplierinfo.user,model_product_supplierinfo,base.group_user,1,0,0,0
-access_pricelist_partnerinfo_user,pricelist.partnerinfo.user,model_pricelist_partnerinfo,base.group_user,1,0,0,0
-access_product_price_type_user,product.price.type.user,model_product_price_type,base.group_user,1,0,0,0
-access_product_pricelist_type_user,product.pricelist.type.user,model_product_pricelist_type,,1,0,0,0
access_product_pricelist_user,product.pricelist.user,model_product_pricelist,base.group_user,1,0,0,0
-access_product_pricelist_version_user,product.pricelist.version.user,model_product_pricelist_version,base.group_user,1,0,0,0
access_product_pricelist_item_user,product.pricelist.item.user,model_product_pricelist_item,base.group_user,1,0,0,0
access_product_pricelist_item_sale_manager,product.pricelist.item salemanager,product.model_product_pricelist_item,base.group_sale_manager,1,1,1,1
-access_product_pricelist_type_partner_manager,product.pricelist.type partner manager,model_product_pricelist_type,base.group_partner_manager,1,0,0,0
access_product_pricelist_partner_manager,product.pricelist partner manager,model_product_pricelist,base.group_partner_manager,1,0,0,0
access_product_product_employee,product.product employee,model_product_product,base.group_user,1,0,0,0
access_product_price_history_employee,prices.history employee,model_product_price_history,base.group_user,1,0,0,0
diff --git a/addons/product/security/product_security.xml b/addons/product/security/product_security.xml
index 5f9fd770aa3..fef66424f61 100644
--- a/addons/product/security/product_security.xml
+++ b/addons/product/security/product_security.xml
@@ -7,8 +7,13 @@
-
- Purchase Pricelists
+
+ Manage Pricelist Items
+
+
+
+
+ Pricelists On Product
@@ -62,13 +67,6 @@
['|',('company_id','=',user.company_id.id),('company_id','=',False)]
-
- product pricelist version company rule
-
-
- ['|',('company_id','=',user.company_id.id),('company_id','=',False)]
-
-
product supplierinfo company rule
diff --git a/addons/product/test/product_pricelist.yml b/addons/product/test/product_pricelist.yml
index 6380e660451..1857151e843 100644
--- a/addons/product/test/product_pricelist.yml
+++ b/addons/product/test/product_pricelist.yml
@@ -51,9 +51,9 @@
-
!python {model: product.product}: |
from openerp.tools import float_compare
- context.update({'quantity':1, 'date': False, 'partner': ref('base.res_partner_4'), 'pricelist': ref("supplier_pricelist")})
+ context.update({'quantity':1, 'date': False, 'partner': ref('base.res_partner_4')})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
- assert float_compare(product.price, 792, precision_digits=2) == 0, "Wrong cost price: LCD Monitor."
+ assert float_compare(product.seller_price, 790, precision_digits=2) == 0, "Wrong cost price: LCD Monitor."
-
I check cost price of LCD Monitor if more than 3 Unit.
-
@@ -61,7 +61,7 @@
from openerp.tools import float_compare
context.update({'quantity':3})
product = self.browse(cr, uid, ref("product_product_6"), context=context)
- assert float_compare(product.price, 787, precision_digits=2) == 0, "Wrong cost price: LCD Monitor if more than 3 Unit."
+ assert float_compare(product.seller_price, 785, precision_digits=2) == 0, "Wrong cost price: LCD Monitor if more than 3 Unit."
-
I print the sale prices report.
-
diff --git a/addons/product/tests/test_pricelist.py b/addons/product/tests/test_pricelist.py
index dc2b4852e7c..3e9cd7ee3c8 100644
--- a/addons/product/tests/test_pricelist.py
+++ b/addons/product/tests/test_pricelist.py
@@ -21,21 +21,19 @@ class TestPricelist(TransactionCase):
self.public_pricelist_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'list0')[1]
self.sale_pricelist_id = self.product_pricelist.create(cr, uid, {
'name': 'Sale pricelist',
- 'type': 'sale',
- 'version_id': [(0, 0, {
- 'name': 'v1.0',
- 'items_id': [(0, 0, {
- 'name': 'Discount 10%',
- 'base': 1, # based on public price
- 'price_discount': -0.1,
- 'product_id': self.usb_adapter_id
+ 'item_ids': [(0, 0, {
+ 'compute_price': 'formula',
+ 'base': 'list_price', # based on public price
+ 'price_discount': 10,
+ 'product_id': self.usb_adapter_id,
+ 'applied_on': '0_product_variant',
}), (0, 0, {
- 'name': 'Discount -0.5',
- 'base': 1, # based on public price
+ 'compute_price': 'formula',
+ 'base': 'list_price', # based on public price
'price_surcharge': -0.5,
- 'product_id': self.datacard_id
+ 'product_id': self.datacard_id,
+ 'applied_on': '0_product_variant',
})]
- })]
}, context=context)
def test_10_discount(self):
@@ -80,7 +78,6 @@ class TestPricelist(TransactionCase):
# make sure 'tonne' resolves down to 1 'kg'.
self.uom.write(cr, uid, tonne, {'rounding': 0.001})
-
# setup product stored in 'tonnes', with a discounted pricelist for qty > 3 tonnes
spam_id = self.product_product.copy(cr, uid, self.usb_adapter_id,
{ 'name': '1 tonne of spam',
@@ -89,16 +86,15 @@ class TestPricelist(TransactionCase):
'list_price': tonne_price,
'type': 'consu',
})
- pricelist_version_id = self.ir_model_data.xmlid_to_res_id(cr, uid, 'product.ver0')
- self.registry('product.pricelist.item').create(cr, uid,
- { 'price_version_id': pricelist_version_id,
- 'sequence': 10,
- 'name': '3+ tonnes: -10 EUR discount/t',
- 'base': 1, # based on public price
- 'min_quantity': 3, # min = 3 tonnes
- 'price_surcharge': -10, # -10 EUR / tonne
- 'product_id': spam_id,
- })
+ self.registry('product.pricelist.item').create(cr, uid, {
+ 'pricelist_id': self.public_pricelist_id,
+ 'sequence': 10,
+ 'applied_on': '0_product_variant',
+ 'compute_price': 'formula',
+ 'base': 'list_price', # based on public price
+ 'min_quantity': 3, # min = 3 tonnes
+ 'price_surcharge': -10, # -10 EUR / tonne
+ 'product_id': spam_id, })
pricelist_id = self.public_pricelist_id
def test_unit_price(qty, uom, expected_unit_price):
diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py
index c4d5ff96be8..8680a398ffb 100644
--- a/addons/product_visible_discount/product_visible_discount.py
+++ b/addons/product_visible_discount/product_visible_discount.py
@@ -7,33 +7,30 @@ from openerp.tools.translate import _
class product_pricelist(osv.osv):
_inherit = 'product.pricelist'
- _columns ={
- 'visible_discount': fields.boolean('Visible Discount'),
- }
- _defaults = {
- 'visible_discount': True,
+ _columns = {
+ 'discount_policy': fields.selection([('with_discount', 'Discount included in the price'), ('without_discount', 'Show discount in the sale order')], string="Discount Policy"),
}
+ _defaults = {'discount_policy': 'with_discount'}
class sale_order_line(osv.osv):
_inherit = "sale.order.line"
- def get_real_price_currency(self, cr, uid, product_id, res_dict, qty, uom, pricelist, context=None):
+ def _get_real_price_currency(self, cr, uid, product_id, res_dict, qty, uom, pricelist, context=None):
"""Retrieve the price before applying the pricelist"""
item_obj = self.pool['product.pricelist.item']
- price_type_obj = self.pool['product.price.type']
product_obj = self.pool['product.product']
field_name = 'list_price'
+ currency_id = None
if res_dict.get(pricelist):
rule_id = res_dict[pricelist][1]
else:
rule_id = False
if rule_id:
- item_base = item_obj.browse(cr, uid, rule_id, context=context).base
- if item_base > 0:
- price_type = price_type_obj.browse(cr, uid, item_base)
- field_name = price_type.field
- currency_id = price_type.currency_id.id
+ item = item_obj.browse(cr, uid, rule_id, context=context)
+ if item.base == 'standard_price':
+ field_name = 'standard_price'
+ currency_id = item.pricelist_id.currency_id.id
product = product_obj.browse(cr, uid, product_id, context=context)
if not currency_id:
@@ -41,7 +38,7 @@ class sale_order_line(osv.osv):
factor = 1.0
if uom and uom != product.uom_id.id:
# the unit price is in a different uom
- factor = self.pool['product.uom']._compute_qty(cr, uid, uom, 1.0, product.uom_id.id)
+ factor = self.pool['product.uom']._compute_price(cr, uid, uom, 1.0, product.uom_id.id)
return product[field_name] * factor, currency_id
@api.multi
@@ -53,8 +50,8 @@ class sale_order_line(osv.osv):
pricelist_context = dict(context_partner, uom=self.product_uom.id, date=self.order_id.date_order)
list_price = self.order_id.pricelist_id.with_context(pricelist_context).price_rule_get(self.product_id.id, self.product_uom_qty or 1.0, self.order_id.partner_id)
- new_list_price, currency_id = self.with_context(context_partner).get_real_price_currency(self.product_id.id, list_price, self.product_uom_qty, self.product_uom.id, self.order_id.pricelist_id.id)
- if self.order_id.pricelist_id.visible_discount and list_price[self.order_id.pricelist_id.id][0] != 0 and new_list_price != 0:
+ new_list_price, currency_id = self.with_context(context_partner)._get_real_price_currency(self.product_id.id, list_price, self.product_uom_qty, self.product_uom.id, self.order_id.pricelist_id.id)
+ if self.order_id.pricelist_id.discount_policy == 'without_discount' and list_price[self.order_id.pricelist_id.id][0] != 0 and new_list_price != 0:
if self.product_id.company_id and self.order_id.pricelist_id.currency_id.id != self.product_id.company_id.currency_id.id:
# new_list_price is in company's currency while price in pricelist currency
ctx = dict(context_partner, date=self.order_id.date_order)
@@ -81,8 +78,8 @@ class sale_order_line(osv.osv):
context_partner = dict(self.env.context, partner_id=self.order_id.partner_id.id)
pricelist_context = dict(context_partner, uom=self.product_uom.id, date=self.order_id.date_order)
list_price = self.order_id.pricelist_id.with_context(pricelist_context).price_rule_get(self.product_id.id, self.product_uom_qty or 1.0, self.order_id.partner_id)
- new_list_price, currency_id = self.with_context(context_partner).get_real_price_currency(self.product_id.id, list_price, self.product_uom_qty, self.product_uom.id, self.order_id.pricelist_id.id)
- if self.order_id.pricelist_id.visible_discount and list_price[self.order_id.pricelist_id.id][0] != 0 and new_list_price != 0:
+ new_list_price, currency_id = self.with_context(context_partner)._get_real_price_currency(self.product_id.id, list_price, self.product_uom_qty, self.product_uom.id, self.order_id.pricelist_id.id)
+ if self.order_id.pricelist_id.discount_policy == 'without_discount' and list_price[self.order_id.pricelist_id.id][0] != 0 and new_list_price != 0:
if self.product_id.company_id and self.order_id.pricelist_id.currency_id.id != self.product_id.company_id.currency_id.id:
# new_list_price is in company's currency while price in pricelist currency
ctx = dict(context_partner, date=self.order_id.date_order)
diff --git a/addons/product_visible_discount/product_visible_discount_view.xml b/addons/product_visible_discount/product_visible_discount_view.xml
index 2bb9e7b9b12..f2356722da4 100644
--- a/addons/product_visible_discount/product_visible_discount_view.xml
+++ b/addons/product_visible_discount/product_visible_discount_view.xml
@@ -9,7 +9,7 @@
-
+
@@ -19,8 +19,11 @@
product.pricelist
-
-
+
+
+
+
+
diff --git a/addons/purchase/partner.py b/addons/purchase/partner.py
index 934a0f56b22..86aa671dd5b 100644
--- a/addons/purchase/partner.py
+++ b/addons/purchase/partner.py
@@ -19,15 +19,14 @@ class res_partner(osv.osv):
}
def _commercial_fields(self, cr, uid, context=None):
- return super(res_partner, self)._commercial_fields(cr, uid, context=context) + ['property_product_pricelist_purchase']
+ return super(res_partner, self)._commercial_fields(cr, uid, context=context)
_columns = {
- 'property_product_pricelist_purchase': fields.property(
- type='many2one',
- relation='product.pricelist',
- domain=[('type','=','purchase')],
- string="Purchase Pricelist",
- help="This pricelist will be used, instead of the default one, for purchases from the current partner"),
+ 'property_purchase_currency_id': fields.property(
+ type='many2one',
+ relation='res.currency',
+ string="Supplier Currency",
+ help="This currency will be used, instead of the default one, for purchases from the current partner"),
'purchase_order_count': fields.function(_purchase_invoice_count, string='# of Purchase Order', type='integer', multi="count"),
'supplier_invoice_count': fields.function(_purchase_invoice_count, string='# Vendor Bills', type='integer', multi="count"),
}
diff --git a/addons/purchase/partner_view.xml b/addons/purchase/partner_view.xml
index a724d5e029f..ab0d3c3456c 100644
--- a/addons/purchase/partner_view.xml
+++ b/addons/purchase/partner_view.xml
@@ -1,19 +1,18 @@
-
+ res.partner.purchase.property.form.inheritres.partner36
-
+
-
+
-
-
+ RFQs and Purchasespurchase.order
diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py
index fbc1714fac5..b523a4593f3 100644
--- a/addons/purchase/purchase.py
+++ b/addons/purchase/purchase.py
@@ -3,6 +3,7 @@
from openerp import SUPERUSER_ID, workflow
from datetime import datetime
+import time
from dateutil.relativedelta import relativedelta
from operator import attrgetter
from openerp.osv import fields, osv
@@ -26,7 +27,7 @@ class purchase_order(osv.osv):
'amount_total': 0.0,
}
val = val1 = 0.0
- cur = order.pricelist_id.currency_id
+ cur = order.currency_id
for line in order.order_line:
line_price = line_obj._calc_line_base_price(cr, uid, line,
context=context)
@@ -226,7 +227,6 @@ class purchase_order(osv.osv):
"Otherwise, keep empty to deliver to your own company."
),
'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states=READONLY_STATES),
- 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states=READONLY_STATES, help="The pricelist sets the currency used for this purchase order. It also computes the vendor price for the selected products/quantities."),
'currency_id': fields.many2one('res.currency','Currency', required=True, states=READONLY_STATES),
'state': fields.selection(STATE_SELECTION, 'Status', readonly=True,
help="The status of the purchase order or the quotation request. "
@@ -302,7 +302,6 @@ class purchase_order(osv.osv):
'shipped': 0,
'invoice_method': 'order',
'invoiced': 0,
- 'pricelist_id': lambda self, cr, uid, context: context.get('partner_id', False) and self.pool.get('res.partner').browse(cr, uid, context['partner_id']).property_product_pricelist_purchase.id,
'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'purchase.order', context=c),
'journal_id': _get_journal,
'currency_id': lambda self, cr, uid, context: self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id,
@@ -364,11 +363,6 @@ class purchase_order(osv.osv):
def button_dummy(self, cr, uid, ids, context=None):
return True
- def onchange_pricelist(self, cr, uid, ids, pricelist_id, context=None):
- if not pricelist_id:
- return {}
- return {'value': {'currency_id': self.pool.get('product.pricelist').browse(cr, uid, pricelist_id, context=context).currency_id.id}}
-
#Destination address is used when dropshipping
def onchange_dest_address_id(self, cr, uid, ids, address_id, context=None):
if not address_id:
@@ -396,6 +390,7 @@ class purchase_order(osv.osv):
return {'value': {
'fiscal_position_id': False,
'payment_term_id': False,
+ 'currency_id': self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id,
}}
company_id = self.pool.get('res.users')._get_company(cr, uid, context=context)
@@ -404,9 +399,9 @@ class purchase_order(osv.osv):
fp = self.pool['account.fiscal.position'].get_fiscal_position(cr, uid, company_id, partner_id, context=context)
supplier = partner.browse(cr, uid, partner_id, context=context)
return {'value': {
- 'pricelist_id': supplier.property_product_pricelist_purchase.id,
'fiscal_position_id': fp or supplier.property_account_position_id and supplier.property_account_position_id.id or False,
'payment_term_id': supplier.property_supplier_payment_term_id.id or False,
+ 'currency_id': supplier.property_purchase_currency_id.id or self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id
}}
def invoice_open(self, cr, uid, ids, context=None):
@@ -941,7 +936,7 @@ class purchase_order(osv.osv):
order_lines_to_move = {}
for porder in [order for order in self.browse(cr, uid, ids, context=context) if order.state == 'draft']:
- order_key = make_key(porder, ('partner_id', 'location_id', 'pricelist_id', 'currency_id'))
+ order_key = make_key(porder, ('partner_id', 'location_id', 'currency_id'))
new_order = new_orders.setdefault(order_key, ({}, []))
new_order[1].append(porder.id)
order_infos = new_order[0]
@@ -955,7 +950,6 @@ class purchase_order(osv.osv):
'dest_address_id': porder.dest_address_id.id,
'picking_type_id': porder.picking_type_id.id,
'location_id': porder.location_id.id,
- 'pricelist_id': porder.pricelist_id.id,
'currency_id': porder.currency_id.id,
'state': 'draft',
'order_line': {},
@@ -1050,7 +1044,7 @@ class purchase_order_line(osv.osv):
context=context)
line_qty = self._calc_line_quantity(cr, uid, line,
context=context)
- cur = line.order_id.pricelist_id.currency_id
+ cur = line.order_id.currency_id
taxes = line.taxes_id.compute_all(line_price, cur,
line_qty, line.product_id,
line.order_id.partner_id)
@@ -1115,9 +1109,9 @@ class purchase_order_line(osv.osv):
procurement_obj.write(cr, uid, procurement_ids_to_except, {'state': 'exception'}, context=context)
return super(purchase_order_line, self).unlink(cr, uid, ids, context=context)
- def onchange_product_uom(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
+ def onchange_product_uom(self, cr, uid, ids, product_id, qty, uom_id,
partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
- name=False, price_unit=False, state='draft', context=None):
+ name=False, price_unit=False, state='draft', currency_id=False, context=None):
"""
onchange handler of product_uom.
"""
@@ -1126,23 +1120,23 @@ class purchase_order_line(osv.osv):
if not uom_id:
return {'value': {'price_unit': price_unit or 0.0, 'name': name or '', 'product_uom' : uom_id or False}}
context = dict(context, purchase_uom_check=True)
- return self.onchange_product_id(cr, uid, ids, pricelist_id, product_id, qty, uom_id,
+ return self.onchange_product_id(cr, uid, ids, product_id, qty, uom_id,
partner_id, date_order=date_order, fiscal_position_id=fiscal_position_id, date_planned=date_planned,
- name=name, price_unit=price_unit, state=state, replace=False, context=context)
+ name=name, price_unit=price_unit, state=state, replace=False, currency_id=currency_id, context=context)
- def _get_date_planned(self, cr, uid, supplier_info, date_order_str, context=None):
+ def _get_date_planned(self, cr, uid, product, date_order_str, context=None):
"""Return the datetime value to use as Schedule Date (``date_planned``) for
PO Lines that correspond to the given product.supplierinfo,
when ordered at `date_order_str`.
- :param browse_record | False supplier_info: product.supplierinfo, used to
- determine delivery delay (if False, default delay = 0)
+ :param browse_record | False product: product.product, used to
+ determine delivery delay thanks to the selected seller field (if False, default delay = 0)
:param str date_order_str: date of order field, as a string in
DEFAULT_SERVER_DATETIME_FORMAT
:rtype: datetime
:return: desired Schedule Date for the PO line
"""
- supplier_delay = int(supplier_info.delay) if supplier_info else 0
+ supplier_delay = int(product.seller_delay) if product else 0
return datetime.strptime(date_order_str, DEFAULT_SERVER_DATETIME_FORMAT) + relativedelta(days=supplier_delay)
def action_cancel(self, cr, uid, ids, context=None):
@@ -1158,26 +1152,23 @@ class purchase_order_line(osv.osv):
res = [user for user in group_uom.users if user.id == uid]
return len(res) and True or False
- def onchange_product_id(self, cr, uid, ids, pricelist_id, product_id, qty, uom_id,
+ def onchange_product_id(self, cr, uid, ids, product_id, qty, uom_id,
partner_id, date_order=False, fiscal_position_id=False, date_planned=False,
- name=False, price_unit=False, state='draft', replace=True, context=None):
+ name=False, price_unit=False, state='draft', replace=True, currency_id=False, context=None):
"""
onchange handler of product_id.
"""
if context is None:
context = {}
-
res = {'value': {'price_unit': price_unit or 0.0, 'name': name or '', 'product_uom' : uom_id or False}}
if not product_id:
if not uom_id:
uom_id = self.default_get(cr, uid, ['product_uom'], context=context).get('product_uom', False)
res['value']['product_uom'] = uom_id
return res
-
product_product = self.pool.get('product.product')
product_uom = self.pool.get('product.uom')
res_partner = self.pool.get('res.partner')
- product_pricelist = self.pool.get('product.pricelist')
account_fiscal_position = self.pool.get('account.fiscal.position')
account_tax = self.pool.get('account.tax')
@@ -1189,10 +1180,14 @@ class purchase_order_line(osv.osv):
# - determine name and notes based on product in partner lang.
context_partner = context.copy()
+ context_partner.update({'partner': partner_id, 'quantity': qty})
if partner_id:
lang = res_partner.browse(cr, uid, partner_id).lang
- context_partner.update( {'lang': lang, 'partner_id': partner_id} )
+ context_partner.update({'lang': lang, 'partner_id': partner_id})
product = product_product.browse(cr, uid, product_id, context=context_partner)
+ price = product.seller_price
+ if product.selected_seller_id.currency_id.id != currency_id:
+ price = self.pool['res.currency'].compute(cr, uid, product.selected_seller_id.currency_id.id, currency_id, price, context=dict(context or {}, date=date_order))
if replace:
#call name_get() with partner in the context to eventually match name and description in the seller_ids field
dummy, name = product_product.name_get(cr, uid, product_id, context=context_partner)[0]
@@ -1208,7 +1203,8 @@ class purchase_order_line(osv.osv):
if not uom_id:
uom_id = product_uom_po_id
- if product.uom_id.category_id.id != product_uom.browse(cr, uid, uom_id, context=context).category_id.id:
+ uom = product_uom.browse(cr, uid, uom_id, context=context)
+ if product.uom_id.category_id.id != uom.category_id.id:
if context.get('purchase_uom_check') and self._check_product_uom_group(cr, uid, context=context):
res['warning'] = {'title': _('Warning!'), 'message': _('Selected Unit of Measure does not belong to the same category as the product Unit of Measure.')}
uom_id = product_uom_po_id
@@ -1218,46 +1214,21 @@ class purchase_order_line(osv.osv):
# - determine product_qty and date_planned based on seller info
if not date_order:
date_order = fields.datetime.now()
-
-
- supplierinfo = False
- precision = self.pool.get('decimal.precision').precision_get(cr, uid, 'Product Unit of Measure')
- for supplier in product.seller_ids:
- if partner_id and (supplier.name.id == partner_id):
- supplierinfo = supplier
- if supplierinfo.product_uom.id != uom_id:
- res['warning'] = {'title': _('Warning!'), 'message': _('The selected supplier only sells this product by %s') % supplierinfo.product_uom.name }
- min_qty = product_uom._compute_qty(cr, uid, supplierinfo.product_uom.id, supplierinfo.min_qty, to_uom_id=uom_id)
- if float_compare(min_qty , qty, precision_digits=precision) == 1: # If the supplier quantity is greater than entered from user, set minimal.
- if qty:
- res['warning'] = {'title': _('Warning!'), 'message': _('The selected vendor has a minimal quantity set to %s %s, you should not purchase less.') % (supplierinfo.min_qty, supplierinfo.product_uom.name)}
- qty = min_qty
- dt = self._get_date_planned(cr, uid, supplierinfo, date_order, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
+ dt = self._get_date_planned(cr, uid, product, date_order, context=context).strftime(DEFAULT_SERVER_DATETIME_FORMAT)
qty = qty or 1.0
res['value'].update({'date_planned': date_planned or dt})
if qty:
res['value'].update({'product_qty': qty})
- price = price_unit
- if price_unit is False or price_unit is None:
- # - determine price_unit and taxes_id
- if pricelist_id:
- date_order_str = datetime.strptime(date_order, DEFAULT_SERVER_DATETIME_FORMAT).strftime(DEFAULT_SERVER_DATE_FORMAT)
- price = product_pricelist.price_get(cr, uid, [pricelist_id],
- product.id, qty or 1.0, partner_id or False, {'uom': uom_id, 'date': date_order_str})[pricelist_id]
- else:
- price = product.standard_price
-
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
product_id_change = onchange_product_id
- product_uom_change = onchange_product_uom
+ product_uom_change = onchange_product_uom
def action_confirm(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'state': 'confirmed'}, context=context)
@@ -1281,7 +1252,6 @@ class procurement_order(osv.osv):
def propagate_cancels(self, cr, uid, ids, context=None):
purchase_line_obj = self.pool.get('purchase.order.line')
lines_to_cancel = []
- uom_obj = self.pool.get("product.uom")
for procurement in self.browse(cr, uid, ids, context=context):
if procurement.rule_id.action == 'buy' and procurement.purchase_line_id:
if procurement.purchase_line_id.state not in ('draft', 'cancel'):
@@ -1420,24 +1390,8 @@ class procurement_order(osv.osv):
res = {}
if context is None:
context = {}
- uom_obj = self.pool.get('product.uom')
- pricelist_obj = self.pool.get('product.pricelist')
prod_obj = self.pool.get('product.product')
acc_pos_obj = self.pool.get('account.fiscal.position')
-
- pricelist_id = partner.property_product_pricelist_purchase.id
- prices_qty = []
- qty = {}
-
- for procurement in procurements:
- seller_qty = procurement.product_id.seller_qty if procurement.location_id.usage != 'customer' else 0.0
- uom_id = procurement.product_id.uom_po_id.id
- qty[procurement.product_id.id] = uom_obj._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty, uom_id)
- if seller_qty:
- qty[procurement.product_id.id] = max(qty[procurement.product_id.id], seller_qty)
- prices_qty += [(procurement.product_id, qty[procurement.product_id.id], partner)]
- prices = pricelist_obj.price_get_multi(cr, uid, [pricelist_id], prices_qty)
-
#Passing partner_id to context for purchase order line integrity of Line name
new_context = context.copy()
new_context.update({'lang': partner.lang, 'partner_id': partner.id})
@@ -1455,26 +1409,31 @@ class procurement_order(osv.osv):
name = names_dict[procurement.product_id.id]
if procurement.product_id.description_purchase:
name += '\n' + procurement.product_id.description_purchase
- price = prices[procurement.product_id.id][pricelist_id]
- price = uom_obj._compute_price(cr, uid, procurement.product_uom.id, price, to_uom_id=procurement.product_id.product_tmpl_id.uom_po_id.id)
-
+ date = schedule_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
values = {
'name': name,
- 'product_qty': qty[procurement.product_id.id],
'product_id': procurement.product_id.id,
'product_uom': procurement.product_id.uom_po_id.id,
- 'price_unit': price or 0.0,
- 'date_planned': schedule_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
+ 'date_planned': date,
'taxes_id': [(6, 0, taxes)],
'procurement_ids': [(4, procurement.id)]
}
+ # calculate price/qty for valid vendor
+ qty = self.pool['product.uom']._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty, procurement.product_id.uom_po_id.id)
+ new_context.update({'quantity': qty})
+ supplier = prod_obj.browse(cr, uid, procurement.product_id.id, context=new_context).selected_seller_id
+ price = 0.0
+ if supplier:
+ price = supplier.price
+ purchase_currency = supplier.name.property_purchase_currency_id
+ price = self.pool['res.currency'].compute(cr, uid, supplier.currency_id.id, purchase_currency.id, price, context=dict(context or {}, date=date))
+ values.update({'product_qty': qty, 'price_unit': price})
res[procurement.id] = values
return res
def _calc_new_qty_price(self, cr, uid, procurement, po_line=None, cancel=False, context=None):
if not po_line:
po_line = procurement.purchase_line_id
-
uom_obj = self.pool.get('product.uom')
qty = uom_obj._compute_qty(cr, uid, procurement.product_uom.id, procurement.product_qty,
procurement.product_id.uom_po_id.id)
@@ -1490,8 +1449,9 @@ class procurement_order(osv.osv):
else:
supplierinfo_obj = self.pool.get('product.supplierinfo')
supplierinfo_ids = supplierinfo_obj.search(cr, uid, [('name', '=', po_line.order_id.partner_id.id), ('product_tmpl_id', '=', po_line.product_id.product_tmpl_id.id)])
- supplierinfo_min_qty = supplierinfo_obj.browse(cr, uid, supplierinfo_ids).min_qty
-
+ if supplierinfo_ids:
+ supplierinfo = supplierinfo_obj.browse(cr, uid, supplierinfo_ids)
+ supplierinfo_min_qty = sorted(supplierinfo, key=lambda x: x['min_qty'])[0].min_qty
if supplierinfo_min_qty == 0.0:
qty += po_line.product_qty
else:
@@ -1503,10 +1463,8 @@ class procurement_order(osv.osv):
price = po_line.price_unit
if qty != po_line.product_qty:
- pricelist_obj = self.pool.get('product.pricelist')
- pricelist_id = po_line.order_id.partner_id.property_product_pricelist_purchase.id
- price = pricelist_obj.price_get(cr, uid, [pricelist_id], procurement.product_id.id, qty, po_line.order_id.partner_id.id, {'uom': procurement.product_uom.id})[pricelist_id]
-
+ price = po_line.product_id.seller_price
+ price = self.pool['product.uom']._compute_price(cr, uid, procurement.product_uom.id, price, procurement.product_uom.id)
return qty, price
def update_origin_po(self, cr, uid, po, proc, context=None):
@@ -1514,7 +1472,7 @@ class procurement_order(osv.osv):
def _get_grouping_dicts(self, cr, uid, ids, context=None):
"""
- It will group the procurements according to the pos they should go into. That way, lines going to the same
+ It will group the procurements according to the pos they should go into. That way, lines going to the same
po, can be processed at once.
Returns two dictionaries:
add_purchase_dicts: key: po value: procs to add to the po
@@ -1522,20 +1480,21 @@ class procurement_order(osv.osv):
values: procs to add
"""
po_obj = self.pool.get('purchase.order')
+ date = time.strftime(DEFAULT_SERVER_DATE_FORMAT)
# Regroup POs
cr.execute("""
SELECT psi.name, p.id, pr.id, pr.picking_type_id, p.location_id, p.partner_dest_id, p.company_id, p.group_id,
- pr.group_propagation_option, pr.group_id, psi.qty
+ pr.group_propagation_option, pr.group_id,psi.qty
FROM procurement_order AS p
LEFT JOIN procurement_rule AS pr ON pr.id = p.rule_id
LEFT JOIN procurement_group AS pg ON p.group_id = pg.id,
product_supplierinfo AS psi, product_product AS pp
WHERE
p.product_id = pp.id AND p.id in %s AND psi.product_tmpl_id = pp.product_tmpl_id
- AND (psi.company_id = p.company_id or psi.company_id IS NULL)
+ AND (psi.company_id = p.company_id or psi.company_id IS NULL) AND ((psi.date_start IS NULL OR psi.date_start<=%s) AND (psi.date_end IS NULL OR psi.date_end>=%s))
ORDER BY psi.sequence,
psi.name, p.rule_id, p.location_id, p.company_id, p.partner_dest_id, p.group_id
- """, (tuple(ids), ))
+ """, (tuple(ids), date, date))
res = cr.fetchall()
old = False
# A giant dict for grouping lines, ... to do at once
@@ -1609,8 +1568,6 @@ class procurement_order(osv.osv):
else:
procs_to_create.append(proc)
- procs = []
-
# FIXME: these are not real tracking values, it should be fixed if tracking values for one2many
# are managed
def format_message(message_description, tracked_values):
@@ -1674,9 +1631,9 @@ class procurement_order(osv.osv):
'partner_id': create_purchase[0],
'location_id': procurement.location_id.id,
'picking_type_id': procurement.rule_id.picking_type_id.id,
- 'pricelist_id': partner.property_product_pricelist_purchase.id,
'date_order': purchase_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
'company_id': procurement.company_id.id,
+ 'currency_id': partner.property_purchase_currency_id.id or self.pool['res.users'].browse(cr, uid, uid, context=context).company_id.currency_id.id,
'fiscal_position_id': fp,
'payment_term_id': partner.property_supplier_payment_term_id.id,
'dest_address_id': procurement.partner_dest_id.id,
diff --git a/addons/purchase/purchase_data.xml b/addons/purchase/purchase_data.xml
index c13d4620361..092e3a782d2 100644
--- a/addons/purchase/purchase_data.xml
+++ b/addons/purchase/purchase_data.xml
@@ -17,31 +17,6 @@
Purchase Orderpurchase.order
-
-
- Purchase Pricelist
- purchase
-
-
-
- Default Purchase Pricelist
- purchase
-
-
-
- Default Purchase Pricelist Version
-
-
-
-
- Default Purchase Pricelist Line
-
-
-
- property_product_pricelist_purchase
-
-
-
diff --git a/addons/purchase/purchase_view.xml b/addons/purchase/purchase_view.xml
index e11e5fea483..6dbd7d6ab72 100644
--- a/addons/purchase/purchase_view.xml
+++ b/addons/purchase/purchase_view.xml
@@ -13,43 +13,15 @@
-
- Pricelist Versions
- ir.actions.act_window
- product.pricelist.version
- form
-
- [('pricelist_id.type','=','purchase')]
-
-
- Click to add a pricelist version.
-
- There can be more than one version of a pricelist, each of
- these must be valid during a certain period of time. Some
- examples of versions: Main Prices, 2010, 2011, Summer Sales,
- etc.
-