diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index af7e506068f..f7d372e2750 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -318,16 +318,26 @@ class account_invoice(models.Model): @api.model def fields_view_get(self, view_id=None, view_type=False, toolbar=False, submenu=False): context = self._context + + def get_view_id(xid, name): + try: + return self.env['ir.model.data'].xmlid_to_res_id('account.' + xid, raise_if_not_found=True) + except ValueError: + try: + return self.env['ir.ui.view'].search([('name', '=', name)], limit=1).id + except Exception: + return False # view not found + if context.get('active_model') == 'res.partner' and context.get('active_ids'): partner = self.env['res.partner'].browse(context['active_ids'])[0] if not view_type: - view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.tree')]).id + view_id = get_view_id('invoice_tree', 'account.invoice.tree') view_type = 'tree' elif view_type == 'form': if partner.supplier and not partner.customer: - view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.supplier.form')]).id + view_id = get_view_id('invoice_supplier_form', 'account.invoice.supplier.form') elif partner.customer and not partner.supplier: - view_id = self.env['ir.ui.view'].search([('name', '=', 'account.invoice.form')]).id + view_id = get_view_id('invoice_form', 'account.invoice.form') res = super(account_invoice, self).fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu) diff --git a/addons/account/report/account_partner_balance.py b/addons/account/report/account_partner_balance.py index ecd287551e5..51fff6c1413 100644 --- a/addons/account/report/account_partner_balance.py +++ b/addons/account/report/account_partner_balance.py @@ -33,10 +33,6 @@ class partner_balance(report_sxw.rml_parse, common_report_header): self.account_ids = [] self.localcontext.update( { 'time': time, - 'lines': self.lines, - 'sum_debit': self._sum_debit, - 'sum_credit': self._sum_credit, - 'sum_litige': self._sum_litige, 'get_fiscalyear': self._get_fiscalyear, 'get_journal': self._get_journal, 'get_filter': self._get_filter, @@ -70,7 +66,20 @@ class partner_balance(report_sxw.rml_parse, common_report_header): "WHERE a.type IN %s " \ "AND a.active", (self.ACCOUNT_TYPE,)) self.account_ids = [a for (a,) in self.cr.fetchall()] - return super(partner_balance, self).set_context(objects, data, ids, report_type=report_type) + res = super(partner_balance, self).set_context(objects, data, ids, report_type=report_type) + lines = self.lines() + sum_debit = sum_credit = sum_litige = 0 + for line in filter(lambda x: x['type'] == 3, lines): + sum_debit += line['debit'] or 0 + sum_credit += line['credit'] or 0 + sum_litige += line['enlitige'] or 0 + self.localcontext.update({ + 'lines': lambda: lines, + 'sum_debit': lambda: sum_debit, + 'sum_credit': lambda: sum_credit, + 'sum_litige': lambda: sum_litige, + }) + return res def lines(self): move_state = ['draft','posted'] @@ -236,62 +245,6 @@ class partner_balance(report_sxw.rml_parse, common_report_header): i = i + 1 return completearray - def _sum_debit(self): - move_state = ['draft','posted'] - if self.target_move == 'posted': - move_state = ['posted'] - - if not self.ids: - return 0.0 - self.cr.execute( - "SELECT sum(debit) " \ - "FROM account_move_line AS l " \ - "JOIN account_move am ON (am.id = l.move_id)" \ - "WHERE l.account_id IN %s" \ - "AND am.state IN %s" \ - "AND " + self.query + "", - (tuple(self.account_ids), tuple(move_state))) - temp_res = float(self.cr.fetchone()[0] or 0.0) - return temp_res - - def _sum_credit(self): - move_state = ['draft','posted'] - if self.target_move == 'posted': - move_state = ['posted'] - - if not self.ids: - return 0.0 - self.cr.execute( - "SELECT sum(credit) " \ - "FROM account_move_line AS l " \ - "JOIN account_move am ON (am.id = l.move_id)" \ - "WHERE l.account_id IN %s" \ - "AND am.state IN %s" \ - "AND " + self.query + "", - (tuple(self.account_ids), tuple(move_state))) - temp_res = float(self.cr.fetchone()[0] or 0.0) - return temp_res - - def _sum_litige(self): - #gives the total of move lines with blocked boolean set to TRUE for the report selection - move_state = ['draft','posted'] - if self.target_move == 'posted': - move_state = ['posted'] - - if not self.ids: - return 0.0 - self.cr.execute( - "SELECT sum(debit-credit) " \ - "FROM account_move_line AS l " \ - "JOIN account_move am ON (am.id = l.move_id)" \ - "WHERE l.account_id IN %s" \ - "AND am.state IN %s" \ - "AND " + self.query + " " \ - "AND l.blocked=TRUE ", - (tuple(self.account_ids), tuple(move_state), )) - temp_res = float(self.cr.fetchone()[0] or 0.0) - return temp_res - def _get_partners(self): if self.result_selection == 'customer': diff --git a/addons/hr_timesheet_invoice/report/account_analytic_profit.py b/addons/hr_timesheet_invoice/report/account_analytic_profit.py index 3f7108fb665..15116ee8431 100644 --- a/addons/hr_timesheet_invoice/report/account_analytic_profit.py +++ b/addons/hr_timesheet_invoice/report/account_analytic_profit.py @@ -39,13 +39,15 @@ class account_analytic_profit(report_sxw.rml_parse): return user_obj.browse(self.cr, self.uid, ids) def _journal_ids(self, form, user_id): + if isinstance(user_id, (int, long)): + user_id = [user_id] line_obj = self.pool['account.analytic.line'] journal_obj = self.pool['account.analytic.journal'] line_ids=line_obj.search(self.cr, self.uid, [ ('date', '>=', form['date_from']), ('date', '<=', form['date_to']), ('journal_id', 'in', form['journal_ids'][0][2]), - ('user_id', '=', user_id), + ('user_id', 'in', user_id), ]) ids=list(set([b.journal_id.id for b in line_obj.browse(self.cr, self.uid, line_ids)])) return journal_obj.browse(self.cr, self.uid, ids) diff --git a/addons/product/pricelist.py b/addons/product/pricelist.py index 07efc907bb2..29a0fa00d67 100644 --- a/addons/product/pricelist.py +++ b/addons/product/pricelist.py @@ -315,7 +315,12 @@ class product_pricelist(osv.osv): price = price * (1.0+(rule.price_discount or 0.0)) if rule.price_round: price = tools.float_round(price, precision_rounding=rule.price_round) - price += (rule.price_surcharge or 0.0) + if context.get('uom'): + # compute price_surcharge based on reference uom + factor = product_uom_obj.browse(cr, uid, context.get('uom'), context=context).factor + else: + factor = 1.0 + price += (rule.price_surcharge or 0.0) / factor if rule.price_min_margin: price = max(price, price_limit+rule.price_min_margin) if rule.price_max_margin: diff --git a/addons/product/product.py b/addons/product/product.py index 3f54cba24d1..0e38e80542c 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -1118,6 +1118,34 @@ class product_product(osv.osv): 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" diff --git a/addons/product/tests/__init__.py b/addons/product/tests/__init__.py index 850189ac0f4..4a1ce43351b 100644 --- a/addons/product/tests/__init__.py +++ b/addons/product/tests/__init__.py @@ -1,5 +1,6 @@ -from . import test_uom +from . import test_uom, test_pricelist fast_suite = [ test_uom, + test_pricelist ] diff --git a/addons/product/tests/test_pricelist.py b/addons/product/tests/test_pricelist.py new file mode 100644 index 00000000000..4e61cb11b75 --- /dev/null +++ b/addons/product/tests/test_pricelist.py @@ -0,0 +1,70 @@ +from openerp.tests.common import TransactionCase + +class TestPricelist(TransactionCase): + """Tests for unit of measure conversion""" + + def setUp(self): + super(TestPricelist, self).setUp() + cr, uid, context = self.cr, self.uid, {} + self.ir_model_data = self.registry('ir.model.data') + self.product_product = self.registry('product.product') + self.product_pricelist = self.registry('product.pricelist') + self.uom = self.registry('product.uom') + + self.usb_adapter_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_48')[1] + self.datacard_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_product_46')[1] + self.unit_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_unit')[1] + self.dozen_id = self.ir_model_data.get_object_reference(cr, uid, 'product', 'product_uom_dozen')[1] + + 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 + }), (0, 0, { + 'name': 'Discount -0.5', + 'base': 1, # based on public price + 'price_surcharge': -0.5, + 'product_id': self.datacard_id + })] + })] + }, context=context) + + def test_10_discount(self): + # Make sure the price using a pricelist is the same than without after + # applying the computation manually + cr, uid, context = self.cr, self.uid, {} + + public_context = dict(context, pricelist=self.public_pricelist_id) + pricelist_context = dict(context, pricelist=self.sale_pricelist_id) + + usb_adapter_without_pricelist = self.product_product.browse(cr, uid, self.usb_adapter_id, context=public_context) + usb_adapter_with_pricelist = self.product_product.browse(cr, uid, self.usb_adapter_id, context=pricelist_context) + self.assertEqual(usb_adapter_with_pricelist.price, usb_adapter_without_pricelist.price*0.9) + + datacard_without_pricelist = self.product_product.browse(cr, uid, self.datacard_id, context=public_context) + datacard_with_pricelist = self.product_product.browse(cr, uid, self.datacard_id, context=pricelist_context) + self.assertEqual(datacard_with_pricelist.price, datacard_without_pricelist.price-0.5) + + # Make sure that changing the unit of measure does not break the unit + # price (after converting) + unit_context = dict(context, + pricelist=self.sale_pricelist_id, + uom=self.unit_id) + dozen_context = dict(context, + pricelist=self.sale_pricelist_id, + uom=self.dozen_id) + + usb_adapter_unit = self.product_product.browse(cr, uid, self.usb_adapter_id, context=unit_context) + usb_adapter_dozen = self.product_product.browse(cr, uid, self.usb_adapter_id, context=dozen_context) + self.assertAlmostEqual(usb_adapter_unit.price*12, usb_adapter_dozen.price) + + datacard_unit = self.product_product.browse(cr, uid, self.datacard_id, context=unit_context) + datacard_dozen = self.product_product.browse(cr, uid, self.datacard_id, context=dozen_context) + self.assertAlmostEqual(datacard_unit.price*12, datacard_dozen.price) diff --git a/addons/product_visible_discount/product_visible_discount.py b/addons/product_visible_discount/product_visible_discount.py index c27bb86c901..a654cd15276 100644 --- a/addons/product_visible_discount/product_visible_discount.py +++ b/addons/product_visible_discount/product_visible_discount.py @@ -43,6 +43,7 @@ class sale_order_line(osv.osv): fiscal_position=False, flag=False, context=None): def get_real_price(res_dict, product_id, qty, uom, pricelist): + """Retrieve the price before applying the pricelist""" item_obj = self.pool.get('product.pricelist.item') price_type_obj = self.pool.get('product.price.type') product_obj = self.pool.get('product.product') @@ -58,9 +59,8 @@ class sale_order_line(osv.osv): factor = 1.0 if uom and uom != product.uom_id.id: - product_uom_obj = self.pool.get('product.uom') - uom_data = product_uom_obj.browse(cr, uid, product.uom_id.id) - factor = uom_data.factor + # the unit price is in a different uom + factor = self.pool['product.uom']._compute_qty(cr, uid, uom, 1.0, product.uom_id.id) return product_read[field_name] * factor @@ -77,7 +77,7 @@ class sale_order_line(osv.osv): price=result['price_unit'] else: return res - + uom = result.get('product_uom', uom) product = product_obj.browse(cr, uid, product, context) pricelist_context = dict(context, uom=uom, date=date_order) list_price = pricelist_obj.price_rule_get(cr, uid, [pricelist], diff --git a/addons/sale/wizard/sale_line_invoice.py b/addons/sale/wizard/sale_line_invoice.py index 6666f20c351..b35328724e3 100644 --- a/addons/sale/wizard/sale_line_invoice.py +++ b/addons/sale/wizard/sale_line_invoice.py @@ -101,7 +101,8 @@ class sale_order_line_make_invoice(osv.osv_memory): flag = False break if flag: - workflow.trg_validate(uid, 'sale.order', order.id, 'manual_invoice', cr) + line.order_id.write({'state': 'progress'}) + workflow.trg_validate(uid, 'sale.order', order.id, 'all_lines', cr) if not invoices: raise osv.except_osv(_('Warning!'), _('Invoice cannot be created for this Sales Order Line due to one of the following reasons:\n1.The state of this sales order line is either "draft" or "cancel"!\n2.The Sales Order Line is Invoiced!')) diff --git a/addons/sale_margin/sale_margin.py b/addons/sale_margin/sale_margin.py index 5cbb17873c1..b5c1821ab98 100644 --- a/addons/sale_margin/sale_margin.py +++ b/addons/sale_margin/sale_margin.py @@ -36,7 +36,11 @@ class sale_order_line(osv.osv): frm_cur = self.pool.get('res.users').browse(cr, uid, uid).company_id.currency_id.id to_cur = self.pool.get('product.pricelist').browse(cr, uid, [pricelist])[0].currency_id.id if product: - purchase_price = self.pool.get('product.product').browse(cr, uid, product).standard_price + product = self.pool['product.product'].browse(cr, uid, product, context=context) + purchase_price = product.standard_price + to_uom = res.get('product_uom', uom) + if to_uom != product.uom_id.id: + purchase_price = self.pool['product.uom']._compute_price(cr, uid, product.uom_id.id, purchase_price, to_uom) ctx = context.copy() ctx['date'] = date_order price = self.pool.get('res.currency').compute(cr, uid, frm_cur, to_cur, purchase_price, round=False, context=ctx) diff --git a/addons/web_calendar/static/src/js/web_calendar.js b/addons/web_calendar/static/src/js/web_calendar.js index b4aefc610a4..2aa40c39455 100644 --- a/addons/web_calendar/static/src/js/web_calendar.js +++ b/addons/web_calendar/static/src/js/web_calendar.js @@ -820,7 +820,7 @@ openerp.web_calendar = function(instance) { } else { var pop = new instance.web.form.FormOpenPopup(this); - pop.show_element(this.dataset.model, id, this.dataset.get_context(), { + pop.show_element(this.dataset.model, parseInt(id), this.dataset.get_context(), { title: _.str.sprintf(_t("View: %s"),title), view_id: +this.open_popup_action, res_id: id,