1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
22 from openerp.osv import fields, osv
23 from openerp.tools.translate import _
25 class product_product(osv.osv):
26 _inherit = "product.product"
28 def get_product_accounts(self, cr, uid, product_id, context=None):
29 """ To get the stock input account, stock output account and stock journal related to product.
30 @param product_id: product id
31 @return: dictionary which contains information regarding stock input account, stock output account and stock journal
35 product_obj = self.pool.get('product.product').browse(cr, uid, product_id, context=context)
37 stock_input_acc = product_obj.property_stock_account_input and product_obj.property_stock_account_input.id or False
38 if not stock_input_acc:
39 stock_input_acc = product_obj.categ_id.property_stock_account_input_categ and product_obj.categ_id.property_stock_account_input_categ.id or False
41 stock_output_acc = product_obj.property_stock_account_output and product_obj.property_stock_account_output.id or False
42 if not stock_output_acc:
43 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
45 journal_id = product_obj.categ_id.property_stock_journal and product_obj.categ_id.property_stock_journal.id or False
46 account_valuation = product_obj.categ_id.property_stock_valuation_account_id and product_obj.categ_id.property_stock_valuation_account_id.id or False
48 'stock_account_input': stock_input_acc,
49 'stock_account_output': stock_output_acc,
50 'stock_journal': journal_id,
51 'property_stock_valuation_account_id': account_valuation
54 # FP Note:;too complex, not good, should be implemented at quant level TODO
55 def do_change_standard_price(self, cr, uid, ids, datas, context=None):
56 """ Changes the Standard Price of Product and creates an account move accordingly.
57 @param datas : dict. contain default datas like new_price, stock_output_account, stock_input_account, stock_journal
58 @param context: A standard dictionary
62 location_obj = self.pool.get('stock.location')
63 move_obj = self.pool.get('account.move')
64 move_line_obj = self.pool.get('account.move.line')
68 new_price = datas.get('new_price', 0.0)
69 stock_output_acc = datas.get('stock_output_account', False)
70 stock_input_acc = datas.get('stock_input_account', False)
71 journal_id = datas.get('stock_journal', False)
72 product_obj=self.browse(cr, uid, ids, context=context)[0]
73 account_valuation = product_obj.categ_id.property_stock_valuation_account_id
74 account_valuation_id = account_valuation and account_valuation.id or False
75 if not account_valuation_id: raise osv.except_osv(_('Error!'), _('Specify valuation Account for Product Category: %s.') % (product_obj.categ_id.name))
77 loc_ids = location_obj.search(cr, uid,[('usage','=','internal')])
79 for location in location_obj.browse(cr, uid, loc_ids, context=context):
82 'location': location.id,
83 'compute_child': False
86 product = self.browse(cr, uid, rec_id, context=c)
87 qty = product.qty_available
88 diff = product.standard_price - new_price
89 if not diff: raise osv.except_osv(_('Error!'), _("No difference between standard price and new price!"))
91 company_id = location.company_id and location.company_id.id or False
92 if not company_id: raise osv.except_osv(_('Error!'), _('Please specify company in Location.'))
97 journal_id = product.categ_id.property_stock_journal and product.categ_id.property_stock_journal.id or False
99 raise osv.except_osv(_('Error!'),
100 _('Please define journal '\
101 'on the product category: "%s" (id: %d).') % \
102 (product.categ_id.name,
103 product.categ_id.id,))
104 move_id = move_obj.create(cr, uid, {
105 'journal_id': journal_id,
106 'company_id': company_id
109 move_ids.append(move_id)
113 if not stock_input_acc:
114 stock_input_acc = product.\
115 property_stock_account_input.id
116 if not stock_input_acc:
117 stock_input_acc = product.categ_id.\
118 property_stock_account_input_categ.id
119 if not stock_input_acc:
120 raise osv.except_osv(_('Error!'),
121 _('Please define stock input account for this product: "%s" (id: %d).') % \
124 amount_diff = qty * diff
125 move_line_obj.create(cr, uid, {
126 'name': product.name,
127 'account_id': stock_input_acc,
128 'debit': amount_diff,
131 move_line_obj.create(cr, uid, {
132 'name': product.categ_id.name,
133 'account_id': account_valuation_id,
134 'credit': amount_diff,
138 if not stock_output_acc:
139 stock_output_acc = product.\
140 property_stock_account_output.id
141 if not stock_output_acc:
142 stock_output_acc = product.categ_id.\
143 property_stock_account_output_categ.id
144 if not stock_output_acc:
145 raise osv.except_osv(_('Error!'),
146 _('Please define stock output account ' \
147 'for this product: "%s" (id: %d).') % \
150 amount_diff = qty * -diff
151 move_line_obj.create(cr, uid, {
152 'name': product.name,
153 'account_id': stock_output_acc,
154 'credit': amount_diff,
157 move_line_obj.create(cr, uid, {
158 'name': product.categ_id.name,
159 'account_id': account_valuation_id,
160 'debit': amount_diff,
164 self.write(cr, uid, rec_id, {'standard_price': new_price})
169 'valuation': fields.property(type='selection', selection=[('manual_periodic', 'Periodical (manual)'),
170 ('real_time', 'Real Time (automated)')], string='Inventory Valuation',
171 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'" \
172 "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."
177 class product_template(osv.osv):
178 _name = 'product.template'
179 _inherit = 'product.template'
181 'cost_method': fields.property(type='selection', selection=[('standard', 'Standard Price'), ('average', 'Average Price'), ('real', 'Real Price')],
182 help="""Standard Price: The cost price is manually updated at the end of a specific period (usually every year).
183 Average Price: The cost price is recomputed at each incoming shipment and used for the product valuation.
184 Real Price: The cost price displayed is the price of the last outgoing product (will be use in case of inventory loss for example).""",
185 string="Costing Method", required=True),
186 'property_stock_account_input': fields.property(
188 relation='account.account',
189 string='Stock Input Account',
190 help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless "
191 "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."),
192 'property_stock_account_output': fields.property(
194 relation='account.account',
195 string='Stock Output Account',
196 help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless "
197 "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."),
201 class product_category(osv.osv):
202 _inherit = 'product.category'
204 'property_stock_journal': fields.property(
205 relation='account.journal',
207 string='Stock Journal',
208 help="When doing real-time inventory valuation, this is the Accounting Journal in which entries will be automatically posted when stock moves are processed."),
209 'property_stock_account_input_categ': fields.property(
211 relation='account.account',
212 string='Stock Input Account',
213 help="When doing real-time inventory valuation, counterpart journal items for all incoming stock moves will be posted in this account, unless "
214 "there is a specific valuation account set on the source location. This is the default value for all products in this category. It "
215 "can also directly be set on each product"),
216 'property_stock_account_output_categ': fields.property(
218 relation='account.account',
219 string='Stock Output Account',
220 help="When doing real-time inventory valuation, counterpart journal items for all outgoing stock moves will be posted in this account, unless "
221 "there is a specific valuation account set on the destination location. This is the default value for all products in this category. It "
222 "can also directly be set on each product"),
223 'property_stock_valuation_account_id': fields.property(
225 relation='account.account',
226 string="Stock Valuation Account",
227 help="When real-time inventory valuation is enabled on a product, this account will hold the current value of the products.",),