diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 642d132136d..a2ddc619a69 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -90,6 +90,7 @@ class product_pricelist(osv.osv): if context and ('partner_id' in context): partner = context['partner_id'] currency_obj = self.pool.get('res.currency') + product_obj = self.pool.get('product.product') result = {} for id in ids: # XXX add date test to select the pricelist version @@ -166,7 +167,10 @@ class product_pricelist(osv.osv): price = False result[id] = price if 'uom' in context: - result[id] = self.pool.get('product.uom')._compute_price(cr, uid, context['uom'], result[id]) + product = product_obj.browse(cr, uid, prod_id) + uom = product.uos_id or product.uom_id + result[id] = self.pool.get('product.uom')._compute_price(cr, + uid, uom.id, result[id], context['uom']) return result product_pricelist() diff --git a/addons/product/product.py b/addons/product/product.py index 8fdf56e5c94..4151df7a4a5 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -56,7 +56,7 @@ class product_uom(osv.osv): _columns = { 'name': fields.char('Name', size=64, required=True), 'category_id': fields.many2one('product.uom.categ', 'UOM Category', required=True, ondelete='cascade'), - 'factor': fields.float('Factor', required=True), + 'factor': fields.float('Factor', digits=(12, 6), required=True), 'rounding': fields.float('Rounding Precision', digits=(16, 3), required=True), 'active': fields.boolean('Active'), } @@ -68,24 +68,34 @@ class product_uom(osv.osv): } def _compute_qty(self, cr, uid, from_uom_id, qty, to_uom_id=False): - if not to_uom_id: to_uom_id = 0 - if not from_uom_id or not qty: + if not from_uom_id or not qty or not to_uom_id: return qty - f = self.read(cr, uid, [from_uom_id, to_uom_id], ['factor','rounding']) - if f[0]['id'] == from_uom_id: - from_unit, to_unit = f[0], f[-1] + uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) + if uoms[0].id == from_uom_id: + from_unit, to_unit = uoms[0], uoms[-1] else: - from_unit, to_unit = f[-1], f[0] - amount = qty * from_unit['factor'] + from_unit, to_unit = uoms[-1], uoms[0] + if from_unit.category_id.id <> to_unit.category_id.id: + return qty + amount = qty / from_unit['factor'] if to_uom_id: - amount = rounding(amount / to_unit['factor'], to_unit['rounding']) + amount = rounding(amount * to_unit['factor'], to_unit['rounding']) return amount - def _compute_price(self, cr, uid, uom_id, qty, to_uom_id=False): - if not uom_id or not qty: - return qty - f = self.read(cr, uid, [uom_id], ['factor','rounding'])[0] - return qty * f['factor'] + def _compute_price(self, cr, uid, from_uom_id, price, to_uom_id=False): + if not from_uom_id or not price or not to_uom_id: + return price + uoms = self.browse(cr, uid, [from_uom_id, to_uom_id]) + if uoms[0].id == from_uom_id: + from_unit, to_unit = uoms[0], uoms[-1] + else: + from_unit, to_unit = uoms[-1], uoms[0] + if from_unit.category_id.id <> to_unit.category_id.id: + return price + amount = price / from_unit.factor + if to_uom_id: + amount = amount * to_unit.factor + return amount product_uom() @@ -254,15 +264,18 @@ class product_product(osv.osv): _product_outgoing_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('out',)) _product_incoming_qty = _get_product_available_func(('confirmed','waiting','assigned'), ('in',)) - def _product_lst_price(self, cr, uid, ids, name, arg, context={}): + def _product_lst_price(self, cr, uid, ids, name, arg, context=None): res = {} - for p in self.browse(cr, uid, ids, context=context): - res[p.id] = p.list_price + product_uom_obj = self.pool.get('product.uom') for id in ids: res.setdefault(id, 0.0) - if 'uom' in context: - for id in ids: - res[id] = self.pool.get('product.uom')._compute_price(cr, uid, context['uom'], res[id]) + for product in self.browse(cr, uid, ids, context=context): + if 'uom' in context: + uom = product.uos_id or product.uom_id + res[product.id] = product_uom_obj._compute_price(cr, uid, + uom.id, product.list_price, context['uom']) + else: + res[product.id] = product.list_price return res def _get_partner_code_name(self, cr, uid, ids, product_id, partner_id, context={}): @@ -369,15 +382,28 @@ class product_product(osv.osv): # Could be overrided for variants matrices prices # def price_get(self, cr, uid, ids, ptype='list_price', context={}): - result = self.read(cr, uid, ids, [ptype, 'price_extra','price_margin']) - result2 = {} - for res in result: - result2[res['id']] = res[ptype] or 0.0 - if ptype=='list_price': - result2[res['id']] = result2[res['id']] * res['price_margin'] + res['price_extra'] + res = {} + product_uom_obj = self.pool.get('product.uom') + + for product in self.browse(cr, uid, ids, context=context): + res[product.id] = product[ptype] or 0.0 + if ptype == 'list_price': + res[product.id] = (res[product.id] * product.price_margin) + \ + product.price_extra if 'uom' in context: - result2[res['id']] = self.pool.get('product.uom')._compute_price(cr, uid, context['uom'], result2[res['id']]) - return result2 + uom = product.uos_id or product.uom_id + res[product.id] = product_uom_obj._compute_price(cr, uid, + uom.id, res[product.id], context['uom']) + return res +# result = self.read(cr, uid, ids, [ptype, 'price_extra','price_margin']) +# result2 = {} +# for res in result: +# result2[res['id']] = res[ptype] or 0.0 +# if ptype=='list_price': +# result2[res['id']] = result2[res['id']] * res['price_margin'] + res['price_extra'] +# if 'uom' in context: +# result2[res['id']] = self.pool.get('product.uom')._compute_price(cr, uid, context['uom'], result2[res['id']]) +# return result2 def copy(self, cr, uid, id, default=None, context=None): if not context: diff --git a/addons/sale/report/order.rml b/addons/sale/report/order.rml index 96e1b37d277..3edf26fcc9c 100644 --- a/addons/sale/report/order.rml +++ b/addons/sale/report/order.rml @@ -1,3 +1,3 @@ -[[ repeatIn(objects,'o') ]] [[ setLang(o.partner_id.lang) ]]Shipping address :[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]][[ o.partner_shipping_id.title or '' ]] [[ o.partner_shipping_id.name ]][[ o.partner_shipping_id.street ]][[ o.partner_shipping_id.street2 or '' ]][[ o.partner_shipping_id.zip or '' ]] [[ o.partner_shipping_id.city or '' ]][[ o.partner_shipping_id.state_id and o.partner_shipping_id.state_id.name or '' ]][[ o.partner_shipping_id.country_id and o.partner_shipping_id.country_id.name or '' ]] Invoice address :[[ o.partner_invoice_id.street ]][[ o.partner_invoice_id.street2 and (', %s' % o.partner_invoice_id.street2 or '') ]][[ o.partner_invoice_id.zip or '' ]] [[ o.partner_invoice_id.city or '' ]] [[ o.partner_invoice_id.country_id and (', %s' % (o.partner_invoice_id.country_id and o.partner_invoice_id.country_id.name or '')) ]] [[ o.partner_id.title or '' ]] [[ o.partner_id.name ]][[ o.partner_order_id.title or '' ]] [[ o.partner_order_id.name ]][[ o.partner_order_id.street ]][[ o.partner_order_id.street2 or '' ]][[ o.partner_order_id.zip or '' ]] [[ o.partner_order_id.city or '' ]][[ o.partner_order_id.state_id and o.partner_order_id.state_id.name or '' ]][[ o.partner_order_id.country_id and o.partner_order_id.country_id.name or '' ]] Tel. : [[ o.partner_order_id.phone or removeParentNode('para') ]]Fax : [[ o.partner_order_id.fax or removeParentNode('para') ]]VAT : [[ o.partner_id.vat or removeParentNode('para') ]] [[ o.state<>'draft' and removeParentNode('para') ]] Quotation N° : [[ o.name ]][[ o.state=='draft' and removeParentNode('para') ]] Order N° : [[ o.name ]] Your order reference : [[ o.client_order_ref or '' ]]Date ordered : [[ time.strftime('%d/%m/%Y', time.strptime(o.date_order, '%Y-%m-%d')) ]]Our salesman : [[ o.user_id.name ]] Product DescriptionApplicable TaxesQtyDisc.Unit PriceNet Price
[[ repeatIn(o.order_line,'line') ]][[ line.name ]][[ ', '.join(map(lambda x: x.name, line.tax_id))]][[ '%.2f' % line.product_uos_qty or '%.2f' % line.product_uom_qty ]] [[ line.product_uos and line.product_uos.name or line.product_uom.name ]][[ (line.discount and str(line.discount)+'%')or '' ]][[ '%.2f' % line.price_unit ]][[ '%.2f' % line.price_subtotal ]][[ repeatIn((line.notes and line.notes.splitlines()) or [], 'l') ]][[ l or removeParentNode('table') ]]
Net Total :[[ '%.2f' % o.amount_untaxed ]] [[ o.pricelist_id.currency_id.name ]] Taxes :[[ '%.2f' % o.amount_tax ]] [[ o.pricelist_id.currency_id.name ]] TOTAL :[[ '%.2f' % o.amount_total ]] [[ o.pricelist_id.currency_id.name ]] [[ format(o.note or '') ]][[ format((o.partner_id.property_payment_term and o.partner_id.property_payment_term[1]) or '') ]]
+[[ repeatIn(objects,'o') ]] [[ setLang(o.partner_id.lang) ]]Shipping address :[[ o.partner_id.title or '' ]] [[ o.partner_id.name ]][[ o.partner_shipping_id.title or '' ]] [[ o.partner_shipping_id.name ]][[ o.partner_shipping_id.street ]][[ o.partner_shipping_id.street2 or '' ]][[ o.partner_shipping_id.zip or '' ]] [[ o.partner_shipping_id.city or '' ]][[ o.partner_shipping_id.state_id and o.partner_shipping_id.state_id.name or '' ]][[ o.partner_shipping_id.country_id and o.partner_shipping_id.country_id.name or '' ]] Invoice address :[[ o.partner_invoice_id.street ]][[ o.partner_invoice_id.street2 and (', %s' % o.partner_invoice_id.street2 or '') ]][[ o.partner_invoice_id.zip or '' ]] [[ o.partner_invoice_id.city or '' ]] [[ o.partner_invoice_id.country_id and (', %s' % (o.partner_invoice_id.country_id and o.partner_invoice_id.country_id.name or '')) ]] [[ o.partner_id.title or '' ]] [[ o.partner_id.name ]][[ o.partner_order_id.title or '' ]] [[ o.partner_order_id.name ]][[ o.partner_order_id.street ]][[ o.partner_order_id.street2 or '' ]][[ o.partner_order_id.zip or '' ]] [[ o.partner_order_id.city or '' ]][[ o.partner_order_id.state_id and o.partner_order_id.state_id.name or '' ]][[ o.partner_order_id.country_id and o.partner_order_id.country_id.name or '' ]] Tel. : [[ o.partner_order_id.phone or removeParentNode('para') ]]Fax : [[ o.partner_order_id.fax or removeParentNode('para') ]]VAT : [[ o.partner_id.vat or removeParentNode('para') ]] [[ o.state<>'draft' and removeParentNode('para') ]] Quotation N° : [[ o.name ]][[ o.state=='draft' and removeParentNode('para') ]] Order N° : [[ o.name ]] Your order reference : [[ o.client_order_ref or '' ]]Date ordered : [[ time.strftime('%d/%m/%Y', time.strptime(o.date_order, '%Y-%m-%d')) ]]Our salesman : [[ o.user_id.name ]] Product DescriptionApplicable TaxesQtyDisc.Unit PriceNet Price
[[ repeatIn(o.order_line,'line') ]][[ line.name ]][[ ', '.join(map(lambda x: x.name, line.tax_id))]][[ line.product_uos and '%.2f' % line.product_uos_qty or '%.2f' % line.product_uom_qty ]] [[ line.product_uos and line.product_uos.name or line.product_uom.name ]][[ (line.discount and str(line.discount)+'%')or '' ]][[ '%.2f' % line.price_unit ]][[ '%.2f' % line.price_subtotal ]][[ repeatIn((line.notes and line.notes.splitlines()) or [], 'l') ]][[ l or removeParentNode('table') ]]
Net Total :[[ '%.2f' % o.amount_untaxed ]] [[ o.pricelist_id.currency_id.name ]] Taxes :[[ '%.2f' % o.amount_tax ]] [[ o.pricelist_id.currency_id.name ]] TOTAL :[[ '%.2f' % o.amount_total ]] [[ o.pricelist_id.currency_id.name ]] [[ format(o.note or '') ]][[ format((o.partner_id.property_payment_term and o.partner_id.property_payment_term[1]) or '') ]]
diff --git a/addons/sale/sale.py b/addons/sale/sale.py index d742a41c44b..cfd051eb8c2 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -496,6 +496,9 @@ class sale_order(osv.osv): return False sale_order() +# TODO add a field price_unit_uos +# - update it on change product and unit price +# - use it in report if there is a uos class sale_order_line(osv.osv): def copy(self, cr, uid, id, default=None, context={}): if not default: default = {} @@ -623,16 +626,20 @@ class sale_order_line(osv.osv): return res def uos_change(self, cr, uid, ids, product_uos, product_uos_qty=0, product_id=None): + product_obj = self.pool.get('product.product') if not product_id: - return {'value': {'product_uom': product_uos, 'product_uom_qty': product_uos_qty}, 'domain':{}} - res = self.pool.get('product.product').read(cr, uid, [product_id], ['uom_id', 'uos_id', 'uos_coeff', 'weight'])[0] + return {'value': {'product_uom': product_uos, + 'product_uom_qty': product_uos_qty}, 'domain':{}} + + product = product_obj.browse(cr, uid, product_id) value = { - 'product_uom' : res['uom_id'], + 'product_uom' : product.uom_id, } + # FIXME must depend on uos/uom of the product and not only of the coeff. try: value.update({ - 'product_uom_qty' : product_uos_qty / res['uos_coeff'], - 'th_weight' : product_uos_qty / res['uos_coeff'] * res['weight'] + 'product_uom_qty' : product_uos_qty / product.uos_coeff, + 'th_weight' : product_uos_qty / product.uos_coeff * product.weight }) except ZeroDivisionError: pass @@ -644,29 +651,61 @@ class sale_order_line(osv.osv): default.update({'state':'draft', 'move_ids':[], 'invoiced':False, 'invoice_lines':[]}) return super(sale_order_line, self).copy(cr, uid, id, default, context) - 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): - if partner_id: - lang=self.pool.get('res.partner').read(cr, uid, [partner_id])[0]['lang'] - context = {'lang':lang} - if not product: - return {'value': {'price_unit': 0.0, 'notes':'', 'th_weight' : 0, 'product_uos_qty': qty}, 'domain':{'product_uom':[]}} - if not pricelist: - raise osv.except_osv('No Pricelist !', 'You have to select a pricelist in the sale form !\nPlease set one before choosing a product.') - price = self.pool.get('product.pricelist').price_get(cr,uid,[pricelist], product, qty or 1.0, partner_id, {'uom': uom})[pricelist] - if price is False: - raise osv.except_osv('No valid pricelist line found !', "Couldn't find a pricelist line matching this product and quantity.\nYou have to change either the product, the quantity or the pricelist.") - res = self.pool.get('product.product').read(cr, uid, [product], context=context)[0] + 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): + product_uom_obj = self.pool.get('product.uom') + partner_obj = self.pool.get('res.partner') + product_obj = self.pool.get('product.product') - result = {'price_unit': price, 'type':res['procure_method'], 'notes':res['description_sale']} + if partner_id: + lang = partner_obj.browse(cr, uid, partner_id).lang + context = {'lang': lang} + + if not product: + return {'value': {'price_unit': 0.0, 'notes':'', 'th_weight' : 0, + 'product_uos_qty': qty}, 'domain': {'product_uom': []}} + + if not pricelist: + raise osv.except_osv('No Pricelist !', + 'You have to select a pricelist in the sale form !\n' + 'Please set one before choosing a product.') + + price = self.pool.get('product.pricelist').price_get(cr, uid, [pricelist], + product, qty or 1.0, partner_id, {'uom': uom})[pricelist] + if price is False: + raise osv.except_osv('No valid pricelist line found !', + "Couldn't find a pricelist line matching this product and quantity.\n" + "You have to change either the product, the quantity or the pricelist.") + + product = product_obj.browse(cr, uid, product, context=context) + + if uom: + uom2 = product_uom_obj.browse(cr, uid, uom) + if product.uom_id.category_id.id <> uom2.category_id.id: + uom = False + + if uos: + if product.uos_id: + uos2 = product_uom_obj.browse(cr, uid, uos) + if product.uos_id.category_id.id <> uos2.category_id.id: + uos = False + else: + uos = False + + result = {'price_unit': price, 'type': product.procure_method, + 'notes': product.description_sale} if update_tax: #The quantity only have changed - result['delay'] = (res['sale_delay'] or 0.0) - taxes = self.pool.get('account.tax').browse(cr, uid, res['taxes_id']) + result['delay'] = (product.sale_delay or 0.0) + taxes = self.pool.get('account.tax').browse(cr, uid, + [x.id for x in product.taxes_id]) taxep = None if partner_id: - taxep = self.pool.get('res.partner').browse(cr, uid, partner_id).property_account_tax + taxep = self.pool.get('res.partner').browse(cr, uid, + partner_id).property_account_tax if not taxep or not taxep.id: - result['tax_id'] = res['taxes_id'] + result['tax_id'] = [x.id for x in product.taxes_id] else: res5 = [taxep.id] for t in taxes: @@ -674,26 +713,34 @@ class sale_order_line(osv.osv): res5.append(t.id) result['tax_id'] = res5 - result['name'] = res['partner_ref'] + result['name'] = product.partner_ref + domain = {} if not uom and not uos: - result['product_uom'] = res['uom_id'] and res['uom_id'][0] - if result['product_uom']: - result['product_uos'] = res['uos_id'] - result['product_uos_qty'] = qty * res['uos_coeff'] - result['th_weight'] = qty * res['weight'] - res2 = self.pool.get('product.uom').read(cr, uid, [result['product_uom']], ['category_id']) - if res2 and res2[0]['category_id']: - domain = {'product_uom':[('category_id','=',res2[0]['category_id'][0])]} + result['product_uom'] = product.uom_id.id + if product.uos_id: + result['product_uos'] = product.uos_id.id + result['product_uos_qty'] = qty * product.uos_coeff + else: + result['product_uos'] = False + result['product_uos_qty'] = qty + result['th_weight'] = qty * product.weight + domain = {'product_uom': + [('category_id', '=', product.uom_id.category_id.id)]} elif uom: # whether uos is set or not - default_uom = res['uom_id'] and res['uom_id'][0] - q = self.pool.get('product.uom')._compute_qty(cr, uid, uom, qty, default_uom) - result['product_uos'] = res['uos_id'] - result['product_uos_qty'] = q * res['uos_coeff'] - result['th_weight'] = q * res['weight'] + default_uom = product.uom_id and product.uom_id.id + q = product_uom_obj._compute_qty(cr, uid, uom, qty, default_uom) + if product.uos_id: + result['product_uos'] = product.uos_id.id + result['product_uos_qty'] = q * product.uos_coeff + else: + result['product_uos'] = False + result['product_uos_qty'] = q + result['th_weight'] = q * product.weight elif uos: # only happens if uom is False - result['product_uom'] = res['uom_id'] and res['uom_id'][0] - result['product_uom_qty'] = qty_uos / res['uos_coeff'] - result['th_weight'] = result['product_uom_qty'] * res['weight'] - return {'value':result, 'domain':domain} + result['product_uom'] = product.uom_id and product.uom_id.id + result['product_uom_qty'] = qty_uos / product.uos_coeff + result['th_weight'] = result['product_uom_qty'] * product.weight + return {'value': result, 'domain': domain} + sale_order_line()