From 4e3fa15df9174617b69ab29601163e2f34c171f6 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 13 Aug 2014 09:51:00 +0200 Subject: [PATCH 1/6] [IMP] cost_method and valuation should be copied when duplicating --- addons/stock_account/product.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/addons/stock_account/product.py b/addons/stock_account/product.py index cdab10af624..f2a49d40204 100644 --- a/addons/stock_account/product.py +++ b/addons/stock_account/product.py @@ -21,6 +21,7 @@ from openerp.osv import fields, osv from openerp.tools.translate import _ +from openerp.fields import Many2one class product_product(osv.osv): _inherit = "product.product" @@ -118,27 +119,28 @@ class product_product(osv.osv): class product_template(osv.osv): _name = 'product.template' _inherit = 'product.template' + _columns = { 'valuation': fields.property(type='selection', selection=[('manual_periodic', 'Periodical (manual)'), ('real_time', 'Real Time (automated)')], string='Inventory Valuation', help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves, with product price as specified by the 'Costing Method'" \ "The inventory variation account set on the product category will represent the current inventory value, and the stock input and stock output account will hold the counterpart moves for incoming and outgoing products." - , required=True), + , required=True, copy=True), 'cost_method': fields.property(type='selection', selection=[('standard', 'Standard Price'), ('average', 'Average Price'), ('real', 'Real Price')], help="""Standard Price: The cost price is manually updated at the end of a specific period (usually every year). Average Price: The cost price is recomputed at each incoming shipment and used for the product valuation. Real Price: The cost price displayed is the price of the last outgoing product (will be use in case of inventory loss for example).""", - string="Costing Method", required=True), + string="Costing Method", required=True, copy=True), 'property_stock_account_input': fields.property( type='many2one', relation='account.account', - string='Stock Input Account', + string='Stock Input Account', help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless " "there is a specific valuation account set on the source location. When not set on the product, the one from the product category is used."), 'property_stock_account_output': fields.property( type='many2one', relation='account.account', - string='Stock Output Account', + string='Stock Output Account', help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless " "there is a specific valuation account set on the destination location. When not set on the product, the one from the product category is used."), } From 6f75507ce79e6cfe1188699fda97dc18c2385039 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Thu, 14 Aug 2014 22:12:45 +0200 Subject: [PATCH 2/6] [WIP] Allow to produce more than the total qty of the production order in the last step --- addons/mrp/mrp.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 2ede7b27b9d..8e661a57178 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -845,7 +845,10 @@ class mrp_production(osv.osv): dicts[product_id] = {} # total qty of consumed product we need after this consumption - total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty) + if product_qty + produced_qty <= production.product_qty: #TODO: Need to add rounding over here? + total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty) + else: + total_consume = (production.product_qty * scheduled.product_qty / production.product_qty) qty = total_consume - consumed_qty # Search for quants related to this related move @@ -918,17 +921,13 @@ class mrp_production(osv.osv): produced_qty = produced_products.get(produce_product.product_id.id, 0) subproduct_factor = self._get_subproduct_factor(cr, uid, production.id, produce_product.id, context=context) rest_qty = (subproduct_factor * production.product_qty) - produced_qty - if float_compare(rest_qty, (subproduct_factor * production_qty), precision_rounding=produce_product.product_id.uom_id.rounding) < 0: - prod_name = produce_product.product_id.name_get()[0][1] - raise osv.except_osv(_('Warning!'), _('You are going to produce total %s quantities of "%s".\nBut you can only produce up to total %s quantities.') % ((subproduct_factor * production_qty), prod_name, rest_qty)) - if float_compare(rest_qty, 0, precision_rounding=produce_product.product_id.uom_id.rounding) > 0: - lot_id = False - if wiz: - lot_id = wiz.lot_id.id - new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) - stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context) - if produce_product.product_id.id == production.product_id.id and new_moves: - main_production_move = new_moves[0] + lot_id = False + if wiz: + lot_id = wiz.lot_id.id + new_moves = stock_mov_obj.action_consume(cr, uid, [produce_product.id], (subproduct_factor * production_qty), location_id=produce_product.location_id.id, restrict_lot_id=lot_id, context=context) + stock_mov_obj.write(cr, uid, new_moves, {'production_id': production_id}, context=context) + if produce_product.product_id.id == production.product_id.id and new_moves: + main_production_move = new_moves[0] if production_mode in ['consume', 'consume_produce']: if wiz: From 7e1919fe0ff57171eac164ca0019d1001f49c955 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 19 Aug 2014 10:29:35 +0200 Subject: [PATCH 3/6] [IMP] Change static buttons with group_ids each time --- addons/mrp/mrp_view.xml | 10 ++++++---- addons/project/res_partner_view.xml | 3 ++- addons/purchase/partner_view.xml | 20 +++++++++++++++----- addons/sale/res_partner_view.xml | 3 ++- addons/sale/sale_view.xml | 10 ++++++---- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index bd622cb4a99..3179c5950d0 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -1049,14 +1049,15 @@ product.template.procurement product.template +
@@ -1067,14 +1068,15 @@ product.product.procurement product.product +
diff --git a/addons/project/res_partner_view.xml b/addons/project/res_partner_view.xml index a77ea467dcb..ed52b8bcaf7 100644 --- a/addons/project/res_partner_view.xml +++ b/addons/project/res_partner_view.xml @@ -8,11 +8,12 @@ res.partner + diff --git a/addons/purchase/partner_view.xml b/addons/purchase/partner_view.xml index 2612913133c..30d5ffb01ca 100644 --- a/addons/purchase/partner_view.xml +++ b/addons/purchase/partner_view.xml @@ -74,21 +74,31 @@ res.partner + + + + + + + res.partner.view.purchase.account.buttons + res.partner + + + + + - diff --git a/addons/sale/res_partner_view.xml b/addons/sale/res_partner_view.xml index bebd02c6246..f2cb93052af 100644 --- a/addons/sale/res_partner_view.xml +++ b/addons/sale/res_partner_view.xml @@ -46,11 +46,12 @@ res.partner + diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index a54ddc95465..be1477a314d 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -498,10 +498,11 @@ product.product.sale.order product.product + - @@ -512,10 +513,11 @@ product.template.sale.order.button product.template + - From 138011c69b968d159b8cd95fa00ea1780384c0a4 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 22 Aug 2014 18:48:57 +0200 Subject: [PATCH 4/6] [IMP] Changing the standard cost price should do the real-time valuation for all variants of the template --- .../stock/wizard/stock_change_product_qty.py | 2 +- addons/stock_account/product.py | 130 +++++++++--------- .../wizard/stock_change_standard_price.py | 15 +- 3 files changed, 81 insertions(+), 66 deletions(-) diff --git a/addons/stock/wizard/stock_change_product_qty.py b/addons/stock/wizard/stock_change_product_qty.py index 47fc792b815..1e7b19fef71 100644 --- a/addons/stock/wizard/stock_change_product_qty.py +++ b/addons/stock/wizard/stock_change_product_qty.py @@ -55,7 +55,7 @@ class stock_change_product_qty(osv.osv_memory): if len(product_ids) == 1: res['product_id'] = product_ids[0] else: - raise orm.except_orm(_('Warning'), _('Please use the Product Variant vue to update the product quantity.')) + raise orm.except_orm(_('Warning'), _('Please use the Product Variant view to update the product quantity.')) if 'location_id' in fields: location_id = res.get('location_id', False) diff --git a/addons/stock_account/product.py b/addons/stock_account/product.py index f2a49d40204..73f321679e4 100644 --- a/addons/stock_account/product.py +++ b/addons/stock_account/product.py @@ -23,8 +23,37 @@ from openerp.osv import fields, osv from openerp.tools.translate import _ from openerp.fields import Many2one -class product_product(osv.osv): - _inherit = "product.product" + + +class product_template(osv.osv): + _name = 'product.template' + _inherit = 'product.template' + + _columns = { + 'valuation': fields.property(type='selection', selection=[('manual_periodic', 'Periodical (manual)'), + ('real_time', 'Real Time (automated)')], string='Inventory Valuation', + help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves, with product price as specified by the 'Costing Method'" \ + "The inventory variation account set on the product category will represent the current inventory value, and the stock input and stock output account will hold the counterpart moves for incoming and outgoing products." + , required=True, copy=True), + 'cost_method': fields.property(type='selection', selection=[('standard', 'Standard Price'), ('average', 'Average Price'), ('real', 'Real Price')], + help="""Standard Price: The cost price is manually updated at the end of a specific period (usually every year). + Average Price: The cost price is recomputed at each incoming shipment and used for the product valuation. + Real Price: The cost price displayed is the price of the last outgoing product (will be use in case of inventory loss for example).""", + string="Costing Method", required=True, copy=True), + 'property_stock_account_input': fields.property( + type='many2one', + relation='account.account', + string='Stock Input Account', + help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless " + "there is a specific valuation account set on the source location. When not set on the product, the one from the product category is used."), + 'property_stock_account_output': fields.property( + type='many2one', + relation='account.account', + string='Stock Output Account', + help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless " + "there is a specific valuation account set on the destination location. When not set on the product, the one from the product category is used."), + } + 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. @@ -33,7 +62,7 @@ class product_product(osv.osv): """ if context is None: context = {} - product_obj = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + product_obj = self.browse(cr, uid, product_id, context=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: @@ -61,6 +90,7 @@ class product_product(osv.osv): 'property_stock_valuation_account_id': account_valuation } + def do_change_standard_price(self, cr, uid, ids, new_price, context=None): """ Changes the Standard Price of Product and creates an account move accordingly.""" location_obj = self.pool.get('stock.location') @@ -80,70 +110,44 @@ class product_product(osv.osv): diff = product.standard_price - new_price if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!")) - qty = product.qty_available - if qty: - # Accounting Entries - move_vals = { - 'journal_id': datas['stock_journal'], - 'company_id': location.company_id.id, - } - move_id = move_obj.create(cr, uid, move_vals, context=context) - - if diff > 0: - amount_diff = qty * diff - debit_account_id = datas['stock_account_input'] - credit_account_id = datas['property_stock_valuation_account_id'] - else: - amount_diff = qty * -diff - debit_account_id = datas['property_stock_valuation_account_id'] - credit_account_id = datas['stock_account_output'] - - move_line_obj.create(cr, uid, { - 'name': _('Standard Price changed'), - 'account_id': debit_account_id, - 'debit': amount_diff, - 'credit': 0, - 'move_id': move_id, - }, context=context) - move_line_obj.create(cr, uid, { - 'name': _('Standard Price changed'), - 'account_id': credit_account_id, - 'debit': 0, - 'credit': amount_diff, - 'move_id': move_id - }, context=context) + for prod_variant in product.product_variant_ids: + qty = prod_variant.qty_available + if qty: + # Accounting Entries + move_vals = { + 'journal_id': datas['stock_journal'], + 'company_id': location.company_id.id, + } + move_id = move_obj.create(cr, uid, move_vals, context=context) + + if diff > 0: + amount_diff = qty * diff + debit_account_id = datas['stock_account_input'] + credit_account_id = datas['property_stock_valuation_account_id'] + else: + amount_diff = qty * -diff + debit_account_id = datas['property_stock_valuation_account_id'] + credit_account_id = datas['stock_account_output'] + + move_line_obj.create(cr, uid, { + 'name': _('Standard Price changed'), + 'account_id': debit_account_id, + 'debit': amount_diff, + 'credit': 0, + 'move_id': move_id, + }, context=context) + move_line_obj.create(cr, uid, { + 'name': _('Standard Price changed'), + 'account_id': credit_account_id, + 'debit': 0, + 'credit': amount_diff, + 'move_id': move_id + }, context=context) self.write(cr, uid, rec_id, {'standard_price': new_price}) return True -class product_template(osv.osv): - _name = 'product.template' - _inherit = 'product.template' - - _columns = { - 'valuation': fields.property(type='selection', selection=[('manual_periodic', 'Periodical (manual)'), - ('real_time', 'Real Time (automated)')], string='Inventory Valuation', - help="If real-time valuation is enabled for a product, the system will automatically write journal entries corresponding to stock moves, with product price as specified by the 'Costing Method'" \ - "The inventory variation account set on the product category will represent the current inventory value, and the stock input and stock output account will hold the counterpart moves for incoming and outgoing products." - , required=True, copy=True), - 'cost_method': fields.property(type='selection', selection=[('standard', 'Standard Price'), ('average', 'Average Price'), ('real', 'Real Price')], - help="""Standard Price: The cost price is manually updated at the end of a specific period (usually every year). - Average Price: The cost price is recomputed at each incoming shipment and used for the product valuation. - Real Price: The cost price displayed is the price of the last outgoing product (will be use in case of inventory loss for example).""", - string="Costing Method", required=True, copy=True), - 'property_stock_account_input': fields.property( - type='many2one', - relation='account.account', - string='Stock Input Account', - help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless " - "there is a specific valuation account set on the source location. When not set on the product, the one from the product category is used."), - 'property_stock_account_output': fields.property( - type='many2one', - relation='account.account', - string='Stock Output Account', - help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless " - "there is a specific valuation account set on the destination location. When not set on the product, the one from the product category is used."), - } + class product_category(osv.osv): diff --git a/addons/stock_account/wizard/stock_change_standard_price.py b/addons/stock_account/wizard/stock_change_standard_price.py index 6b99d596b4a..09959359123 100644 --- a/addons/stock_account/wizard/stock_change_standard_price.py +++ b/addons/stock_account/wizard/stock_change_standard_price.py @@ -33,6 +33,8 @@ class change_standard_price(osv.osv_memory): "If cost price is decreased, stock variation account will be creadited and stock input account will be debited."), } + + def default_get(self, cr, uid, fields, context=None): """ To get default values for the object. @param self: The object pointer. @@ -44,8 +46,12 @@ class change_standard_price(osv.osv_memory): """ if context is None: context = {} - product_pool = self.pool.get('product.product') + if context.get("active_model") == 'product.product': + product_pool = self.pool.get('product.product') + else: + product_pool = self.pool.get('product.template') product_obj = product_pool.browse(cr, uid, context.get('active_id', False)) + res = super(change_standard_price, self).default_get(cr, uid, fields, context=context) price = product_obj.standard_price @@ -68,8 +74,13 @@ class change_standard_price(osv.osv_memory): context = {} rec_id = context and context.get('active_id', False) assert rec_id, _('Active ID is not set in Context.') - prod_obj = self.pool.get('product.product') + if context.get("active_model") == 'product.product': + prod_obj = self.pool.get('product.product') + else: + prod_obj = self.pool.get('product.template') + res = self.browse(cr, uid, ids, context=context) + prod_obj.do_change_standard_price(cr, uid, [rec_id], res[0].new_price, context) return {'type': 'ir.actions.act_window_close'} From b8a40a4e0f79b5fd18b425dc99200281fba3f5ab Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 26 Aug 2014 12:29:47 +0200 Subject: [PATCH 5/6] [IMP] Clean --- addons/mrp/mrp.py | 6 +----- addons/mrp/mrp_view.xml | 1 - 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 8e661a57178..428782574d9 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -845,7 +845,7 @@ class mrp_production(osv.osv): dicts[product_id] = {} # total qty of consumed product we need after this consumption - if product_qty + produced_qty <= production.product_qty: #TODO: Need to add rounding over here? + if product_qty + produced_qty <= production.product_qty: total_consume = ((product_qty + produced_qty) * scheduled.product_qty / production.product_qty) else: total_consume = (production.product_qty * scheduled.product_qty / production.product_qty) @@ -905,10 +905,6 @@ class mrp_production(osv.osv): main_production_move = False if production_mode == 'consume_produce': # To produce remaining qty of final product - #vals = {'state':'confirmed'} - #final_product_todo = [x.id for x in production.move_created_ids] - #stock_mov_obj.write(cr, uid, final_product_todo, vals) - #stock_mov_obj.action_confirm(cr, uid, final_product_todo, context) produced_products = {} for produced_product in production.move_created_ids2: if produced_product.scrapped: diff --git a/addons/mrp/mrp_view.xml b/addons/mrp/mrp_view.xml index 3179c5950d0..327196aa361 100644 --- a/addons/mrp/mrp_view.xml +++ b/addons/mrp/mrp_view.xml @@ -758,7 +758,6 @@ - From 327c488f2b2882d25f83e922a77baab412a835e7 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 26 Aug 2014 15:15:23 +0200 Subject: [PATCH 6/6] [IMP] Pass product template to template method --- addons/stock_account/stock_account.py | 4 ++-- addons/stock_account/wizard/stock_change_standard_price.py | 6 +++--- addons/stock_landed_costs/stock_landed_costs.py | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/addons/stock_account/stock_account.py b/addons/stock_account/stock_account.py index 7e14ea860b7..6dffebdb9c1 100644 --- a/addons/stock_account/stock_account.py +++ b/addons/stock_account/stock_account.py @@ -164,8 +164,8 @@ class stock_quant(osv.osv): :returns: journal_id, source account, destination account, valuation account :raise: osv.except_osv() is any mandatory account or journal is not defined. """ - product_obj = self.pool.get('product.product') - accounts = product_obj.get_product_accounts(cr, uid, move.product_id.id, context) + product_obj = self.pool.get('product.template') + accounts = product_obj.get_product_accounts(cr, uid, move.product_id.product_tmpl_id.id, context) if move.location_id.valuation_out_account_id: acc_src = move.location_id.valuation_out_account_id.id else: diff --git a/addons/stock_account/wizard/stock_change_standard_price.py b/addons/stock_account/wizard/stock_change_standard_price.py index 09959359123..68e39adc03d 100644 --- a/addons/stock_account/wizard/stock_change_standard_price.py +++ b/addons/stock_account/wizard/stock_change_standard_price.py @@ -72,12 +72,12 @@ class change_standard_price(osv.osv_memory): """ if context is None: context = {} - rec_id = context and context.get('active_id', False) + rec_id = context.get('active_id', False) assert rec_id, _('Active ID is not set in Context.') if context.get("active_model") == 'product.product': prod_obj = self.pool.get('product.product') - else: - prod_obj = self.pool.get('product.template') + rec_id = prod_obj.browse(cr, uid, rec_id, context=context).product_tmpl_id.id + prod_obj = self.pool.get('product.template') res = self.browse(cr, uid, ids, context=context) diff --git a/addons/stock_landed_costs/stock_landed_costs.py b/addons/stock_landed_costs/stock_landed_costs.py index 5298924313e..b4c350a229a 100644 --- a/addons/stock_landed_costs/stock_landed_costs.py +++ b/addons/stock_landed_costs/stock_landed_costs.py @@ -99,11 +99,11 @@ class stock_landed_cost(osv.osv): } def _create_accounting_entries(self, cr, uid, line, move_id, context=None): - product_obj = self.pool.get('product.product') + product_obj = self.pool.get('product.template') cost_product = line.cost_line_id and line.cost_line_id.product_id if not cost_product: return False - accounts = product_obj.get_product_accounts(cr, uid, line.product_id.id, context=context) + accounts = product_obj.get_product_accounts(cr, uid, line.product_id.product_tmpl_id.id, context=context) debit_account_id = accounts['property_stock_valuation_account_id'] credit_account_id = cost_product.property_account_expense and cost_product.property_account_expense.id or cost_product.categ_id.property_account_expense_categ.id if not credit_account_id: