From 7c72cd000ccc9c38ba6e6b4abd1621e0dc2415a2 Mon Sep 17 00:00:00 2001 From: Quentin De Paoli Date: Thu, 10 Jun 2010 14:44:52 +0200 Subject: [PATCH] [REF] product: refactoring : move fields related to stock into the stock module [IMP] stock: account moves on stock moves. Bugfixing + correct implementation + refactoring bzr revid: qdp@cyan-20100610124452-ewla273tk6zg4lsv --- addons/product/product.py | 10 --- addons/product/product_view.xml | 1 - addons/stock/product.py | 34 +++++--- addons/stock/product_view.xml | 12 +++ addons/stock/stock.py | 143 ++++++++++++++++---------------- 5 files changed, 106 insertions(+), 94 deletions(-) diff --git a/addons/product/product.py b/addons/product/product.py index 52d643940f5..f577437cedb 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -209,13 +209,6 @@ class product_category(osv.osv): 'child_id': fields.one2many('product.category', 'parent_id', string='Child Categories'), 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of product categories."), 'type': fields.selection([('view','View'), ('normal','Normal')], 'Category Type'), - 'property_stock_variation': fields.property( - 'account.account', - type='many2one', - relation='account.account', - string="Stock variation Account", - method=True, - view_load=True, help="This account will be used in product when valuation type is real-time valuation ",), } @@ -465,9 +458,6 @@ class product_product(osv.osv): 'price_extra': fields.float('Variant Price Extra', digits_compute=dp.get_precision('Sale Price')), 'price_margin': fields.float('Variant Price Margin', digits_compute=dp.get_precision('Sale Price')), 'pricelist_id': fields.dummy(string='Pricelist',relation='product.pricelist', type='many2one'), - 'valuation':fields.selection([('manual_periodic','Manual Periodic Valuation'), - ('real_time','Real Time valuation'), - ('','')],'Valuation',help="Decide if the system must automatically creates account moves based on stock moves"), } def onchange_uom(self, cursor, user, ids, uom_id,uom_po_id): diff --git a/addons/product/product_view.xml b/addons/product/product_view.xml index 44250e51bf5..8fbe2edbe08 100644 --- a/addons/product/product_view.xml +++ b/addons/product/product_view.xml @@ -148,7 +148,6 @@ - diff --git a/addons/stock/product.py b/addons/stock/product.py index 5336143befb..16db5f4e1f2 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -27,12 +27,12 @@ class product_product(osv.osv): _inherit = "product.product" - def get_product_accounts(self, cr, uid, product_id, context={}): + def get_product_accounts(self, cr, uid, product_id, context=None): """ To get the stock input account, stock output account and stock journal related to product. @param product_id: product id @return: dictionary which contains information regarding stock input account, stock output account and stock journal - """ - product_obj = self.pool.get('product.product').browse(cr, uid, product_id, False) + """ + product_obj = self.pool.get('product.product').browse(cr, uid, product_id, context) stock_input_acc = product_obj.property_stock_account_input and product_obj.property_stock_account_input.id or False if not stock_input_acc: @@ -42,14 +42,15 @@ class product_product(osv.osv): if not stock_output_acc: stock_output_acc = product_obj.categ_id.property_stock_account_output_categ and product_obj.categ_id.property_stock_account_output_categ.id or False - journal_id = product_obj.categ_id.property_stock_journal and product_obj.categ_id.property_stock_journal.id or False + account_variation = product_obj.categ_id.property_stock_variation and product_obj.categ_id.property_stock_variation.id or False - res = {} - res.update({'stock_account_input': stock_input_acc}) - res.update({'stock_account_output': stock_output_acc}) - res.update({'stock_journal': journal_id}) - return res + return { + 'stock_account_input': stock_input_acc, + 'stock_account_output': stock_output_acc, + 'stock_journal': journal_id, + 'property_stock_variation': account_variation + } def do_change_standard_price(self, cr, uid, ids, datas, context={}): """ Changes the Standard Price of Product and creates an account move accordingly. @@ -312,7 +313,14 @@ class product_product(osv.osv): 'track_incoming': fields.boolean('Track Incoming Lots', help="Forces to use a tracking lot during receptions"), 'track_outgoing': fields.boolean('Track Outgoing Lots', help="Forces to use a tracking lot during deliveries"), 'location_id': fields.dummy(string='Location', relation='stock.location', type='many2one', domain=[('usage','=','internal')]), + 'valuation':fields.selection([('manual_periodic', 'Periodic (manual)'), + ('real_time','Real Time (automatized)'),], 'Stock Valuation', help="Decide if the system must automatically creates account moves based on stock moves", required=True), } + + _defaults = { + 'valuation': lambda *a: 'manual_periodic', + } + def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): res = super(product_product,self).fields_view_get(cr, uid, view_id, view_type, context, toolbar=toolbar, submenu=submenu) if context == None: @@ -398,7 +406,6 @@ class product_template(osv.osv): string='Stock Output Account', method=True, view_load=True, help='This account will be used, instead of the default one, to value output stock'), } - product_template() @@ -417,8 +424,13 @@ class product_category(osv.osv): type='many2one', relation='account.account', string='Stock Output Account', method=True, view_load=True, help='This account will be used to value the output stock'), + 'property_stock_variation': fields.property('account.account', + type='many2one', + relation='account.account', + string="Stock variation Account", + method=True, view_load=True, + help="This account will be used in product when valuation type is real-time valuation ",), } - product_category() # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/stock/product_view.xml b/addons/stock/product_view.xml index 02c60968e04..4f6ede6c460 100644 --- a/addons/stock/product_view.xml +++ b/addons/stock/product_view.xml @@ -123,6 +123,18 @@ + + product.valuation.stock.form.inherit + product.product + form + + + + + + + + diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 185e1a64c78..72f0cb0936d 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- ############################################################################## # # OpenERP, Open Source Management Solution @@ -158,7 +157,7 @@ class stock_location(osv.osv): _columns = { 'name': fields.char('Location Name', size=64, required=True, translate=True), 'active': fields.boolean('Active', help="If the active field is set to true, it will allow you to hide the stock location without removing it."), - 'usage': fields.selection([('supplier', 'Supplier Location'), ('view', 'View'), ('internal', 'Internal Location'), ('customer', 'Customer Location'), ('inventory', 'Inventory'), ('procurement', 'Procurement'), ('production', 'Production')], 'Location Type', required=True), + 'usage': fields.selection([('supplier', 'Supplier Location'), ('view', 'View'), ('internal', 'Internal Location'), ('customer', 'Customer Location'), ('inventory', 'Inventory'), ('procurement', 'Procurement'), ('production', 'Production'), ('transit', 'Transit Location for Inter-Companies Transfers')], 'Location Type', required=True), 'allocation_method': fields.selection([('fifo', 'FIFO'), ('lifo', 'LIFO'), ('nearest', 'Nearest')], 'Allocation Method', required=True), 'complete_name': fields.function(_complete_name, method=True, type='char', size=100, string="Location Name"), @@ -195,7 +194,7 @@ class stock_location(osv.osv): 'parent_right': fields.integer('Right Parent', select=1), 'stock_real_value': fields.function(_product_value, method=True, type='float', string='Real Stock Value', multi="stock"), 'stock_virtual_value': fields.function(_product_value, method=True, type='float', string='Virtual Stock Value', multi="stock"), - 'company_id': fields.many2one('res.company', 'Company', required=True,select=1), + 'company_id': fields.many2one('res.company', 'Company', select=1, help='Let this field empty if this location is shared for every companies'), 'scrap_location': fields.boolean('Scrap Location', help='Check this box if the current location is a place for destroyed items'), } _defaults = { @@ -1737,15 +1736,54 @@ class stock_move(osv.osv): #self.action_cancel(cr,uid, ids2, context) return True + def _get_accounting_values(self, cr, uid, move, context=None): + product_obj=self.pool.get('product.product') + product_uom_obj = self.pool.get('product.uom') + price_type_obj = self.pool.get('product.price.type') + accounts = product_obj.get_product_accounts(cr,uid,move.product_id.id,context) + acc_src = accounts['stock_account_input'] + acc_dest = accounts['stock_account_output'] + acc_variation = accounts['property_stock_variation'] + journal_id = accounts['stock_journal'] + + if not acc_src: + raise osv.except_osv(_('Error!'), _('There is no stock input account defined ' \ + 'for this product: "%s" (id: %d)') % \ + (move.product_id.name, move.product_id.id,)) + if not acc_dest: + raise osv.except_osv(_('Error!'), _('There is no stock output account defined ' \ + 'for this product: "%s" (id: %d)') % \ + (move.product_id.name, move.product_id.id,)) + if not journal_id: + raise osv.except_osv(_('Error!'), _('There is no journal defined '\ + 'on the product category: "%s" (id: %d)') % \ + (move.product_id.categ_id.name, move.product_id.categ_id.id,)) + if not acc_variation: + raise osv.except_osv(_('Error!'), _('There is no variation account defined '\ + 'on the product category: "%s" (id: %d)') % \ + (move.product_id.categ_id.name, move.product_id.categ_id.id,)) + if acc_src != acc_dest: + default_uom = move.product_id.uom_id.id + q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom) + if move.product_id.cost_method == 'average' and move.price_unit: + amount = q * move.price_unit + # Base computation on valuation price type + else: + company_id = move.company_id.id + context['currency_id'] = move.company_id.currency_id.id + pricetype = price_type_obj.browse(cr,uid,move.company_id.property_valuation_price_type.id) + amount_unit = move.product_id.price_get(pricetype.field, context)[move.product_id.id] + amount = amount_unit * q or 1.0 + # amount = q * move.product_id.standard_price + return journal_id, acc_src, acc_dest, acc_variation, amount + + def action_done(self, cr, uid, ids, context={}): """ Makes the move done and if all moves are done, it will finish the picking. @return: """ track_flag = False picking_ids = [] - lines=[] - sale=[] - purchase=[] product_uom_obj = self.pool.get('product.uom') price_type_obj = self.pool.get('product.price.type') product_obj=self.pool.get('product.product') @@ -1770,76 +1808,37 @@ class stock_move(osv.osv): # acc_src = None acc_dest = None - if move.product_id.valuation=='real_time': - - journal_id = move.product_id.categ_id.property_stock_journal and move.product_id.categ_id.property_stock_journal.id or False - accounts=product_obj.get_product_accounts(cr,uid,move.product_id.id,context) - account_variation = move.product_id.categ_id.property_stock_variation.id - acc_src=accounts['stock_account_input'] - acc_dest=accounts['stock_account_output'] - journal_id=accounts['stock_journal'] - if not acc_src: - raise osv.except_osv(_('Error!'), - _('There is no stock input account defined ' \ - 'for this product: "%s" (id: %d)') % \ - (move.product_id.name, - move.product_id.id,)) - if not acc_dest: - raise osv.except_osv(_('Error!'), - _('There is no stock output account defined ' \ - 'for this product: "%s" (id: %d)') % \ - (move.product_id.name, - move.product_id.id,)) - if not journal_id: - raise osv.except_osv(_('Error!'), - _('There is no journal defined '\ - 'on the product category: "%s" (id: %d)') % \ - (move.product_id.categ_id.name, - move.product_id.categ_id.id,)) - if not account_variation: - raise osv.except_osv(_('Error!'), - _('There is no variation account defined '\ - 'on the product category: "%s" (id: %d)') % \ - (move.product_id.categ_id.name, - move.product_id.categ_id.id,)) - if acc_src != acc_dest: - default_uom = move.product_id.uom_id.id - q = product_uom_obj._compute_qty(cr, uid, move.product_uom.id, move.product_qty, default_uom) - if move.product_id.cost_method == 'average' and move.price_unit: - amount = q * move.price_unit - # Base computation on valuation price type - else: - company_id = move.company_id.id - context['currency_id'] = move.company_id.currency_id.id - pricetype = price_type_obj.browse(cr,uid,move.company_id.property_valuation_price_type.id) - amount_unit = move.product_id.price_get(pricetype.field, context)[move.product_id.id] - amount = amount_unit * q or 1.0 - # amount = q * move.product_id.standard_price - partner_id = False - if move.picking_id: - - if (move.location_id.usage=='internal' and move.location_dest_id.usage=='customer') or \ - (move.location_id.usage=='internal' and move.location_dest_id.usage=='internal'\ - and move.location_id.company_id.id!=move.location_dest_id.company_id.id): - sale.append(self.create_account_move(cr,uid,move,acc_dest ,account_variation ,amount,context)) + if move.product_id.valuation == 'real_time': + lines = [] + if ((move.location_id.usage == 'internal' and move.location_dest_id.usage == 'customer') or (move.location_id.usage == 'internal' and move.location_dest_id.usage == 'transit')): + if move.location_id.company_id: + context.update({'force_company': move.location_id.company_id.id}) + journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context) + lines = [(journal_id, self.create_account_move(cr, uid, move, acc_dest, acc_variation, amount, context))] - if (move.location_id.usage =='supplier' and move.location_dest_id.usage=='internal') or\ - (move.location_id.usage=='internal' and move.location_dest_id.usage=='internal'\ - and move.location_id.company_id.id!=move.location_dest_id.company_id.id): - - purchase.append(self.create_account_move(cr,uid,move,account_variation, acc_src ,amount,context)) - - - lines.append(purchase and purchase[0] ) - lines.append( sale and sale[0]) - - move_obj.create(cr, uid, { + elif ((move.location_id.usage == 'supplier' and move.location_dest_id.usage == 'internal') or (move.location_id.usage == 'transit' and move.location_dest_id.usage == 'internal')): + if move.location_dest_id.company_id: + context.update({'force_company': move.location_dest_id.company_id.id}) + journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context) + lines = [(journal_id, self.create_account_move(cr, uid, move, acc_variation, acc_src, amount, context))] + elif (move.location_id.usage == 'internal' and move.location_dest_id.usage == 'internal' and move.location_id.company_id != move.location_dest_id.company_id): + if move.location_id.company_id: + context.update({'force_company': move.location_id.company_id.id}) + journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context) + line1 = [(journal_id, self.create_account_move(cr, uid, move, acc_dest, acc_variation, amount, context))] + if move.location_dest_id.company_id: + context.update({'force_company': move.location_dest_id.company_id.id}) + journal_id, acc_src, acc_dest, acc_variation, amount = self._get_accounting_values(cr, uid, move, context) + line2 = [(journal_id, self.create_account_move(cr, uid, move, acc_variation, acc_src, amount, context))] + lines = line1 + line2 + for j_id, line in lines: + move_obj.create(cr, uid, { 'name': move.name, - 'journal_id': journal_id, + 'journal_id': j_id, 'type':'cont_voucher', - 'line_id': len(lines)==2 and lines[0] +lines[1] or lines, + 'line_id': line, 'ref': move.picking_id and move.picking_id.name, - }) + }) tracking_lot = False if context: tracking_lot = context.get('tracking_lot', False)