[IMP] product*: adaptation due to the new Sale module
Major changes: - No use of UoS anymore - Partial migration to new api Reason: complete rewrite of the Sale module. Responsible: fp, dbo, nim
This commit is contained in:
@@ -247,7 +247,7 @@ class product_pricelist(osv.osv):
|
||||
if qty_uom_id != product.uom_id.id:
|
||||
try:
|
||||
qty_in_product_uom = product_uom_obj._compute_qty(
|
||||
cr, uid, context['uom'], qty, product.uom_id.id or product.uos_id.id)
|
||||
cr, uid, context['uom'], qty, product.uom_id.id)
|
||||
except UserError:
|
||||
# Ignored - incompatible UoM in context, use default product UoM
|
||||
pass
|
||||
|
||||
@@ -412,7 +412,7 @@ class product_template(osv.osv):
|
||||
|
||||
product = self.browse(cr, uid, id, context=context)
|
||||
if 'uom' in context:
|
||||
uom = product.uos_id or product.uom_id
|
||||
uom = product.uom_id
|
||||
value = product_uom_obj._compute_price(cr, uid,
|
||||
context['uom'], value, uom.id)
|
||||
|
||||
@@ -508,12 +508,6 @@ class product_template(osv.osv):
|
||||
'uom_id': fields.many2one('product.uom', 'Unit of Measure', required=True, help="Default Unit of Measure used for all stock operation."),
|
||||
'uom_rel_id': fields.related('uom_id', type="many2one", relation="product.uom", readonly=True, string='Default UoM'),
|
||||
'uom_po_id': fields.many2one('product.uom', 'Purchase Unit of Measure', required=True, help="Default Unit of Measure used for purchase orders. It must be in the same category than the default unit of measure."),
|
||||
'uos_id' : fields.many2one('product.uom', 'Unit of Sale',
|
||||
help='Specify a unit of measure here if invoicing is made in another unit of measure than inventory. Keep empty to use the default unit of measure.'),
|
||||
'uos_coeff': fields.float('Unit of Measure -> UOS Coeff', digits_compute= dp.get_precision('Product UoS'),
|
||||
help='Coefficient to convert default Unit of Measure to Unit of Sale\n'
|
||||
' uos = uom * coeff'),
|
||||
'mes_type': fields.selection((('fixed', 'Fixed'), ('variable', 'Variable')), 'Measure Type'),
|
||||
'company_id': fields.many2one('res.company', 'Company', select=1),
|
||||
# image: all image fields are base64 encoded and PIL-supported
|
||||
'image': fields.binary("Image",
|
||||
@@ -586,7 +580,7 @@ class product_template(osv.osv):
|
||||
if ptype == 'list_price':
|
||||
res[product.id] += product._name == "product.product" and product.price_extra or 0.0
|
||||
if 'uom' in context:
|
||||
uom = product.uom_id or product.uos_id
|
||||
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
|
||||
@@ -746,11 +740,9 @@ class product_template(osv.osv):
|
||||
'company_id': lambda s,cr,uid,c: s.pool.get('res.company')._company_default_get(cr, uid, 'product.template', context=c),
|
||||
'list_price': 1,
|
||||
'standard_price': 0.0,
|
||||
'sale_ok': 1,
|
||||
'sale_ok': 1,
|
||||
'uom_id': _get_uom_id,
|
||||
'uom_po_id': _get_uom_id,
|
||||
'uos_coeff': 1.0,
|
||||
'mes_type': 'fixed',
|
||||
'categ_id' : _default_category,
|
||||
'type' : 'consu',
|
||||
'active': True,
|
||||
@@ -851,7 +843,7 @@ class product_product(osv.osv):
|
||||
|
||||
for product in self.browse(cr, uid, ids, context=context):
|
||||
if 'uom' in context:
|
||||
uom = product.uos_id or product.uom_id
|
||||
uom = product.uom_id
|
||||
res[product.id] = product_uom_obj._compute_price(cr, uid,
|
||||
uom.id, product.list_price, context['uom'])
|
||||
else:
|
||||
@@ -865,7 +857,7 @@ class product_product(osv.osv):
|
||||
|
||||
product = self.browse(cr, uid, id, context=context)
|
||||
if 'uom' in context:
|
||||
uom = product.uos_id or product.uom_id
|
||||
uom = product.uom_id
|
||||
value = product_uom_obj._compute_price(cr, uid,
|
||||
context['uom'], value, uom.id)
|
||||
value = value - product.price_extra
|
||||
@@ -1157,37 +1149,6 @@ class product_product(osv.osv):
|
||||
ctx = dict(context or {}, create_product_product=True)
|
||||
return super(product_product, self).create(cr, uid, vals, context=ctx)
|
||||
|
||||
def need_procurement(self, cr, uid, ids, context=None):
|
||||
return False
|
||||
|
||||
def _compute_uos_qty(self, cr, uid, ids, uom, qty, uos, context=None):
|
||||
'''
|
||||
Computes product's invoicing quantity in UoS from quantity in UoM.
|
||||
Takes into account the
|
||||
:param uom: Source unit
|
||||
:param qty: Source quantity
|
||||
:param uos: Target UoS unit.
|
||||
'''
|
||||
if not uom or not qty or not uos:
|
||||
return qty
|
||||
uom_obj = self.pool['product.uom']
|
||||
product_id = ids[0] if isinstance(ids, (list, tuple)) else ids
|
||||
product = self.browse(cr, uid, product_id, context=context)
|
||||
if isinstance(uos, (int, long)):
|
||||
uos = uom_obj.browse(cr, uid, uos, context=context)
|
||||
if isinstance(uom, (int, long)):
|
||||
uom = uom_obj.browse(cr, uid, uom, context=context)
|
||||
if product.uos_id: # Product has UoS defined
|
||||
# We cannot convert directly between units even if the units are of the same category
|
||||
# as we need to apply the conversion coefficient which is valid only between quantities
|
||||
# in product's default UoM/UoS
|
||||
qty_default_uom = uom_obj._compute_qty_obj(cr, uid, uom, qty, product.uom_id) # qty in product's default UoM
|
||||
qty_default_uos = qty_default_uom * product.uos_coeff
|
||||
return uom_obj._compute_qty_obj(cr, uid, product.uos_id, qty_default_uos, uos)
|
||||
else:
|
||||
return uom_obj._compute_qty_obj(cr, uid, uom, qty, uos)
|
||||
|
||||
|
||||
|
||||
class product_packaging(osv.osv):
|
||||
_name = "product.packaging"
|
||||
|
||||
@@ -48,10 +48,6 @@
|
||||
<field name="name">Product Unit of Measure</field>
|
||||
<field name="digits" eval="3"/>
|
||||
</record>
|
||||
<record forcecreate="True" id="decimal_product_uos" model="decimal.precision">
|
||||
<field name="name">Product UoS</field>
|
||||
<field name="digits" eval="3"/>
|
||||
</record>
|
||||
|
||||
<!--
|
||||
Resource: product.uom
|
||||
|
||||
@@ -33,28 +33,49 @@
|
||||
-->
|
||||
|
||||
|
||||
<record id="product_product_0" model="product.product">
|
||||
<field name="name">Prepaid Consulting</field>
|
||||
<field name="categ_id" ref="product_category_3"/>
|
||||
<field name="standard_price">40</field>
|
||||
<field name="list_price">90</field>
|
||||
<field name="type">service</field>
|
||||
<field name="uom_id" ref="product_uom_hour"/>
|
||||
<field name="uom_po_id" ref="product_uom_hour"/>
|
||||
<field name="description">Example of product to invoice on order.</field>
|
||||
</record>
|
||||
|
||||
<record id="product_product_1" model="product.product">
|
||||
<field name="name">On Site Monitoring</field>
|
||||
<field name="name">GAP Analysis Service</field>
|
||||
<field name="categ_id" ref="product_category_3"/>
|
||||
<field name="standard_price">20.5</field>
|
||||
<field name="list_price">30.75</field>
|
||||
<field name="type">service</field>
|
||||
<field name="uom_id" ref="product_uom_hour"/>
|
||||
<field name="uom_po_id" ref="product_uom_hour"/>
|
||||
<field name="description">This type of service include basic monitoring of products.</field>
|
||||
<field name="description_sale">This type of service include basic monitoring of products.</field>
|
||||
<field name="description">Example of products to invoice based on delivery.</field>
|
||||
</record>
|
||||
|
||||
<record id="product_product_1b" model="product.product">
|
||||
<field name="name">External Audit</field>
|
||||
<field name="categ_id" ref="product_category_3"/>
|
||||
<field name="standard_price">160</field>
|
||||
<field name="list_price">180</field>
|
||||
<field name="type">service</field>
|
||||
<field name="uom_id" ref="product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product_uom_unit"/>
|
||||
<field name="description">Example of products to invoice based on cost.</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="product_product_2" model="product.product">
|
||||
<field name="name">On Site Assistance</field>
|
||||
<field name="name">Support Services</field>
|
||||
<field name="categ_id" ref="product_category_3"/>
|
||||
<field name="standard_price">25.5</field>
|
||||
<field name="list_price">38.25</field>
|
||||
<field name="type">service</field>
|
||||
<field name="uom_id" ref="product_uom_hour"/>
|
||||
<field name="uom_po_id" ref="product_uom_hour"/>
|
||||
<field name="description">This type of service include assistance for security questions, system configuration requirements, implementation or special needs.</field>
|
||||
<field name="description">Example of product to invoice based on delivery.</field>
|
||||
</record>
|
||||
|
||||
|
||||
|
||||
@@ -144,10 +144,6 @@
|
||||
</group>
|
||||
<group name="email_template_and_project"
|
||||
attrs="{'invisible':[('type', '!=', 'service')]}"/>
|
||||
<group groups="product.group_uos" string="Unit of Measure">
|
||||
<field name="uos_id" options="{'no_open':True,'no_create':True}"/>
|
||||
<field name="uos_coeff" groups="base.group_no_one"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Notes" name="notes">
|
||||
@@ -397,7 +393,7 @@
|
||||
|
||||
<!-- product product -->
|
||||
|
||||
<menuitem id="prod_config_main" name="Products" parent="base.menu_sale_config" sequence="2" groups="base.group_no_one"/>
|
||||
<menuitem id="prod_config_main" name="Products" parent="base.menu_sale_config" sequence="2"/>
|
||||
|
||||
<record id="product_product_tree_view" model="ir.ui.view">
|
||||
<field name="name">product.product.tree</field>
|
||||
|
||||
@@ -85,9 +85,9 @@ class TestPricelist(TransactionCase):
|
||||
spam_id = self.product_product.copy(cr, uid, self.usb_adapter_id,
|
||||
{ 'name': '1 tonne of spam',
|
||||
'uom_id': self.tonne_id,
|
||||
'uos_id': self.tonne_id,
|
||||
'uom_po_id': self.tonne_id,
|
||||
'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,
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
<field name="standard_price">5.0</field>
|
||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product.product_uom_unit"/>
|
||||
<field name="uos_id" ref="product.product_uom_kgm" />
|
||||
<field name="uos_coeff">3</field>
|
||||
<field name="name">French cheese Camembert</field>
|
||||
<field name="life_time">15</field>
|
||||
<field name="use_time">10</field>
|
||||
@@ -31,8 +29,6 @@
|
||||
<field name="standard_price">6.0</field>
|
||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product.product_uom_unit"/>
|
||||
<field name="uos_id" ref="product.product_uom_kgm" />
|
||||
<field name="uos_coeff">0.5</field>
|
||||
<field name="name">Ham</field>
|
||||
<field name="life_time">30</field>
|
||||
<field name="use_time">25</field>
|
||||
@@ -54,8 +50,6 @@
|
||||
<field name="standard_price">6.0</field>
|
||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product.product_uom_unit"/>
|
||||
<field name="uos_id" ref="product.product_uom_kgm" />
|
||||
<field name="uos_coeff">0.5</field>
|
||||
<field name="name">Bread</field>
|
||||
<field name="life_time">30</field>
|
||||
<field name="use_time">25</field>
|
||||
@@ -77,8 +71,6 @@
|
||||
<field name="standard_price">6.0</field>
|
||||
<field name="uom_id" ref="product.product_uom_unit"/>
|
||||
<field name="uom_po_id" ref="product.product_uom_unit"/>
|
||||
<field name="uos_id" ref="product.product_uom_litre" />
|
||||
<field name="uos_coeff">0.5</field>
|
||||
<field name="name">Cow milk</field>
|
||||
<field name="life_time">30</field>
|
||||
<field name="use_time">25</field>
|
||||
|
||||
@@ -73,16 +73,14 @@ class product_product(osv.osv):
|
||||
|
||||
#Cost price is calculated afterwards as it is a property
|
||||
sqlstr="""select
|
||||
sum(l.price_unit * l.quantity)/sum(nullif(l.quantity * pu.factor / pu2.factor,0)) as avg_unit_price,
|
||||
sum(l.quantity * pu.factor / pu2.factor) as num_qty,
|
||||
sum(l.price_unit * l.quantity)/sum(nullif(l.quantity,0)) as avg_unit_price,
|
||||
sum(l.quantity) as num_qty,
|
||||
sum(l.quantity * (l.price_subtotal/(nullif(l.quantity,0)))) as total,
|
||||
sum(l.quantity * pu.factor * pt.list_price / pu2.factor) as sale_expected
|
||||
sum(l.quantity * pt.list_price) as sale_expected
|
||||
from account_invoice_line l
|
||||
left join account_invoice i on (l.invoice_id = i.id)
|
||||
left join product_product product on (product.id=l.product_id)
|
||||
left join product_template pt on (pt.id = product.product_tmpl_id)
|
||||
left join product_uom pu on (pt.uom_id = pu.id)
|
||||
left join product_uom pu2 on (l.uos_id = pu2.id)
|
||||
where l.product_id = %s and i.state in %s and i.type IN %s and (i.date_invoice IS NULL or (i.date_invoice>=%s and i.date_invoice<=%s and i.company_id=%s))
|
||||
"""
|
||||
invoice_types = ('out_invoice', 'in_refund')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from openerp import api
|
||||
from openerp.osv import fields, osv
|
||||
from openerp.tools.translate import _
|
||||
|
||||
@@ -33,7 +33,7 @@ class sale_order_line(osv.osv):
|
||||
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
|
||||
currency_id = price_type.currency_id.id
|
||||
|
||||
product = product_obj.browse(cr, uid, product_id, context=context)
|
||||
if not currency_id:
|
||||
@@ -44,49 +44,57 @@ class sale_order_line(osv.osv):
|
||||
factor = self.pool['product.uom']._compute_qty(cr, uid, uom, 1.0, product.uom_id.id)
|
||||
return product[field_name] * factor, currency_id
|
||||
|
||||
def product_id_change(self, cr, uid, ids, pricelist, product, qty=0,
|
||||
uom=False, qty_uos=0, uos=False, name='', partner_id=False,
|
||||
lang=False, update_tax=True, date_order=False, packaging=False,
|
||||
fiscal_position_id=False, flag=False, context=None):
|
||||
res=super(sale_order_line, self).product_id_change(cr, uid, ids, pricelist, product, qty,
|
||||
uom, qty_uos, uos, name, partner_id,
|
||||
lang, update_tax, date_order, packaging=packaging, fiscal_position_id=fiscal_position_id, flag=flag, context=context)
|
||||
@api.multi
|
||||
@api.onchange('product_id')
|
||||
def product_id_change(self):
|
||||
res = super(sale_order_line, self).product_id_change()
|
||||
context_partner = dict(self.env.context, partner_id=self.order_id.partner_id.id)
|
||||
if self.product_id and self.order_id.pricelist_id and self.env.user.has_group('sale.group_discount_per_so_line'):
|
||||
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)
|
||||
|
||||
if context is None:
|
||||
context = {}
|
||||
context_partner = dict(context, lang=lang, partner_id=partner_id)
|
||||
result=res['value']
|
||||
pricelist_obj=self.pool.get('product.pricelist')
|
||||
product_obj = self.pool.get('product.product')
|
||||
if product and pricelist and self.pool.get('res.users').has_group(cr, uid, 'sale.group_discount_per_so_line'):
|
||||
if result.get('price_unit',False):
|
||||
price=result['price_unit']
|
||||
else:
|
||||
return res
|
||||
uom = result.get('product_uom', uom)
|
||||
product = product_obj.browse(cr, uid, product, context=context_partner)
|
||||
pricelist_context = dict(context_partner, uom=uom, date=date_order)
|
||||
list_price = pricelist_obj.price_rule_get(cr, uid, [pricelist],
|
||||
product.id, qty or 1.0, partner_id, context=pricelist_context)
|
||||
|
||||
so_pricelist = pricelist_obj.browse(cr, uid, pricelist, context=context_partner)
|
||||
|
||||
new_list_price, currency_id = self.get_real_price_currency(cr, uid, product.id, list_price, qty, uom, pricelist, context=context_partner)
|
||||
if so_pricelist.visible_discount and list_price[pricelist][0] != 0 and new_list_price != 0:
|
||||
if product.company_id and so_pricelist.currency_id.id != product.company_id.currency_id.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:
|
||||
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=date_order)
|
||||
new_list_price = self.pool['res.currency'].compute(cr, uid,
|
||||
currency_id.id, so_pricelist.currency_id.id,
|
||||
new_list_price, context=ctx)
|
||||
discount = (new_list_price - price) / new_list_price * 100
|
||||
ctx = dict(context_partner, date=self.order_id.date_order)
|
||||
new_list_price = self.env['res.currency'].browse(currency_id).with_context(ctx).compute(new_list_price, self.order_id.pricelist_id.currency_id.id)
|
||||
discount = (new_list_price - self.price_unit) / new_list_price * 100
|
||||
if discount > 0:
|
||||
result['price_unit'] = new_list_price
|
||||
result['discount'] = discount
|
||||
self.price_unit = new_list_price
|
||||
self.discount = discount
|
||||
else:
|
||||
result['discount'] = 0.0
|
||||
self.discount = 0.0
|
||||
else:
|
||||
result['discount'] = 0.0
|
||||
self.discount = 0.0
|
||||
else:
|
||||
result['discount'] = 0.0
|
||||
self.discount = 0.0
|
||||
return res
|
||||
|
||||
@api.onchange('product_uom')
|
||||
def product_uom_change(self):
|
||||
res = super(sale_order_line, self).product_uom_change()
|
||||
if not self.product_uom:
|
||||
self.price_unit = 0.0
|
||||
return
|
||||
if self.order_id.pricelist_id and self.order_id.partner_id and self.env.user.has_group('sale.group_discount_per_so_line'):
|
||||
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:
|
||||
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)
|
||||
new_list_price = self.env['res.currency'].browse(currency_id).with_context(ctx).compute(new_list_price, self.order_id.pricelist_id.currency_id.id)
|
||||
discount = (new_list_price - self.price_unit) / new_list_price * 100
|
||||
if discount > 0:
|
||||
self.price_unit = new_list_price
|
||||
self.discount = discount
|
||||
else:
|
||||
self.discount = 0.0
|
||||
else:
|
||||
self.discount = 0.0
|
||||
else:
|
||||
self.discount = 0.0
|
||||
return res
|
||||
|
||||
Reference in New Issue
Block a user