Merge lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing into lp:stock-logistic-warehouse
- adding_stock_lot_costing
- Merge into 7.0
Status: | Merged |
---|---|
Merged at revision: | 32 |
Proposed branch: | lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing |
Merge into: | lp:stock-logistic-warehouse |
Diff against target: |
1166 lines (+1103/-0) 12 files modified
stock_lot_valuation/AUTHORS.txt (+1/-0) stock_lot_valuation/__init__.py (+23/-0) stock_lot_valuation/__openerp__.py (+52/-0) stock_lot_valuation/i18n/stock_lot_valuation.pot (+205/-0) stock_lot_valuation/product.py (+32/-0) stock_lot_valuation/product_view.xml (+15/-0) stock_lot_valuation/stock.py (+358/-0) stock_lot_valuation/stock_view.xml (+22/-0) stock_lot_valuation/test/stock.yml (+230/-0) stock_lot_valuation/wizard/__init__.py (+21/-0) stock_lot_valuation/wizard/stock_change_standard_price.py (+110/-0) stock_lot_valuation/wizard/stock_change_standard_price_view.xml (+34/-0) |
To merge this branch: | bzr merge lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Joao Alfredo Gama Batista | code review. no test | Approve | |
Maxime Chambreuil (http://www.savoirfairelinux.com) | code review | Needs Fixing | |
Joël Grand-Guillaume @ camptocamp | code review, no tests | Approve | |
Guewen Baconnier @ Camptocamp | code review, nitpicking, no test | Needs Fixing | |
Review via email: mp+164766@code.launchpad.net |
Commit message
Description of the change
Stock valuation (standard or average price, ...) based on lots.
This module extends standard stock valuation (based on products). Valuing lots allows to have different costs for different lots of the same product.
Usage
-----
Set the 'Lot valuation' flag on product form (used for real time valuation).
As for products, lots have 'cost' and 'costing method' fields. Also, a 'Change Standard Price' wizard is available
Eric Caudal - www.elico-corp.com (elicoidal) wrote : | # |
Guewen Baconnier @ Camptocamp (gbaconnier-c2c) wrote : | # |
Well done!
Seems good to me, some nitpickings:
Most are styling and pep8 issues, but the most annoying ones are the missing propagations of the context in some calls.
l.183
Could be worse to cut the long help:
l.195-196 (applies for other places in the code too)
pep8: indentation, see http://
l.199-202
The expression is hard to read.
I think I would prefer a version with if / elif (or maybe an inline ... if ... else ... expression but it would probably be too complex here)
l.224 pep8: missing spaces around =
l.227,241 pep8: should not be on the same line and the line is too long
l.229 propagation of the context is missing, space after commas
l.250 prefer the form `... if ... else ...` than `... and ... or ...`
l.253-256 (and some other places) the \ is useless here as it is enclosed in ()
l.257-260, l.279-290, l.305-318: propagation of context is missing (+ pep8 indentation)
l.360 and lot of calls under: propagation of the context is missing
l. 554,560,561, ... : s/incomming/
pep8: in some places, a space is missing after comma or colon.
I will be pleased to merge your proposal once fixed.
Congrats for the tests ;-)
Lorenzo Battistini (elbati) wrote : | # |
On 06/18/2013 09:53 AM, Guewen Baconnier @ Camptocamp wrote:
> Review: Needs Fixing code review, nitpicking, no test
Thank you Guewen, I did the changes.
- 52. By Lorenzo Battistini
-
[fix] lines lenght, indentation, style
- 53. By Lorenzo Battistini
-
[fix] The expression is hard to read
- 54. By Lorenzo Battistini
-
[fix] propagation of the context is missing, space after commas
- 55. By Lorenzo Battistini
-
[fix] prefer the form `... if ... else ...` than `... and ... or ...`
- 56. By Lorenzo Battistini
-
[fix] propagation of context is missing
- 57. By Lorenzo Battistini
-
[fix] propagation of the context is missing
- 58. By Lorenzo Battistini
-
[fix] incomming/incoming
- 59. By Lorenzo Battistini
-
[fix] space after comma
Joël Grand-Guillaume @ camptocamp (jgrandguillaume-c2c) wrote : | # |
LGTM
Joao Alfredo Gama Batista (joao-gama) wrote : | # |
Hi Lorenzo,
Just a small comment:
l.746: You forgot to change the copyright owner.
The rest is ok. Thanks for your contribution!
Maxime Chambreuil (http://www.savoirfairelinux.com) (max3903) wrote : | # |
Thanks Lorenzo.
Can you please add the translation file and run flake8 on your code ?
- 60. By Lorenzo Battistini
-
[FIX] copyright
Lorenzo Battistini (elbati) wrote : | # |
On 10/17/2013 04:09 PM, Joao Alfredo Gama Batista wrote:
> Review: Needs Fixing code review, no tests
>
> Hi Lorenzo,
>
> Just a small comment:
>
> l.746: You forgot to change the copyright owner.
Added our copyright.
I keep the Tiny one because I copied that file from stock module.
- 61. By Lorenzo Battistini
-
[IMP] PEP8
- 62. By Lorenzo Battistini
-
[FIX] osv.except_osv
- 63. By Lorenzo Battistini
-
[IMP] PEP8
- 64. By Lorenzo Battistini
-
[ADD] pot file
Lorenzo Battistini (elbati) wrote : | # |
On 10/17/2013 04:56 PM, Maxime Chambreuil
(http://
> Can you please add the translation file and run flake8 on your code ?
Added stock_lot_
Thanks
Joao Alfredo Gama Batista (joao-gama) wrote : | # |
LGTM.
Preview Diff
1 | === added directory 'stock_lot_valuation' | |||
2 | === added file 'stock_lot_valuation/AUTHORS.txt' | |||
3 | --- stock_lot_valuation/AUTHORS.txt 1970-01-01 00:00:00 +0000 | |||
4 | +++ stock_lot_valuation/AUTHORS.txt 2013-10-18 16:18:27 +0000 | |||
5 | @@ -0,0 +1,1 @@ | |||
6 | 1 | Lorenzo Battistini <lorenzo.battistini@agilebg.com> | ||
7 | 0 | 2 | ||
8 | === added file 'stock_lot_valuation/__init__.py' | |||
9 | --- stock_lot_valuation/__init__.py 1970-01-01 00:00:00 +0000 | |||
10 | +++ stock_lot_valuation/__init__.py 2013-10-18 16:18:27 +0000 | |||
11 | @@ -0,0 +1,23 @@ | |||
12 | 1 | # -*- coding: utf-8 -*- | ||
13 | 2 | ############################################################################## | ||
14 | 3 | # | ||
15 | 4 | # Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>) | ||
16 | 5 | # | ||
17 | 6 | # This program is free software: you can redistribute it and/or modify | ||
18 | 7 | # it under the terms of the GNU Affero General Public License as published | ||
19 | 8 | # by the Free Software Foundation, either version 3 of the License, or | ||
20 | 9 | # (at your option) any later version. | ||
21 | 10 | # | ||
22 | 11 | # This program is distributed in the hope that it will be useful, | ||
23 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
24 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
25 | 14 | # GNU Affero General Public License for more details. | ||
26 | 15 | # | ||
27 | 16 | # You should have received a copy of the GNU Affero General Public License | ||
28 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
29 | 18 | # | ||
30 | 19 | ############################################################################## | ||
31 | 20 | |||
32 | 21 | import product | ||
33 | 22 | import stock | ||
34 | 23 | import wizard | ||
35 | 0 | 24 | ||
36 | === added file 'stock_lot_valuation/__openerp__.py' | |||
37 | --- stock_lot_valuation/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
38 | +++ stock_lot_valuation/__openerp__.py 2013-10-18 16:18:27 +0000 | |||
39 | @@ -0,0 +1,52 @@ | |||
40 | 1 | # -*- coding: utf-8 -*- | ||
41 | 2 | ############################################################################## | ||
42 | 3 | # | ||
43 | 4 | # Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>) | ||
44 | 5 | # | ||
45 | 6 | # This program is free software: you can redistribute it and/or modify | ||
46 | 7 | # it under the terms of the GNU Affero General Public License as published | ||
47 | 8 | # by the Free Software Foundation, either version 3 of the License, or | ||
48 | 9 | # (at your option) any later version. | ||
49 | 10 | # | ||
50 | 11 | # This program is distributed in the hope that it will be useful, | ||
51 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
52 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
53 | 14 | # GNU Affero General Public License for more details. | ||
54 | 15 | # | ||
55 | 16 | # You should have received a copy of the GNU Affero General Public License | ||
56 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
57 | 18 | # | ||
58 | 19 | ############################################################################## | ||
59 | 20 | |||
60 | 21 | { | ||
61 | 22 | 'name': "Lot Valuation", | ||
62 | 23 | 'version': '0.1', | ||
63 | 24 | 'category': 'Warehouse Management', | ||
64 | 25 | 'description': """ | ||
65 | 26 | Stock valuation (standard or average price, ...) based on lots. | ||
66 | 27 | This module extends standard stock valuation (based on products). | ||
67 | 28 | Valuing lots allows to have different costs for different lots of the same | ||
68 | 29 | product. | ||
69 | 30 | |||
70 | 31 | Usage | ||
71 | 32 | ----- | ||
72 | 33 | Set the 'Lot valuation' flag on product form (used for real time valuation). | ||
73 | 34 | As for products, lots have 'cost' and 'costing method' fields. Also, a | ||
74 | 35 | 'Change Standard Price' wizard is available. | ||
75 | 36 | """, | ||
76 | 37 | 'author': 'Agile Business Group', | ||
77 | 38 | 'website': 'http://www.agilebg.com', | ||
78 | 39 | 'license': 'AGPL-3', | ||
79 | 40 | "depends": ['stock'], | ||
80 | 41 | "data": [ | ||
81 | 42 | "wizard/stock_change_standard_price_view.xml", | ||
82 | 43 | "product_view.xml", | ||
83 | 44 | "stock_view.xml", | ||
84 | 45 | ], | ||
85 | 46 | "demo": [], | ||
86 | 47 | 'test': [ | ||
87 | 48 | 'test/stock.yml', # TODO cover user interface operations | ||
88 | 49 | ], | ||
89 | 50 | "active": False, | ||
90 | 51 | "installable": True | ||
91 | 52 | } | ||
92 | 0 | 53 | ||
93 | === added directory 'stock_lot_valuation/i18n' | |||
94 | === added file 'stock_lot_valuation/i18n/stock_lot_valuation.pot' | |||
95 | --- stock_lot_valuation/i18n/stock_lot_valuation.pot 1970-01-01 00:00:00 +0000 | |||
96 | +++ stock_lot_valuation/i18n/stock_lot_valuation.pot 2013-10-18 16:18:27 +0000 | |||
97 | @@ -0,0 +1,205 @@ | |||
98 | 1 | # Translation of OpenERP Server. | ||
99 | 2 | # This file contains the translation of the following modules: | ||
100 | 3 | # * stock_lot_valuation | ||
101 | 4 | # | ||
102 | 5 | msgid "" | ||
103 | 6 | msgstr "" | ||
104 | 7 | "Project-Id-Version: OpenERP Server 7.0\n" | ||
105 | 8 | "Report-Msgid-Bugs-To: \n" | ||
106 | 9 | "POT-Creation-Date: 2013-10-18 16:17+0000\n" | ||
107 | 10 | "PO-Revision-Date: 2013-10-18 16:17+0000\n" | ||
108 | 11 | "Last-Translator: <>\n" | ||
109 | 12 | "Language-Team: \n" | ||
110 | 13 | "MIME-Version: 1.0\n" | ||
111 | 14 | "Content-Type: text/plain; charset=UTF-8\n" | ||
112 | 15 | "Content-Transfer-Encoding: \n" | ||
113 | 16 | "Plural-Forms: \n" | ||
114 | 17 | |||
115 | 18 | #. module: stock_lot_valuation | ||
116 | 19 | #: selection:stock.production.lot,cost_method:0 | ||
117 | 20 | msgid "Average Price" | ||
118 | 21 | msgstr "" | ||
119 | 22 | |||
120 | 23 | #. module: stock_lot_valuation | ||
121 | 24 | #: field:lot.change.standard.price,stock_journal:0 | ||
122 | 25 | msgid "Stock journal" | ||
123 | 26 | msgstr "" | ||
124 | 27 | |||
125 | 28 | #. module: stock_lot_valuation | ||
126 | 29 | #: field:lot.change.standard.price,enable_stock_in_out_acc:0 | ||
127 | 30 | msgid "Enable Related Account" | ||
128 | 31 | msgstr "" | ||
129 | 32 | |||
130 | 33 | #. module: stock_lot_valuation | ||
131 | 34 | #: model:ir.model,name:stock_lot_valuation.model_stock_picking | ||
132 | 35 | msgid "Picking List" | ||
133 | 36 | msgstr "" | ||
134 | 37 | |||
135 | 38 | #. module: stock_lot_valuation | ||
136 | 39 | #: code:addons/stock_lot_valuation/stock.py:132 | ||
137 | 40 | #, python-format | ||
138 | 41 | msgid "Please specify company in Location." | ||
139 | 42 | msgstr "" | ||
140 | 43 | |||
141 | 44 | #. module: stock_lot_valuation | ||
142 | 45 | #: code:addons/stock_lot_valuation/stock.py:145 | ||
143 | 46 | #, python-format | ||
144 | 47 | msgid "Please define journal on the product category: '%s' (id: %d)." | ||
145 | 48 | msgstr "" | ||
146 | 49 | |||
147 | 50 | #. module: stock_lot_valuation | ||
148 | 51 | #: model:ir.model,name:stock_lot_valuation.model_product_product | ||
149 | 52 | msgid "Product" | ||
150 | 53 | msgstr "" | ||
151 | 54 | |||
152 | 55 | #. module: stock_lot_valuation | ||
153 | 56 | #: help:stock.production.lot,standard_price:0 | ||
154 | 57 | msgid "Cost price (in company currency) of the lot used for standard stock valuation in accounting." | ||
155 | 58 | msgstr "" | ||
156 | 59 | |||
157 | 60 | #. module: stock_lot_valuation | ||
158 | 61 | #: field:lot.change.standard.price,new_price:0 | ||
159 | 62 | msgid "Price" | ||
160 | 63 | msgstr "" | ||
161 | 64 | |||
162 | 65 | #. module: stock_lot_valuation | ||
163 | 66 | #: model:ir.model,name:stock_lot_valuation.model_stock_production_lot | ||
164 | 67 | msgid "Serial Number" | ||
165 | 68 | msgstr "" | ||
166 | 69 | |||
167 | 70 | #. module: stock_lot_valuation | ||
168 | 71 | #: code:addons/stock_lot_valuation/stock.py:123 | ||
169 | 72 | #, python-format | ||
170 | 73 | msgid "No difference between standard price! and new price" | ||
171 | 74 | msgstr "" | ||
172 | 75 | |||
173 | 76 | #. module: stock_lot_valuation | ||
174 | 77 | #: selection:stock.production.lot,cost_method:0 | ||
175 | 78 | msgid "Standard Price" | ||
176 | 79 | msgstr "" | ||
177 | 80 | |||
178 | 81 | #. module: stock_lot_valuation | ||
179 | 82 | #: model:ir.model,name:stock_lot_valuation.model_stock_partial_picking | ||
180 | 83 | msgid "Partial Picking Processing Wizard" | ||
181 | 84 | msgstr "" | ||
182 | 85 | |||
183 | 86 | #. module: stock_lot_valuation | ||
184 | 87 | #: view:lot.change.standard.price:0 | ||
185 | 88 | #: view:stock.production.lot:0 | ||
186 | 89 | msgid "Cost Price" | ||
187 | 90 | msgstr "" | ||
188 | 91 | |||
189 | 92 | #. module: stock_lot_valuation | ||
190 | 93 | #: help:lot.change.standard.price,new_price:0 | ||
191 | 94 | msgid "If cost price is increased, stock variation account will be debited and stock output account will be credited with the value = (difference of amount * quantity available).\n" | ||
192 | 95 | "If cost price is decreased, stock variation account will be creadited and stock input account will be debited." | ||
193 | 96 | msgstr "" | ||
194 | 97 | |||
195 | 98 | #. module: stock_lot_valuation | ||
196 | 99 | #: field:product.product,lot_valuation:0 | ||
197 | 100 | msgid "Lot valuation" | ||
198 | 101 | msgstr "" | ||
199 | 102 | |||
200 | 103 | #. module: stock_lot_valuation | ||
201 | 104 | #: help:stock.production.lot,cost_method:0 | ||
202 | 105 | msgid "Standard Price: The cost price is manually updated at the end of a specific period. \n" | ||
203 | 106 | "Average Price: The cost price is recomputed at each incoming shipment." | ||
204 | 107 | msgstr "" | ||
205 | 108 | |||
206 | 109 | #. module: stock_lot_valuation | ||
207 | 110 | #: view:stock.production.lot:0 | ||
208 | 111 | msgid "update" | ||
209 | 112 | msgstr "" | ||
210 | 113 | |||
211 | 114 | #. module: stock_lot_valuation | ||
212 | 115 | #: code:addons/stock_lot_valuation/stock.py:100 | ||
213 | 116 | #: code:addons/stock_lot_valuation/stock.py:122 | ||
214 | 117 | #: code:addons/stock_lot_valuation/stock.py:131 | ||
215 | 118 | #: code:addons/stock_lot_valuation/stock.py:144 | ||
216 | 119 | #: code:addons/stock_lot_valuation/stock.py:167 | ||
217 | 120 | #: code:addons/stock_lot_valuation/stock.py:196 | ||
218 | 121 | #, python-format | ||
219 | 122 | msgid "Error!" | ||
220 | 123 | msgstr "" | ||
221 | 124 | |||
222 | 125 | #. module: stock_lot_valuation | ||
223 | 126 | #: field:stock.production.lot,cost_method:0 | ||
224 | 127 | msgid "Costing Method" | ||
225 | 128 | msgstr "" | ||
226 | 129 | |||
227 | 130 | #. module: stock_lot_valuation | ||
228 | 131 | #: code:addons/stock_lot_valuation/wizard/stock_change_standard_price.py:100 | ||
229 | 132 | #, python-format | ||
230 | 133 | msgid "Active ID is not set in Context." | ||
231 | 134 | msgstr "" | ||
232 | 135 | |||
233 | 136 | #. module: stock_lot_valuation | ||
234 | 137 | #: view:lot.change.standard.price:0 | ||
235 | 138 | msgid "_Apply" | ||
236 | 139 | msgstr "" | ||
237 | 140 | |||
238 | 141 | #. module: stock_lot_valuation | ||
239 | 142 | #: view:lot.change.standard.price:0 | ||
240 | 143 | msgid "Change Price" | ||
241 | 144 | msgstr "" | ||
242 | 145 | |||
243 | 146 | #. module: stock_lot_valuation | ||
244 | 147 | #: field:lot.change.standard.price,stock_account_output:0 | ||
245 | 148 | msgid "Stock Output Account" | ||
246 | 149 | msgstr "" | ||
247 | 150 | |||
248 | 151 | #. module: stock_lot_valuation | ||
249 | 152 | #: help:product.product,lot_valuation:0 | ||
250 | 153 | msgid "Use lot valuation instead of product valuation" | ||
251 | 154 | msgstr "" | ||
252 | 155 | |||
253 | 156 | #. module: stock_lot_valuation | ||
254 | 157 | #: code:addons/stock_lot_valuation/stock.py:197 | ||
255 | 158 | #, python-format | ||
256 | 159 | msgid "Please define stock output account for this product: '%s' (id: %d)." | ||
257 | 160 | msgstr "" | ||
258 | 161 | |||
259 | 162 | #. module: stock_lot_valuation | ||
260 | 163 | #: model:ir.model,name:stock_lot_valuation.model_stock_move | ||
261 | 164 | msgid "Stock Move" | ||
262 | 165 | msgstr "" | ||
263 | 166 | |||
264 | 167 | #. module: stock_lot_valuation | ||
265 | 168 | #: view:lot.change.standard.price:0 | ||
266 | 169 | msgid "or" | ||
267 | 170 | msgstr "" | ||
268 | 171 | |||
269 | 172 | #. module: stock_lot_valuation | ||
270 | 173 | #: field:stock.production.lot,standard_price:0 | ||
271 | 174 | msgid "Cost" | ||
272 | 175 | msgstr "" | ||
273 | 176 | |||
274 | 177 | #. module: stock_lot_valuation | ||
275 | 178 | #: field:lot.change.standard.price,stock_account_input:0 | ||
276 | 179 | msgid "Stock Input Account" | ||
277 | 180 | msgstr "" | ||
278 | 181 | |||
279 | 182 | #. module: stock_lot_valuation | ||
280 | 183 | #: model:ir.actions.act_window,name:stock_lot_valuation.action_view_change_standard_price | ||
281 | 184 | #: model:ir.model,name:stock_lot_valuation.model_lot_change_standard_price | ||
282 | 185 | #: view:lot.change.standard.price:0 | ||
283 | 186 | msgid "Change Standard Price" | ||
284 | 187 | msgstr "" | ||
285 | 188 | |||
286 | 189 | #. module: stock_lot_valuation | ||
287 | 190 | #: code:addons/stock_lot_valuation/stock.py:168 | ||
288 | 191 | #, python-format | ||
289 | 192 | msgid "Please define stock input account for this product: '%s' (id: %d)." | ||
290 | 193 | msgstr "" | ||
291 | 194 | |||
292 | 195 | #. module: stock_lot_valuation | ||
293 | 196 | #: view:lot.change.standard.price:0 | ||
294 | 197 | msgid "Cancel" | ||
295 | 198 | msgstr "" | ||
296 | 199 | |||
297 | 200 | #. module: stock_lot_valuation | ||
298 | 201 | #: code:addons/stock_lot_valuation/stock.py:101 | ||
299 | 202 | #, python-format | ||
300 | 203 | msgid "Specify valuation Account for Product Category: %s." | ||
301 | 204 | msgstr "" | ||
302 | 205 | |||
303 | 0 | 206 | ||
304 | === added file 'stock_lot_valuation/product.py' | |||
305 | --- stock_lot_valuation/product.py 1970-01-01 00:00:00 +0000 | |||
306 | +++ stock_lot_valuation/product.py 2013-10-18 16:18:27 +0000 | |||
307 | @@ -0,0 +1,32 @@ | |||
308 | 1 | # -*- coding: utf-8 -*- | ||
309 | 2 | ############################################################################## | ||
310 | 3 | # | ||
311 | 4 | # Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>) | ||
312 | 5 | # | ||
313 | 6 | # This program is free software: you can redistribute it and/or modify | ||
314 | 7 | # it under the terms of the GNU Affero General Public License as published | ||
315 | 8 | # by the Free Software Foundation, either version 3 of the License, or | ||
316 | 9 | # (at your option) any later version. | ||
317 | 10 | # | ||
318 | 11 | # This program is distributed in the hope that it will be useful, | ||
319 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
320 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
321 | 14 | # GNU Affero General Public License for more details. | ||
322 | 15 | # | ||
323 | 16 | # You should have received a copy of the GNU Affero General Public License | ||
324 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
325 | 18 | # | ||
326 | 19 | ############################################################################## | ||
327 | 20 | |||
328 | 21 | from openerp.osv import fields, orm | ||
329 | 22 | from openerp.tools.translate import _ | ||
330 | 23 | |||
331 | 24 | |||
332 | 25 | class product_product(orm.Model): | ||
333 | 26 | _inherit = "product.product" | ||
334 | 27 | |||
335 | 28 | _columns = { | ||
336 | 29 | 'lot_valuation': fields.boolean( | ||
337 | 30 | 'Lot valuation', | ||
338 | 31 | help="Use lot valuation instead of product valuation"), | ||
339 | 32 | } | ||
340 | 0 | 33 | ||
341 | === added file 'stock_lot_valuation/product_view.xml' | |||
342 | --- stock_lot_valuation/product_view.xml 1970-01-01 00:00:00 +0000 | |||
343 | +++ stock_lot_valuation/product_view.xml 2013-10-18 16:18:27 +0000 | |||
344 | @@ -0,0 +1,15 @@ | |||
345 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
346 | 2 | <openerp> | ||
347 | 3 | <data> | ||
348 | 4 | <record id="product_normal_form_view" model="ir.ui.view"> | ||
349 | 5 | <field name="name">product_normal_form_view</field> | ||
350 | 6 | <field name="model">product.product</field> | ||
351 | 7 | <field name="inherit_id" ref="stock.view_normal_property_acc_form"></field> | ||
352 | 8 | <field name="arch" type="xml"> | ||
353 | 9 | <field name="valuation" position="after"> | ||
354 | 10 | <field name="lot_valuation"></field> | ||
355 | 11 | </field> | ||
356 | 12 | </field> | ||
357 | 13 | </record> | ||
358 | 14 | </data> | ||
359 | 15 | </openerp> | ||
360 | 0 | 16 | ||
361 | === added file 'stock_lot_valuation/stock.py' | |||
362 | --- stock_lot_valuation/stock.py 1970-01-01 00:00:00 +0000 | |||
363 | +++ stock_lot_valuation/stock.py 2013-10-18 16:18:27 +0000 | |||
364 | @@ -0,0 +1,358 @@ | |||
365 | 1 | # -*- coding: utf-8 -*- | ||
366 | 2 | ############################################################################## | ||
367 | 3 | # | ||
368 | 4 | # Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>) | ||
369 | 5 | # | ||
370 | 6 | # This program is free software: you can redistribute it and/or modify | ||
371 | 7 | # it under the terms of the GNU Affero General Public License as published | ||
372 | 8 | # by the Free Software Foundation, either version 3 of the License, or | ||
373 | 9 | # (at your option) any later version. | ||
374 | 10 | # | ||
375 | 11 | # This program is distributed in the hope that it will be useful, | ||
376 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
377 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
378 | 14 | # GNU Affero General Public License for more details. | ||
379 | 15 | # | ||
380 | 16 | # You should have received a copy of the GNU Affero General Public License | ||
381 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
382 | 18 | # | ||
383 | 19 | ############################################################################## | ||
384 | 20 | |||
385 | 21 | from openerp.osv import fields, orm | ||
386 | 22 | from openerp.tools.translate import _ | ||
387 | 23 | import openerp.addons.decimal_precision as dp | ||
388 | 24 | |||
389 | 25 | |||
390 | 26 | class stock_production_lot(orm.Model): | ||
391 | 27 | _inherit = "stock.production.lot" | ||
392 | 28 | |||
393 | 29 | _columns = { | ||
394 | 30 | 'standard_price': fields.float( | ||
395 | 31 | 'Cost', digits_compute=dp.get_precision('Lot Price'), | ||
396 | 32 | help="Cost price (in company currency) of the lot used for " | ||
397 | 33 | "standard stock valuation in accounting.", | ||
398 | 34 | groups="base.group_user" | ||
399 | 35 | ), | ||
400 | 36 | 'cost_method': fields.selection( | ||
401 | 37 | [ | ||
402 | 38 | ('standard', 'Standard Price'), | ||
403 | 39 | ('average', 'Average Price') | ||
404 | 40 | ], 'Costing Method', | ||
405 | 41 | help="Standard Price: The cost price is manually updated at the " | ||
406 | 42 | "end of a specific period. \nAverage Price: The cost price is " | ||
407 | 43 | "recomputed at each incoming shipment." | ||
408 | 44 | ), | ||
409 | 45 | } | ||
410 | 46 | |||
411 | 47 | def price_get(self, cr, uid, ids, context=None): | ||
412 | 48 | if context is None: | ||
413 | 49 | context = {} | ||
414 | 50 | res = {} | ||
415 | 51 | product_uom_obj = self.pool.get('product.uom') | ||
416 | 52 | for lot in self.browse(cr, uid, ids, context=context): | ||
417 | 53 | res[lot.id] = lot['standard_price'] or 0.0 | ||
418 | 54 | if 'uom' in context: | ||
419 | 55 | uom = lot.product_id.uom_id or lot.product_id.uos_id | ||
420 | 56 | res[lot.id] = product_uom_obj._compute_price( | ||
421 | 57 | cr, uid, | ||
422 | 58 | uom.id, res[lot.id], context['uom']) | ||
423 | 59 | # Convert from price_type currency to asked one | ||
424 | 60 | if 'currency_id' in context: | ||
425 | 61 | currency_id = False | ||
426 | 62 | if lot.company_id and lot.company_id.currency_id: | ||
427 | 63 | currency_id = lot.company_id.currency_id.id | ||
428 | 64 | elif ( | ||
429 | 65 | lot.product_id.company_id | ||
430 | 66 | and lot.product_id.company_id.currency_id | ||
431 | 67 | ): | ||
432 | 68 | currency_id = lot.product_id.company_id.currency_id.id | ||
433 | 69 | if currency_id: | ||
434 | 70 | res[lot.id] = self.pool.get('res.currency').compute( | ||
435 | 71 | cr, uid, | ||
436 | 72 | currency_id, | ||
437 | 73 | context['currency_id'], res[lot.id], context=context) | ||
438 | 74 | return res | ||
439 | 75 | |||
440 | 76 | def do_change_standard_price(self, cr, uid, ids, datas, context=None): | ||
441 | 77 | """ Changes the Standard Price of Lot and creates an account move | ||
442 | 78 | accordingly. | ||
443 | 79 | @param datas : dict. contain default datas like new_price, | ||
444 | 80 | stock_output_account, stock_input_account, stock_journal | ||
445 | 81 | @param context: A standard dictionary | ||
446 | 82 | """ | ||
447 | 83 | location_obj = self.pool.get('stock.location') | ||
448 | 84 | move_obj = self.pool.get('account.move') | ||
449 | 85 | move_line_obj = self.pool.get('account.move.line') | ||
450 | 86 | if context is None: | ||
451 | 87 | context = {} | ||
452 | 88 | |||
453 | 89 | new_price = datas.get('new_price', 0.0) | ||
454 | 90 | stock_output_acc = datas.get('stock_output_account', False) | ||
455 | 91 | stock_input_acc = datas.get('stock_input_account', False) | ||
456 | 92 | journal_id = datas.get('stock_journal', False) | ||
457 | 93 | lot_obj = self.browse(cr, uid, ids, context=context)[0] | ||
458 | 94 | account_valuation = ( | ||
459 | 95 | lot_obj.product_id.categ_id.property_stock_valuation_account_id) | ||
460 | 96 | account_valuation_id = ( | ||
461 | 97 | account_valuation and account_valuation.id or False) | ||
462 | 98 | if not account_valuation_id: | ||
463 | 99 | raise orm.except_orm( | ||
464 | 100 | _('Error!'), | ||
465 | 101 | _('Specify valuation Account for Product Category: %s.') | ||
466 | 102 | % (lot_obj.product_id.categ_id.name)) | ||
467 | 103 | move_ids = [] | ||
468 | 104 | loc_ids = location_obj.search( | ||
469 | 105 | cr, uid, [('usage', '=', 'internal')], | ||
470 | 106 | context=context) | ||
471 | 107 | for rec_id in ids: | ||
472 | 108 | for location in location_obj.browse( | ||
473 | 109 | cr, uid, loc_ids, context=context | ||
474 | 110 | ): | ||
475 | 111 | c = context.copy() | ||
476 | 112 | c.update({ | ||
477 | 113 | 'location_id': location.id, | ||
478 | 114 | 'compute_child': False | ||
479 | 115 | }) | ||
480 | 116 | |||
481 | 117 | lot = self.browse(cr, uid, rec_id, context=c) | ||
482 | 118 | qty = lot.stock_available | ||
483 | 119 | diff = lot.standard_price - new_price | ||
484 | 120 | if not diff: | ||
485 | 121 | raise orm.except_orm( | ||
486 | 122 | _('Error!'), | ||
487 | 123 | _("No difference between standard price!" | ||
488 | 124 | " and new price")) | ||
489 | 125 | if qty: | ||
490 | 126 | company_id = ( | ||
491 | 127 | location.company_id and location.company_id.id or False | ||
492 | 128 | ) | ||
493 | 129 | if not company_id: | ||
494 | 130 | raise orm.except_orm( | ||
495 | 131 | _('Error!'), | ||
496 | 132 | _('Please specify company in Location.')) | ||
497 | 133 | # | ||
498 | 134 | # Accounting Entries | ||
499 | 135 | # | ||
500 | 136 | product = lot.product_id | ||
501 | 137 | if ( | ||
502 | 138 | not journal_id | ||
503 | 139 | and product.categ_id.property_stock_journal | ||
504 | 140 | ): | ||
505 | 141 | journal_id = product.categ_id.property_stock_journal.id | ||
506 | 142 | if not journal_id: | ||
507 | 143 | raise orm.except_orm( | ||
508 | 144 | _('Error!'), | ||
509 | 145 | _("Please define journal " | ||
510 | 146 | "on the product category: '%s' (id: %d).") % | ||
511 | 147 | (product.categ_id.name, product.categ_id.id,)) | ||
512 | 148 | move_id = move_obj.create(cr, uid, { | ||
513 | 149 | 'journal_id': journal_id, | ||
514 | 150 | 'company_id': company_id | ||
515 | 151 | }, context=context) | ||
516 | 152 | |||
517 | 153 | move_ids.append(move_id) | ||
518 | 154 | |||
519 | 155 | if diff > 0: | ||
520 | 156 | if not stock_input_acc: | ||
521 | 157 | stock_input_acc = ( | ||
522 | 158 | product.property_stock_account_input.id | ||
523 | 159 | ) | ||
524 | 160 | if not stock_input_acc: | ||
525 | 161 | stock_input_acc = ( | ||
526 | 162 | product.categ_id. | ||
527 | 163 | property_stock_account_input_categ.id | ||
528 | 164 | ) | ||
529 | 165 | if not stock_input_acc: | ||
530 | 166 | raise orm.except_orm( | ||
531 | 167 | _('Error!'), | ||
532 | 168 | _("Please define stock input account " | ||
533 | 169 | "for this product: '%s' (id: %d).") % | ||
534 | 170 | (product.name, product.id,)) | ||
535 | 171 | amount_diff = qty * diff | ||
536 | 172 | move_line_obj.create(cr, uid, { | ||
537 | 173 | 'name': product.name, | ||
538 | 174 | 'account_id': stock_input_acc, | ||
539 | 175 | 'debit': amount_diff, | ||
540 | 176 | 'move_id': move_id, | ||
541 | 177 | }, context=context) | ||
542 | 178 | move_line_obj.create(cr, uid, { | ||
543 | 179 | 'name': product.categ_id.name, | ||
544 | 180 | 'account_id': account_valuation_id, | ||
545 | 181 | 'credit': amount_diff, | ||
546 | 182 | 'move_id': move_id | ||
547 | 183 | }, context=context) | ||
548 | 184 | elif diff < 0: | ||
549 | 185 | if not stock_output_acc: | ||
550 | 186 | stock_output_acc = ( | ||
551 | 187 | product.property_stock_account_output.id | ||
552 | 188 | ) | ||
553 | 189 | if not stock_output_acc: | ||
554 | 190 | stock_output_acc = ( | ||
555 | 191 | product.categ_id. | ||
556 | 192 | property_stock_account_output_categ.id | ||
557 | 193 | ) | ||
558 | 194 | if not stock_output_acc: | ||
559 | 195 | raise orm.except_orm( | ||
560 | 196 | _('Error!'), | ||
561 | 197 | _("Please define stock output account " | ||
562 | 198 | "for this product: '%s' (id: %d).") % | ||
563 | 199 | (product.name, product.id,)) | ||
564 | 200 | amount_diff = qty * -diff | ||
565 | 201 | move_line_obj.create(cr, uid, { | ||
566 | 202 | 'name': product.name, | ||
567 | 203 | 'account_id': stock_output_acc, | ||
568 | 204 | 'credit': amount_diff, | ||
569 | 205 | 'move_id': move_id | ||
570 | 206 | }, context=context) | ||
571 | 207 | move_line_obj.create(cr, uid, { | ||
572 | 208 | 'name': product.categ_id.name, | ||
573 | 209 | 'account_id': account_valuation_id, | ||
574 | 210 | 'debit': amount_diff, | ||
575 | 211 | 'move_id': move_id | ||
576 | 212 | }, context=context) | ||
577 | 213 | |||
578 | 214 | self.write(cr, uid, rec_id, {'standard_price': new_price}) | ||
579 | 215 | |||
580 | 216 | return move_ids | ||
581 | 217 | |||
582 | 218 | |||
583 | 219 | class stock_move(orm.Model): | ||
584 | 220 | _inherit = "stock.move" | ||
585 | 221 | |||
586 | 222 | def _get_reference_accounting_values_for_valuation( | ||
587 | 223 | self, cr, uid, move, context=None | ||
588 | 224 | ): | ||
589 | 225 | res = super( | ||
590 | 226 | stock_move, self)._get_reference_accounting_values_for_valuation( | ||
591 | 227 | cr, uid, move, context=context) | ||
592 | 228 | if move.product_id.lot_valuation and move.prodlot_id: | ||
593 | 229 | product_uom_obj = self.pool.get('product.uom') | ||
594 | 230 | qty = product_uom_obj._compute_qty( | ||
595 | 231 | cr, uid, move.product_uom.id, | ||
596 | 232 | move.product_qty, move.product_id.uom_id.id) | ||
597 | 233 | if context is None: | ||
598 | 234 | context = {} | ||
599 | 235 | currency_ctx = dict( | ||
600 | 236 | context, currency_id=move.company_id.currency_id.id) | ||
601 | 237 | amount_unit = move.prodlot_id.price_get( | ||
602 | 238 | context=currency_ctx)[move.prodlot_id.id] | ||
603 | 239 | reference_amount = amount_unit * qty | ||
604 | 240 | new_res = (reference_amount, move.company_id.currency_id.id) | ||
605 | 241 | res = new_res | ||
606 | 242 | return res | ||
607 | 243 | |||
608 | 244 | def do_partial(self, cr, uid, ids, partial_datas, context=None): | ||
609 | 245 | if context is None: | ||
610 | 246 | context = {} | ||
611 | 247 | pick_obj = self.pool.get('stock.picking') | ||
612 | 248 | for move in self.browse(cr, uid, ids, context=context): | ||
613 | 249 | pick_obj.write_lot(cr, uid, move, partial_datas, context=context) | ||
614 | 250 | res = super(stock_move, self).do_partial( | ||
615 | 251 | cr, uid, ids, partial_datas, context=context) | ||
616 | 252 | return res | ||
617 | 253 | |||
618 | 254 | |||
619 | 255 | class stock_picking(orm.Model): | ||
620 | 256 | _inherit = "stock.picking" | ||
621 | 257 | |||
622 | 258 | def compute_price(self, cr, uid, partial_datas, move, context=None): | ||
623 | 259 | if context is None: | ||
624 | 260 | context = {} | ||
625 | 261 | lot_obj = self.pool.get('stock.production.lot') | ||
626 | 262 | uom_obj = self.pool.get('product.uom') | ||
627 | 263 | move_obj = self.pool.get('stock.move') | ||
628 | 264 | currency_obj = self.pool.get('res.currency') | ||
629 | 265 | partial_data = partial_datas.get('move%s' % (move.id), {}) | ||
630 | 266 | product_uom = partial_data.get('product_uom', False) | ||
631 | 267 | product_qty = partial_data.get('product_qty', 0.0) | ||
632 | 268 | product_currency = partial_data.get('product_currency', False) | ||
633 | 269 | product_price = partial_data.get('product_price', 0.0) | ||
634 | 270 | |||
635 | 271 | lot = lot_obj.browse(cr, uid, move.prodlot_id.id, context=context) | ||
636 | 272 | product = lot.product_id | ||
637 | 273 | move_currency_id = move.company_id.currency_id.id | ||
638 | 274 | context['currency_id'] = move_currency_id | ||
639 | 275 | qty = uom_obj._compute_qty( | ||
640 | 276 | cr, uid, product_uom, product_qty, product.uom_id.id) | ||
641 | 277 | if qty > 0: | ||
642 | 278 | new_price = currency_obj.compute( | ||
643 | 279 | cr, uid, product_currency, | ||
644 | 280 | move_currency_id, product_price) | ||
645 | 281 | new_price = uom_obj._compute_price( | ||
646 | 282 | cr, uid, product_uom, new_price, | ||
647 | 283 | product.uom_id.id) | ||
648 | 284 | if lot.stock_available <= 0: | ||
649 | 285 | new_std_price = new_price | ||
650 | 286 | else: | ||
651 | 287 | # Get the standard price | ||
652 | 288 | amount_unit = lot.price_get(context=context)[lot.id] | ||
653 | 289 | new_std_price = ( | ||
654 | 290 | ((amount_unit * lot.stock_available) | ||
655 | 291 | + (new_price * qty)) / (lot.stock_available + qty) | ||
656 | 292 | ) | ||
657 | 293 | |||
658 | 294 | lot_obj.write( | ||
659 | 295 | cr, uid, [lot.id], {'standard_price': new_std_price}, | ||
660 | 296 | context=context | ||
661 | 297 | ) | ||
662 | 298 | |||
663 | 299 | # Record the values that were chosen in the wizard, so they can be | ||
664 | 300 | # used for inventory valuation if real-time valuation is enabled. | ||
665 | 301 | move_obj.write(cr, uid, [move.id], { | ||
666 | 302 | 'price_unit': product_price, | ||
667 | 303 | 'price_currency_id': product_currency | ||
668 | 304 | }, context=context) | ||
669 | 305 | |||
670 | 306 | def write_lot(self, cr, uid, move, partial_datas, context=None): | ||
671 | 307 | lot_obj = self.pool.get('stock.production.lot') | ||
672 | 308 | currency_obj = self.pool.get('res.currency') | ||
673 | 309 | uom_obj = self.pool.get('product.uom') | ||
674 | 310 | if partial_datas.get('move%s' % (move.id)): | ||
675 | 311 | partial_data = partial_datas.get('move%s' % (move.id), {}) | ||
676 | 312 | product_price = partial_data.get('product_price', 0.0) | ||
677 | 313 | product_currency = partial_data.get('product_currency', False) | ||
678 | 314 | product_uom = partial_data.get('product_uom', False) | ||
679 | 315 | if partial_data.get('prodlot_id'): | ||
680 | 316 | lot = lot_obj.browse( | ||
681 | 317 | cr, uid, partial_data['prodlot_id'], context) | ||
682 | 318 | product = lot.product_id | ||
683 | 319 | if ( | ||
684 | 320 | move.product_id.lot_valuation and ( | ||
685 | 321 | move.picking_id.type == 'in' | ||
686 | 322 | ) and (lot.cost_method == 'average') | ||
687 | 323 | ): | ||
688 | 324 | self.compute_price( | ||
689 | 325 | cr, uid, partial_datas, move, context=context) | ||
690 | 326 | if ( | ||
691 | 327 | move.product_id.lot_valuation and product_price | ||
692 | 328 | and not lot.standard_price | ||
693 | 329 | ): | ||
694 | 330 | new_price = currency_obj.compute( | ||
695 | 331 | cr, uid, product_currency, | ||
696 | 332 | move.company_id.currency_id.id, product_price) | ||
697 | 333 | new_price = uom_obj._compute_price( | ||
698 | 334 | cr, uid, product_uom, new_price, | ||
699 | 335 | product.uom_id.id) | ||
700 | 336 | lot.write({'standard_price': new_price}) | ||
701 | 337 | |||
702 | 338 | def do_partial(self, cr, uid, ids, partial_datas, context=None): | ||
703 | 339 | if context is None: | ||
704 | 340 | context = {} | ||
705 | 341 | for pick in self.browse(cr, uid, ids, context=context): | ||
706 | 342 | for move in pick.move_lines: | ||
707 | 343 | self.write_lot(cr, uid, move, partial_datas, context=context) | ||
708 | 344 | res = super(stock_picking, self).do_partial( | ||
709 | 345 | cr, uid, ids, partial_datas, context=context) | ||
710 | 346 | return res | ||
711 | 347 | |||
712 | 348 | |||
713 | 349 | class stock_partial_picking(orm.TransientModel): | ||
714 | 350 | _inherit = "stock.partial.picking" | ||
715 | 351 | |||
716 | 352 | def _product_cost_for_average_update(self, cr, uid, move): | ||
717 | 353 | res = super( | ||
718 | 354 | stock_partial_picking, self | ||
719 | 355 | )._product_cost_for_average_update(cr, uid, move) | ||
720 | 356 | if move.prodlot_id and move.product_id.lot_valuation: | ||
721 | 357 | res['cost'] = move.prodlot_id.standard_price | ||
722 | 358 | return res | ||
723 | 0 | 359 | ||
724 | === added file 'stock_lot_valuation/stock_view.xml' | |||
725 | --- stock_lot_valuation/stock_view.xml 1970-01-01 00:00:00 +0000 | |||
726 | +++ stock_lot_valuation/stock_view.xml 2013-10-18 16:18:27 +0000 | |||
727 | @@ -0,0 +1,22 @@ | |||
728 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
729 | 2 | <openerp> | ||
730 | 3 | <data> | ||
731 | 4 | <record id="view_production_lot_form" model="ir.ui.view"> | ||
732 | 5 | <field name="name">view_production_lot_form</field> | ||
733 | 6 | <field name="model">stock.production.lot</field> | ||
734 | 7 | <field name="inherit_id" ref="stock.view_production_lot_form"></field> | ||
735 | 8 | <field name="arch" type="xml"> | ||
736 | 9 | <field name="stock_available" position="after" version="7.0"> | ||
737 | 10 | <field name="cost_method"></field> | ||
738 | 11 | <label string="Cost Price" for="standard_price" align="1.0" groups="base.group_user"/> | ||
739 | 12 | <div groups="base.group_user"> | ||
740 | 13 | <field name="standard_price" attrs="{'readonly':[('cost_method','=','average')]}" nolabel="1"/> | ||
741 | 14 | <button name="%(action_view_change_standard_price)d" string="update" | ||
742 | 15 | type="action" attrs="{'invisible':[('cost_method','<>','average')]}" | ||
743 | 16 | class="oe_link" groups="product.group_costing_method"/> | ||
744 | 17 | </div> | ||
745 | 18 | </field> | ||
746 | 19 | </field> | ||
747 | 20 | </record> | ||
748 | 21 | </data> | ||
749 | 22 | </openerp> | ||
750 | 0 | 23 | ||
751 | === added directory 'stock_lot_valuation/test' | |||
752 | === added file 'stock_lot_valuation/test/stock.yml' | |||
753 | --- stock_lot_valuation/test/stock.yml 1970-01-01 00:00:00 +0000 | |||
754 | +++ stock_lot_valuation/test/stock.yml 2013-10-18 16:18:27 +0000 | |||
755 | @@ -0,0 +1,230 @@ | |||
756 | 1 | - | ||
757 | 2 | !record {model: stock.location, id: location_refrigerator}: | ||
758 | 3 | name: Refrigerator | ||
759 | 4 | usage: internal | ||
760 | 5 | - | ||
761 | 6 | !record {model: stock.location, id: location_delivery_counter}: | ||
762 | 7 | name: Delivery Counter | ||
763 | 8 | usage: internal | ||
764 | 9 | - | ||
765 | 10 | !record {model: stock.location, id: location_refrigerator_small}: | ||
766 | 11 | name: Small Refrigerator | ||
767 | 12 | usage: internal | ||
768 | 13 | location_id: location_refrigerator | ||
769 | 14 | - | ||
770 | 15 | !record {model: stock.location, id: location_opening}: | ||
771 | 16 | name: opening | ||
772 | 17 | usage: inventory | ||
773 | 18 | - | ||
774 | 19 | !record {model: stock.location, id: location_convenience_shop}: | ||
775 | 20 | name: Convenient Store | ||
776 | 21 | usage: supplier | ||
777 | 22 | - | ||
778 | 23 | !record {model: stock.warehouse, id: warehouse_icecream}: | ||
779 | 24 | name: Ice Cream Shop | ||
780 | 25 | lot_input_id: location_refrigerator | ||
781 | 26 | lot_stock_id: location_refrigerator | ||
782 | 27 | lot_output_id: location_delivery_counter | ||
783 | 28 | - | ||
784 | 29 | !record {model: product.product, id: product_icecream}: | ||
785 | 30 | default_code: 001 | ||
786 | 31 | name: Ice Cream | ||
787 | 32 | type: product | ||
788 | 33 | categ_id: product.product_category_1 | ||
789 | 34 | list_price: 100.0 | ||
790 | 35 | standard_price: 70.0 | ||
791 | 36 | uom_id: product.product_uom_kgm | ||
792 | 37 | uom_po_id: product.product_uom_kgm | ||
793 | 38 | procure_method: make_to_stock | ||
794 | 39 | property_stock_inventory: location_opening | ||
795 | 40 | valuation: real_time | ||
796 | 41 | cost_method: average | ||
797 | 42 | property_stock_account_input: account.o_expense | ||
798 | 43 | property_stock_account_output: account.o_income | ||
799 | 44 | description: Ice cream can be mass-produced and thus is widely available in developed parts of the world. Ice cream can be purchased in large cartons (vats and squrounds) from supermarkets and grocery stores, in smaller quantities from ice cream shops, convenience stores, and milk bars, and in individual servings from small carts or vans at public events. | ||
800 | 45 | lot_valuation: True | ||
801 | 46 | |||
802 | 47 | - | ||
803 | 48 | !record {model: stock.production.lot, id: lot_icecream_0}: | ||
804 | 49 | name: Lot0 for Ice cream | ||
805 | 50 | product_id: product_icecream | ||
806 | 51 | cost_method: average | ||
807 | 52 | standard_price: 70.0 | ||
808 | 53 | - | ||
809 | 54 | !record {model: stock.production.lot, id: lot_icecream_1}: | ||
810 | 55 | name: Lot1 for Ice cream | ||
811 | 56 | product_id: product_icecream | ||
812 | 57 | - | ||
813 | 58 | !record {model: stock.inventory, id: stock_inventory_icecream}: | ||
814 | 59 | name: Inventory for icecream | ||
815 | 60 | - | ||
816 | 61 | !record {model: stock.inventory.line, id: stock_inventory_line_icecream_lot0}: | ||
817 | 62 | product_id: product_icecream | ||
818 | 63 | product_uom: product.product_uom_kgm | ||
819 | 64 | inventory_id: stock_inventory_icecream | ||
820 | 65 | product_qty: 50.0 | ||
821 | 66 | prod_lot_id: lot_icecream_0 | ||
822 | 67 | location_id: location_refrigerator | ||
823 | 68 | - | ||
824 | 69 | !record {model: stock.inventory.line, id: stock_inventory_line_icecream_lot1}: | ||
825 | 70 | product_id: product_icecream | ||
826 | 71 | product_uom: product.product_uom_kgm | ||
827 | 72 | inventory_id: stock_inventory_icecream | ||
828 | 73 | product_qty: 40.0 | ||
829 | 74 | prod_lot_id: lot_icecream_1 | ||
830 | 75 | location_id: location_refrigerator | ||
831 | 76 | |||
832 | 77 | - | ||
833 | 78 | !record {model: stock.picking, id: outgoing_shipment}: | ||
834 | 79 | type: out | ||
835 | 80 | location_dest_id: location_delivery_counter | ||
836 | 81 | - | ||
837 | 82 | !record {model: stock.move, id: outgoing_shipment_icecream}: | ||
838 | 83 | picking_id: outgoing_shipment | ||
839 | 84 | product_id: product_icecream | ||
840 | 85 | product_uom: product.product_uom_kgm | ||
841 | 86 | product_qty: 130.0 | ||
842 | 87 | location_id: location_refrigerator | ||
843 | 88 | location_dest_id: location_delivery_counter | ||
844 | 89 | prodlot_id: lot_icecream_0 | ||
845 | 90 | - | ||
846 | 91 | !record {model: stock.picking, id: incoming_shipment}: | ||
847 | 92 | type: in | ||
848 | 93 | invoice_state: 2binvoiced | ||
849 | 94 | partner_id: base.res_partner_address_9 | ||
850 | 95 | location_dest_id: location_refrigerator | ||
851 | 96 | - | ||
852 | 97 | !record {model: stock.move, id: incoming_shipment_icecream}: | ||
853 | 98 | picking_id: incoming_shipment | ||
854 | 99 | product_id: product_icecream | ||
855 | 100 | product_uom: product.product_uom_kgm | ||
856 | 101 | product_qty: 50.0 | ||
857 | 102 | location_id: location_convenience_shop | ||
858 | 103 | location_dest_id: location_refrigerator | ||
859 | 104 | prodlot_id: lot_icecream_0 | ||
860 | 105 | |||
861 | 106 | - | ||
862 | 107 | I update the price of the Ice-cream. | ||
863 | 108 | - | ||
864 | 109 | !python {model: stock.change.standard.price}: | | ||
865 | 110 | context.update({'active_model':'product.product', 'active_id': ref('product_icecream'), 'active_ids':[ref('product_icecream')]}) | ||
866 | 111 | - | ||
867 | 112 | !record {model: stock.change.standard.price, id: change_price}: | ||
868 | 113 | new_price: 120 | ||
869 | 114 | - | ||
870 | 115 | !python {model: stock.change.standard.price}: | | ||
871 | 116 | self.change_price(cr, uid, [ref('change_price')], context=context) | ||
872 | 117 | - | ||
873 | 118 | I check price of Ice-cream after update price. | ||
874 | 119 | - | ||
875 | 120 | !python {model: product.product}: | | ||
876 | 121 | product = self.browse(cr, uid, ref('product_icecream'), context=context) | ||
877 | 122 | assert product.standard_price == 120, "Price is not updated." | ||
878 | 123 | |||
879 | 124 | - | ||
880 | 125 | I confirm physical inventory of Ice-cream which are came in different lots. | ||
881 | 126 | - | ||
882 | 127 | !python {model: stock.inventory}: | | ||
883 | 128 | self.action_confirm(cr, uid, [ref('stock_inventory_icecream')], context=context) | ||
884 | 129 | - | ||
885 | 130 | I check move details after confirmed physical inventory. | ||
886 | 131 | - | ||
887 | 132 | !python {model: stock.inventory}: | | ||
888 | 133 | inventory = self.browse(cr, uid, ref('stock_inventory_icecream'), context=context) | ||
889 | 134 | assert len(inventory.move_ids) == len(inventory.inventory_line_id), "moves are not correspond." | ||
890 | 135 | for move_line in inventory.move_ids: | ||
891 | 136 | for line in inventory.inventory_line_id: | ||
892 | 137 | if move_line.product_id.id == line.product_id.id and move_line.prodlot_id.id == line.prod_lot_id.id: | ||
893 | 138 | location_id = line.product_id.property_stock_inventory.id | ||
894 | 139 | assert move_line.product_qty == line.product_qty, "Qty is not correspond." | ||
895 | 140 | assert move_line.product_uom.id == line.product_uom.id, "UOM is not correspond." | ||
896 | 141 | assert move_line.date == inventory.date, "Date is not correspond." | ||
897 | 142 | assert move_line.location_id.id == location_id, "Source location is not correspond." | ||
898 | 143 | assert move_line.location_dest_id.id == line.location_id.id, "Destination location is not correspond." | ||
899 | 144 | assert move_line.state == 'confirmed', "Move is not confirmed." | ||
900 | 145 | |||
901 | 146 | - | ||
902 | 147 | Now I check vitual stock of Ice-cream after confirmed physical inventory. | ||
903 | 148 | - | ||
904 | 149 | !python {model: product.product}: | | ||
905 | 150 | product = self.browse(cr, uid, ref('product_icecream'), context=context) | ||
906 | 151 | assert product.virtual_available == 90, "Vitual stock is not updated." | ||
907 | 152 | |||
908 | 153 | - | ||
909 | 154 | I complete physical inventory of Ice-cream. | ||
910 | 155 | - | ||
911 | 156 | !python {model: stock.inventory}: | | ||
912 | 157 | self.action_done(cr, uid, [ref('stock_inventory_icecream')], context=context) | ||
913 | 158 | balance = self.pool.get('account.account').browse(cr, uid, ref('account.stk')).balance | ||
914 | 159 | assert balance == 3500.0, "Purchased Stocks balance is %s, not 3500 (50*70)" % balance | ||
915 | 160 | - | ||
916 | 161 | I update the price of the Ice-cream lot. | ||
917 | 162 | - | ||
918 | 163 | !python {model: lot.change.standard.price}: | | ||
919 | 164 | context.update({'active_model':'stock.production.lot', 'active_id': ref('lot_icecream_0'), 'active_ids':[ref('lot_icecream_0')]}) | ||
920 | 165 | - | ||
921 | 166 | !record {model: lot.change.standard.price, id: change_price}: | ||
922 | 167 | new_price: 120 | ||
923 | 168 | - | ||
924 | 169 | !python {model: lot.change.standard.price}: | | ||
925 | 170 | self.change_price(cr, uid, [ref('change_price')], context=context) | ||
926 | 171 | - | ||
927 | 172 | I check price of Ice-cream lot after update price. | ||
928 | 173 | - | ||
929 | 174 | !python {model: stock.production.lot}: | | ||
930 | 175 | lot = self.browse(cr, uid, ref('lot_icecream_0'), context=context) | ||
931 | 176 | assert lot.standard_price == 120, "Price is not updated." | ||
932 | 177 | balance = self.pool.get('account.account').browse(cr, uid, ref('account.stk')).balance | ||
933 | 178 | assert balance == 6000.0, "Purchased Stocks balance is %s, not 6000 (old balance + (lot.standard_price - new_price) * lot.stock_available)" % balance | ||
934 | 179 | |||
935 | 180 | - | ||
936 | 181 | I confirm outgoing shipment of 130 kgm Ice-cream. | ||
937 | 182 | - | ||
938 | 183 | !workflow {model: stock.picking, action: button_confirm, ref: outgoing_shipment} | ||
939 | 184 | - | ||
940 | 185 | I check shipment details after confirmed. | ||
941 | 186 | - | ||
942 | 187 | !python {model: stock.picking}: | | ||
943 | 188 | shipment = self.browse(cr, uid, ref("outgoing_shipment")) | ||
944 | 189 | assert shipment.state == "confirmed", "Shipment should be confirmed." | ||
945 | 190 | for move_line in shipment.move_lines: | ||
946 | 191 | assert move_line.state == "confirmed", "Move should be confirmed." | ||
947 | 192 | |||
948 | 193 | - | ||
949 | 194 | I confirm incoming shipment of 50 kgm Ice-cream. | ||
950 | 195 | - | ||
951 | 196 | !workflow {model: stock.picking, action: button_confirm, ref: incoming_shipment} | ||
952 | 197 | - | ||
953 | 198 | I receive 40kgm Ice-cream so I make backorder of incoming shipment for 40 kgm. | ||
954 | 199 | - | ||
955 | 200 | !python {model: stock.partial.picking}: | | ||
956 | 201 | context.update({'active_model': 'stock.picking', 'active_id': ref('incoming_shipment'), 'active_ids': [ref('incoming_shipment')]}) | ||
957 | 202 | - | ||
958 | 203 | !record {model: stock.partial.picking, id: partial_incoming}: | ||
959 | 204 | move_ids: | ||
960 | 205 | - quantity: 40 | ||
961 | 206 | product_id: product_icecream | ||
962 | 207 | product_uom: product.product_uom_kgm | ||
963 | 208 | move_id: incoming_shipment_icecream | ||
964 | 209 | location_id: location_convenience_shop | ||
965 | 210 | location_dest_id: location_refrigerator | ||
966 | 211 | prodlot_id: lot_icecream_0 | ||
967 | 212 | cost: 100 | ||
968 | 213 | - | ||
969 | 214 | !python {model: stock.partial.picking }: | | ||
970 | 215 | self.do_partial(cr, uid, [ref('partial_incoming')], context=context) | ||
971 | 216 | - | ||
972 | 217 | I check backorder shipment after received partial shipment. | ||
973 | 218 | - | ||
974 | 219 | !python {model: stock.picking}: | | ||
975 | 220 | lot = self.pool.get('stock.production.lot').browse(cr, uid, ref('lot_icecream_0'), context=context) | ||
976 | 221 | assert lot.standard_price == 111.11, "Price is not updated to 111.11 (((120*50)+(100*40))/(50+40))" | ||
977 | 222 | shipment = self.browse(cr, uid, ref("incoming_shipment")) | ||
978 | 223 | backorder = shipment.backorder_id | ||
979 | 224 | assert backorder, "Backorder should be created after partial shipment." | ||
980 | 225 | assert backorder.state == 'done', "Backorder should be close after received." | ||
981 | 226 | for move_line in backorder.move_lines: | ||
982 | 227 | assert move_line.product_qty == 40, "Qty in backorder does not correspond." | ||
983 | 228 | assert move_line.state == 'done', "Move line of backorder should be closed." | ||
984 | 229 | balance = self.pool.get('account.account').browse(cr, uid, ref('account.stk')).balance | ||
985 | 230 | assert balance == 10444.4, "Purchased Stocks balance is %s, not 10444.4 (old balance + 111.11×40)" % balance | ||
986 | 0 | 231 | ||
987 | === added directory 'stock_lot_valuation/wizard' | |||
988 | === added file 'stock_lot_valuation/wizard/__init__.py' | |||
989 | --- stock_lot_valuation/wizard/__init__.py 1970-01-01 00:00:00 +0000 | |||
990 | +++ stock_lot_valuation/wizard/__init__.py 2013-10-18 16:18:27 +0000 | |||
991 | @@ -0,0 +1,21 @@ | |||
992 | 1 | # -*- coding: utf-8 -*- | ||
993 | 2 | ############################################################################## | ||
994 | 3 | # | ||
995 | 4 | # Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>) | ||
996 | 5 | # | ||
997 | 6 | # This program is free software: you can redistribute it and/or modify | ||
998 | 7 | # it under the terms of the GNU Affero General Public License as published | ||
999 | 8 | # by the Free Software Foundation, either version 3 of the License, or | ||
1000 | 9 | # (at your option) any later version. | ||
1001 | 10 | # | ||
1002 | 11 | # This program is distributed in the hope that it will be useful, | ||
1003 | 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1004 | 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1005 | 14 | # GNU Affero General Public License for more details. | ||
1006 | 15 | # | ||
1007 | 16 | # You should have received a copy of the GNU Affero General Public License | ||
1008 | 17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1009 | 18 | # | ||
1010 | 19 | ############################################################################## | ||
1011 | 20 | |||
1012 | 21 | import stock_change_standard_price | ||
1013 | 0 | 22 | ||
1014 | === added file 'stock_lot_valuation/wizard/stock_change_standard_price.py' | |||
1015 | --- stock_lot_valuation/wizard/stock_change_standard_price.py 1970-01-01 00:00:00 +0000 | |||
1016 | +++ stock_lot_valuation/wizard/stock_change_standard_price.py 2013-10-18 16:18:27 +0000 | |||
1017 | @@ -0,0 +1,110 @@ | |||
1018 | 1 | # -*- coding: utf-8 -*- | ||
1019 | 2 | ############################################################################## | ||
1020 | 3 | # | ||
1021 | 4 | # OpenERP, Open Source Management Solution | ||
1022 | 5 | # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>). | ||
1023 | 6 | # Copyright (C) 2013 Agile Business Group sagl (<http://www.agilebg.com>) | ||
1024 | 7 | # | ||
1025 | 8 | # This program is free software: you can redistribute it and/or modify | ||
1026 | 9 | # it under the terms of the GNU Affero General Public License as | ||
1027 | 10 | # published by the Free Software Foundation, either version 3 of the | ||
1028 | 11 | # License, or (at your option) any later version. | ||
1029 | 12 | # | ||
1030 | 13 | # This program is distributed in the hope that it will be useful, | ||
1031 | 14 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1032 | 15 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1033 | 16 | # GNU Affero General Public License for more details. | ||
1034 | 17 | # | ||
1035 | 18 | # You should have received a copy of the GNU Affero General Public License | ||
1036 | 19 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1037 | 20 | # | ||
1038 | 21 | ############################################################################## | ||
1039 | 22 | |||
1040 | 23 | from openerp.osv import fields, orm | ||
1041 | 24 | from openerp.tools.translate import _ | ||
1042 | 25 | import openerp.addons.decimal_precision as dp | ||
1043 | 26 | |||
1044 | 27 | |||
1045 | 28 | class change_standard_price(orm.TransientModel): | ||
1046 | 29 | _name = "lot.change.standard.price" | ||
1047 | 30 | _description = "Change Standard Price" | ||
1048 | 31 | _columns = { | ||
1049 | 32 | 'new_price': fields.float( | ||
1050 | 33 | 'Price', required=True, | ||
1051 | 34 | digits_compute=dp.get_precision('Account'), | ||
1052 | 35 | help="If cost price is increased, stock variation account will be " | ||
1053 | 36 | "debited and stock output account will be credited with the " | ||
1054 | 37 | "value = (difference of amount * quantity available).\n" | ||
1055 | 38 | "If cost price is decreased, stock variation account will be " | ||
1056 | 39 | "creadited and stock input account will be debited." | ||
1057 | 40 | ), | ||
1058 | 41 | 'stock_account_input': fields.many2one( | ||
1059 | 42 | 'account.account', 'Stock Input Account'), | ||
1060 | 43 | 'stock_account_output': fields.many2one( | ||
1061 | 44 | 'account.account', 'Stock Output Account'), | ||
1062 | 45 | 'stock_journal': fields.many2one( | ||
1063 | 46 | 'account.journal', 'Stock journal', required=True), | ||
1064 | 47 | 'enable_stock_in_out_acc': fields.boolean('Enable Related Account',), | ||
1065 | 48 | } | ||
1066 | 49 | |||
1067 | 50 | def default_get(self, cr, uid, fields, context=None): | ||
1068 | 51 | """ To get default values for the object. | ||
1069 | 52 | @param self: The object pointer. | ||
1070 | 53 | @param cr: A database cursor | ||
1071 | 54 | @param uid: ID of the user currently logged in | ||
1072 | 55 | @param fields: List of fields for which we want default values | ||
1073 | 56 | @param context: A standard dictionary | ||
1074 | 57 | @return: A dictionary which of fields with values. | ||
1075 | 58 | """ | ||
1076 | 59 | if context is None: | ||
1077 | 60 | context = {} | ||
1078 | 61 | lot_pool = self.pool.get('stock.production.lot') | ||
1079 | 62 | product_pool = self.pool.get('product.product') | ||
1080 | 63 | lot_obj = lot_pool.browse(cr, uid, context.get('active_id', False)) | ||
1081 | 64 | res = super(change_standard_price, self).default_get( | ||
1082 | 65 | cr, uid, fields, context=context) | ||
1083 | 66 | |||
1084 | 67 | accounts = product_pool.get_product_accounts( | ||
1085 | 68 | cr, uid, lot_obj.product_id.id, context={}) | ||
1086 | 69 | |||
1087 | 70 | price = lot_obj.standard_price | ||
1088 | 71 | |||
1089 | 72 | if 'new_price' in fields: | ||
1090 | 73 | res.update({'new_price': price}) | ||
1091 | 74 | if 'stock_account_input' in fields: | ||
1092 | 75 | res.update( | ||
1093 | 76 | {'stock_account_input': accounts['stock_account_input']}) | ||
1094 | 77 | if 'stock_account_output' in fields: | ||
1095 | 78 | res.update( | ||
1096 | 79 | {'stock_account_output': accounts['stock_account_output']}) | ||
1097 | 80 | if 'stock_journal' in fields: | ||
1098 | 81 | res.update({'stock_journal': accounts['stock_journal']}) | ||
1099 | 82 | if 'enable_stock_in_out_acc' in fields: | ||
1100 | 83 | res.update({'enable_stock_in_out_acc': True}) | ||
1101 | 84 | |||
1102 | 85 | return res | ||
1103 | 86 | |||
1104 | 87 | def change_price(self, cr, uid, ids, context=None): | ||
1105 | 88 | """ Changes the Standard Price of Product. | ||
1106 | 89 | And creates an account move accordingly. | ||
1107 | 90 | @param self: The object pointer. | ||
1108 | 91 | @param cr: A database cursor | ||
1109 | 92 | @param uid: ID of the user currently logged in | ||
1110 | 93 | @param ids: List of IDs selected | ||
1111 | 94 | @param context: A standard dictionary | ||
1112 | 95 | @return: | ||
1113 | 96 | """ | ||
1114 | 97 | if context is None: | ||
1115 | 98 | context = {} | ||
1116 | 99 | rec_id = context and context.get('active_id', False) | ||
1117 | 100 | assert rec_id, _('Active ID is not set in Context.') | ||
1118 | 101 | lot_pool = self.pool.get('stock.production.lot') | ||
1119 | 102 | res = self.browse(cr, uid, ids, context=context) | ||
1120 | 103 | datas = { | ||
1121 | 104 | 'new_price': res[0].new_price, | ||
1122 | 105 | 'stock_output_account': res[0].stock_account_output.id, | ||
1123 | 106 | 'stock_input_account': res[0].stock_account_input.id, | ||
1124 | 107 | 'stock_journal': res[0].stock_journal.id | ||
1125 | 108 | } | ||
1126 | 109 | lot_pool.do_change_standard_price(cr, uid, [rec_id], datas, context) | ||
1127 | 110 | return {'type': 'ir.actions.act_window_close'} | ||
1128 | 0 | 111 | ||
1129 | === added file 'stock_lot_valuation/wizard/stock_change_standard_price_view.xml' | |||
1130 | --- stock_lot_valuation/wizard/stock_change_standard_price_view.xml 1970-01-01 00:00:00 +0000 | |||
1131 | +++ stock_lot_valuation/wizard/stock_change_standard_price_view.xml 2013-10-18 16:18:27 +0000 | |||
1132 | @@ -0,0 +1,34 @@ | |||
1133 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
1134 | 2 | <openerp> | ||
1135 | 3 | <data> | ||
1136 | 4 | <record id="view_change_standard_price" model="ir.ui.view"> | ||
1137 | 5 | <field name="name">Change Standard Price</field> | ||
1138 | 6 | <field name="model">lot.change.standard.price</field> | ||
1139 | 7 | <field name="arch" type="xml"> | ||
1140 | 8 | <form string="Change Standard Price" version="7.0"> | ||
1141 | 9 | <separator string="Change Price"/> | ||
1142 | 10 | <group> | ||
1143 | 11 | <field name="new_price" string="Cost Price"/> | ||
1144 | 12 | </group> | ||
1145 | 13 | <footer> | ||
1146 | 14 | <button name="change_price" string="_Apply" type="object" class="oe_highlight"/> | ||
1147 | 15 | or | ||
1148 | 16 | <button string="Cancel" class="oe_link" special="cancel" /> | ||
1149 | 17 | </footer> | ||
1150 | 18 | </form> | ||
1151 | 19 | </field> | ||
1152 | 20 | </record> | ||
1153 | 21 | |||
1154 | 22 | <record id="action_view_change_standard_price" model="ir.actions.act_window"> | ||
1155 | 23 | <field name="name">Change Standard Price</field> | ||
1156 | 24 | <field name="type">ir.actions.act_window</field> | ||
1157 | 25 | <field name="res_model">lot.change.standard.price</field> | ||
1158 | 26 | <field name="view_type">form</field> | ||
1159 | 27 | <field name="view_mode">form</field> | ||
1160 | 28 | <field name="view_id" ref="view_change_standard_price"/> | ||
1161 | 29 | <field name="target">new</field> | ||
1162 | 30 | </record> | ||
1163 | 31 | |||
1164 | 32 | </data> | ||
1165 | 33 | </openerp> | ||
1166 | 34 |
I think this is a first step to start getting FIFO cost calculation on
product
Eric CAUDAL
Eric Caudal www.elico- corp.com
/CEO/
--
*Elico Corporation, Shanghai branch
/OpenERP Premium Certified Training Partner/ *
Cell: + 86 186 2136 1670
Office: + 86 21 6211 8017/27/37
Skype: elico.corp
<email address hidden> <mailto:<email address hidden>>
http://
Elico Corp logistic- core-editors) /code.launchpad .net/~elbati/ stock-logistic- warehouse/ adding_ stock_lot_ costing/ +merge/ 164766
On 05/21/2013 12:35 AM, Lorenzo Battistini - Agile BG wrote:
> Lorenzo Battistini - Agile BG has proposed merging lp:~elbati/stock-logistic-warehouse/adding_stock_lot_costing into lp:stock-logistic-warehouse.
>
> Requested reviews:
> Stock and Logistic Core Editors (stock-
>
> For more details, see:
> https:/
>
> Stock valuation (standard or average price, ...) based on lots.
> This module extends standard stock valuation (based on products). Valuing lots allows to have different costs for different lots of the same product.
>
> Usage
> -----
> Set the 'Lot valuation' flag on product form (used for real time valuation).
> As for products, lots have 'cost' and 'costing method' fields. Also, a 'Change Standard Price' wizard is available